@mnci/cli 1.19.8 → 2.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/dist/cli.js CHANGED
@@ -80,12 +80,11 @@ function runNpx(arguments_, cwd) {
80
80
  throw new Error(`npx ${arguments_.join(" ")} failed with exit code ${status}`);
81
81
  }
82
82
  }
83
- function runFormatter(cwd, linter, target = ".") {
84
- const [command, arguments_] = linter === "oxlint" ? ["oxfmt", ["--write", target]] : ["prettier", ["--write", "--log-level", "warn", target]];
85
- const status = runShell("npx", [command, ...arguments_], cwd);
83
+ function runFormatter(cwd, target = ".") {
84
+ const status = runShell("npx", ["eslint", target, "--fix"], cwd);
86
85
  if (status !== 0) {
87
86
  logger.warn(
88
- `${command} could not format '${target}' (exit code ${status}). The project was generated; run 'npm run format' to normalise it.`
87
+ `eslint could not format '${target}' (exit code ${status}). The project was generated; run 'npm run format' to normalise it.`
89
88
  );
90
89
  }
91
90
  }
@@ -142,20 +141,7 @@ async function promptStack() {
142
141
  { name: "Vitest", value: "vitest" }
143
142
  ]
144
143
  });
145
- const linter = await (0, import_prompts.select)({
146
- message: "Linter and formatter",
147
- choices: [
148
- {
149
- name: "ESLint + Prettier \u2014 every file type, the widest rule coverage",
150
- value: "eslint"
151
- },
152
- {
153
- name: "oxlint + oxfmt for JS/TS, ESLint for the rest \u2014 much faster, same style",
154
- value: "oxlint"
155
- }
156
- ]
157
- });
158
- return { testRunner, linter };
144
+ return { testRunner };
159
145
  }
160
146
 
161
147
  // src/util/fsx.ts
@@ -1220,7 +1206,7 @@ async function runAdd(kind, name, options) {
1220
1206
  case "python-vendor": {
1221
1207
  await addPythonVendor(workspaceRoot, resolvedName, options, kindProvided);
1222
1208
  syncProjectReferences(workspaceRoot);
1223
- runFormatter(workspaceRoot, stack.linter);
1209
+ runFormatter(workspaceRoot);
1224
1210
  return;
1225
1211
  }
1226
1212
  default: {
@@ -1231,7 +1217,7 @@ async function runAdd(kind, name, options) {
1231
1217
  }
1232
1218
  }
1233
1219
  syncProjectReferences(workspaceRoot);
1234
- runFormatter(workspaceRoot, stack.linter);
1220
+ runFormatter(workspaceRoot);
1235
1221
  logger.success(`Added ${resolvedKind} '${resolvedName}'.`);
1236
1222
  }
1237
1223
  function syncProjectReferences(workspaceRoot) {
@@ -1248,14 +1234,7 @@ function readWorkspaceStack(workspaceRoot) {
1248
1234
  );
1249
1235
  const stack = nxJson.mnci?.stack;
1250
1236
  return {
1251
- testRunner: stack?.testRunner === "vitest" ? "vitest" : "jest",
1252
- // Defaults to `eslint` when absent, the same call `mnci upgrade` makes and
1253
- // for the same reason: every workspace generated before the choice existed
1254
- // has no persisted value and is an ESLint workspace. Reading it at all is
1255
- // the fix — this function returned `testRunner` alone, so `add` finished by
1256
- // formatting an oxlint workspace with Prettier, against Prettier's defaults,
1257
- // since the overlay had deleted the config that would have corrected it.
1258
- linter: stack?.linter === "oxlint" ? "oxlint" : "eslint"
1237
+ testRunner: stack?.testRunner === "vitest" ? "vitest" : "jest"
1259
1238
  };
1260
1239
  }
1261
1240
 
@@ -1266,8 +1245,7 @@ var import_node_path12 = require("path");
1266
1245
  // src/overlay.ts
1267
1246
  var import_node_fs6 = require("fs");
1268
1247
  var import_node_path11 = require("path");
