@mnci/cli 1.10.0 → 1.11.1
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 +56 -13
- package/dist/cli.js +133 -126
- package/dist/cli.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -121,9 +121,11 @@ that would just fail felt worse than being upfront that it doesn't exist yet.
|
|
|
121
121
|
default for): independent versioning from **conventional commits**,
|
|
122
122
|
`{projectName}@{version}` tags, **tag-only git** (`commit: false`) — nothing
|
|
123
123
|
is ever pushed to `main`; future runs resolve versions from tag names.
|
|
124
|
-
3. Writes
|
|
125
|
-
|
|
126
|
-
`
|
|
124
|
+
3. Writes `eslint.config.mjs` (three lines importing `@mnci/eslint-config` —
|
|
125
|
+
the whole linting opinion, in one root config), `.prettierrc.json` +
|
|
126
|
+
`.prettierignore`, `.npmrc` (comment-only — see **Known gaps** below),
|
|
127
|
+
`commitlint.config.mjs`, a husky `commit-msg` hook, the chosen CI provider's
|
|
128
|
+
pipeline file(s)
|
|
127
129
|
(`azure-pipelines.yml` and/or `.github/workflows/ci.yml`, `--ci`, default
|
|
128
130
|
`azure`; `github`/`both` also gets `.github/dependabot.yml` — weekly
|
|
129
131
|
dependency-update PRs), a `<workspace-name>.code-workspace` file (VS Code
|
|
@@ -143,9 +145,23 @@ this existed; nothing let an already-generated workspace pick one up.
|
|
|
143
145
|
same options `new` would have and calls the exact same `applyOverlay` `new`
|
|
144
146
|
itself calls — the one function that does every bit of `mnci`-owned file
|
|
145
147
|
writing (`nx.json`'s `release`/`sync`/`generators`/`mnci` blocks, `.npmrc`,
|
|
146
|
-
`
|
|
147
|
-
|
|
148
|
-
|
|
148
|
+
`eslint.config.mjs`, `.prettierrc.json`, `commitlint.config.mjs`,
|
|
149
|
+
`.husky/commit-msg`, the CI pipeline file(s), and the curated root
|
|
150
|
+
`package.json` scripts). Nothing else in the workspace — app/lib source,
|
|
151
|
+
`project.json` targets from `mnci add` — is ever touched.
|
|
152
|
+
|
|
153
|
+
`upgrade` also **deletes** things, which is stronger than the overwriting it
|
|
154
|
+
has always done — one more reason to run `git diff` first, as the command's own
|
|
155
|
+
output tells you to:
|
|
156
|
+
|
|
157
|
+
- `create-nx-workspace`'s `.prettierrc` and `.vscode/`, both superseded (by
|
|
158
|
+
`.prettierrc.json`, which Prettier would otherwise never reach, and by the
|
|
159
|
+
`.code-workspace` file).
|
|
160
|
+
- **every per-project `eslint.config.*` under `apps/`, `libs/` and
|
|
161
|
+
`packages/`.** This is the migration path for a workspace generated before
|
|
162
|
+
mnci owned linting: without it an upgrade would install the root config while
|
|
163
|
+
each project kept linting against its own stale copy. The root config is never
|
|
164
|
+
touched, and neither is a config anywhere outside those three directories.
|
|
149
165
|
|
|
150
166
|
```sh
|
|
151
167
|
mnci upgrade # re-apply from persisted config alone
|
|
@@ -182,13 +198,33 @@ stack:
|
|
|
182
198
|
| --------------- | ------------------ | ------- | ---------------------------------------------------------------------------------------- |
|
|
183
199
|
| `--test-runner` | `jest` \| `vitest` | `jest` | `nx.json` generator `unitTestRunner` default; the hand-built function app follows it too |
|
|
184
200
|
|
|
185
|
-
**Linting and formatting are unified across the workspace
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
201
|
+
**Linting and formatting are unified across the workspace, from exactly one
|
|
202
|
+
config file each.** The root `eslint.config.mjs` is three lines importing
|
|
203
|
+
[`@mnci/eslint-config`](../eslint-config/README.md); every `@nx/*` generator
|
|
204
|
+
drops a config into the project it creates, and `mnci add` deletes it. Projects
|
|
205
|
+
still get their `lint` target: `@nx/eslint/plugin` infers it by mapping config
|
|
206
|
+
_directories_ onto the project roots beneath them, so the root config covers
|
|
207
|
+
them all. (Verified, not assumed — and the e2e enforces both "every project has
|
|
208
|
+
a `lint` target" and "the root config genuinely reports violations in a project
|
|
209
|
+
with no config of its own", because a future Nx change there would silently
|
|
210
|
+
switch linting off workspace-wide.)
|
|
211
|
+
|
|
212
|
+
ESLint handles code quality only; **Prettier owns all formatting**, configured
|
|
213
|
+
for JavaScript Standard Style (no semicolons, single quotes, 2-space indents, no
|
|
214
|
+
trailing commas). `mnci` runs Prettier itself at the end of `new` and every
|
|
215
|
+
`add`, so a generated workspace passes its own `format:check` immediately — Nx's
|
|
216
|
+
generators emit semicolons and double quotes, and without that pass the first
|
|
217
|
+
commit buries every real change under generator noise.
|
|
218
|
+
|
|
219
|
+
One caveat worth knowing: `space-before-function-paren`, Standard's signature
|
|
220
|
+
rule, is **not** enforced. Prettier rewrites `function f (a)` to `function f(a)`
|
|
221
|
+
on every run and has closed the corresponding option permanently, so enabling it
|
|
222
|
+
would make `npm run lint` and `npm run format:check` mutually unsatisfiable.
|
|
223
|
+
Choosing Prettier means accepting its call there.
|
|
224
|
+
|
|
225
|
+
`npm run lint` checks code quality; `npm run format` (write) and
|
|
226
|
+
`npm run format:check` (CI-safe) handle formatting. The CI runs lint/test/build
|
|
227
|
+
only — formatting is left as a local/pre-commit step.
|
|
192
228
|
|
|
193
229
|
TypeScript is not a question — every workspace runs the **dual compiler** from
|
|
194
230
|
[Nx's TS 7 guide](https://nx.dev/docs/technologies/typescript/guides/typescript-7):
|
|
@@ -466,6 +502,13 @@ than a surprise:
|
|
|
466
502
|
|
|
467
503
|
## Known gaps (accepted for the experiment)
|
|
468
504
|
|
|
505
|
+
- **The generated `.npmrc` is empty (comments only), so `npm publish` will not
|
|
506
|
+
authenticate.** This is a deliberate deferral, not an oversight: the previous
|
|
507
|
+
file claimed here that scope routing made accidental public publishes
|
|
508
|
+
impossible, when in fact no `@scope:registry` line was ever emitted. Rather
|
|
509
|
+
than ship half-wired auth, the CI token export (`NODE_AUTH_TOKEN`/`PAT`) is
|
|
510
|
+
left in place so wiring it later is one line, and the generated `.npmrc`
|
|
511
|
+
carries a comment saying so. Publish-path e2e assertions are relaxed to match.
|
|
469
512
|
- No `doctor`/`resurrect`/`spell` — out of scope until the model is proven.
|
|
470
513
|
- **Flutter apps build for web only.** Android would require the Android SDK and
|
|
471
514
|
NDK on every build agent; iOS is impossible on a Linux agent regardless. Add
|
package/dist/cli.js
CHANGED
|
@@ -43,6 +43,27 @@ var import_prompts4 = require("@inquirer/prompts");
|
|
|
43
43
|
|
|
44
44
|
// src/nx.ts
|
|
45
45
|
var import_cross_spawn = __toESM(require("cross-spawn"));
|
|
46
|
+
|
|
47
|
+
// src/util/logger.ts
|
|
48
|
+
var logger = {
|
|
49
|
+
info(message) {
|
|
50
|
+
console.log(message);
|
|
51
|
+
},
|
|
52
|
+
step(message) {
|
|
53
|
+
console.log(`\u2192 ${message}`);
|
|
54
|
+
},
|
|
55
|
+
success(message) {
|
|
56
|
+
console.log(`\u2713 ${message}`);
|
|
57
|
+
},
|
|
58
|
+
warn(message) {
|
|
59
|
+
console.warn(`! ${message}`);
|
|
60
|
+
},
|
|
61
|
+
error(message) {
|
|
62
|
+
console.error(`\u2717 ${message}`);
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
// src/nx.ts
|
|
46
67
|
function runShell(command, arguments_, cwd) {
|
|
47
68
|
const result = import_cross_spawn.default.sync(command, arguments_, { stdio: "inherit", cwd });
|
|
48
69
|
return result.status ?? 1;
|
|
@@ -59,6 +80,14 @@ function runNpx(arguments_, cwd) {
|
|
|
59
80
|
throw new Error(`npx ${arguments_.join(" ")} failed with exit code ${status}`);
|
|
60
81
|
}
|
|
61
82
|
}
|
|
83
|
+
function runPrettier(cwd, target = ".") {
|
|
84
|
+
const status = runShell("npx", ["prettier", "--write", "--log-level", "warn", target], cwd);
|
|
85
|
+
if (status !== 0) {
|
|
86
|
+
logger.warn(
|
|
87
|
+
`Prettier could not format '${target}' (exit code ${status}). The project was generated; run 'npm run format' to normalise it.`
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
62
91
|
|
|
63
92
|
// src/prompts.ts
|
|
64
93
|
var import_prompts = require("@inquirer/prompts");
|
|
@@ -136,25 +165,6 @@ function fileExists(path) {
|
|
|
136
165
|
return (0, import_node_fs.existsSync)(path);
|
|
137
166
|
}
|
|
138
167
|
|
|
139
|
-
// src/util/logger.ts
|
|
140
|
-
var logger = {
|
|
141
|
-
info(message) {
|
|
142
|
-
console.log(message);
|
|
143
|
-
},
|
|
144
|
-
step(message) {
|
|
145
|
-
console.log(`\u2192 ${message}`);
|
|
146
|
-
},
|
|
147
|
-
success(message) {
|
|
148
|
-
console.log(`\u2713 ${message}`);
|
|
149
|
-
},
|
|
150
|
-
warn(message) {
|
|
151
|
-
console.warn(`! ${message}`);
|
|
152
|
-
},
|
|
153
|
-
error(message) {
|
|
154
|
-
console.error(`\u2717 ${message}`);
|
|
155
|
-
}
|
|
156
|
-
};
|
|
157
|
-
|
|
158
168
|
// src/util/names.ts
|
|
159
169
|
function assertValidProjectName(name, label) {
|
|
160
170
|
if (!/^[a-z][a-z0-9-]*$/.test(name)) {
|
|
@@ -202,6 +212,20 @@ function addNxTargets(manifestPath, newTargets) {
|
|
|
202
212
|
toJson({ ...manifest, nx: { ...nx, targets: { ...targets, ...newTargets } } })
|
|
203
213
|
);
|
|
204
214
|
}
|
|
215
|
+
var ESLINT_CONFIG_FILENAMES = [
|
|
216
|
+
"eslint.config.js",
|
|
217
|
+
"eslint.config.mjs",
|
|
218
|
+
"eslint.config.cjs",
|
|
219
|
+
"eslint.config.ts",
|
|
220
|
+
"eslint.config.mts",
|
|
221
|
+
"eslint.config.cts"
|
|
222
|
+
];
|
|
223
|
+
function removeGeneratedEslintConfig(workspaceRoot, projectRoot) {
|
|
224
|
+
for (const filename of ESLINT_CONFIG_FILENAMES) {
|
|
225
|
+
(0, import_node_fs2.rmSync)((0, import_node_path2.join)(workspaceRoot, projectRoot, filename), { force: true });
|
|
226
|
+
}
|
|
227
|
+
(0, import_node_fs2.rmSync)((0, import_node_path2.join)(workspaceRoot, ".vscode"), { recursive: true, force: true });
|
|
228
|
+
}
|
|
205
229
|
function findCodeWorkspaceFile(workspaceRoot) {
|
|
206
230
|
try {
|
|
207
231
|
const entry = (0, import_node_fs2.readdirSync)(workspaceRoot).find((file) => file.endsWith(".code-workspace"));
|
|
@@ -377,7 +401,6 @@ function goLintTarget() {
|
|
|
377
401
|
function goBuildTarget(name) {
|
|
378
402
|
return {
|
|
379
403
|
executor: "@nx-go/nx-go:build",
|
|
380
|
-
// eslint-disable-next-line unicorn/no-incorrect-template-string-interpolation -- {workspaceRoot} is an Nx output token
|
|
381
404
|
outputs: [`{workspaceRoot}/dist/apps/${name}`],
|
|
382
405
|
options: { outputPath: `../../dist/apps/${name}/${name}` }
|
|
383
406
|
};
|
|
@@ -388,7 +411,6 @@ function goPackageTarget(tag, name) {
|
|
|
388
411
|
return {
|
|
389
412
|
executor: "nx:run-commands",
|
|
390
413
|
dependsOn: ["build"],
|
|
391
|
-
// eslint-disable-next-line unicorn/no-incorrect-template-string-interpolation -- {workspaceRoot} is an Nx output token
|
|
392
414
|
outputs: [`{workspaceRoot}/${zip}`],
|
|
393
415
|
options: { command }
|
|
394
416
|
};
|
|
@@ -516,7 +538,7 @@ function runNodeApp(workspaceRoot, name, stack, framework) {
|
|
|
516
538
|
`apps/${name}`,
|
|
517
539
|
"--bundler=esbuild",
|
|
518
540
|
`--unitTestRunner=${stack.testRunner}`,
|
|
519
|
-
"--linter=
|
|
541
|
+
"--linter=none",
|
|
520
542
|
"--e2eTestRunner=none",
|
|
521
543
|
`--framework=${framework}`,
|
|
522
544
|
"--no-interactive"
|
|
@@ -530,7 +552,6 @@ function nodeAppPackageTarget(name) {
|
|
|
530
552
|
return {
|
|
531
553
|
executor: "nx:run-commands",
|
|
532
554
|
dependsOn: ["build"],
|
|
533
|
-
// eslint-disable-next-line unicorn/no-incorrect-template-string-interpolation -- {workspaceRoot} is an Nx output token
|
|
534
555
|
outputs: [`{workspaceRoot}/${zip}`],
|
|
535
556
|
options: { command }
|
|
536
557
|
};
|
|
@@ -541,6 +562,7 @@ function addNodeApp(workspaceRoot, name, stack, framework = "none") {
|
|
|
541
562
|
addNxTargets((0, import_node_path5.join)(workspaceRoot, "apps", name, "package.json"), {
|
|
542
563
|
package: nodeAppPackageTarget(name)
|
|
543
564
|
});
|
|
565
|
+
removeGeneratedEslintConfig(workspaceRoot, `apps/${name}`);
|
|
544
566
|
registerProjectCommands(workspaceRoot, name, { build: true, start: `nx run ${name}:serve` });
|
|
545
567
|
}
|
|
546
568
|
function ensureAzureFunctionsPackage(workspaceRoot) {
|
|
@@ -564,10 +586,11 @@ var NODE_FUNCTION_APP_HOST_JSON = `{
|
|
|
564
586
|
}
|
|
565
587
|
}
|
|
566
588
|
`;
|
|
567
|
-
var NODE_FUNCTION_APP_HELLO = `import { app
|
|
589
|
+
var NODE_FUNCTION_APP_HELLO = `import { app } from '@azure/functions'
|
|
590
|
+
import type { HttpRequest, HttpResponseInit } from '@azure/functions'
|
|
568
591
|
import { buildGreeting } from './greeting'
|
|
569
592
|
|
|
570
|
-
async function hello
|
|
593
|
+
async function hello(request: HttpRequest): Promise<HttpResponseInit> {
|
|
571
594
|
const name = request.query.get('name') ?? 'world'
|
|
572
595
|
return { body: buildGreeting(name) }
|
|
573
596
|
}
|
|
@@ -614,7 +637,6 @@ function nodeFunctionAppPackageTarget(name) {
|
|
|
614
637
|
return {
|
|
615
638
|
executor: "nx:run-commands",
|
|
616
639
|
dependsOn: ["build"],
|
|
617
|
-
// eslint-disable-next-line unicorn/no-incorrect-template-string-interpolation -- {workspaceRoot} is an Nx output token
|
|
618
640
|
outputs: [`{workspaceRoot}/${zip}`],
|
|
619
641
|
options: { command }
|
|
620
642
|
};
|
|
@@ -648,62 +670,12 @@ function addNodeFunctionApp(workspaceRoot, name, stack) {
|
|
|
648
670
|
package: nodeFunctionAppPackageTarget(name),
|
|
649
671
|
start: nodeFunctionAppStartTarget(name)
|
|
650
672
|
});
|
|
673
|
+
removeGeneratedEslintConfig(workspaceRoot, `apps/${name}`);
|
|
651
674
|
registerProjectCommands(workspaceRoot, name, { build: true, start: `nx run ${name}:start` });
|
|
652
675
|
}
|
|
653
676
|
|
|
654
677
|
// src/commands/add/npmLib.ts
|
|
655
678
|
var import_node_path6 = require("path");
|
|
656
|
-
var NPM_LIB_ESLINT_CONFIG = `import { globSync, readFileSync } from 'node:fs';
|
|
657
|
-
import { dirname, join } from 'node:path';
|
|
658
|
-
import { fileURLToPath } from 'node:url';
|
|
659
|
-
import baseConfig from '../../eslint.config.mjs';
|
|
660
|
-
|
|
661
|
-
// Private workspace libs are compiled INTO this package's rollup bundle and
|
|
662
|
-
// never declared in the manifest (a consumer could not install them), so the
|
|
663
|
-
// dependency check must ignore them. Computed at lint time: adding a new
|
|
664
|
-
// internal lib never requires touching this file.
|
|
665
|
-
const workspaceRoot = join(dirname(fileURLToPath(import.meta.url)), '..', '..');
|
|
666
|
-
const privateWorkspacePackages = globSync(['libs/*/package.json', 'packages/*/package.json'], { cwd: workspaceRoot })
|
|
667
|
-
.map((manifestPath) => JSON.parse(readFileSync(join(workspaceRoot, manifestPath), 'utf8')))
|
|
668
|
-
.filter((manifest) => manifest.private === true)
|
|
669
|
-
.map((manifest) => manifest.name);
|
|
670
|
-
|
|
671
|
-
export default [
|
|
672
|
-
...baseConfig,
|
|
673
|
-
{
|
|
674
|
-
files: ['**/*.json'],
|
|
675
|
-
rules: {
|
|
676
|
-
'@nx/dependency-checks': [
|
|
677
|
-
'error',
|
|
678
|
-
{
|
|
679
|
-
ignoredDependencies: privateWorkspacePackages,
|
|
680
|
-
ignoredFiles: [
|
|
681
|
-
'{projectRoot}/eslint.config.{js,cjs,mjs,ts,cts,mts}',
|
|
682
|
-
'{projectRoot}/rollup.config.{js,ts,mjs,mts,cjs,cts}',
|
|
683
|
-
// Same reasoning as the two config files above, extended to the test
|
|
684
|
-
// toolchain: rollup bundles from the entry point only, so neither the
|
|
685
|
-
// Vitest config nor the spec files reach the published package \u2014 their
|
|
686
|
-
// imports must not drive the published manifest.
|
|
687
|
-
//
|
|
688
|
-
// Without this, a vitest-stack workspace fails \`npm run lint\` on a
|
|
689
|
-
// freshly generated npm-lib: Nx's own spec imports \`vitest\`, and
|
|
690
|
-
// @nx/dependency-checks then demands it be declared as a runtime
|
|
691
|
-
// dependency of a package that never uses it at runtime.
|
|
692
|
-
'{projectRoot}/vitest.config.{js,ts,mjs,mts,cjs,cts}',
|
|
693
|
-
'{projectRoot}/**/*.spec.{js,ts,jsx,tsx}',
|
|
694
|
-
],
|
|
695
|
-
},
|
|
696
|
-
],
|
|
697
|
-
},
|
|
698
|
-
languageOptions: {
|
|
699
|
-
parser: await import('jsonc-eslint-parser'),
|
|
700
|
-
},
|
|
701
|
-
},
|
|
702
|
-
{
|
|
703
|
-
ignores: ['**/out-tsc'],
|
|
704
|
-
},
|
|
705
|
-
];
|
|
706
|
-
`;
|
|
707
679
|
async function addNpmLib(workspaceRoot, name, options, kindProvided, stack) {
|
|
708
680
|
const scope = options.scope ?? (kindProvided ? defaultScope(workspaceRoot) : await promptText("npm scope for the published package", defaultScope(workspaceRoot)));
|
|
709
681
|
runNx(
|
|
@@ -715,16 +687,13 @@ async function addNpmLib(workspaceRoot, name, options, kindProvided, stack) {
|
|
|
715
687
|
`--importPath=${scope}/${name}`,
|
|
716
688
|
"--bundler=rollup",
|
|
717
689
|
`--unitTestRunner=${stack.testRunner}`,
|
|
718
|
-
"--linter=
|
|
690
|
+
"--linter=none",
|
|
719
691
|
"--no-interactive"
|
|
720
692
|
],
|
|
721
693
|
workspaceRoot
|
|
722
694
|
);
|
|
723
695
|
markPublic((0, import_node_path6.join)(workspaceRoot, "packages", name, "package.json"));
|
|
724
|
-
|
|
725
|
-
(0, import_node_path6.join)(workspaceRoot, "packages", name, "eslint.config.mjs"),
|
|
726
|
-
NPM_LIB_ESLINT_CONFIG
|
|
727
|
-
);
|
|
696
|
+
removeGeneratedEslintConfig(workspaceRoot, `packages/${name}`);
|
|
728
697
|
registerProjectCommands(workspaceRoot, name, { build: true });
|
|
729
698
|
}
|
|
730
699
|
function markPublic(manifestPath) {
|
|
@@ -805,7 +774,6 @@ function pythonAppPackageTarget(name) {
|
|
|
805
774
|
return {
|
|
806
775
|
executor: "nx:run-commands",
|
|
807
776
|
dependsOn: ["build"],
|
|
808
|
-
// eslint-disable-next-line unicorn/no-incorrect-template-string-interpolation -- {workspaceRoot} is an Nx output token
|
|
809
777
|
outputs: [`{workspaceRoot}/${zip}`],
|
|
810
778
|
options: { command }
|
|
811
779
|
};
|
|
@@ -816,7 +784,6 @@ function pythonFunctionAppPackageTarget(name, moduleDirectory) {
|
|
|
816
784
|
const command = `node -e "const fs=require('node:fs');fs.mkdirSync('dist/drop',{recursive:true});const A=require('adm-zip');const z=new A();z.addLocalFile('${root}/function_app.py');z.addLocalFile('${root}/host.json');z.addLocalFile('${root}/requirements.txt');z.addLocalFolder('${root}/${moduleDirectory}','${moduleDirectory}');z.writeZip('${zip}')"`;
|
|
817
785
|
return {
|
|
818
786
|
executor: "nx:run-commands",
|
|
819
|
-
// eslint-disable-next-line unicorn/no-incorrect-template-string-interpolation -- {workspaceRoot} is an Nx output token
|
|
820
787
|
outputs: [`{workspaceRoot}/${zip}`],
|
|
821
788
|
options: { command }
|
|
822
789
|
};
|
|
@@ -976,7 +943,6 @@ function reactAppTargets(name) {
|
|
|
976
943
|
for (const environment of REACT_ENVIRONMENTS) {
|
|
977
944
|
targets[`build-${environment}`] = {
|
|
978
945
|
executor: "nx:run-commands",
|
|
979
|
-
// eslint-disable-next-line unicorn/no-incorrect-template-string-interpolation -- {workspaceRoot} is an Nx output token
|
|
980
946
|
outputs: [`{workspaceRoot}/apps/${name}/dist-${environment}`],
|
|
981
947
|
options: {
|
|
982
948
|
command: `vite build --mode ${environment} --outDir dist-${environment}`,
|
|
@@ -991,7 +957,6 @@ function reactAppTargets(name) {
|
|
|
991
957
|
executor: "nx:run-commands",
|
|
992
958
|
dependsOn: REACT_ENVIRONMENTS.map((environment) => `build-${environment}`),
|
|
993
959
|
outputs: REACT_ENVIRONMENTS.map(
|
|
994
|
-
// eslint-disable-next-line unicorn/no-incorrect-template-string-interpolation
|
|
995
960
|
(environment) => `{workspaceRoot}/dist/drop/react-app-${name}-${environment}.zip`
|
|
996
961
|
),
|
|
997
962
|
options: {
|
|
@@ -1025,7 +990,7 @@ function addReactApp(workspaceRoot, name, stack) {
|
|
|
1025
990
|
`apps/${name}`,
|
|
1026
991
|
"--bundler=vite",
|
|
1027
992
|
`--unitTestRunner=${stack.testRunner}`,
|
|
1028
|
-
"--linter=
|
|
993
|
+
"--linter=none",
|
|
1029
994
|
"--style=css",
|
|
1030
995
|
"--e2eTestRunner=none",
|
|
1031
996
|
"--no-interactive"
|
|
@@ -1039,6 +1004,7 @@ function addReactApp(workspaceRoot, name, stack) {
|
|
|
1039
1004
|
}
|
|
1040
1005
|
allowEnvFiles(workspaceRoot);
|
|
1041
1006
|
addNxTargets((0, import_node_path8.join)(reactAppRoot, "package.json"), reactAppTargets(name));
|
|
1007
|
+
removeGeneratedEslintConfig(workspaceRoot, `apps/${name}`);
|
|
1042
1008
|
registerProjectCommands(workspaceRoot, name, { build: true, start: `nx run ${name}:serve` });
|
|
1043
1009
|
}
|
|
1044
1010
|
|
|
@@ -1100,12 +1066,13 @@ async function runAdd(kind, name, options) {
|
|
|
1100
1066
|
`libs/${resolvedName}`,
|
|
1101
1067
|
"--bundler=tsc",
|
|
1102
1068
|
`--unitTestRunner=${stack.testRunner}`,
|
|
1103
|
-
"--linter=
|
|
1069
|
+
"--linter=none",
|
|
1104
1070
|
"--no-interactive"
|
|
1105
1071
|
],
|
|
1106
1072
|
workspaceRoot
|
|
1107
1073
|
);
|
|
1108
1074
|
markPrivate((0, import_node_path9.join)(workspaceRoot, "libs", resolvedName, "package.json"));
|
|
1075
|
+
removeGeneratedEslintConfig(workspaceRoot, `libs/${resolvedName}`);
|
|
1109
1076
|
registerProjectCommands(workspaceRoot, resolvedName, { build: true });
|
|
1110
1077
|
break;
|
|
1111
1078
|
}
|
|
@@ -1156,6 +1123,7 @@ async function runAdd(kind, name, options) {
|
|
|
1156
1123
|
case "python-vendor": {
|
|
1157
1124
|
await addPythonVendor(workspaceRoot, resolvedName, options, kindProvided);
|
|
1158
1125
|
syncProjectReferences(workspaceRoot);
|
|
1126
|
+
runPrettier(workspaceRoot);
|
|
1159
1127
|
return;
|
|
1160
1128
|
}
|
|
1161
1129
|
default: {
|
|
@@ -1166,6 +1134,7 @@ async function runAdd(kind, name, options) {
|
|
|
1166
1134
|
}
|
|
1167
1135
|
}
|
|
1168
1136
|
syncProjectReferences(workspaceRoot);
|
|
1137
|
+
runPrettier(workspaceRoot);
|
|
1169
1138
|
logger.success(`Added ${resolvedKind} '${resolvedName}'.`);
|
|
1170
1139
|
}
|
|
1171
1140
|
function syncProjectReferences(workspaceRoot) {
|
|
@@ -1198,6 +1167,7 @@ var import_prompts7 = require("@inquirer/prompts");
|
|
|
1198
1167
|
var import_node_path11 = require("path");
|
|
1199
1168
|
|
|
1200
1169
|
// src/overlay.ts
|
|
1170
|
+
var import_node_fs6 = require("fs");
|
|
1201
1171
|
var import_node_path10 = require("path");
|
|
1202
1172
|
var DEFAULT_STACK = { testRunner: "jest" };
|
|
1203
1173
|
var PRETTIER_VERSION = "^3.8.1";
|
|
@@ -1205,34 +1175,16 @@ var TS_COMPILER_DEPENDENCIES = {
|
|
|
1205
1175
|
"@typescript/native": "npm:typescript@^7.0.2",
|
|
1206
1176
|
typescript: "npm:@typescript/typescript6@^6.0.2"
|
|
1207
1177
|
};
|
|
1208
|
-
function npmrcContent(
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
const registryHost = `${base}/npm/registry/`.replace(/^https:\/\//, "");
|
|
1219
|
-
const feedHost = `${base}/`.replace(/^https:\/\//, "");
|
|
1220
|
-
lines.push(
|
|
1221
|
-
`@${scopeName}:registry=${base}/npm/registry/`,
|
|
1222
|
-
"; Azure Artifacts auth \u2014 the base64 PAT is expanded at runtime from the",
|
|
1223
|
-
"; PAT env var (CI: your PAT variable group; locally: export PAT=<base64 token>).",
|
|
1224
|
-
`//${registryHost}:username=${registry.organization}`,
|
|
1225
|
-
`//${registryHost}:_password=\${PAT}`,
|
|
1226
|
-
`//${registryHost}:email=npm@example.com`,
|
|
1227
|
-
`//${feedHost}:username=${registry.organization}`,
|
|
1228
|
-
`//${feedHost}:_password=\${PAT}`,
|
|
1229
|
-
`//${feedHost}:email=npm@example.com`
|
|
1230
|
-
);
|
|
1231
|
-
} else {
|
|
1232
|
-
lines.push(`//registry.npmjs.org/:_authToken=\${NODE_AUTH_TOKEN}`);
|
|
1233
|
-
}
|
|
1234
|
-
lines.push("");
|
|
1235
|
-
return lines.join("\n");
|
|
1178
|
+
function npmrcContent(_registry, _scope) {
|
|
1179
|
+
return `; Intentionally empty.
|
|
1180
|
+
;
|
|
1181
|
+
; Publish authentication is not configured yet. The generated CI exports a
|
|
1182
|
+
; token for the release step (NODE_AUTH_TOKEN for public npm, PAT for Azure
|
|
1183
|
+
; Artifacts), but nothing here consumes it, so 'npm publish' will not
|
|
1184
|
+
; authenticate until this is wired up.
|
|
1185
|
+
;
|
|
1186
|
+
; See "Known gaps" in the @mnci/cli README.
|
|
1187
|
+
`;
|
|
1236
1188
|
}
|
|
1237
1189
|
function releaseConfig(ci) {
|
|
1238
1190
|
const githubReleases = ci === "github";
|
|
@@ -1259,6 +1211,18 @@ function releaseConfig(ci) {
|
|
|
1259
1211
|
};
|
|
1260
1212
|
}
|
|
1261
1213
|
var SYNC_CONFIG = { applyChanges: true };
|
|
1214
|
+
var ESLINT_PLUGIN_CONFIG = {
|
|
1215
|
+
plugin: "@nx/eslint/plugin",
|
|
1216
|
+
options: { targetName: "lint" }
|
|
1217
|
+
};
|
|
1218
|
+
function pluginName(entry) {
|
|
1219
|
+
return typeof entry === "string" ? entry : entry.plugin;
|
|
1220
|
+
}
|
|
1221
|
+
function withEslintPlugin(nxJson) {
|
|
1222
|
+
const plugins = nxJson.plugins ?? [];
|
|
1223
|
+
const registered = plugins.some((entry) => pluginName(entry) === ESLINT_PLUGIN_CONFIG.plugin);
|
|
1224
|
+
return registered ? { ...nxJson, plugins } : { ...nxJson, plugins: [...plugins, ESLINT_PLUGIN_CONFIG] };
|
|
1225
|
+
}
|
|
1262
1226
|
function withReleaseConfig(nxJson, ci) {
|
|
1263
1227
|
return { ...nxJson, defaultBase: "main", release: releaseConfig(ci) };
|
|
1264
1228
|
}
|
|
@@ -1269,13 +1233,30 @@ var COMMIT_MSG_HOOK = `npx --no -- commitlint --edit "$1"
|
|
|
1269
1233
|
var PRETTIER_CONFIG = `{
|
|
1270
1234
|
"semi": false,
|
|
1271
1235
|
"singleQuote": true,
|
|
1272
|
-
"trailingComma": "
|
|
1236
|
+
"trailingComma": "none",
|
|
1273
1237
|
"arrowParens": "avoid",
|
|
1274
1238
|
"printWidth": 100,
|
|
1275
1239
|
"tabWidth": 2,
|
|
1276
1240
|
"useTabs": false
|
|
1277
1241
|
}
|
|
1278
1242
|
`;
|
|
1243
|
+
var ESLINT_CONFIG_VERSION = "^0.1.0";
|
|
1244
|
+
function eslintConfigSpec() {
|
|
1245
|
+
return process.env.MNCI_ESLINT_CONFIG_SPEC ?? ESLINT_CONFIG_VERSION;
|
|
1246
|
+
}
|
|
1247
|
+
var ESLINT_VERSION = "^9.39.0";
|
|
1248
|
+
function eslintToolchainDependencies(nxVersion) {
|
|
1249
|
+
return {
|
|
1250
|
+
eslint: ESLINT_VERSION,
|
|
1251
|
+
"@nx/eslint": nxVersion,
|
|
1252
|
+
"@nx/eslint-plugin": nxVersion,
|
|
1253
|
+
"@mnci/eslint-config": eslintConfigSpec()
|
|
1254
|
+
};
|
|
1255
|
+
}
|
|
1256
|
+
var ESLINT_CONFIG = `import mnci from '@mnci/eslint-config'
|
|
1257
|
+
|
|
1258
|
+
export default mnci({ workspaceRoot: import.meta.dirname })
|
|
1259
|
+
`;
|
|
1279
1260
|
var PRETTIER_IGNORE = `node_modules
|
|
1280
1261
|
dist
|
|
1281
1262
|
dist-dev
|
|
@@ -1352,7 +1333,13 @@ function rootScripts() {
|
|
|
1352
1333
|
}
|
|
1353
1334
|
function generatorDefaults(stack) {
|
|
1354
1335
|
const shared = {
|
|
1355
|
-
|
|
1336
|
+
// `none`, not `eslint`, and the workspace is still fully linted. The root
|
|
1337
|
+
// config plus `@nx/eslint/plugin` ({@link ESLINT_PLUGIN_CONFIG}) give every
|
|
1338
|
+
// project its `lint` target; `eslint` here would only make the generator
|
|
1339
|
+
// scaffold a per-project config mnci deletes anyway — and drag in
|
|
1340
|
+
// `eslint-plugin-import@2.31.0`, which peer-caps at ESLint 9 and breaks the
|
|
1341
|
+
// install outright on this workspace's ESLint 10.
|
|
1342
|
+
linter: "none",
|
|
1356
1343
|
unitTestRunner: stack.testRunner
|
|
1357
1344
|
};
|
|
1358
1345
|
return {
|
|
@@ -1797,6 +1784,21 @@ updates:
|
|
|
1797
1784
|
schedule:
|
|
1798
1785
|
interval: weekly
|
|
1799
1786
|
`;
|
|
1787
|
+
var NX_SCAFFOLDING_TO_REMOVE = [".prettierrc", ".vscode"];
|
|
1788
|
+
function removeNxScaffolding(workspaceRoot) {
|
|
1789
|
+
for (const entry of NX_SCAFFOLDING_TO_REMOVE) {
|
|
1790
|
+
(0, import_node_fs6.rmSync)((0, import_node_path10.join)(workspaceRoot, entry), { recursive: true, force: true });
|
|
1791
|
+
}
|
|
1792
|
+
removeProjectEslintConfigs(workspaceRoot);
|
|
1793
|
+
}
|
|
1794
|
+
function removeProjectEslintConfigs(workspaceRoot) {
|
|
1795
|
+
const matches = (0, import_node_fs6.globSync)("{apps,libs,packages}/*/eslint.config.{js,mjs,cjs,ts,mts,cts}", {
|
|
1796
|
+
cwd: workspaceRoot
|
|
1797
|
+
});
|
|
1798
|
+
for (const match of matches) {
|
|
1799
|
+
(0, import_node_fs6.rmSync)((0, import_node_path10.join)(workspaceRoot, match), { force: true });
|
|
1800
|
+
}
|
|
1801
|
+
}
|
|
1800
1802
|
function applyOverlay(workspaceRoot, options) {
|
|
1801
1803
|
const nxJsonPath = (0, import_node_path10.join)(workspaceRoot, "nx.json");
|
|
1802
1804
|
const nxJson = readJson(nxJsonPath);
|
|
@@ -1806,19 +1808,20 @@ function applyOverlay(workspaceRoot, options) {
|
|
|
1806
1808
|
};
|
|
1807
1809
|
const sync = { ...nxJson.sync, ...SYNC_CONFIG };
|
|
1808
1810
|
const mnci = { ...nxJson.mnci, ...mnciConfig(options) };
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
toJson({ ...withReleaseConfig(nxJson, options.ci), generators, sync, mnci })
|
|
1812
|
-
);
|
|
1811
|
+
const patched = withEslintPlugin(withReleaseConfig(nxJson, options.ci));
|
|
1812
|
+
writeFileEnsured(nxJsonPath, toJson({ ...patched, generators, sync, mnci }));
|
|
1813
1813
|
const manifestPath = (0, import_node_path10.join)(workspaceRoot, "package.json");
|
|
1814
1814
|
const manifest = readJson(manifestPath);
|
|
1815
1815
|
const scripts = {
|
|
1816
1816
|
...manifest.scripts,
|
|
1817
1817
|
...rootScripts()
|
|
1818
1818
|
};
|
|
1819
|
+
const existingDevDeps = manifest.devDependencies;
|
|
1819
1820
|
const devDeps = {
|
|
1820
|
-
...
|
|
1821
|
+
...existingDevDeps,
|
|
1821
1822
|
...TS_COMPILER_DEPENDENCIES,
|
|
1823
|
+
// The preset pins `nx` itself; the ESLint plugins must match it exactly.
|
|
1824
|
+
...eslintToolchainDependencies(existingDevDeps?.nx ?? "latest"),
|
|
1822
1825
|
prettier: PRETTIER_VERSION
|
|
1823
1826
|
};
|
|
1824
1827
|
writeFileEnsured(
|
|
@@ -1830,8 +1833,10 @@ function applyOverlay(workspaceRoot, options) {
|
|
|
1830
1833
|
const hookPath = (0, import_node_path10.join)(workspaceRoot, ".husky/commit-msg");
|
|
1831
1834
|
writeFileEnsured(hookPath, COMMIT_MSG_HOOK);
|
|
1832
1835
|
markExecutable(hookPath);
|
|
1836
|
+
writeFileEnsured((0, import_node_path10.join)(workspaceRoot, "eslint.config.mjs"), ESLINT_CONFIG);
|
|
1833
1837
|
writeFileEnsured((0, import_node_path10.join)(workspaceRoot, ".prettierrc.json"), PRETTIER_CONFIG);
|
|
1834
1838
|
writeFileEnsured((0, import_node_path10.join)(workspaceRoot, ".prettierignore"), PRETTIER_IGNORE);
|
|
1839
|
+
removeNxScaffolding(workspaceRoot);
|
|
1835
1840
|
writeFileEnsured(
|
|
1836
1841
|
(0, import_node_path10.join)(workspaceRoot, `${options.workspaceName}.code-workspace`),
|
|
1837
1842
|
vscodeWorkspace(options.workspaceName)
|
|
@@ -1922,7 +1927,7 @@ async function runNew(name, options) {
|
|
|
1922
1927
|
"Applying MoNecromanCI overlay (VS Code workspace, release config, .npmrc, commitlint, pipeline, stack)"
|
|
1923
1928
|
);
|
|
1924
1929
|
applyOverlay(workspaceRoot, { workspaceName, scope, registry, agent, variableGroup, ci, stack });
|
|
1925
|
-
logger.step("Installing the toolchain (commitlint + husky)");
|
|
1930
|
+
logger.step("Installing the toolchain (commitlint + husky + linting/formatting)");
|
|
1926
1931
|
const installStatus = runShell(
|
|
1927
1932
|
"npm",
|
|
1928
1933
|
["install", "--save-dev", "husky", "@commitlint/cli", "@commitlint/config-conventional"],
|
|
@@ -1932,6 +1937,8 @@ async function runNew(name, options) {
|
|
|
1932
1937
|
throw new Error(`npm install of the toolchain failed with exit code ${installStatus}`);
|
|
1933
1938
|
}
|
|
1934
1939
|
runShell("npx", ["husky"], workspaceRoot);
|
|
1940
|
+
logger.step("Formatting the workspace (Prettier, JavaScript Standard Style)");
|
|
1941
|
+
runPrettier(workspaceRoot);
|
|
1935
1942
|
logger.success("Done. Next steps:");
|
|
1936
1943
|
logger.info(` cd ${workspaceName}`);
|
|
1937
1944
|
logger.info(
|
|
@@ -2039,7 +2046,7 @@ async function runInteractive() {
|
|
|
2039
2046
|
}
|
|
2040
2047
|
|
|
2041
2048
|
// src/util/versionChecker.ts
|
|
2042
|
-
var
|
|
2049
|
+
var import_node_fs7 = require("fs");
|
|
2043
2050
|
var import_node_path14 = require("path");
|
|
2044
2051
|
var import_cross_spawn2 = __toESM(require("cross-spawn"));
|
|
2045
2052
|
var PACKAGE_NAME = "@mnci/cli";
|
|
@@ -2097,7 +2104,7 @@ function releaseTriple(version) {
|
|
|
2097
2104
|
function readCliVersion(packageDirectory) {
|
|
2098
2105
|
try {
|
|
2099
2106
|
const manifestPath = (0, import_node_path14.join)(packageDirectory, "..", "package.json");
|
|
2100
|
-
const manifest = JSON.parse((0,
|
|
2107
|
+
const manifest = JSON.parse((0, import_node_fs7.readFileSync)(manifestPath, "utf8"));
|
|
2101
2108
|
return manifest.version ?? "0.0.0";
|
|
2102
2109
|
} catch {
|
|
2103
2110
|
return "0.0.0";
|