@smoothbricks/cli 0.10.2 → 0.10.4
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 +31 -23
- package/dist/bun/preload.d.ts +13 -0
- package/dist/bun/preload.d.ts.map +1 -0
- package/dist/bun/preload.js +12 -0
- package/dist/cli.js +36 -4
- package/dist/generate/index.d.ts.map +1 -1
- package/dist/generate/index.js +0 -6
- package/dist/github-ci/index.d.ts +17 -2
- package/dist/github-ci/index.d.ts.map +1 -1
- package/dist/github-ci/index.js +192 -26
- package/dist/github-ci/outputs.d.ts +22 -0
- package/dist/github-ci/outputs.d.ts.map +1 -0
- package/dist/github-ci/outputs.js +648 -0
- package/dist/lib/cli-package.js +2 -2
- package/dist/lib/json.d.ts +118 -11
- package/dist/lib/json.d.ts.map +1 -1
- package/dist/lib/json.js +317 -29
- package/dist/lib/workspace.d.ts +7 -14
- package/dist/lib/workspace.d.ts.map +1 -1
- package/dist/lib/workspace.js +76 -82
- package/dist/monorepo/ci-workflow.d.ts +2 -0
- package/dist/monorepo/ci-workflow.d.ts.map +1 -1
- package/dist/monorepo/ci-workflow.js +28 -2
- package/dist/monorepo/git-config.js +2 -2
- package/dist/monorepo/index.d.ts +3 -1
- package/dist/monorepo/index.d.ts.map +1 -1
- package/dist/monorepo/index.js +6 -2
- package/dist/monorepo/lockfile.d.ts +13 -1
- package/dist/monorepo/lockfile.d.ts.map +1 -1
- package/dist/monorepo/lockfile.js +33 -21
- package/dist/monorepo/managed-files.d.ts +12 -0
- package/dist/monorepo/managed-files.d.ts.map +1 -1
- package/dist/monorepo/managed-files.js +86 -28
- package/dist/monorepo/package-policy.d.ts.map +1 -1
- package/dist/monorepo/package-policy.js +78 -45
- package/dist/monorepo/packed-manifest.d.ts +5 -2
- package/dist/monorepo/packed-manifest.d.ts.map +1 -1
- package/dist/monorepo/packed-manifest.js +16 -47
- package/dist/monorepo/packed-package.d.ts.map +1 -1
- package/dist/monorepo/packed-package.js +28 -16
- package/dist/monorepo/packs/index.d.ts +3 -0
- package/dist/monorepo/packs/index.d.ts.map +1 -1
- package/dist/monorepo/packs/index.js +18 -6
- package/dist/monorepo/publish-workflow.d.ts +1 -0
- package/dist/monorepo/publish-workflow.d.ts.map +1 -1
- package/dist/monorepo/publish-workflow.js +242 -3
- package/dist/monorepo/runtime.d.ts +15 -0
- package/dist/monorepo/runtime.d.ts.map +1 -1
- package/dist/monorepo/runtime.js +66 -12
- package/dist/monorepo/tool-validation.d.ts.map +1 -1
- package/dist/monorepo/tool-validation.js +32 -23
- package/dist/monorepo/wrangler.d.ts.map +1 -1
- package/dist/monorepo/wrangler.js +13 -13
- package/dist/nx/index.d.ts +12 -4
- package/dist/nx/index.d.ts.map +1 -1
- package/dist/nx/index.js +86 -19
- package/dist/pr/index.d.ts.map +1 -1
- package/dist/pr/index.js +28 -32
- package/dist/release/candidates.d.ts +3 -2
- package/dist/release/candidates.d.ts.map +1 -1
- package/dist/release/candidates.js +4 -5
- package/dist/release/github-release.d.ts +3 -0
- package/dist/release/github-release.d.ts.map +1 -1
- package/dist/release/github-release.js +11 -0
- package/dist/release/index.d.ts +20 -1
- package/dist/release/index.d.ts.map +1 -1
- package/dist/release/index.js +221 -89
- package/dist/release/orchestration.d.ts +8 -1
- package/dist/release/orchestration.d.ts.map +1 -1
- package/dist/release/orchestration.js +38 -1
- package/dist/wrangler/prepare-env.d.ts +0 -1
- package/dist/wrangler/prepare-env.d.ts.map +1 -1
- package/dist/wrangler/prepare-env.js +100 -51
- package/dist/wrangler/scaffold.d.ts.map +1 -1
- package/dist/wrangler/scaffold.js +12 -12
- package/managed/raw/git-format-staged.yml +19 -1
- package/managed/raw/tooling/direnv/github-actions-bootstrap.sh +6 -0
- package/managed/raw/tooling/direnv/merge-newer-pins.sh +3 -3
- package/managed/raw/tooling/direnv/setup-environment.ts +187 -19
- package/managed/raw/tooling/git-hooks/pre-commit.sh +0 -6
- package/managed/templates/github/actions/cache-nix-devenv/action.yml +12 -10
- package/managed/templates/github/actions/cache-node-modules/action.yml +6 -2
- package/managed/templates/github/actions/cache-nx/action.yml +2 -2
- package/managed/templates/github/actions/cache-ttsc-plugins/action.yml +19 -0
- package/managed/templates/github/actions/save-nix-devenv/action.yml +11 -3
- package/managed/templates/github/actions/setup-devenv/action.yml +5 -0
- package/managed/templates/github/workflows/managed-files.yml +80 -0
- package/package.json +18 -4
- package/src/bun/preload.ts +12 -0
- package/src/cli.ts +49 -11
- package/src/generate/index.ts +0 -6
- package/src/github-ci/index.test.ts +486 -0
- package/src/github-ci/index.ts +220 -30
- package/src/github-ci/outputs.ts +505 -0
- package/src/lib/cli-package.ts +2 -2
- package/src/lib/json.ts +291 -32
- package/src/lib/workspace.ts +88 -100
- package/src/monorepo/__tests__/ci-workflow.test.ts +14 -3
- package/src/monorepo/__tests__/publish-workflow.test.ts +222 -1
- package/src/monorepo/ci-workflow.ts +28 -2
- package/src/monorepo/git-config.ts +2 -2
- package/src/monorepo/index.ts +6 -2
- package/src/monorepo/lockfile.test.ts +35 -36
- package/src/monorepo/lockfile.ts +48 -24
- package/src/monorepo/managed-files.test.ts +63 -0
- package/src/monorepo/managed-files.ts +94 -34
- package/src/monorepo/package-policy.test.ts +70 -51
- package/src/monorepo/package-policy.ts +104 -59
- package/src/monorepo/packed-manifest.ts +18 -52
- package/src/monorepo/packed-package.ts +43 -22
- package/src/monorepo/packs/index.test.ts +18 -1
- package/src/monorepo/packs/index.ts +20 -7
- package/src/monorepo/publish-workflow.ts +482 -3
- package/src/monorepo/runtime.test.ts +42 -3
- package/src/monorepo/runtime.ts +90 -12
- package/src/monorepo/tool-validation.ts +35 -25
- package/src/monorepo/wrangler.test.ts +11 -15
- package/src/monorepo/wrangler.ts +15 -15
- package/src/nx/index.test.ts +43 -0
- package/src/nx/index.ts +110 -23
- package/src/pr/index.ts +42 -36
- package/src/release/__tests__/candidates.test.ts +5 -4
- package/src/release/__tests__/fixture-repo.test.ts +18 -16
- package/src/release/__tests__/github-release.test.ts +11 -0
- package/src/release/__tests__/orchestration.test.ts +57 -2
- package/src/release/__tests__/trust-publisher.test.ts +98 -2
- package/src/release/candidates.ts +10 -17
- package/src/release/github-release.ts +12 -0
- package/src/release/index.ts +321 -112
- package/src/release/orchestration.ts +55 -3
- package/src/wrangler/prepare-env.ts +75 -54
- package/src/wrangler/scaffold.test.ts +9 -12
- package/src/wrangler/scaffold.ts +13 -13
|
@@ -29,7 +29,6 @@ const buildOutputDependencies = [
|
|
|
29
29
|
'*-web',
|
|
30
30
|
'*-html',
|
|
31
31
|
'*-css',
|
|
32
|
-
'*-ios',
|
|
33
32
|
'*-android',
|
|
34
33
|
'*-native',
|
|
35
34
|
'*-napi',
|
|
@@ -263,7 +262,7 @@ describe('workspace package script policy', () => {
|
|
|
263
262
|
try {
|
|
264
263
|
expect(validateWorkspaceDependencies(root)).toBe(2);
|
|
265
264
|
|
|
266
|
-
const resolvedTargetsByProject = new Map([['native', new Set(['build', 'tsc-js', '
|
|
265
|
+
const resolvedTargetsByProject = new Map([['native', new Set(['build', 'tsc-js', 'tsdown-js'])]]);
|
|
267
266
|
applyWorkspaceDependencyDefaults(root, { resolvedTargetsByProject });
|
|
268
267
|
|
|
269
268
|
const native = await readJson(join(root, 'packages/native/package.json'));
|
|
@@ -285,17 +284,17 @@ describe('workspace package script policy', () => {
|
|
|
285
284
|
dependencies: { '@smoothbricks/lib': 'workspace:*' },
|
|
286
285
|
scripts: {
|
|
287
286
|
'build:custom': 'nx run native:build:custom',
|
|
288
|
-
'build:
|
|
287
|
+
'build:bundle': 'nx run native:build:bundle',
|
|
289
288
|
'build:ts': 'nx run native:build:ts',
|
|
290
289
|
},
|
|
291
290
|
nx: {
|
|
292
291
|
name: 'native',
|
|
293
292
|
targets: {
|
|
294
|
-
build: { dependsOn: ['^build', 'build:ts', 'build:
|
|
293
|
+
build: { dependsOn: ['^build', 'build:ts', 'build:bundle', 'build:custom'] },
|
|
295
294
|
'build:custom': {},
|
|
296
|
-
'build:
|
|
295
|
+
'build:bundle': {
|
|
297
296
|
executor: 'nx:run-commands',
|
|
298
|
-
options: { command: '
|
|
297
|
+
options: { command: 'tsdown', cwd: '{projectRoot}' },
|
|
299
298
|
},
|
|
300
299
|
'build:ts': {
|
|
301
300
|
executor: 'nx:run-commands',
|
|
@@ -308,25 +307,21 @@ describe('workspace package script policy', () => {
|
|
|
308
307
|
});
|
|
309
308
|
try {
|
|
310
309
|
await writeJson(join(root, 'packages/native/tsconfig.lib.json'), {});
|
|
311
|
-
await writeFile(
|
|
312
|
-
join(root, 'packages/native/build.zig'),
|
|
313
|
-
'pub fn build(b: *std.Build) void { _ = b.step("wasm", "Build wasm"); }\n',
|
|
314
|
-
);
|
|
315
310
|
expect(validateWorkspaceDependencies(root)).toBe(6);
|
|
316
311
|
|
|
317
|
-
const resolvedTargetsByProject = new Map([['native', new Set(['build', 'custom', 'tsc-js', '
|
|
312
|
+
const resolvedTargetsByProject = new Map([['native', new Set(['build', 'custom', 'tsc-js', 'tsdown-js'])]]);
|
|
318
313
|
applyWorkspaceDependencyDefaults(root, { resolvedTargetsByProject });
|
|
319
314
|
|
|
320
315
|
const native = await readJson(join(root, 'packages/native/package.json'));
|
|
321
316
|
expect(native.scripts).toEqual({
|
|
322
317
|
'build:custom': 'nx run native:custom',
|
|
323
|
-
'build:
|
|
318
|
+
'build:bundle': 'nx run native:tsdown-js',
|
|
324
319
|
'build:ts': 'nx run native:tsc-js',
|
|
325
320
|
});
|
|
326
321
|
expect(native.nx).toEqual({
|
|
327
322
|
name: 'native',
|
|
328
323
|
targets: {
|
|
329
|
-
build: { dependsOn: ['^build', 'tsc-js', '
|
|
324
|
+
build: { dependsOn: ['^build', 'tsc-js', 'tsdown-js', 'custom'] },
|
|
330
325
|
},
|
|
331
326
|
});
|
|
332
327
|
expect(validateWorkspaceDependencies(root, { resolvedTargetsByProject })).toBe(0);
|
|
@@ -345,7 +340,7 @@ describe('workspace package script policy', () => {
|
|
|
345
340
|
nx: {
|
|
346
341
|
name: 'native',
|
|
347
342
|
targets: {
|
|
348
|
-
build: { executor: 'nx:noop', dependsOn: ['^build', 'tsc-js', '
|
|
343
|
+
build: { executor: 'nx:noop', dependsOn: ['^build', 'tsc-js', 'tsdown-js', 'custom'] },
|
|
349
344
|
},
|
|
350
345
|
},
|
|
351
346
|
},
|
|
@@ -353,14 +348,10 @@ describe('workspace package script policy', () => {
|
|
|
353
348
|
});
|
|
354
349
|
try {
|
|
355
350
|
await writeJson(join(root, 'packages/native/tsconfig.lib.json'), {});
|
|
356
|
-
await writeFile(
|
|
357
|
-
join(root, 'packages/native/build.zig'),
|
|
358
|
-
'pub fn build(b: *std.Build) void { _ = b.step("wasm", "Build wasm"); }\n',
|
|
359
|
-
);
|
|
360
351
|
const resolvedTargetsByProject = new Map([
|
|
361
352
|
[
|
|
362
353
|
'native',
|
|
363
|
-
{ targets: new Set(['build', 'tsc-js', '
|
|
354
|
+
{ targets: new Set(['build', 'tsc-js', 'tsdown-js']), buildDependsOn: ['^build', 'tsc-js', 'tsdown-js'] },
|
|
364
355
|
],
|
|
365
356
|
]);
|
|
366
357
|
|
|
@@ -370,7 +361,7 @@ describe('workspace package script policy', () => {
|
|
|
370
361
|
expect(native.nx).toEqual({
|
|
371
362
|
name: 'native',
|
|
372
363
|
targets: {
|
|
373
|
-
build: { executor: 'nx:noop', dependsOn: ['^build', 'tsc-js', '
|
|
364
|
+
build: { executor: 'nx:noop', dependsOn: ['^build', 'tsc-js', 'tsdown-js', 'custom'] },
|
|
374
365
|
},
|
|
375
366
|
});
|
|
376
367
|
} finally {
|
|
@@ -390,8 +381,8 @@ describe('workspace package script policy', () => {
|
|
|
390
381
|
targets: {
|
|
391
382
|
build: {
|
|
392
383
|
executor: 'nx:run-commands',
|
|
393
|
-
options: { command: '
|
|
394
|
-
dependsOn: ['^build', '
|
|
384
|
+
options: { command: 'tsdown', cwd: '{projectRoot}' },
|
|
385
|
+
dependsOn: ['^build', 'tsdown-js'],
|
|
395
386
|
},
|
|
396
387
|
},
|
|
397
388
|
},
|
|
@@ -399,12 +390,8 @@ describe('workspace package script policy', () => {
|
|
|
399
390
|
],
|
|
400
391
|
});
|
|
401
392
|
try {
|
|
402
|
-
await writeFile(
|
|
403
|
-
join(root, 'packages/native/build.zig'),
|
|
404
|
-
'pub fn build(b: *std.Build) void { _ = b.step("wasm", "Build wasm"); }\n',
|
|
405
|
-
);
|
|
406
393
|
const resolvedTargetsByProject = new Map([
|
|
407
|
-
['native', { targets: new Set(['build', '
|
|
394
|
+
['native', { targets: new Set(['build', 'tsdown-js']), buildDependsOn: ['^build', 'tsdown-js'] }],
|
|
408
395
|
]);
|
|
409
396
|
|
|
410
397
|
applyWorkspaceDependencyDefaults(root, { resolvedTargetsByProject });
|
|
@@ -415,8 +402,8 @@ describe('workspace package script policy', () => {
|
|
|
415
402
|
targets: {
|
|
416
403
|
build: {
|
|
417
404
|
executor: 'nx:run-commands',
|
|
418
|
-
options: { command: '
|
|
419
|
-
dependsOn: ['^build', '
|
|
405
|
+
options: { command: 'tsdown', cwd: '{projectRoot}' },
|
|
406
|
+
dependsOn: ['^build', 'tsdown-js'],
|
|
420
407
|
},
|
|
421
408
|
},
|
|
422
409
|
});
|
|
@@ -435,7 +422,7 @@ describe('workspace package script policy', () => {
|
|
|
435
422
|
nx: {
|
|
436
423
|
name: 'native',
|
|
437
424
|
targets: {
|
|
438
|
-
build: { executor: 'nx:noop', dependsOn: ['^build', 'tsc-js', '
|
|
425
|
+
build: { executor: 'nx:noop', dependsOn: ['^build', 'tsc-js', 'tsdown-js'] },
|
|
439
426
|
},
|
|
440
427
|
},
|
|
441
428
|
},
|
|
@@ -445,7 +432,7 @@ describe('workspace package script policy', () => {
|
|
|
445
432
|
const resolvedTargetsByProject = new Map([
|
|
446
433
|
[
|
|
447
434
|
'native',
|
|
448
|
-
{ targets: new Set(['build', 'tsc-js', '
|
|
435
|
+
{ targets: new Set(['build', 'tsc-js', 'tsdown-js']), buildDependsOn: ['^build', 'tsc-js', 'tsdown-js'] },
|
|
449
436
|
],
|
|
450
437
|
]);
|
|
451
438
|
|
|
@@ -499,7 +486,7 @@ describe('workspace package script policy', () => {
|
|
|
499
486
|
});
|
|
500
487
|
try {
|
|
501
488
|
const resolvedTargetsByProject = new Map([
|
|
502
|
-
['native', { targets: new Set(['build', 'tsc-js', '
|
|
489
|
+
['native', { targets: new Set(['build', 'tsc-js', 'tsdown-js']), buildDependsOn: buildOutputDependencies }],
|
|
503
490
|
]);
|
|
504
491
|
|
|
505
492
|
applyWorkspaceDependencyDefaults(root, { resolvedTargetsByProject });
|
|
@@ -559,20 +546,6 @@ describe('workspace package script policy', () => {
|
|
|
559
546
|
}
|
|
560
547
|
});
|
|
561
548
|
|
|
562
|
-
it('rejects build.zig without steps', async () => {
|
|
563
|
-
const root = await createWorkspace({
|
|
564
|
-
rootName: '@smoothbricks/codebase',
|
|
565
|
-
packages: [{ dir: 'native', name: '@smoothbricks/native', nx: { name: 'native' } }],
|
|
566
|
-
});
|
|
567
|
-
try {
|
|
568
|
-
await writeFile(join(root, 'packages/native/build.zig'), 'pub fn build(b: *std.Build) void { _ = b; }\n');
|
|
569
|
-
|
|
570
|
-
expect(validateWorkspaceDependencies(root)).toBe(1);
|
|
571
|
-
} finally {
|
|
572
|
-
await rm(root, { recursive: true, force: true });
|
|
573
|
-
}
|
|
574
|
-
});
|
|
575
|
-
|
|
576
549
|
it('rejects old test tsconfig output for packages with test runners', async () => {
|
|
577
550
|
const root = await createWorkspace({
|
|
578
551
|
rootName: '@smoothbricks/codebase',
|
|
@@ -729,17 +702,13 @@ describe('workspace package script policy', () => {
|
|
|
729
702
|
}
|
|
730
703
|
});
|
|
731
704
|
|
|
732
|
-
it('accepts noEmit test tsconfig
|
|
705
|
+
it('accepts a noEmit test tsconfig', async () => {
|
|
733
706
|
const root = await createWorkspace({
|
|
734
707
|
rootName: '@smoothbricks/codebase',
|
|
735
708
|
packages: [{ dir: 'native', name: '@smoothbricks/native', nx: { name: 'native', targets: { lint: {} } } }],
|
|
736
709
|
});
|
|
737
710
|
try {
|
|
738
711
|
await writeJson(join(root, 'packages/native/tsconfig.test.json'), { compilerOptions: { noEmit: true } });
|
|
739
|
-
await writeFile(
|
|
740
|
-
join(root, 'packages/native/build.zig'),
|
|
741
|
-
'pub fn build(b: *std.Build) void { _ = b.step("build", "Build native artifact"); }\n',
|
|
742
|
-
);
|
|
743
712
|
|
|
744
713
|
expect(validateWorkspaceDependencies(root)).toBe(0);
|
|
745
714
|
} finally {
|
|
@@ -974,6 +943,56 @@ describe('workspace package script policy', () => {
|
|
|
974
943
|
await rm(root, { recursive: true, force: true });
|
|
975
944
|
}
|
|
976
945
|
});
|
|
946
|
+
it('validates generic CI skip tags against resolved project targets', async () => {
|
|
947
|
+
const root = await createWorkspace({
|
|
948
|
+
rootName: '@fixture/root',
|
|
949
|
+
packages: [
|
|
950
|
+
{
|
|
951
|
+
dir: 'worker',
|
|
952
|
+
name: '@fixture/worker',
|
|
953
|
+
nx: { name: 'worker', tags: ['ci:skip:test', 'ci:skip:test.integration'], targets: {} },
|
|
954
|
+
},
|
|
955
|
+
],
|
|
956
|
+
});
|
|
957
|
+
try {
|
|
958
|
+
const resolvedTargetsByProject = new Map([['worker', new Set(['build', 'test', 'test.integration'])]]);
|
|
959
|
+
expect(validateWorkspaceDependencies(root, { resolvedTargetsByProject })).toBe(0);
|
|
960
|
+
} finally {
|
|
961
|
+
await rm(root, { recursive: true, force: true });
|
|
962
|
+
}
|
|
963
|
+
});
|
|
964
|
+
|
|
965
|
+
it('rejects malformed and missing-target CI skip tags', async () => {
|
|
966
|
+
const root = await createWorkspace({
|
|
967
|
+
rootName: '@fixture/root',
|
|
968
|
+
packages: [
|
|
969
|
+
{
|
|
970
|
+
dir: 'malformed',
|
|
971
|
+
name: '@fixture/malformed',
|
|
972
|
+
nx: { name: 'malformed', tags: ['ci:skip:'], targets: {} },
|
|
973
|
+
},
|
|
974
|
+
{
|
|
975
|
+
dir: 'missing',
|
|
976
|
+
name: '@fixture/missing',
|
|
977
|
+
nx: { name: 'missing', tags: ['ci:skip:test'], targets: {} },
|
|
978
|
+
},
|
|
979
|
+
],
|
|
980
|
+
});
|
|
981
|
+
const errors = captureConsoleErrors();
|
|
982
|
+
try {
|
|
983
|
+
const resolvedTargetsByProject = new Map([
|
|
984
|
+
['malformed', new Set(['test'])],
|
|
985
|
+
['missing', new Set(['build'])],
|
|
986
|
+
]);
|
|
987
|
+
expect(validateWorkspaceDependencies(root, { resolvedTargetsByProject })).toBe(2);
|
|
988
|
+
expect(errors).toContain(
|
|
989
|
+
'packages/malformed: nx tag ci:skip: must use ci:skip:<target> with one exact Nx target name',
|
|
990
|
+
);
|
|
991
|
+
expect(errors).toContain('packages/missing: nx tag ci:skip:test names missing Nx target test');
|
|
992
|
+
} finally {
|
|
993
|
+
await rm(root, { recursive: true, force: true });
|
|
994
|
+
}
|
|
995
|
+
});
|
|
977
996
|
});
|
|
978
997
|
|
|
979
998
|
async function createWorkspace(input: {
|
|
@@ -21,15 +21,22 @@ import {
|
|
|
21
21
|
checkWorkspaceConfigPolicy,
|
|
22
22
|
} from '@smoothbricks/nx-plugin/workspace-config-policy';
|
|
23
23
|
import {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
ensureNx,
|
|
25
|
+
ensurePublishConfig,
|
|
26
|
+
ensureRepositoryObject,
|
|
27
|
+
ensureScripts,
|
|
28
|
+
type PackageExportMap,
|
|
29
|
+
type PackageExports,
|
|
30
|
+
type PackageJson,
|
|
27
31
|
readJsonObject,
|
|
28
|
-
recordProperty,
|
|
29
32
|
requiredJsonObject,
|
|
30
|
-
|
|
33
|
+
type StringMap,
|
|
34
|
+
setMissingPackageStringField,
|
|
35
|
+
setMissingRepositoryField,
|
|
36
|
+
setNxName,
|
|
37
|
+
setPublishAccess,
|
|
38
|
+
setRepositoryField,
|
|
31
39
|
setStringProperty,
|
|
32
|
-
stringProperty,
|
|
33
40
|
writeJsonObject,
|
|
34
41
|
} from '../lib/json.js';
|
|
35
42
|
import {
|
|
@@ -68,12 +75,12 @@ export function applyRootScriptDefaults(root: string): void {
|
|
|
68
75
|
if (!rootPackage) {
|
|
69
76
|
return;
|
|
70
77
|
}
|
|
71
|
-
const scripts =
|
|
78
|
+
const scripts = ensureScripts(rootPackage);
|
|
72
79
|
let changed = false;
|
|
73
80
|
for (const [name, command] of Object.entries(rootScriptPolicy)) {
|
|
74
81
|
changed = setStringProperty(scripts, name, command) || changed;
|
|
75
82
|
}
|
|
76
|
-
const nx =
|
|
83
|
+
const nx = ensureNx(rootPackage);
|
|
77
84
|
if (!Array.isArray(nx.includedScripts) || nx.includedScripts.length !== 0) {
|
|
78
85
|
nx.includedScripts = [];
|
|
79
86
|
changed = true;
|
|
@@ -97,7 +104,7 @@ export function applyNxPluginDefaults(root: string): void {
|
|
|
97
104
|
|
|
98
105
|
export function applyPublicPackageDefaults(root: string): void {
|
|
99
106
|
const rootPackage = requiredJsonObject(join(root, 'package.json'));
|
|
100
|
-
const rootLicense =
|
|
107
|
+
const rootLicense = rootPackage.license;
|
|
101
108
|
const rootRepository = repositoryInfo(rootPackage);
|
|
102
109
|
|
|
103
110
|
for (const pkg of listPublicPackages(root)) {
|
|
@@ -110,18 +117,21 @@ export function applyPublicPackageDefaults(root: string): void {
|
|
|
110
117
|
rootLicense &&
|
|
111
118
|
rootLicense !== 'UNLICENSED'
|
|
112
119
|
) {
|
|
113
|
-
changed =
|
|
120
|
+
changed = setMissingPackageStringField(pkg.json, 'license', rootLicense) || changed;
|
|
114
121
|
}
|
|
115
|
-
const publishConfig =
|
|
116
|
-
changed =
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
122
|
+
const publishConfig = ensurePublishConfig(pkg.json);
|
|
123
|
+
changed = setPublishAccess(publishConfig, 'public') || changed;
|
|
124
|
+
|
|
125
|
+
if (typeof pkg.json.repository !== 'string') {
|
|
126
|
+
const repository = ensureRepositoryObject(pkg.json);
|
|
127
|
+
changed =
|
|
128
|
+
setRepositoryField(repository, 'type', existingRepository?.type ?? rootRepository?.type ?? 'git') || changed;
|
|
129
|
+
if (existingRepository) {
|
|
130
|
+
changed = setMissingRepositoryField(repository, 'url', existingRepository.url) || changed;
|
|
131
|
+
}
|
|
132
|
+
changed = setRepositoryField(repository, 'directory', pkg.path.replaceAll('\\', '/')) || changed;
|
|
123
133
|
}
|
|
124
|
-
|
|
134
|
+
|
|
125
135
|
changed = normalizeExportConditionOrder(pkg.json.exports) || changed;
|
|
126
136
|
if (hasDevelopmentSourceExport(pkg.json.exports)) {
|
|
127
137
|
changed = addFileEntry(pkg.json, 'src') || changed;
|
|
@@ -172,7 +182,7 @@ export function applyNxReleaseDefaults(root: string): void {
|
|
|
172
182
|
|
|
173
183
|
export function applyNxProjectNameDefaults(root: string): void {
|
|
174
184
|
const rootPackage = requiredJsonObject(join(root, 'package.json'));
|
|
175
|
-
const rootName =
|
|
185
|
+
const rootName = rootPackage.name;
|
|
176
186
|
if (!rootName) {
|
|
177
187
|
return;
|
|
178
188
|
}
|
|
@@ -181,8 +191,8 @@ export function applyNxProjectNameDefaults(root: string): void {
|
|
|
181
191
|
if (!suggestedName) {
|
|
182
192
|
continue;
|
|
183
193
|
}
|
|
184
|
-
const nx =
|
|
185
|
-
const changed =
|
|
194
|
+
const nx = ensureNx(pkg.json);
|
|
195
|
+
const changed = setNxName(nx, suggestedName);
|
|
186
196
|
if (changed) {
|
|
187
197
|
writeJsonObject(pkg.packageJsonPath, pkg.json);
|
|
188
198
|
console.log(`updated ${pkg.path}/package.json nx.name`);
|
|
@@ -198,11 +208,10 @@ export function listValidCommitScopes(root: string): ReadonlySet<string> {
|
|
|
198
208
|
|
|
199
209
|
export function listNxProjectNames(root: string): string[] {
|
|
200
210
|
const rootPackage = readJsonObject(join(root, 'package.json'));
|
|
201
|
-
const rootName = rootPackage
|
|
211
|
+
const rootName = rootPackage?.name ?? null;
|
|
202
212
|
const names: string[] = [];
|
|
203
213
|
for (const pkg of getWorkspacePackageManifests(root)) {
|
|
204
|
-
const
|
|
205
|
-
const configuredName = nx ? stringProperty(nx, 'name') : null;
|
|
214
|
+
const configuredName = pkg.json.nx?.name;
|
|
206
215
|
const suggestedName = rootName ? suggestNxProjectName(rootName, pkg.name) : null;
|
|
207
216
|
if (configuredName) {
|
|
208
217
|
names.push(configuredName);
|
|
@@ -220,11 +229,11 @@ export function validateRootPackagePolicy(root: string): number {
|
|
|
220
229
|
return 1;
|
|
221
230
|
}
|
|
222
231
|
let failures = 0;
|
|
223
|
-
if (!
|
|
232
|
+
if (!rootPackage.name) {
|
|
224
233
|
console.error('package.json must define name');
|
|
225
234
|
failures++;
|
|
226
235
|
}
|
|
227
|
-
if (!
|
|
236
|
+
if (!rootPackage.license) {
|
|
228
237
|
console.error('package.json must define repo-wide license');
|
|
229
238
|
failures++;
|
|
230
239
|
}
|
|
@@ -234,19 +243,19 @@ export function validateRootPackagePolicy(root: string): number {
|
|
|
234
243
|
}
|
|
235
244
|
failures += validateRootScripts(rootPackage);
|
|
236
245
|
failures += validateRootNxScriptInference(rootPackage);
|
|
237
|
-
const packageManager =
|
|
246
|
+
const packageManager = rootPackage.packageManager;
|
|
238
247
|
if (!packageManager?.startsWith('bun@')) {
|
|
239
248
|
console.error('package.json packageManager must use bun@<version>');
|
|
240
249
|
failures++;
|
|
241
250
|
}
|
|
242
251
|
const bunVersion = packageManager?.startsWith('bun@') ? packageManager.slice('bun@'.length) : null;
|
|
243
|
-
const devDependencies =
|
|
252
|
+
const devDependencies = rootPackage.devDependencies;
|
|
244
253
|
if (!bunVersion || !devDependencies || devDependencies['@types/bun'] !== bunVersion) {
|
|
245
254
|
console.error('package.json devDependencies.@types/bun must match packageManager bun version');
|
|
246
255
|
failures++;
|
|
247
256
|
}
|
|
248
|
-
const engines =
|
|
249
|
-
if (!engines
|
|
257
|
+
const engines = rootPackage.engines;
|
|
258
|
+
if (!engines?.node) {
|
|
250
259
|
console.error('package.json engines.node must be defined');
|
|
251
260
|
failures++;
|
|
252
261
|
}
|
|
@@ -268,7 +277,7 @@ export function validateNxReleaseConfig(root: string): number {
|
|
|
268
277
|
|
|
269
278
|
export function validateNxProjectNames(root: string): number {
|
|
270
279
|
const rootPackage = readJsonObject(join(root, 'package.json'));
|
|
271
|
-
const rootName = rootPackage
|
|
280
|
+
const rootName = rootPackage?.name ?? null;
|
|
272
281
|
if (!rootName) {
|
|
273
282
|
return 0;
|
|
274
283
|
}
|
|
@@ -278,8 +287,7 @@ export function validateNxProjectNames(root: string): number {
|
|
|
278
287
|
if (!suggestedName) {
|
|
279
288
|
continue;
|
|
280
289
|
}
|
|
281
|
-
const
|
|
282
|
-
const configuredName = nx ? stringProperty(nx, 'name') : null;
|
|
290
|
+
const configuredName = pkg.json.nx?.name ?? null;
|
|
283
291
|
if (configuredName !== suggestedName) {
|
|
284
292
|
console.error(
|
|
285
293
|
`${pkg.path}: package.json nx.name must be "${suggestedName}" so fix(${suggestedName}): maps to this project`,
|
|
@@ -318,16 +326,19 @@ export function validatePublicPackageMetadata(root: string): number {
|
|
|
318
326
|
console.error(`${pkg.path}: npm:public package must not be private`);
|
|
319
327
|
failures++;
|
|
320
328
|
}
|
|
321
|
-
if (!
|
|
329
|
+
if (!pkg.json.license) {
|
|
322
330
|
console.error(`${pkg.path}: public package must define license`);
|
|
323
331
|
failures++;
|
|
324
332
|
}
|
|
325
|
-
const publishConfig =
|
|
326
|
-
if (
|
|
333
|
+
const publishConfig = pkg.json.publishConfig;
|
|
334
|
+
if (publishConfig?.access !== 'public') {
|
|
327
335
|
console.error(`${pkg.path}: public package must define publishConfig.access = public`);
|
|
328
336
|
failures++;
|
|
329
337
|
}
|
|
330
|
-
const repository =
|
|
338
|
+
const repository =
|
|
339
|
+
pkg.json.repository !== null && pkg.json.repository !== undefined && typeof pkg.json.repository !== 'string'
|
|
340
|
+
? pkg.json.repository
|
|
341
|
+
: null;
|
|
331
342
|
const packageRepository = packageRepositoryInfo(pkg);
|
|
332
343
|
if (!packageRepository) {
|
|
333
344
|
console.error(`${pkg.path}: public package must define repository.url`);
|
|
@@ -345,11 +356,11 @@ export function validatePublicPackageMetadata(root: string): number {
|
|
|
345
356
|
);
|
|
346
357
|
failures++;
|
|
347
358
|
}
|
|
348
|
-
if (!repository
|
|
359
|
+
if (!repository?.type) {
|
|
349
360
|
console.error(`${pkg.path}: public package must define repository.type`);
|
|
350
361
|
failures++;
|
|
351
362
|
}
|
|
352
|
-
if (!repository ||
|
|
363
|
+
if (!repository || repository.directory !== pkg.path.replaceAll('\\', '/')) {
|
|
353
364
|
console.error(`${pkg.path}: public package repository.directory must be ${pkg.path.replaceAll('\\', '/')}`);
|
|
354
365
|
failures++;
|
|
355
366
|
}
|
|
@@ -357,11 +368,14 @@ export function validatePublicPackageMetadata(root: string): number {
|
|
|
357
368
|
console.error(`${pkg.path}: public package must define files`);
|
|
358
369
|
failures++;
|
|
359
370
|
}
|
|
360
|
-
|
|
371
|
+
const hasBin =
|
|
372
|
+
typeof pkg.json.bin === 'string' ||
|
|
373
|
+
(pkg.json.bin !== null && pkg.json.bin !== undefined && typeof pkg.json.bin === 'object');
|
|
374
|
+
if (!isPackageExportMap(pkg.json.exports) && !hasBin) {
|
|
361
375
|
console.error(`${pkg.path}: public package must define exports or bin`);
|
|
362
376
|
failures++;
|
|
363
377
|
}
|
|
364
|
-
if (
|
|
378
|
+
if (typeof pkg.json.types !== 'string' && !hasBin) {
|
|
365
379
|
console.error(`${pkg.path}: public library package must define types`);
|
|
366
380
|
failures++;
|
|
367
381
|
}
|
|
@@ -371,10 +385,11 @@ export function validatePublicPackageMetadata(root: string): number {
|
|
|
371
385
|
|
|
372
386
|
export function validateWorkspaceDependencies(root: string, options: PackageTargetPolicyOptions = {}): number {
|
|
373
387
|
let failures = 0;
|
|
388
|
+
failures += validateCiSkipTags(root, options);
|
|
374
389
|
const workspaceNames = new Set(getWorkspacePackages(root).map((pkg) => pkg.name));
|
|
375
390
|
for (const pkg of listPackageJsonRecords(root)) {
|
|
376
391
|
for (const field of workspaceDependencyFields) {
|
|
377
|
-
const dependencies =
|
|
392
|
+
const dependencies = pkg.json[field];
|
|
378
393
|
if (!dependencies) {
|
|
379
394
|
continue;
|
|
380
395
|
}
|
|
@@ -404,14 +419,41 @@ export function validateWorkspaceDependencies(root: string, options: PackageTarg
|
|
|
404
419
|
return failures;
|
|
405
420
|
}
|
|
406
421
|
|
|
422
|
+
const ciSkipTagPrefix = 'ci:skip:';
|
|
423
|
+
const invalidCiSkipTargetSyntax = /[,*?[\]{}]/;
|
|
424
|
+
|
|
425
|
+
function validateCiSkipTags(root: string, options: PackageTargetPolicyOptions): number {
|
|
426
|
+
let failures = 0;
|
|
427
|
+
for (const pkg of getWorkspacePackages(root)) {
|
|
428
|
+
const resolved = options.resolvedTargetsByProject?.get(pkg.projectName);
|
|
429
|
+
const resolvedTargets = resolved && 'targets' in resolved ? resolved.targets : resolved;
|
|
430
|
+
for (const tag of pkg.tags) {
|
|
431
|
+
if (tag !== 'ci:skip' && !tag.startsWith(ciSkipTagPrefix)) {
|
|
432
|
+
continue;
|
|
433
|
+
}
|
|
434
|
+
const target = tag.startsWith(ciSkipTagPrefix) ? tag.slice(ciSkipTagPrefix.length) : '';
|
|
435
|
+
if (!target || invalidCiSkipTargetSyntax.test(target)) {
|
|
436
|
+
console.error(`${pkg.path}: nx tag ${tag} must use ci:skip:<target> with one exact Nx target name`);
|
|
437
|
+
failures++;
|
|
438
|
+
continue;
|
|
439
|
+
}
|
|
440
|
+
if (resolvedTargets && !resolvedTargets.has(target)) {
|
|
441
|
+
console.error(`${pkg.path}: nx tag ${tag} names missing Nx target ${target}`);
|
|
442
|
+
failures++;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
return failures;
|
|
447
|
+
}
|
|
448
|
+
|
|
407
449
|
// ---------------------------------------------------------------------------
|
|
408
450
|
// Helpers kept in CLI (not Nx-specific)
|
|
409
451
|
// ---------------------------------------------------------------------------
|
|
410
452
|
|
|
411
|
-
function fixWorkspaceDependencyRanges(pkg:
|
|
453
|
+
function fixWorkspaceDependencyRanges(pkg: PackageJson, workspaceNames: Set<string>): boolean {
|
|
412
454
|
let changed = false;
|
|
413
455
|
for (const field of workspaceDependencyFields) {
|
|
414
|
-
const dependencies =
|
|
456
|
+
const dependencies = pkg[field];
|
|
415
457
|
if (!dependencies) {
|
|
416
458
|
continue;
|
|
417
459
|
}
|
|
@@ -425,8 +467,8 @@ function fixWorkspaceDependencyRanges(pkg: Record<string, unknown>, workspaceNam
|
|
|
425
467
|
return changed;
|
|
426
468
|
}
|
|
427
469
|
|
|
428
|
-
function validateRootScripts(rootPackage:
|
|
429
|
-
const scripts =
|
|
470
|
+
function validateRootScripts(rootPackage: PackageJson): number {
|
|
471
|
+
const scripts = rootPackage.scripts;
|
|
430
472
|
let failures = 0;
|
|
431
473
|
for (const [name, command] of Object.entries(rootScriptPolicy)) {
|
|
432
474
|
if (scripts?.[name] !== command) {
|
|
@@ -443,9 +485,9 @@ function validateRootScripts(rootPackage: Record<string, unknown>): number {
|
|
|
443
485
|
return failures;
|
|
444
486
|
}
|
|
445
487
|
|
|
446
|
-
function validateRootNxScriptInference(rootPackage:
|
|
447
|
-
const
|
|
448
|
-
if (
|
|
488
|
+
function validateRootNxScriptInference(rootPackage: PackageJson): number {
|
|
489
|
+
const includedScripts = rootPackage.nx?.includedScripts;
|
|
490
|
+
if (Array.isArray(includedScripts) && includedScripts.length === 0) {
|
|
449
491
|
return 0;
|
|
450
492
|
}
|
|
451
493
|
console.error('package.json nx.includedScripts must be [] so root scripts do not become recursive Nx targets.');
|
|
@@ -469,12 +511,12 @@ function unscopedPackageName(packageName: string): string {
|
|
|
469
511
|
return packageName.startsWith('@') ? packageName.slice(packageName.indexOf('/') + 1) : packageName;
|
|
470
512
|
}
|
|
471
513
|
|
|
472
|
-
function recordKeysAreSorted(record:
|
|
514
|
+
function recordKeysAreSorted(record: StringMap): boolean {
|
|
473
515
|
const keys = Object.keys(record);
|
|
474
|
-
return keys.every((key, index) => index === 0 || keys[index - 1] <= key);
|
|
516
|
+
return keys.every((key, index) => index === 0 || (keys[index - 1] ?? '') <= key);
|
|
475
517
|
}
|
|
476
518
|
|
|
477
|
-
function sortRecordInPlace(record:
|
|
519
|
+
function sortRecordInPlace(record: StringMap): boolean {
|
|
478
520
|
if (recordKeysAreSorted(record)) {
|
|
479
521
|
return false;
|
|
480
522
|
}
|
|
@@ -488,8 +530,11 @@ function sortRecordInPlace(record: Record<string, unknown>): boolean {
|
|
|
488
530
|
return true;
|
|
489
531
|
}
|
|
490
532
|
|
|
491
|
-
function
|
|
492
|
-
|
|
533
|
+
function isPackageExportMap(value: PackageExports): value is PackageExportMap {
|
|
534
|
+
return value !== null && value !== undefined && typeof value === 'object';
|
|
535
|
+
}
|
|
536
|
+
function normalizeExportConditionOrder(value: PackageExports): boolean {
|
|
537
|
+
if (!isPackageExportMap(value)) {
|
|
493
538
|
return false;
|
|
494
539
|
}
|
|
495
540
|
let changed = false;
|
|
@@ -518,8 +563,8 @@ function normalizeExportConditionOrder(value: unknown): boolean {
|
|
|
518
563
|
return true;
|
|
519
564
|
}
|
|
520
565
|
|
|
521
|
-
function hasDevelopmentSourceExport(value:
|
|
522
|
-
if (!
|
|
566
|
+
function hasDevelopmentSourceExport(value: PackageExports): boolean {
|
|
567
|
+
if (!isPackageExportMap(value)) {
|
|
523
568
|
return false;
|
|
524
569
|
}
|
|
525
570
|
for (const [key, child] of Object.entries(value)) {
|
|
@@ -533,12 +578,12 @@ function hasDevelopmentSourceExport(value: unknown): boolean {
|
|
|
533
578
|
return false;
|
|
534
579
|
}
|
|
535
580
|
|
|
536
|
-
function addFileEntry(pkg:
|
|
581
|
+
function addFileEntry(pkg: PackageJson, entry: string): boolean {
|
|
537
582
|
const files = pkg.files;
|
|
538
583
|
if (!Array.isArray(files) || files.includes(entry)) {
|
|
539
584
|
return false;
|
|
540
585
|
}
|
|
541
|
-
const firstNegated = files.findIndex((file) =>
|
|
586
|
+
const firstNegated = files.findIndex((file) => file.startsWith('!'));
|
|
542
587
|
if (firstNegated === -1) {
|
|
543
588
|
files.push(entry);
|
|
544
589
|
} else {
|