@mnci/cli 1.19.8 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -30,7 +30,6 @@ first-party (or established community) Nx equivalent:
30
30
  mnci new my-repo # create a monorepo (prompts scope + registry)
31
31
  mnci new my-repo --yes --registry npm --scope @my
32
32
  mnci new my-repo --yes --registry npm --scope @my --nx-cloud # opt in to Nx Cloud
33
- mnci new my-repo --yes --registry npm --scope @my --linter oxlint # Rust toolchain
34
33
 
35
34
  cd my-repo
36
35
  mnci add react-app web # @nx/react (Vite + Jest)
@@ -79,14 +78,12 @@ ever needed is noise that trains people to ignore the output.
79
78
  | Check | The failure it catches |
80
79
  | ------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
81
80
  | Exactly one root ESLint config, and no per-project ones | The config fragmenting — every `@nx/*` generator writes one, so each project ends up linting against whichever config sits nearest |
82
- | No stray `.prettierrc` | It outranks `.prettierrc.mjs`, so the whole formatting opinion is silently discarded while both files look fine |
83
81
  | `@nx/eslint/plugin` registered in `nx.json` | Without it `npm run lint` exits 0 while linting nothing |
84
82
  | The **resolved** `eslint` major | A declared range and an installed version are different things — manifests once said `^10` while the pin said 9 |
85
83
  | `.npmrc` matches the recorded registry | The two registry kinds get different files; an Azure workspace also needs its scope routed |
86
84
  | `versionActions` on publishable Dart/Python packages | Its absence aborts `nx release` for the **whole** workspace, not just that project |
87
85
  | `nx sync:check` | A stale TypeScript project reference that was never committed |
88
- | Only the chosen linter's config files present | Two formatter configs is not a visible error — each is valid, the CLI picks one, the editor may pick the other, and the two gates disagree silently |
89
- | oxlint toolchain declared | `@mnci/oxlint-config` peers on `oxlint`, so a missing declaration turns `npm run lint` into "command not found" at the worst moment |
86
+ | No retired formatter is still configured | A leftover `.prettierrc*`, `.oxfmtrc.json` or `oxlint.config.ts`, or a `prettier`/`oxlint`/`oxfmt` devDependency, runs from no command line — which is what makes it dangerous, since an editor extension still resolves it and reformats on save, undoing Standard after every gate has passed |
90
87
 
91
88
  Everything else is plain Nx, surfaced as a small curated set of root scripts —
92
89
  each a single cross-platform command:
@@ -157,15 +154,14 @@ that would just fail felt worse than being upfront that it doesn't exist yet.
157
154
  nothing at all while reporting green.
158
155
  3. Writes `eslint.config.mjs` (one import from `@mnci/eslint-config` — the whole
159
156
  linting opinion, in one root config — plus a commented inventory naming every
160
- config block and how to override it), `.prettierrc.mjs` (which re-exports
161
- `@mnci/eslint-config/prettier`, so lint and format ship as one package and
162
- upgrade together) + `.prettierignore`, `.npmrc` (publish auth — see **Publish auth** below),
157
+ config block and how to override it — there is no formatter config, because
158
+ ESLint is the formatter), `.npmrc` (publish auth — see **Publish auth** below),
163
159
  `commitlint.config.mjs`, a husky `commit-msg` hook, the chosen CI provider's
164
160
  pipeline file(s)
165
161
  (`azure-pipelines.yml` and/or `.github/workflows/ci.yml`, `--ci`, default
166
162
  `azure`; `github`/`both` also gets `.github/dependabot.yml` — weekly
167
163
  dependency-update PRs), a `<workspace-name>.code-workspace` file (VS Code
168
- workspace configuration with folder structure, ESLint/Prettier settings,
164
+ workspace configuration with folder structure, ESLint settings,
169
165
  recommended extensions, and an empty `tasks` array that `mnci add` fills in
170
166
  per project — see below — open it in VS Code via `File > Open Workspace
171
167
  from File`), and the curated root scripts.
@@ -182,12 +178,12 @@ same options `new` would have and calls the exact same `applyOverlay` `new`
182
178
  itself calls — the one function that does every bit of `mnci`-owned file
