Skip to content

How it works ​

The big picture ​

                       your eslint.config.mjs
                                │
                        mocg({ flags })          ← umbrella factory
                                │
        ┌───────────────────────┼───────────────────────┐
        │                       │                        │
   Node base            Framework layer            Add-on layer
 createNodeConfig   react / next / vue / nest   vite / vitest / jest
   (always)          (optional peers)           zod / i18n / tailwind
        │                       │                        │
        └───────────────────────┴────────────────────────┘
                                │
                    a single flat-config array

mocg() is a thin orchestrator. It decides which layers to include, then composes them in the right order. All the actual rules live in small, focused modules under the package.

Composition order matters ​

ESLint flat config is last-write-wins: a later config block overrides an earlier one for any file it matches. mocg() owns that order so you don't have to:

  1. Node base (createNodeConfig) — JavaScript + TypeScript rules, with per-file overrides placed last internally.
  2. Framework layer — React, Next.js, Vue, or (as a base replacement) NestJS, layered on top of the Node base. Next.js is React + Next rules, so it supersedes the React stack: the React layer is composed inside the Next stack and applied exactly once, never twice.
  3. Add-ons — Vite Fast Refresh, Vitest/Jest test rules, Zod, i18n, Tailwind.

Because the umbrella controls this, your config file stays a single mocg() call and still gets correct precedence.

Bundled core vs. optional peers ​

dependencies (bundled, version-locked)        peerDependencies (optional)
────────────────────────────────────          ───────────────────────────
typescript-eslint, unicorn, sonarjs,           eslint-plugin-react (+hooks,
perfectionist, @stylistic, import-x,             refresh, compiler)
jsdoc, n, security, no-secrets, promise,       eslint-plugin-vue (+ parser)
regexp, depend, prettier, …                    @darraghor/…nestjs-typed
                                               eslint-plugin-jest, i18next,
zod + vitest plugins also bundled              better-tailwindcss
  • Core plugins are dependencies. Their versions are pinned by this package, so every project runs the same, tested combination.
  • Framework plugins are optional peers. A Node-only backend never installs React or Vue plugins. The installer adds exactly the peers your stack needs.

Optional peers load lazily ​

Each framework lives behind a subpath export (eslint-config-mocg/react, /next, /vue, /nest). mocg() imports these dynamically, only when the corresponding flag is on. If a required peer is missing, you get an actionable error instead of a cryptic module-resolution failure:

[eslint-config-mocg] The "React" config requires packages that are not installed.

  Install them:   npm i -D eslint-plugin-react eslint-plugin-react-hooks …
  Or run:         npx eslint-config-mocg init

This is also why mocg() returns a Promise — ESLint supports async flat config, so export default mocg() works directly.

Auto-detection ​

mocg() reads your package.json (all dependency fields) and enables stacks/add-ons whose marker packages are present — react → React, next → Next.js, @nestjs/core → NestJS, vite → Vite Fast Refresh, vitest → Vitest rules, and so on. An explicit flag always wins:

js
mocg()                  // fully auto-detected
mocg({ react: true })   // force React on
mocg({ vue: false })    // force Vue off even if detected
mocg({ next: false })   // force Next off even if `next` is detected

Next supersedes React

A Next.js project depends on next, react, and react-dom, so both stacks would match. Detection resolves this in one place: when next is present it wins, and react is dropped from the detected stacks. The Next stack already composes the React layer, so React is applied exactly once.

Type-aware linting ​

The TypeScript layer uses projectService, so type-aware rules work without you wiring up parserOptions.project. The config auto-discovers your tsconfig.json (or a candidate like tsconfig.base.json), and you can point it explicitly:

js
mocg({ tsconfig: 'tsconfig.app.json' });

See the API reference for every option.

Internal tooling — Master of Code Global