1269
- var DEFAULT_STACK = { testRunner: "jest", linter: "eslint" };
1270
- var PRETTIER_VERSION = "^3.8.1";
1248
+ var DEFAULT_STACK = { testRunner: "jest" };
1271
1249
  var TS_COMPILER_DEPENDENCIES = {
1272
1250
  "@typescript/native": "npm:typescript@^7.0.2",
1273
1251
  typescript: "npm:@typescript/typescript6@^6.0.2"
@@ -1375,32 +1353,12 @@ var COMMITLINT_CONFIG = `export default { extends: ['@commitlint/config-conventi
1375
1353
  `;
1376
1354
  var COMMIT_MSG_HOOK = `npx --no -- commitlint --edit "$1"
1377
1355
  `;
1378
- var PRETTIER_CONFIG = `// The formatting half of @mnci/eslint-config.
1379
- //
1380
- // Prettier's options live in the same package as the ESLint rules on purpose:
1381
- // linting and formatting are one decision. \`eslint-config-prettier\` is composed
1382
- // last inside that package so every formatting rule defers to these settings, and
1383
- // splitting the two across packages means a version pair that can drift until
1384
- // \`npm run lint\` and \`npm run format:check\` disagree with each other.
1385
- //
1386
- // Consuming it as a shareable config also means a formatting fix reaches this
1387
- // workspace through \`npm update\`, the same way a rule change does, instead of
1388
- // waiting for \`mnci upgrade\` to rewrite a file.
1389
- //
1390
- // TO OVERRIDE an option, spread it rather than re-exporting:
1391
- //
1392
- // import mnci from '@mnci/eslint-config/prettier'
1393
- // export default { ...mnci, printWidth: 120 }
1394
- //
1395
- // TO SEE the resolved options for a file: npx prettier --find-config-path <file>
1396
- export { default } from '@mnci/eslint-config/prettier'
1397
- `;
1398
1356
  var ESLINT_CONFIG_VERSION = "^0.1.0";
1399
1357
  function eslintConfigSpec() {
1400
1358
  return process.env.MNCI_ESLINT_CONFIG_SPEC ?? ESLINT_CONFIG_VERSION;
1401
1359
  }
1402
1360
  var ESLINT_VERSION = "^10.8.0";
1403
- function devcontainerJson(workspaceName, linter) {
1361
+ function devcontainerJson(workspaceName) {
1404
1362
  return `${toJson({
1405
1363
  name: workspaceName,
1406
1364
  image: `mcr.microsoft.com/devcontainers/typescript-node:${NODE_VERSION}-bookworm`,
@@ -1423,7 +1381,7 @@ function devcontainerJson(workspaceName, linter) {
1423
1381
  // The same recommendations the `.code-workspace` file carries, so opening
1424
1382
  // the folder in a container suggests the identical toolset.
1425
1383
  customizations: {
1426
- vscode: { extensions: vscodeExtensions(linter) }
1384
+ vscode: { extensions: VSCODE_RECOMMENDED_EXTENSIONS }
1427
1385
  }
1428
1386
  })}
1429
1387
  `;
@@ -1433,7 +1391,13 @@ var ROOT_LINT_TARGET = {
1433
1391
  cache: true,
1434
1392
  options: {
1435
1393
  command: [
1436
- "eslint .",
1394
+ // `--cache` is the one ESLint speed option that actually pays here:
1395
+ // measured best-of-3 on this repo, an unchanged re-run drops from 9,546ms
1396
+ // to 2,835ms (3.4x). `--concurrency=auto` was measured too and REJECTED —
1397
+ // it came out 8% SLOWER, because per-worker TypeScript type-service
1398
+ // startup costs more than the parallelism saves. The cache file is
1399
+ // per-machine and gitignored.
1400
+ "eslint . --cache",
1437
1401
  '--ignore-pattern "apps/**"',
1438
1402
  '--ignore-pattern "libs/**"',
1439
1403
  '--ignore-pattern "packages/**"',
@@ -1477,38 +1441,23 @@ function eslintToolchainDependencies(nxVersion) {
1477
1441
  "@mnci/eslint-config": eslintConfigSpec()
1478
1442
  };
1479
1443
  }
1480
- var LINTER_ONLY_DEPENDENCIES = {
1481
- eslint: ["prettier"],
1482
- oxlint: ["@mnci/oxlint-config", "oxlint", "oxfmt"]
1483
- };
1484
- function withoutStaleLinterDependencies(devDeps, linter) {
1485
- const stale = new Set(LINTER_ONLY_DEPENDENCIES[linter === "oxlint" ? "eslint" : "oxlint"]);
1486
- return Object.fromEntries(Object.entries(devDeps).filter(([name]) => !stale.has(name)));
1487
- }
1488
- function rootLintTarget(linter) {
1489
- if (linter !== "oxlint") return ROOT_LINT_TARGET;
1490
- return {
1491
- executor: "nx:run-commands",
1492
- cache: true,
1493
- options: {
1494
- commands: ["oxlint .", ROOT_LINT_TARGET.options.command],
1495
- parallel: false,
1496
- cwd: "."
1497
- }
1498
- };
1499
- }
1500
- var OXLINT_VERSION = "^1.76.0";
1501
- var OXFMT_VERSION = "^0.61.0";
1502
- function oxlintToolchainDependencies() {
1503
- return {
1504
- "@mnci/oxlint-config": oxlintConfigSpec(),
1505
- oxlint: OXLINT_VERSION,
1506
- oxfmt: OXFMT_VERSION
1507
- };
1508
- }
1509
- var OXLINT_CONFIG_VERSION = "^0.1.0";
1510
- function oxlintConfigSpec() {
1511
- return process.env.MNCI_OXLINT_CONFIG_SPEC ?? OXLINT_CONFIG_VERSION;
1444
+ var RETIRED_FORMATTER_FILES = [
1445
+ ".prettierrc",
1446
+ ".prettierrc.json",
1447
+ ".prettierrc.mjs",
1448
+ ".prettierignore",
1449
+ "oxlint.config.ts",
1450
+ ".oxfmtrc.json"
1451
+ ];
1452
+ function withoutRetiredFormatterDependencies(devDeps) {
1453
+ const retired = /* @__PURE__ */ new Set([
1454
+ "prettier",
1455
+ "eslint-config-prettier",
1456
+ "oxlint",
1457
+ "oxfmt",
1458
+ "@mnci/oxlint-config"
1459
+ ]);
1460
+ return Object.fromEntries(Object.entries(devDeps).filter(([name]) => !retired.has(name)));
1512
1461
  }
1513
1462
  var ESLINT_BLOCK_INVENTORY = `// WHAT IS IN HERE. Each line is one config block, by the \`name\` it carries.
1514
1463
  //
@@ -1535,9 +1484,9 @@ var ESLINT_BLOCK_INVENTORY = `// WHAT IS IN HERE. Each line is one config block,
1535
1484
  // mnci/tests *.spec/*.test relaxations \u2014 eslint-plugin-jest
1536
1485
  // (Vitest's globals too; the two stacks share them)
1537
1486
  // mnci/nx-dependency-checks @nx/eslint-plugin, on publishable packages' manifests
1538
- // mnci/prettier-compat eslint-config-prettier \u2014 switches off every rule
1539
- // Prettier owns. Composed LAST, on purpose.
1540
- // mnci/stylistic the 3 Standard rules Prettier does not touch
1487
+ // mnci/standard JavaScript Standard Style as ESLint rules \u2014 the
1488
+ // whole formatting opinion. Composed LAST, on
1489
+ // purpose: nothing may follow that disables it.
1541
1490
  //
1542
1491
  // To list them as ESLint actually resolves them: npx eslint --inspect-config
1543
1492
  `;
@@ -1567,77 +1516,9 @@ ${ESLINT_BLOCK_INVENTORY}//
1567
1516
  // impossible to satisfy at the same time. Formatting lives in .prettierrc.mjs.
1568
1517
  export default mnci({ workspaceRoot: import.meta.dirname })
1569
1518
  `;
1570
- var ESLINT_CONFIG_NON_JS = `import { nonJs } from '@mnci/eslint-config'
1571
-
1572
- // This workspace lints JS/TS with oxlint (see oxlint.config.ts). ESLint keeps
1573
- // the file types oxlint cannot parse at all \u2014 YAML, TOML, Markdown, CSS, HTML,
1574
- // JSON \u2014 plus @nx/dependency-checks on publishable manifests.
1575
- //
1576
- // The JS/TS rule blocks are deliberately NOT composed here. Running both linters
1577
- // over the same file would report one defect twice under two different names.
1578
- //
1579
- // TO OVERRIDE, append a named block after the spread, exactly as the full config
1580
- // documents. To change a JS/TS rule, edit oxlint.config.ts instead \u2014 this file
1581
- // does not lint JS/TS.
1582
- export default [...nonJs({ workspaceRoot: import.meta.dirname })]
1583
- `;
1584
- var OXLINT_CONFIG = `import { defineConfig } from 'oxlint'
1585
- import mnci from '@mnci/oxlint-config'
1586
-
1587
- // oxlint owns JS/TS/JSX here; eslint.config.mjs keeps everything oxlint cannot
1588
- // parse. The contract @mnci/oxlint-config holds itself to: anything
1589
- // @mnci/eslint-config accepts must pass oxlint, so switching linters never turns
1590
- // a green codebase red.
1591
- //
1592
- // oxlint.config.ts, not .oxlintrc.json, and that is not a preference: .oxlintrc's
1593
- // \`extends\` takes PATHS relative to the config file, so
1594
- // \`extends: ["@mnci/oxlint-config"]\` fails with "No such file or directory".
1595
- // defineConfig is the only shareable-config route oxlint offers.
1596
- //
1597
- // Type-aware rules (no-floating-promises and friends) are opt-in \u2014 pass
1598
- // { typeAware: true } AND run oxlint with --type-aware. They report findings the
1599
- // ESLint stack accepts, so they are off by default to keep the contract above.
1600
- export default defineConfig({ extends: [mnci()] })
1601
- `;
1602
- var OXFMT_CONFIG = `${JSON.stringify(
1603
- {
1604
- $schema: "./node_modules/oxfmt/configuration_schema.json",
1605
- semi: false,
1606
- singleQuote: true,
1607
- trailingComma: "none",
1608
- arrowParens: "avoid",
1609
- printWidth: 100,
1610
- tabWidth: 2,
1611
- useTabs: false
1612
- },
1613
- null,
1614
- 2
1615
- )}
1616
- `;
1617
- var PRETTIER_IGNORE = `node_modules
1618
- dist
1619
- dist-dev
1620
- dist-uat
1621
- dist-prod
1622
- coverage
1623
- .next
1624
- out
1625
- .nx
1626
- tmp
1627
- package-lock.json
1628
- *.lock
1629
- .venv
1630
- venv
1631
- __pycache__
1632
- .pytest_cache
1633
- .ruff_cache
1634
- .dart_tool
1635
- `;
1636
- var LINTER_EXTENSIONS = {
1637
- eslint: ["dbaeumer.vscode-eslint", "esbenp.prettier-vscode"],
1638
- oxlint: ["oxc.oxc-vscode", "dbaeumer.vscode-eslint"]
1639
- };
1640
- var SHARED_VSCODE_EXTENSIONS = [
1519
+ var VSCODE_RECOMMENDED_EXTENSIONS = [
1520
+ "dbaeumer.vscode-eslint",
1521
+ "esbenp.prettier-vscode",
1641
1522
  "nrwl.angular-console",
1642
1523
  "firsttris.vscode-jest-runner"
1643
1524
  ];
@@ -1653,25 +1534,9 @@ var FORMATTED_LANGUAGES = [
1653
1534
  "css",
1654
1535
  "html"
1655
1536
  ];
1656
- var OXFMT_ONLY_LANGUAGES = ["toml"];
1657
- function formattedLanguages(linter) {
1658
- return [...FORMATTED_LANGUAGES, ...linter === "oxlint" ? OXFMT_ONLY_LANGUAGES : []];
1659
- }
1660
- function vscodeExtensions(linter) {
1661
- return [...LINTER_EXTENSIONS[linter], ...SHARED_VSCODE_EXTENSIONS];
1662
- }
1663
- function vscodeSettings(linter) {
1664
- const formatter = linter === "oxlint" ? "oxc.oxc-vscode" : "esbenp.prettier-vscode";
1665
- const eslintLanguages = linter === "oxlint" ? ["json", "jsonc", "markdown", "yaml", "html", "css"] : [
1666
- "javascript",
1667
- "javascriptreact",
1668
- "typescript",
1669
- "typescriptreact",
1670
- "json",
1671
- "jsonc",
1672
- "markdown",
1673
- "yaml"
1674
- ];
1537
+ function vscodeSettings() {
1538
+ const formatter = "dbaeumer.vscode-eslint";
1539
+ const eslintLanguages = [...FORMATTED_LANGUAGES];
1675
1540
  return {
1676
1541
  "eslint.validate": eslintLanguages,
1677
1542
  "editor.codeActionsOnSave": {
@@ -1694,19 +1559,19 @@ function vscodeSettings(linter) {
1694
1559
  // while `.json`/`.jsonc`/`.yaml` were — exactly the three that had explicit
1695
1560
  // entries here and nothing else did.
1696
1561
  ...Object.fromEntries(
1697
- formattedLanguages(linter).map((language) => [
1562
+ FORMATTED_LANGUAGES.map((language) => [
1698
1563
  `[${language}]`,
1699
1564
  { "editor.defaultFormatter": formatter }
1700
1565
  ])
1701
1566
  )
1702
1567
  };
1703
1568
  }
1704
- function vscodeWorkspace(workspaceName, linter, existingTasks) {
1569
+ function vscodeWorkspace(workspaceName, existingTasks) {
1705
1570
  return JSON.stringify(
1706
1571
  {
1707
1572
  folders: [{ path: ".", name: workspaceName }],
1708
- settings: vscodeSettings(linter),
1709
- extensions: { recommendations: vscodeExtensions(linter) },
1573
+ settings: vscodeSettings(),
1574
+ extensions: { recommendations: VSCODE_RECOMMENDED_EXTENSIONS },
1710
1575
  tasks: {
1711
1576
  version: existingTasks?.version ?? "2.0.0",
1712
1577
  tasks: existingTasks?.tasks ?? []
@@ -1726,12 +1591,14 @@ var ROOT_SCRIPTS = {
1726
1591
  "release:preview": "nx release --dry-run",
1727
1592
  prepare: "husky"
1728
1593
  };
1729
- function rootScripts(linter = "eslint") {
1730
- const formatter = linter === "oxlint" ? "oxfmt" : "prettier";
1594
+ function rootScripts() {
1731
1595
  return {
1732
1596
  ...ROOT_SCRIPTS,
1733
- format: linter === "oxlint" ? "oxfmt ." : "prettier --write .",
1734
- "format:check": `${formatter} --check .`,
1597
+ // ESLint is the formatter, so `format` is `--fix` and there is no separate
1598
+ // `format:check`: `lint` already reports formatting as ordinary errors.
1599
+ // Carrying a second script that ran the same tool twice would just make a
1600
+ // CI run slower for no extra coverage.
1601
+ format: "eslint . --fix --cache",
1735
1602
  "python:install": `${PYTHON_INSTALL_GUARD} && ${PYTHON_WORKSPACE_INSTALL_GUARD}`
1736
1603
  };
1737
1604
  }
@@ -1765,7 +1632,7 @@ function mnciConfig(options) {
1765
1632
  agent: options.agent,
1766
1633
  variableGroup: options.variableGroup,
1767
1634
  ci: options.ci,
1768
- stack: { testRunner: options.stack.testRunner, linter: options.stack.linter }
1635
+ stack: { testRunner: options.stack.testRunner }
1769
1636
  };
1770
1637
  }
1771
1638
  function readMnciConfig(workspaceRoot) {
@@ -1783,7 +1650,7 @@ var PYTHON_WORKSPACE_INSTALL_GUARD = `node -e "const fs=require('node:fs'),path=
1783
1650
  var PIP_AUDIT_GUARD = `node -e "if(!require('node:fs').existsSync('requirements-dev.txt')){console.log('No Python projects - skipping.');process.exit(0)}const py=process.platform==='win32'?'python':'python3';require('node:child_process').spawnSync(py,['-m','pip_audit'],{stdio:'inherit'});process.exit(0)"`;
1784
1651
  var GO_MODULE_DOWNLOAD_GUARD = `node -e "if(!require('node:fs').existsSync('go.mod')){console.log('No Go projects - skipping.');process.exit(0)}process.exit(require('node:child_process').spawnSync('go',['mod','download'],{stdio:'inherit'}).status ?? 1)"`;
1785
1652
  var GOLANGCI_LINT_INSTALL_GUARD = `node -e "const fs=require('node:fs'),cp=require('node:child_process');if(!fs.existsSync('go.mod')){console.log('No Go projects - skipping.');process.exit(0)}if(cp.spawnSync('golangci-lint',['--version'],{stdio:'ignore'}).status===0){console.log('golangci-lint already installed - skipping.');process.exit(0)}process.exit(cp.spawnSync('go',['install','github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest'],{stdio:'inherit'}).status ?? 1)"`;
1786
- var GO_TOOL_PATH_PRELUDE = `const fs=require('node:fs'),cp=require('node:child_process');if(!fs.existsSync('go.mod')){console.log('No Go projects - skipping.');process.exit(0)}const r=cp.spawnSync('go',['env','GOPATH'],{encoding:'utf8'});if(r.status!==0){console.log('Could not resolve GOPATH - skipping.');process.exit(0)}const bin=require('node:path').join(r.stdout.trim(),'bin');`;
1653
+ var GO_TOOL_PATH_PRELUDE = "const fs=require('node:fs'),cp=require('node:child_process');if(!fs.existsSync('go.mod')){console.log('No Go projects - skipping.');process.exit(0)}const r=cp.spawnSync('go',['env','GOPATH'],{encoding:'utf8'});if(r.status!==0){console.log('Could not resolve GOPATH - skipping.');process.exit(0)}const bin=require('node:path').join(r.stdout.trim(),'bin');";
1787
1654
  var GO_TOOL_PATH_AZURE = `node -e "${GO_TOOL_PATH_PRELUDE}console.log('##vso[task.prependpath]'+bin)"`;
1788
1655
  var GO_TOOL_PATH_GITHUB = `node -e "${GO_TOOL_PATH_PRELUDE}if(!process.env.GITHUB_PATH){console.log('Not running in GitHub Actions - skipping.');process.exit(0)}fs.appendFileSync(process.env.GITHUB_PATH,bin+${String.raw`'\n'`})"`;
1789
1656
  var FLUTTER_SDK_VERSION = "3.44.8";
@@ -1812,7 +1679,7 @@ function poolBlock(agent) {
1812
1679
  return /^(?:ubuntu|windows|macos)-/i.test(agent) ? ` vmImage: ${agent}` : ` name: ${agent}`;
1813
1680
  }
1814
1681
  function azurePipelinesYaml(agent, variableGroup, pythonPublishUrl2, registryKind = "azure-artifacts") {
1815
- const onMain = `and(succeeded(), ne(variables['Build.Reason'], 'PullRequest'), eq(variables['Build.SourceBranchName'], 'main'))`;
1682
+ const onMain = "and(succeeded(), ne(variables['Build.Reason'], 'PullRequest'), eq(variables['Build.SourceBranchName'], 'main'))";
1816
1683
  const [npmAuthName, npmAuthValue] = npmAuthEnvVariable(registryKind, (name) => `$(${name})`);
1817
1684
  return `name: monorepo-ci-$(Date:yyyyMMdd)$(Rev:.r)
1818
1685
 
@@ -1966,17 +1833,6 @@ steps:
1966
1833
  - script: npx nx sync:check
1967
1834
  displayName: Verify the workspace is synced (run 'npx nx sync' locally and commit if this fails)
1968
1835
 
1969
- # Prettier owns ALL formatting in this workspace, so it needs a gate of its
1970
- # own: ESLint is configured for correctness only and deliberately reports
1971
- # nothing about formatting. Without this step the entire formatting opinion is
1972
- # advisory, and a workspace drifts out of compliance with no signal anywhere.
1973
- #
1974
- # Stays workspace-wide even on a PR, unlike the verify step below: it is one
1975
- # Prettier invocation over the whole tree, not a per-project Nx target, so
1976
- # there is nothing to scope down and formatting is never partially checked.
1977
- - script: npm run format:check
1978
- displayName: Check formatting (run 'npm run format' locally to fix)
1979
-
1980
1836
  # The one verify step, and deliberately the only one: affected projects on a
1981
1837
  # pull request, EVERY project on anything else (a push to main included, so a
1982
1838
  # release is always verified in full). Every fallback \u2014 no target branch, an
@@ -2031,7 +1887,7 @@ steps:
2031
1887
  `;
2032
1888
  }
2033
1889
  function githubActionsYaml(agent, pythonPublishUrl2, registryKind = "azure-artifacts", ci = "github") {
2034
- const onMain = `github.event_name == 'push' && github.ref_name == 'main'`;
1890
+ const onMain = "github.event_name == 'push' && github.ref_name == 'main'";
2035
1891
  const [npmAuthName, npmAuthValue] = npmAuthEnvVariable(
2036
1892
  registryKind,
2037
1893
  (name) => `\${{ secrets.${name} }}`
@@ -2173,19 +2029,6 @@ jobs:
2173
2029
  - run: npx nx sync:check
2174
2030
  name: Verify the workspace is synced (run 'npx nx sync' locally and commit if this fails)
2175
2031
 
2176
- # Prettier owns ALL formatting in this workspace, so it needs a gate of
2177
- # its own: ESLint is configured for correctness only and deliberately
2178
- # reports nothing about formatting. Without this step the entire
2179
- # formatting opinion is advisory, and a workspace drifts out of
2180
- # compliance with no signal anywhere.
2181
- #
2182
- # Stays workspace-wide even on a PR, unlike the verify step below: it is
2183
- # one Prettier invocation over the whole tree, not a per-project Nx
2184
- # target, so there is nothing to scope down and formatting is never
2185
- # partially checked.
2186
- - run: npm run format:check
2187
- name: Check formatting (run 'npm run format' locally to fix)
2188
-
2189
2032
  # The one verify step, and deliberately the only one: affected projects on
2190
2033
  # a pull request, EVERY project on anything else (a push to main included,
2191
2034
  # so a release is always verified in full). Every fallback \u2014 no target
@@ -2310,23 +2153,15 @@ function applyOverlay(workspaceRoot, options) {
2310
2153
  const manifest = readJson(manifestPath);
2311
2154
  const scripts = {
2312
2155
  ...manifest.scripts,
2313
- ...rootScripts(options.stack.linter)
2156
+ ...rootScripts()
2314
2157
  };
2315
2158
  const existingDevDeps = manifest.devDependencies;
2316
- const devDeps = withoutStaleLinterDependencies(
2317
- {
2318
- ...existingDevDeps,
2319
- ...TS_COMPILER_DEPENDENCIES,
2320
- // The preset pins `nx` itself; the ESLint plugins must match it exactly.
2321
- ...eslintToolchainDependencies(existingDevDeps?.nx ?? "latest"),
2322
- // Additive for the linter, exclusive for the formatter: the hybrid keeps
2323
- // the whole ESLint toolchain for the file types oxlint cannot parse, but
2324
- // exactly one of prettier/oxfmt formats the workspace. Declaring both is
2325
- // how an editor ends up applying the one CI does not check.
2326
- ...options.stack.linter === "oxlint" ? oxlintToolchainDependencies() : { prettier: PRETTIER_VERSION }
2327
- },
2328
- options.stack.linter
2329
- );
2159
+ const devDeps = withoutRetiredFormatterDependencies({
2160
+ ...existingDevDeps,
2161
+ ...TS_COMPILER_DEPENDENCIES,
2162
+ // The preset pins `nx` itself; the ESLint plugins must match it exactly.
2163
+ ...eslintToolchainDependencies(existingDevDeps?.nx ?? "latest")
2164
+ });
2330
2165
  const overrides = {
2331
2166
  ...manifest.overrides,
2332
2167
  ...ESLINT_PEER_OVERRIDES
@@ -2337,7 +2172,7 @@ function applyOverlay(workspaceRoot, options) {
2337
2172
  includedScripts: existingNx?.includedScripts ?? [],
2338
2173
  targets: {
2339
2174
  ...existingNx?.targets,
2340
- lint: rootLintTarget(options.stack.linter)
2175
+ lint: ROOT_LINT_TARGET
2341
2176
  }
2342
2177
  };
2343
2178
  writeFileEnsured(
@@ -2356,28 +2191,20 @@ function applyOverlay(workspaceRoot, options) {
2356
2191
  const hookPath = (0, import_node_path11.join)(workspaceRoot, ".husky/commit-msg");
2357
2192
  writeFileEnsured(hookPath, COMMIT_MSG_HOOK);
2358
2193
  markExecutable(hookPath);
2359
- if (options.stack.linter === "oxlint") {
2360
- writeFileEnsured((0, import_node_path11.join)(workspaceRoot, "oxlint.config.ts"), OXLINT_CONFIG);
2361
- writeFileEnsured((0, import_node_path11.join)(workspaceRoot, ".oxfmtrc.json"), OXFMT_CONFIG);
2362
- writeFileEnsured((0, import_node_path11.join)(workspaceRoot, "eslint.config.mjs"), ESLINT_CONFIG_NON_JS);
2363
- removeIfPresent((0, import_node_path11.join)(workspaceRoot, ".prettierrc.mjs"));
2364
- } else {
2365
- writeFileEnsured((0, import_node_path11.join)(workspaceRoot, "eslint.config.mjs"), ESLINT_CONFIG);
2366
- writeFileEnsured((0, import_node_path11.join)(workspaceRoot, ".prettierrc.mjs"), PRETTIER_CONFIG);
2367
- removeIfPresent((0, import_node_path11.join)(workspaceRoot, "oxlint.config.ts"));
2368
- removeIfPresent((0, import_node_path11.join)(workspaceRoot, ".oxfmtrc.json"));
2194
+ writeFileEnsured((0, import_node_path11.join)(workspaceRoot, "eslint.config.mjs"), ESLINT_CONFIG);
2195
+ for (const retired of RETIRED_FORMATTER_FILES) {
2196
+ removeIfPresent((0, import_node_path11.join)(workspaceRoot, retired));
2369
2197
  }
2370
2198
  writeFileEnsured(
2371
2199
  (0, import_node_path11.join)(workspaceRoot, ".devcontainer/devcontainer.json"),
2372
- devcontainerJson(options.workspaceName, options.stack.linter)
2200
+ devcontainerJson(options.workspaceName)
2373
2201
  );
2374
- writeFileEnsured((0, import_node_path11.join)(workspaceRoot, ".prettierignore"), PRETTIER_IGNORE);
2375
2202
  removeNxScaffolding(workspaceRoot);
2376
2203
  const codeWorkspacePath = (0, import_node_path11.join)(workspaceRoot, `${options.workspaceName}.code-workspace`);
2377
2204
  const existing = readCodeWorkspace(codeWorkspacePath);
2378
2205
  writeFileEnsured(
2379
2206
  codeWorkspacePath,
2380
- vscodeWorkspace(options.workspaceName, options.stack.linter, existing?.tasks)
2207
+ vscodeWorkspace(options.workspaceName, existing?.tasks)
2381
2208
  );
2382
2209
  if (options.workspaceName !== "undefined") {
2383
2210
  (0, import_node_fs6.rmSync)((0, import_node_path11.join)(workspaceRoot, "undefined.code-workspace"), { force: true });
@@ -2420,65 +2247,28 @@ function checkEslintConfigs(workspaceRoot) {
2420
2247
  }
2421
2248
  ];
2422
2249
  }
2423
- function checkPrettierConfig(workspaceRoot) {
2424
- const strayExists = fileExists((0, import_node_path12.join)(workspaceRoot, ".prettierrc"));
2425
- return {
2426
- check: "Prettier config is mnci\u2019s",
2427
- ok: !strayExists,
2428
- detail: ".prettierrc exists and outranks .prettierrc.json, so mnci\u2019s config is ignored",
2429
- remedy: "delete .prettierrc (or run `mnci upgrade`, which deletes it)"
2430
- };
2431
- }
2432
- function checkLinterModeIsConsistent(workspaceRoot) {
2433
- const linter = readMnciConfig(workspaceRoot).stack?.linter ?? "eslint";
2434
- const oxlintFiles = ["oxlint.config.ts", ".oxfmtrc.json"].filter(
2435
- (file) => fileExists((0, import_node_path12.join)(workspaceRoot, file))
2436
- );
2437
- const hasPrettier = fileExists((0, import_node_path12.join)(workspaceRoot, ".prettierrc.mjs"));
2438
- const stray = linter === "oxlint" ? hasPrettier ? [".prettierrc.mjs"] : [] : oxlintFiles;
2439
- return {
2440
- check: `linter is ${linter} and only ${linter}'s config files are present`,
2441
- ok: stray.length === 0,
2442
- detail: `${stray.join(", ")} belongs to the other linter mode, so two configs are in play at once`,
2443
- remedy: "run `mnci upgrade`, which removes the mode the workspace did not choose"
2444
- };
2445
- }
2446
2250
  function declaredDevDependencies(workspaceRoot) {
2447
- const manifestPath = (0, import_node_path12.join)(workspaceRoot, "package.json");
2448
- if (!fileExists(manifestPath)) return {};
2449
- const manifest = readJson(manifestPath);
2450
- return manifest.devDependencies ?? {};
2451
- }
2452
- function checkOneFormatterDeclared(workspaceRoot) {
2453
- const linter = readMnciConfig(workspaceRoot).stack?.linter ?? "eslint";
2454
- const stale = linter === "oxlint" ? "prettier" : "oxfmt";
2455
- const declared = declaredDevDependencies(workspaceRoot)[stale] !== void 0;
2456
- return {
2457
- check: `only ${linter === "oxlint" ? "oxfmt" : "prettier"} is declared as the formatter`,
2458
- ok: !declared,
2459
- detail: `${stale} is declared but nothing runs it \u2014 the editor may still resolve and apply it, disagreeing with \`npm run format:check\``,
2460
- remedy: "run `mnci upgrade`, which drops the formatter of the mode you left"
2461
- };
2251
+ try {
2252
+ const manifest = readJson(
2253
+ (0, import_node_path12.join)(workspaceRoot, "package.json")
2254
+ );
2255
+ return manifest.devDependencies ?? {};
2256
+ } catch {
2257
+ return {};
2258
+ }
2462
2259
  }
2463
- function checkOxlintToolchainDeclared(workspaceRoot) {
2464
- const linter = readMnciConfig(workspaceRoot).stack?.linter ?? "eslint";
2465
- if (linter !== "oxlint") {
2466
- return {
2467
- check: "oxlint toolchain declared",
2468
- ok: true,
2469
- detail: "not an oxlint workspace",
2470
- remedy: ""
2471
- };
2260
+ function checkNoRetiredFormatter(workspaceRoot) {
2261
+ const files = RETIRED_FORMATTER_FILES.filter((file) => fileExists((0, import_node_path12.join)(workspaceRoot, file)));
2262
+ const declared = ["prettier", "eslint-config-prettier", "oxlint", "oxfmt", "@mnci/oxlint-config"].filter((name) => declaredDevDependencies(workspaceRoot)[name] !== void 0);
2263
+ const stale = [...files, ...declared];
2264
+ if (stale.length === 0) {
2265
+ return { check: "ESLint is the only linter and formatter", ok: true };
2472
2266
  }
2473
- const devDeps = declaredDevDependencies(workspaceRoot);
2474
- const missing = ["oxlint", "oxfmt", "@mnci/oxlint-config"].filter(
2475
- (dependency) => devDeps[dependency] === void 0
2476
- );
2477
2267
  return {
2478
- check: "oxlint toolchain declared in devDependencies",
2479
- ok: missing.length === 0,
2480
- detail: `missing: ${missing.join(", ")} \u2014 lint or format will fail with "command not found"`,
2481
- remedy: "run `mnci upgrade`, which writes the toolchain the chosen linter needs"
2268
+ check: "no retired formatter is still configured",
2269
+ ok: false,
2270
+ detail: `Found: ${stale.join(", ")}. These no longer run, but an editor extension still resolves them and will reformat on save against an opinion no gate checks.`,
2271
+ remedy: "Run 'mnci upgrade' to remove them."
2482
2272
  };
2483
2273
  }
2484
2274
  function checkEslintPlugin(nxJson) {
@@ -2574,10 +2364,7 @@ function collectFindings(workspaceRoot) {
2574
2364
  const nxJson = readJson(nxJsonPath);
2575
2365
  return [
2576
2366
  ...checkEslintConfigs(workspaceRoot),
2577
- checkPrettierConfig(workspaceRoot),
2578
- checkLinterModeIsConsistent(workspaceRoot),
2579
- checkOneFormatterDeclared(workspaceRoot),
2580
- checkOxlintToolchainDeclared(workspaceRoot),
2367
+ checkNoRetiredFormatter(workspaceRoot),
2581
2368
  checkEslintPlugin(nxJson),
2582
2369
  checkResolvedEslint(workspaceRoot),
2583
2370
  checkNpmrc(workspaceRoot, nxJson.mnci?.registry, nxJson.mnci?.scope),
@@ -2617,17 +2404,11 @@ function nxCloudProviderValue(ci) {
2617
2404
  return ci === "azure" ? "azure" : "github";
2618
2405
  }
2619
2406
  async function resolveStack(options) {
2620
- if (options.testRunner || options.linter || options.yes) {
2621
- return {
2622
- testRunner: options.testRunner ?? DEFAULT_STACK.testRunner,
2623
- linter: options.linter ?? DEFAULT_STACK.linter
2624
- };
2407
+ if (options.testRunner || options.yes) {
2408
+ return { testRunner: options.testRunner ?? DEFAULT_STACK.testRunner };
2625
2409
  }
2626
2410
  const prompted = await promptStack();
2627
- return {
2628
- testRunner: prompted.testRunner,
2629
- linter: prompted.linter
2630
- };
2411
+ return { testRunner: prompted.testRunner };
2631
2412
  }
2632
2413
  var CI_PROVIDERS = /* @__PURE__ */ new Set(["azure", "github", "both"]);
2633
2414
  async function resolveCi(options) {
@@ -2696,10 +2477,8 @@ async function runNew(name, options) {
2696
2477
  throw new Error(`npm install of the toolchain failed with exit code ${installStatus}`);
2697
2478
  }
2698
2479
  runShell("npx", ["husky"], workspaceRoot);
2699
- logger.step(
2700
- `Formatting the workspace (${stack.linter === "oxlint" ? "oxfmt" : "Prettier"}, JavaScript Standard Style)`
2701
- );
2702
- runFormatter(workspaceRoot, stack.linter);
2480
+ logger.step("Formatting the workspace (eslint --fix, JavaScript Standard Style)");
2481
+ runFormatter(workspaceRoot);
2703
2482
  logger.success("Done. Next steps:");
2704
2483
  logger.info(` cd ${workspaceName}`);
2705
2484
  logger.info(" mnci add react-app web # or: react-lib, react-internal-lib, node-app,");
@@ -2776,7 +2555,6 @@ function resolveOverlayOptions(workspaceRoot, options, persisted) {
2776
2555
  );
2777
2556
  }
2778
2557
  const variableGroup = options.variableGroup ?? persisted.variableGroup ?? "Build";
2779
- const linter = options.linter ?? persisted.stack?.linter ?? "eslint";
2780
2558
  const testRunner = options.testRunner ?? persisted.stack?.testRunner;
2781
2559
  if (!testRunner) {
2782
2560
  throw new Error(
@@ -2790,7 +2568,7 @@ function resolveOverlayOptions(workspaceRoot, options, persisted) {
2790
2568
  agent,
2791
2569
  variableGroup,
2792
2570
  ci,
2793
- stack: { testRunner, linter }
2571
+ stack: { testRunner }
2794
2572
  };
2795
2573
  }
2796
2574
  function runUpgrade(workspaceRoot, options) {
@@ -2805,7 +2583,7 @@ function runUpgrade(workspaceRoot, options) {
2805
2583
  "Re-applying the MoNecromanCI overlay (release config, .npmrc, commitlint, pipeline, stack)"
2806
2584
  );
2807
2585
  applyOverlay(workspaceRoot, resolved);
2808
- runFormatter(workspaceRoot, resolved.stack.linter);
2586
+ runFormatter(workspaceRoot);
2809
2587
  logger.success("Done. Review the changes with `git diff` before committing.");
2810
2588
  }
2811
2589
 
@@ -2904,10 +2682,7 @@ function buildProgram(cliVersion) {
2904
2682
  program.command("new").argument("[name]", "workspace name").description("Create a new monorepo (Nx TS preset + release/pipeline overlay)").option("-y, --yes", "accept defaults for anything not passed as a flag").option("--scope <scope>", "npm scope for publishable packages (e.g. @demo)").option("--registry <kind>", "azure-artifacts | npm").option("--organization <name>", "Azure DevOps organization").option("--project <name>", "Azure DevOps project").option("--artifacts-feed <name>", "Azure Artifacts feed").option(
2905
2683
  "--agent <pool>",
2906
2684
  "CI build agent: a vmImage (e.g. ubuntu-latest) or self-hosted pool name"
2907
- ).option("--variable-group <name>", "Azure DevOps variable group holding the npm PAT").option("--ci <provider>", "CI provider: azure | github | both").option("--test-runner <runner>", "unit-test runner: jest | vitest").option(
2908
- "--linter <linter>",
2909
- "linter + formatter: eslint (ESLint + Prettier) | oxlint (oxlint + oxfmt for JS/TS, ESLint for the rest)"
2910
- ).option("--nx-cloud", "connect the workspace to Nx Cloud (remote caching + CI insights)").action(async (name, options) => {
2685
+ ).option("--variable-group <name>", "Azure DevOps variable group holding the npm PAT").option("--ci <provider>", "CI provider: azure | github | both").option("--test-runner <runner>", "unit-test runner: jest | vitest").option("--nx-cloud", "connect the workspace to Nx Cloud (remote caching + CI insights)").action(async (name, options) => {
2911
2686
  await runNew(name, options);
2912
2687
  });
2913
2688
  program.command("upgrade").description(
@@ -2915,9 +2690,6 @@ function buildProgram(cliVersion) {
2915
2690
  ).option("--scope <scope>", "npm scope for publishable packages (overrides the persisted value)").option("--registry <kind>", "azure-artifacts | npm (overrides the persisted value)").option("--organization <name>", "Azure DevOps organization").option("--project <name>", "Azure DevOps project").option("--artifacts-feed <name>", "Azure Artifacts feed").option("--agent <pool>", "CI build agent (overrides the persisted value)").option("--variable-group <name>", "Azure DevOps variable group holding the npm PAT").option("--ci <provider>", "CI provider: azure | github | both (overrides the persisted value)").option(
2916
2691
  "--test-runner <runner>",
2917
2692
  "unit-test runner: jest | vitest (overrides the persisted value)"
2918
- ).option(
2919
- "--linter <linter>",
2920
- "linter + formatter: eslint | oxlint (overrides the persisted value)"
2921
2693
  ).action((options) => {
2922
2694
  runUpgrade(process.cwd(), options);
2923
2695
  });