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