183
179
  writing (`nx.json`'s `release`/`sync`/`generators`/`namedInputs.sharedGlobals`/
184
180
  `mnci` blocks, `.npmrc`,
185
- `eslint.config.mjs`, `.prettierrc.mjs`, `commitlint.config.mjs`,
181
+ `eslint.config.mjs`, `commitlint.config.mjs`,
186
182
  `.husky/commit-msg`, the CI pipeline file(s), `.devcontainer/devcontainer.json`, the
187
183
  `<workspace-name>.code-workspace` file, and the curated root `package.json`
188
184
  scripts). Nothing else in the workspace — app/lib source, `project.json` targets
189
- from `mnci add` — is ever touched, and it finishes by running Prettier over the
190
- result, the same way `new` and every `add` do.
185
+ from `mnci add` — is ever touched, and it finishes by running `eslint --fix`
186
+ over the result, the same way `new` and every `add` do.
191
187
 
192
188
  The `.code-workspace` file is the one partial case, and deliberately so: its
193
189
  folders, settings and extensions are regenerated, but the **`tasks` array is read
@@ -199,10 +195,14 @@ project's build/qa/start entry on upgrade.
199
195
  has always done — one more reason to run `git diff` first, as the command's own
200
196
  output tells you to:
201
197
 
202
- - `create-nx-workspace`'s `.prettierrc` and `.vscode/`, plus mnci's own former
203
- `.prettierrc.json`. The two Prettier files are deleted for the same reason:
204
- both outrank `.prettierrc.mjs`, which Prettier would otherwise never reach.
205
- `.vscode/` is superseded by the `.code-workspace` file.
198
+ - `create-nx-workspace`'s `.prettierrc` and `.vscode/`, plus every formatter
199
+ config a past mnci version wrote — `.prettierrc.json`, `.prettierrc.mjs`,
200
+ `.prettierignore`, `.oxfmtrc.json`, `oxlint.config.ts`. None of them runs from
201
+ a command line any more, and that is exactly what makes them dangerous: a
202
+ globally installed `esbenp.prettier-vscode` or `oxc.oxc-vscode` still resolves
203
+ one and still reformats on save, undoing Standard while `lint` stays green
204
+ because the damage lands after the check. `.vscode/` is superseded by the
205
+ `.code-workspace` file.
206
206
  - **every per-project `eslint.config.*` under `apps/`, `libs/` and
207
207
  `packages/`.** This is the migration path for a workspace generated before
208
208
  mnci owned linting: without it an upgrade would install the root config while
@@ -255,100 +255,35 @@ a `lint` target" and "the root config genuinely reports violations in a project
255
255
  with no config of its own", because a future Nx change there would silently
256
256
  switch linting off workspace-wide.)
257
257
 
258
- ESLint handles code quality only; **Prettier owns all formatting**, configured
259
- for JavaScript Standard Style (no semicolons, single quotes, 2-space indents, no
260
- trailing commas). Both opinions come from the same package: `.prettierrc.mjs`
261
- re-exports `@mnci/eslint-config/prettier`, so a formatting fix arrives through
262
- `npm update` exactly as a rule fix does, and the two cannot drift into
263
- contradicting each other. `mnci` runs Prettier itself at the end of `new` and every
264
- `add`, so a generated workspace passes its own `format:check` immediately — Nx's
265
- generators emit semicolons and double quotes, and without that pass the first
266
- commit buries every real change under generator noise.
267
-
268
- **Choosing the linter.** `--linter=eslint` (default) or `--linter=oxlint`, also a
269
- prompt at `mnci new` and overridable on `mnci upgrade`. The formatter rides along
270
- — ESLint pairs with Prettier, oxlint with oxfmt — because both are the same seven
271
- Standard options, so the choice is about speed and toolchain, never about how the
272
- code ends up looking.
273
-
274
- **`oxlint` is a hybrid, not a swap**, and that is deliberate. oxlint parses
275
- JS/TS/JSX/Vue and nothing else, so an oxlint workspace keeps a trimmed ESLint
276
- config (`@mnci/eslint-config`'s `nonJs()`) for the file types oxlint cannot read:
277
-
278
- | | `eslint` | `oxlint` |
279
- | ------------------------------- | -------------------------------------- | -------------------------------------------------------- |
280
- | JS/TS lint | ESLint, 452 rules | oxlint, 206 native + 225 bridged |
281
- | YAML/TOML/MD/CSS/HTML/JSON lint | ESLint | **ESLint** (unchanged) |
282
- | `@nx/dependency-checks` | yes | **yes** (unchanged) |
283
- | Formatter | Prettier | oxfmt (~6x faster on this repo) |
284
- | Formats `.toml` | no — Prettier has no TOML parser | **yes** |
285
- | Config files | `eslint.config.mjs`, `.prettierrc.mjs` | `oxlint.config.ts`, `.oxfmtrc.json`, `eslint.config.mjs` |
286
- | VS Code extension | ESLint + Prettier | Oxc (covers both) + ESLint |
287
-
288
- A pure swap was considered and rejected: it would leave a duplicate key in a CI
289
- pipeline, a malformed `pyproject.toml`, and a publishable package's wrong manifest
290
- all reported by nothing. Switching modes with `mnci upgrade --linter=<other>`
291
- rewrites the config files **and deletes the mode you left**, so a workspace never
292
- carries two formatter configs.
293
-
294
- **Exactly one formatter is declared, and that is a fix rather than tidiness.**
295
- `prettier` under `eslint`, `oxfmt` under `oxlint`, and switching modes drops the
296
- other from `devDependencies`. prettier stays in `node_modules` either way, since
297
- `@mnci/eslint-config` depends on it — so what matters is the _declaration_:
298
- `esbenp.prettier-vscode` resolves the formatter from the **project's**
299
- dependencies, so an oxlint workspace that still declared prettier is one where a
300
- globally installed Prettier extension reformats on save against the opinion oxfmt
301
- is not applying, while `npm run format:check` (oxfmt) reports the result as
302
- unformatted. Both configs valid, both binaries resolvable, neither one wrong on
303
- its own — the `.prettierrc` precedence bug in another costume. `mnci doctor`
304
- checks it.
305
-
306
- **oxfmt formats `.toml`; Prettier cannot.** `prettier` on a `.toml` exits with
307
- `No parser could be inferred for file`, so an oxlint workspace gets a formatted
308
- `pyproject.toml` and the default cannot — the one gap
309
- `@mnci/eslint-config`'s parser-only TOML block has always documented as
310
- unenforceable. It is the only file type where the two formatters differ in what
311
- they can parse, which is why `[toml]` is pinned for format-on-save under `oxlint`
312
- only: pinning it under `eslint` would route the file to a formatter that errors
313
- on it.
314
-
315
- **Finding and overriding a rule.** One root config importing a package is easy to
316
- own and hard to read: nothing in three lines says which of ~20 plugins reported
317
- the thing you disagree with. Every block in `@mnci/eslint-config` therefore
318
- carries a `name` — `mnci/base`, `mnci/react`, `mnci/type-aware`,
319
- `mnci/prettier-compat` — and the generated `eslint.config.mjs` ships the full list
320
- as a comment, next to the recipe for overriding one. `npx eslint --inspect-config`
321
- lists them as ESLint actually resolves them, `npx eslint --print-config <file>`
322
- shows the merged result for a single file, and an override is a named block
323
- appended after the spread, since later blocks win. Don't edit the package inside
324
- `node_modules`: it is a dependency, and `npm update` will replace it.
325
- `packages/eslint-config/README.md` has the block-by-block table.
326
-
327
- One caveat worth knowing: `space-before-function-paren`, Standard's signature
328
- rule, is **not** enforced. Prettier rewrites `function f (a)` to `function f(a)`
329
- on every run and has closed the corresponding option permanently, so enabling it
330
- would make `npm run lint` and `npm run format:check` mutually unsatisfiable.
331
- Choosing Prettier means accepting its call there.
332
-
333
- `npm run lint` checks code quality; `npm run format` (write) and
334
- `npm run format:check` (CI-safe) handle formatting. **Both run in CI**, as
335
- separate steps: ESLint is configured for correctness only and deliberately
336
- reports nothing about formatting, so Prettier needs a gate of its own or the
337
- whole formatting opinion is advisory and a workspace drifts out of compliance
338
- with no signal anywhere.
339
-
340
- TypeScript is not a question — every workspace runs the **dual compiler** from
341
- [Nx's TS 7 guide](https://nx.dev/docs/technologies/typescript/guides/typescript-7):
342
- `typescript` resolves to a TS 6 package (keeping the programmatic API that Nx's
343
- graph/plugins, Vite, typescript-eslint and the editor need) while
344
- `@typescript/native` provides TS 7's native `tsc`. The inferred
345
- `typecheck`/`build` tasks then run on the **fast TS 7 compiler**, and Nx keeps
346
- analysing config through the TS 6 API — no target rewiring, frozen per repo by
347
- the lockfile. (A plain `typescript@7` install would break Nx, since TS 7 ships
348
- no programmatic API yet; the two aliases are what make it work.)
349
-
350
- - **Test runner**: passed straight to the `@nx/*` generators; the function app
351
- gets a matching `jest.config.mjs` (+ ts-jest) or `vitest.config.ts`.
258
+ `mnci` runs `eslint --fix` itself at the end of `new` and every `add`, so a
259
+ generated workspace passes its own `lint` immediately — Nx's generators emit
260
+ semicolons and double quotes, and without that pass the first commit buries every
261
+ real change under generator noise.
262
+
263
+ **One linter, which is also the formatter.** There is no `--linter` flag and no
264
+ choice to make: `@mnci/eslint-config` carries code quality, type-aware rules and
265
+ JavaScript Standard Style formatting in a single ESLint config at the root.
266
+ `npm run format` is `eslint . --fix --cache`; there is no `format:check`,
267
+ because `lint` already reports formatting as ordinary errors with a rule name,
268
+ a line and a column.
269
+
270
+ That is the point of the arrangement rather than a side effect. A formatter and
271
+ a linter that both hold style opinions have to be kept in agreement, and the
272
+ older setup dodged that only by having ESLint hold *no* style opinion at all —
273
+ which is exactly why a formatting mistake produced no squiggle and no message in
274
+ the editor. It also makes `space-before-function-paren` enforceable for the
275
+ first time: every Prettier-compatible formatter, oxfmt included, rewrites
276
+ `function f (a)` back to `function f(a)`.
277
+
278
+ **Upgrading an older workspace.** `mnci upgrade` deletes every config a previous
279
+ version could have written for a second tool — `.prettierrc`, `.prettierrc.json`,
280
+ `.prettierrc.mjs`, `.prettierignore`, `.oxfmtrc.json`, `oxlint.config.ts` — and
281
+ drops `prettier`, `oxlint`, `oxfmt` and `@mnci/oxlint-config` from
282
+ `devDependencies`. Both halves matter: the files are inert from the command line,
283
+ but an editor extension still resolves them, and the VS Code extension resolves a
284
+ formatter from the **project's** dependencies, so a stale declaration is enough
285
+ to reformat on save against an opinion nothing checks. `mnci doctor` reports a
286
+ workspace that has not been upgraded yet.
352
287
 
353
288
  ## Layout convention = release scoping
354
289
 
@@ -473,8 +408,8 @@ vulnerable transitive package) is exactly what this monorepo's own
473
408
  attempts automatically.
474
409
 
475
410
  Then `nx sync:check` (fails fast and clearly if the workspace wasn't
476
- synced+committed locally — see above), then `npm run format:check`, then **one
477
- verify step** running `lint,typecheck,test,build`. `typecheck` is in that list
411
+ synced+committed locally — see above), then **one verify step** running
412
+ `lint,typecheck,test,build`. `typecheck` is in that list
478
413
  because a bundler-built project strips types without reading them, so `build`
479
414
  passing proves nothing about type correctness.
480
415
 
@@ -483,9 +418,10 @@ project on anything else — including a push to `main`, so a release is always
483
418
  verified in full. There is deliberately no separate `npm run lint` step: that is
484
419
  `nx run-many -t lint`, a strict subset of the list above, and on an
485
420
  affected-scoped PR it would re-lint every project and throw the benefit away.
486
- `format:check` does stay workspace-wide, because `prettier --check .` is one
487
- invocation over the whole tree rather than a per-project target, so there is
488
- nothing to narrow.
421
+
422
+ There is no `format:check` step either, and its removal is not a saving but a
423
+ consequence: `lint` reports formatting itself now, so a second step would run
424
+ the same binary twice over the same tree.
489
425
 
490
426
  Every fallback in that step verifies **everything**: no PR target branch, an
491
427
  unresolvable merge-base (shallow clone, absent remote branch), any non-PR run.