@smoothbricks/cli 0.10.5 → 0.10.7
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 +3 -2
- package/dist/github-ci/index.d.ts +1 -0
- package/dist/github-ci/index.d.ts.map +1 -1
- package/dist/github-ci/index.js +28 -5
- package/dist/lib/json.d.ts +3 -1
- package/dist/lib/json.d.ts.map +1 -1
- package/dist/lib/json.js +5 -20
- package/dist/lib/workspace.d.ts.map +1 -1
- package/dist/lib/workspace.js +1 -0
- 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 +10 -4
- package/dist/monorepo/github-runs-on.d.ts +14 -0
- package/dist/monorepo/github-runs-on.d.ts.map +1 -0
- package/dist/monorepo/github-runs-on.js +43 -0
- package/dist/monorepo/index.d.ts +2 -2
- package/dist/monorepo/index.d.ts.map +1 -1
- package/dist/monorepo/index.js +7 -7
- package/dist/monorepo/managed-files.d.ts +14 -3
- package/dist/monorepo/managed-files.d.ts.map +1 -1
- package/dist/monorepo/managed-files.js +54 -74
- package/dist/monorepo/package-policy.js +2 -2
- package/dist/monorepo/packs/index.d.ts.map +1 -1
- package/dist/monorepo/packs/index.js +3 -1
- package/dist/monorepo/publish-workflow.d.ts +3 -0
- package/dist/monorepo/publish-workflow.d.ts.map +1 -1
- package/dist/monorepo/publish-workflow.js +17 -14
- package/dist/monorepo/runtime.d.ts +2 -2
- package/dist/monorepo/runtime.d.ts.map +1 -1
- package/dist/monorepo/runtime.js +11 -16
- package/dist/monorepo/tool-validation.d.ts.map +1 -1
- package/dist/monorepo/tool-validation.js +20 -23
- package/dist/nx/index.d.ts +7 -4
- package/dist/nx/index.d.ts.map +1 -1
- package/dist/nx/index.js +117 -56
- package/managed/raw/tooling/devenv +71 -0
- package/managed/raw/tooling/direnv/github-actions-bootstrap.sh +24 -7
- package/managed/templates/github/actions/cache-nix-devenv/action.yml +13 -11
- package/managed/templates/github/actions/save-nix-devenv/action.yml +34 -3
- package/managed/templates/github/actions/setup-devenv/action.yml +44 -15
- package/managed/templates/github/workflows/managed-files.yml +64 -12
- package/package.json +4 -4
- package/src/cli.ts +10 -3
- package/src/github-ci/index.test.ts +53 -4
- package/src/github-ci/index.ts +37 -5
- package/src/lib/json.ts +3 -1
- package/src/lib/workspace.ts +1 -0
- package/src/monorepo/__tests__/ci-workflow.test.ts +37 -1
- package/src/monorepo/__tests__/publish-workflow.test.ts +39 -4
- package/src/monorepo/ci-workflow.ts +14 -4
- package/src/monorepo/github-runs-on.ts +45 -0
- package/src/monorepo/index.ts +7 -7
- package/src/monorepo/managed-files.test.ts +58 -0
- package/src/monorepo/managed-files.ts +59 -76
- package/src/monorepo/package-policy.test.ts +31 -0
- package/src/monorepo/package-policy.ts +2 -2
- package/src/monorepo/packs/index.ts +3 -1
- package/src/monorepo/publish-workflow.ts +28 -15
- package/src/monorepo/runtime.test.ts +23 -17
- package/src/monorepo/runtime.ts +12 -17
- package/src/monorepo/tool-validation.test.ts +15 -8
- package/src/monorepo/tool-validation.ts +20 -25
- package/src/nx/index.test.ts +45 -14
- package/src/nx/index.ts +133 -64
- package/managed/raw/patches/ttsc@0.19.3.patch +0 -67
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
} from '@smoothbricks/nx-plugin/workspace-config-policy';
|
|
8
8
|
import type { Options as PrettierOptions } from 'prettier';
|
|
9
9
|
import { isSmoothBricksCodebasePackageName } from '../lib/cli-package.js';
|
|
10
|
+
import { renderRunsOnLine, type WorkflowRunsOn } from './github-runs-on.js';
|
|
10
11
|
|
|
11
12
|
const PUBLISH_WORKFLOW_FORMAT_OPTIONS = Object.freeze({
|
|
12
13
|
parser: 'yaml',
|
|
@@ -55,6 +56,8 @@ export interface PublishWorkflowDefinitionOptions {
|
|
|
55
56
|
deployProvider?: 'cloudflare';
|
|
56
57
|
repoName?: string;
|
|
57
58
|
platformTargetGlobs?: readonly string[];
|
|
59
|
+
/** Linux jobs only. Default ubuntu-latest. Same smoo.github.runsOn as CI. */
|
|
60
|
+
runsOn?: WorkflowRunsOn;
|
|
58
61
|
}
|
|
59
62
|
|
|
60
63
|
export interface PublishWorkflowInputs {
|
|
@@ -441,7 +444,8 @@ function yamlLinesForStep(step: PublishWorkflowStep, options: PublishWorkflowDef
|
|
|
441
444
|
case PublishWorkflowStepKind.UploadTraceDbs:
|
|
442
445
|
return [
|
|
443
446
|
` - name: ${step.name}`,
|
|
444
|
-
|
|
447
|
+
' # Default is success(); failure() uploads traces only when the job already failed.',
|
|
448
|
+
' if: failure()',
|
|
445
449
|
' uses: actions/upload-artifact@v7.0.1',
|
|
446
450
|
' with:',
|
|
447
451
|
` name: trace-results-${githubExpression('github.run_id')}`,
|
|
@@ -465,7 +469,8 @@ function yamlLinesForStep(step: PublishWorkflowStep, options: PublishWorkflowDef
|
|
|
465
469
|
case PublishWorkflowStepKind.SaveNixDevenv:
|
|
466
470
|
return [
|
|
467
471
|
` - name: ${step.name}`,
|
|
468
|
-
|
|
472
|
+
' # success() is default; always() still saves GH Nix cache after a red job.',
|
|
473
|
+
' if: always()',
|
|
469
474
|
' uses: ./.github/actions/save-nix-devenv',
|
|
470
475
|
' with:',
|
|
471
476
|
` nix-cache-hit: ${githubExpression('steps.setup.outputs.nix-cache-hit')}`,
|
|
@@ -498,7 +503,7 @@ function deployEnvLines(options: PublishWorkflowDefinitionOptions): string[] {
|
|
|
498
503
|
|
|
499
504
|
function conditionalRunStep(step: PublishWorkflowStep, run: string): string[] {
|
|
500
505
|
const condition = "steps.version.outputs.mode != 'none'";
|
|
501
|
-
return [` - name: ${step.name}`, ` if: ${
|
|
506
|
+
return [` - name: ${step.name}`, ` if: ${condition}`, ` run: ${run}`];
|
|
502
507
|
}
|
|
503
508
|
|
|
504
509
|
function renderSingleJobPublishWorkflowSteps(
|
|
@@ -514,7 +519,7 @@ function renderSingleJobPublishWorkflowSteps(
|
|
|
514
519
|
if (step.kind === PublishWorkflowStepKind.Build) {
|
|
515
520
|
lines.push(
|
|
516
521
|
' - name: 🐧 Build supplemental Linux targets',
|
|
517
|
-
|
|
522
|
+
" if: steps.version.outputs.mode != 'none'",
|
|
518
523
|
` run: smoo github-ci nx-run-many --targets "${LINUX_PLATFORM_TARGET_GLOBS.join(',')}" --projects "${githubExpression(
|
|
519
524
|
'steps.version.outputs.projects',
|
|
520
525
|
)}"`,
|
|
@@ -566,7 +571,7 @@ defaults:
|
|
|
566
571
|
|
|
567
572
|
jobs:
|
|
568
573
|
linux-release-candidate:
|
|
569
|
-
|
|
574
|
+
${renderRunsOnLine(options.runsOn)}
|
|
570
575
|
permissions:
|
|
571
576
|
contents: write
|
|
572
577
|
id-token: none
|
|
@@ -642,7 +647,7 @@ function renderLinuxReleaseCandidateSteps(
|
|
|
642
647
|
} else if (step.kind === PublishWorkflowStepKind.Build) {
|
|
643
648
|
lines.push(
|
|
644
649
|
` - name: ${step.name}`,
|
|
645
|
-
|
|
650
|
+
" if: steps.version.outputs.mode != 'none'",
|
|
646
651
|
' run:',
|
|
647
652
|
` smoo github-ci nx-run-many --targets build --projects "${githubExpression(
|
|
648
653
|
'steps.version.outputs.projects',
|
|
@@ -667,7 +672,7 @@ function renderLinuxReleaseCandidateSteps(
|
|
|
667
672
|
'',
|
|
668
673
|
` # Step ${stepNumber}`,
|
|
669
674
|
' - name: 🐧 Build supplemental Linux targets',
|
|
670
|
-
|
|
675
|
+
" if: steps.version.outputs.mode != 'none'",
|
|
671
676
|
' run:',
|
|
672
677
|
` smoo github-ci nx-run-many --targets "${LINUX_PLATFORM_TARGET_GLOBS.join(
|
|
673
678
|
',',
|
|
@@ -700,7 +705,7 @@ function renderLinuxReleaseCandidateSteps(
|
|
|
700
705
|
'',
|
|
701
706
|
` # Step ${stepNumber++}`,
|
|
702
707
|
' - name: 📤 Upload validated build outputs',
|
|
703
|
-
|
|
708
|
+
" if: steps.version.outputs.mode != 'none'",
|
|
704
709
|
' uses: actions/upload-artifact@v7.0.1',
|
|
705
710
|
' with:',
|
|
706
711
|
` name: publish-release-outputs-${githubExpression('github.run_id')}`,
|
|
@@ -714,7 +719,7 @@ function renderLinuxReleaseCandidateSteps(
|
|
|
714
719
|
'',
|
|
715
720
|
` # Step ${stepNumber++}`,
|
|
716
721
|
' - name: 📤 Upload supplemental Linux outputs',
|
|
717
|
-
|
|
722
|
+
" if: steps.version.outputs.mode != 'none'",
|
|
718
723
|
' uses: actions/upload-artifact@v7.0.1',
|
|
719
724
|
' with:',
|
|
720
725
|
` name: publish-linux-outputs-${githubExpression('github.run_id')}`,
|
|
@@ -730,7 +735,8 @@ function renderLinuxReleaseCandidateSteps(
|
|
|
730
735
|
'',
|
|
731
736
|
` # Step ${stepNumber}`,
|
|
732
737
|
' - name: 🧹 Cleanup and cache Nix/devenv',
|
|
733
|
-
|
|
738
|
+
' # success() is default; always() still saves GH Nix cache after a red job.',
|
|
739
|
+
' if: always()',
|
|
734
740
|
' uses: ./.github/actions/save-nix-devenv',
|
|
735
741
|
' with:',
|
|
736
742
|
` nix-cache-hit: ${githubExpression('steps.setup.outputs.nix-cache-hit')}`,
|
|
@@ -779,6 +785,11 @@ function renderMacosPlatformSteps(options: PublishWorkflowDefinitionOptions): st
|
|
|
779
785
|
` "${githubExpression('runner.temp')}/macos-platform-outputs"`,
|
|
780
786
|
'',
|
|
781
787
|
` # Step ${stepNumber++}`,
|
|
788
|
+
' - name: 🧪 Unit test macOS and iOS packages',
|
|
789
|
+
' run:',
|
|
790
|
+
` smoo github-ci nx-run-many --targets test --projects-with-targets "${MACOS_PLATFORM_TARGET_GLOBS.join(',')}"`,
|
|
791
|
+
'',
|
|
792
|
+
` # Step ${stepNumber++}`,
|
|
782
793
|
' - name: 📤 Upload macOS platform outputs',
|
|
783
794
|
' uses: actions/upload-artifact@v7.0.1',
|
|
784
795
|
' with:',
|
|
@@ -792,7 +803,8 @@ function renderMacosPlatformSteps(options: PublishWorkflowDefinitionOptions): st
|
|
|
792
803
|
'',
|
|
793
804
|
` # Step ${stepNumber}`,
|
|
794
805
|
' - name: 🧹 Cleanup and cache Nix/devenv',
|
|
795
|
-
|
|
806
|
+
' # success() is default; always() still saves GH Nix cache after a red job.',
|
|
807
|
+
' if: always()',
|
|
796
808
|
' uses: ./.github/actions/save-nix-devenv',
|
|
797
809
|
' with:',
|
|
798
810
|
` nix-cache-hit: ${githubExpression('steps.setup.outputs.nix-cache-hit')}`,
|
|
@@ -876,7 +888,7 @@ function renderFinalLinuxPublishSteps(options: PublishWorkflowDefinitionOptions)
|
|
|
876
888
|
'',
|
|
877
889
|
` # Step ${stepNumber++}`,
|
|
878
890
|
' - name: 📦 Apply verified Linux outputs',
|
|
879
|
-
` if: ${
|
|
891
|
+
` if: ${mode} != 'none'`,
|
|
880
892
|
' run:',
|
|
881
893
|
` smoo github-ci apply-outputs --source-sha "${githubExpression(
|
|
882
894
|
'needs.linux-release-candidate.outputs.release-sha',
|
|
@@ -896,7 +908,7 @@ function renderFinalLinuxPublishSteps(options: PublishWorkflowDefinitionOptions)
|
|
|
896
908
|
'',
|
|
897
909
|
` # Step ${stepNumber++}`,
|
|
898
910
|
' - name: 🍎 Apply verified macOS outputs',
|
|
899
|
-
` if: ${
|
|
911
|
+
` if: ${mode} != 'none'`,
|
|
900
912
|
' run:',
|
|
901
913
|
` smoo github-ci apply-outputs --source-sha "${githubExpression('github.sha')}"`,
|
|
902
914
|
` "${githubExpression('runner.temp')}/publish-artifacts/publish-macos-outputs-${githubExpression(
|
|
@@ -905,7 +917,7 @@ function renderFinalLinuxPublishSteps(options: PublishWorkflowDefinitionOptions)
|
|
|
905
917
|
'',
|
|
906
918
|
` # Step ${stepNumber++}`,
|
|
907
919
|
` - name: ✅ Validate restored release (${githubExpression(mode)})`,
|
|
908
|
-
` if: ${
|
|
920
|
+
` if: ${mode} != 'none'`,
|
|
909
921
|
' run: smoo monorepo validate',
|
|
910
922
|
'',
|
|
911
923
|
' # --- Release ------------------------------------------------------------',
|
|
@@ -937,7 +949,8 @@ function renderFinalLinuxPublishSteps(options: PublishWorkflowDefinitionOptions)
|
|
|
937
949
|
'',
|
|
938
950
|
` # Step ${stepNumber}`,
|
|
939
951
|
' - name: 🧹 Cleanup and cache Nix/devenv',
|
|
940
|
-
|
|
952
|
+
' # success() is default; always() still saves GH Nix cache after a red job.',
|
|
953
|
+
' if: always()',
|
|
941
954
|
' uses: ./.github/actions/save-nix-devenv',
|
|
942
955
|
' with:',
|
|
943
956
|
` nix-cache-hit: ${githubExpression('steps.setup.outputs.nix-cache-hit')}`,
|
|
@@ -3,16 +3,16 @@ import type { PackageJson } from '../lib/json.js';
|
|
|
3
3
|
import { runtimeTypesRangeForPublishedVersions, validateRuntimePins } from './runtime.js';
|
|
4
4
|
|
|
5
5
|
describe('runtimeTypesRangeForPublishedVersions', () => {
|
|
6
|
-
test('
|
|
6
|
+
test('anchors a caret range at the newest published version in the installed Node major', () => {
|
|
7
7
|
expect(
|
|
8
8
|
runtimeTypesRangeForPublishedVersions('@types/node', '24.12.0', 'major', ['24.0.0', '24.12.4', '25.9.1']),
|
|
9
|
-
).toBe('
|
|
9
|
+
).toBe('^24.12.4');
|
|
10
10
|
});
|
|
11
11
|
|
|
12
|
-
test('
|
|
13
|
-
expect(
|
|
12
|
+
test('rejects fallback types from a different Node major', () => {
|
|
13
|
+
expect(() =>
|
|
14
14
|
runtimeTypesRangeForPublishedVersions('@types/node', '26.0.0', 'major', ['24.12.4', '25.9.0', '25.9.1']),
|
|
15
|
-
).
|
|
15
|
+
).toThrow('runtime major 26');
|
|
16
16
|
});
|
|
17
17
|
|
|
18
18
|
test('uses the installed Bun version when @types/bun has published it', () => {
|
|
@@ -23,10 +23,10 @@ describe('runtimeTypesRangeForPublishedVersions', () => {
|
|
|
23
23
|
expect(runtimeTypesRangeForPublishedVersions('@types/bun', '1.3.15', 'exact', ['1.3.13', '1.3.14'])).toBe('1.3.14');
|
|
24
24
|
});
|
|
25
25
|
|
|
26
|
-
test('ignores non-stable version strings when choosing the
|
|
27
|
-
expect(
|
|
28
|
-
'
|
|
29
|
-
);
|
|
26
|
+
test('ignores non-stable version strings when choosing the same-major anchor', () => {
|
|
27
|
+
expect(
|
|
28
|
+
runtimeTypesRangeForPublishedVersions('@types/node', '26.0.0', 'major', ['25.9.1', '26.0.2', '26.1.0-beta.1']),
|
|
29
|
+
).toBe('^26.0.2');
|
|
30
30
|
});
|
|
31
31
|
});
|
|
32
32
|
|
|
@@ -35,23 +35,29 @@ describe('validateRuntimePins', () => {
|
|
|
35
35
|
const aligned = () => ({
|
|
36
36
|
engines: { node: '>=24.0.0' },
|
|
37
37
|
packageManager: 'bun@1.3.14',
|
|
38
|
-
devDependencies: { '@types/node': '
|
|
38
|
+
devDependencies: { '@types/node': '^24.13.0' },
|
|
39
39
|
});
|
|
40
40
|
|
|
41
|
-
test('
|
|
42
|
-
|
|
41
|
+
test('accepts caret ranges anchored at any patch or minor in the PATH Node major', () => {
|
|
42
|
+
for (const version of ['^24.0.3', '^24.13.0']) {
|
|
43
|
+
const pkg = aligned();
|
|
44
|
+
pkg.devDependencies['@types/node'] = version;
|
|
45
|
+
expect(validateRuntimePins(pkg, runtime)).toBe(0);
|
|
46
|
+
}
|
|
43
47
|
});
|
|
44
48
|
|
|
45
49
|
test('fails when @types/node tracks a different major than the PATH node', () => {
|
|
46
50
|
const pkg = aligned();
|
|
47
|
-
pkg.devDependencies['@types/node'] = '
|
|
51
|
+
pkg.devDependencies['@types/node'] = '^26.1.1';
|
|
48
52
|
expect(validateRuntimePins(pkg, runtime)).toBe(1);
|
|
49
53
|
});
|
|
50
54
|
|
|
51
|
-
test('
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
+
test('rejects exact and tilde @types/node pins even when the major matches', () => {
|
|
56
|
+
for (const version of ['24.13.0', '~24.13.0']) {
|
|
57
|
+
const pkg = aligned();
|
|
58
|
+
pkg.devDependencies['@types/node'] = version;
|
|
59
|
+
expect(validateRuntimePins(pkg, runtime)).toBe(1);
|
|
60
|
+
}
|
|
55
61
|
});
|
|
56
62
|
|
|
57
63
|
test('fails engines.node and packageManager drift against the PATH runtimes', () => {
|
package/src/monorepo/runtime.ts
CHANGED
|
@@ -66,8 +66,8 @@ export async function syncRootRuntimeVersions(root: string): Promise<void> {
|
|
|
66
66
|
/**
|
|
67
67
|
* Validate that package.json runtime pins agree with the LIVE PATH runtimes,
|
|
68
68
|
* never a stored template. Offline by design: structural alignment only
|
|
69
|
-
* (
|
|
70
|
-
* consulted. Repair: `smoo monorepo init --runtime-only` inside the devenv shell.
|
|
69
|
+
* (Node types float within the runtime major; Bun types stay exact) — the registry
|
|
70
|
+
* is not consulted. Repair: `smoo monorepo init --runtime-only` inside the devenv shell.
|
|
71
71
|
*/
|
|
72
72
|
export async function validateRootRuntimeVersions(root: string): Promise<number> {
|
|
73
73
|
const packageJson = readJsonObject(join(root, 'package.json'));
|
|
@@ -100,15 +100,10 @@ export function validateRuntimePins(packageJson: PackageJson, runtime: RuntimeVe
|
|
|
100
100
|
failures++;
|
|
101
101
|
}
|
|
102
102
|
const typesNode = packageJson.devDependencies?.['@types/node'] ?? null;
|
|
103
|
-
const typesNodeParts = typesNode ?
|
|
103
|
+
const typesNodeParts = typesNode ? /^\^(\d+)\.(\d+)\.(\d+)$/.exec(typesNode) : null;
|
|
104
104
|
if (!typesNodeParts || typesNodeParts[1] !== nodeMajor) {
|
|
105
105
|
console.error(
|
|
106
|
-
`package.json @types/node is ${typesNode ?? 'missing'} but the PATH node is v${runtime.node} —
|
|
107
|
-
);
|
|
108
|
-
failures++;
|
|
109
|
-
} else if (typesNodeParts[2] === '0' && typesNodeParts[3] === '0') {
|
|
110
|
-
console.error(
|
|
111
|
-
`package.json @types/node is pinned to the ~${nodeMajor}.0.0 floor — tilde locks the first patch line and strands the repo on early broken releases; ${repair} to repin to the newest published ${nodeMajor}.x`,
|
|
106
|
+
`package.json @types/node is ${typesNode ?? 'missing'} but the PATH node is v${runtime.node} — expected a caret range within the runtime major (^${nodeMajor}.x.y); ${repair}`,
|
|
112
107
|
);
|
|
113
108
|
failures++;
|
|
114
109
|
}
|
|
@@ -144,15 +139,15 @@ export function runtimeTypesRangeForPublishedVersions(
|
|
|
144
139
|
|
|
145
140
|
if (pinMode === 'major') {
|
|
146
141
|
const runtimeMajor = parsedRuntimeVersion[0].toString();
|
|
147
|
-
//
|
|
148
|
-
//
|
|
149
|
-
// broken early release (e.g. @types/node 24.0.x's URLPattern/DOM TS2403
|
|
150
|
-
// conflict, fixed in 24.13) with no path to the repaired minors.
|
|
142
|
+
// Anchor the range at the newest published types in the runtime major. The caret
|
|
143
|
+
// admits later patches and minors without crossing into a different Node major.
|
|
151
144
|
const latestInMajor = latestVersion(versions.filter((version) => versionMajor(version) === runtimeMajor));
|
|
152
|
-
if (latestInMajor) {
|
|
153
|
-
|
|
145
|
+
if (!latestInMajor) {
|
|
146
|
+
throw new Error(`Unable to find published ${packageName} versions for runtime major ${runtimeMajor}`);
|
|
154
147
|
}
|
|
155
|
-
|
|
148
|
+
return `^${latestInMajor}`;
|
|
149
|
+
}
|
|
150
|
+
if (versions.includes(runtimeVersion)) {
|
|
156
151
|
return runtimeVersion;
|
|
157
152
|
}
|
|
158
153
|
|
|
@@ -162,7 +157,7 @@ export function runtimeTypesRangeForPublishedVersions(
|
|
|
162
157
|
}
|
|
163
158
|
|
|
164
159
|
console.warn(`${packageName} has no published types for runtime ${runtimeVersion}; using ${latest}`);
|
|
165
|
-
return
|
|
160
|
+
return latest;
|
|
166
161
|
}
|
|
167
162
|
|
|
168
163
|
function latestVersion(versions: readonly string[]): string | null {
|
|
@@ -19,7 +19,7 @@ const registryVersions: Record<string, string[]> = {
|
|
|
19
19
|
'eslint-stdout': ['1.1.1', '1.1.2'],
|
|
20
20
|
nx: ['23.1.0'],
|
|
21
21
|
prettier: ['3.6.1'],
|
|
22
|
-
ttsc: ['0.
|
|
22
|
+
ttsc: ['0.25.0'],
|
|
23
23
|
typescript: ['6.0.3'],
|
|
24
24
|
// alias package is not fetched from registry under this name when using fallback
|
|
25
25
|
'@typescript/native': ['7.0.2'],
|
|
@@ -50,6 +50,9 @@ describe('tool configuration validation', () => {
|
|
|
50
50
|
devDependencies: {
|
|
51
51
|
'@smoothbricks/cli': 'workspace:*',
|
|
52
52
|
},
|
|
53
|
+
patchedDependencies: {
|
|
54
|
+
'ttsc@0.19.3': 'patches/ttsc@0.19.3.patch',
|
|
55
|
+
},
|
|
53
56
|
});
|
|
54
57
|
const devenvPath = join(root, 'tooling/direnv/devenv.nix');
|
|
55
58
|
await mkdir(dirname(devenvPath), { recursive: true });
|
|
@@ -76,10 +79,10 @@ describe('tool configuration validation', () => {
|
|
|
76
79
|
expect(rootPackage.devDependencies['@smoothbricks/nx-plugin']).toBe('workspace:*');
|
|
77
80
|
expect(rootPackage.devDependencies['eslint-stdout']).toBe('workspace:*');
|
|
78
81
|
expect(rootPackage.devDependencies.nx).toBe('23.1.0');
|
|
79
|
-
expect(rootPackage.devDependencies.ttsc).toBe('0.
|
|
82
|
+
expect(rootPackage.devDependencies.ttsc).toBe('0.25.0');
|
|
80
83
|
expect(rootPackage.devDependencies.typescript).toBe('^6.0.3');
|
|
81
84
|
expect(rootPackage.devDependencies['@typescript/native']).toBe('npm:typescript@^7.0.2');
|
|
82
|
-
expect(rootPackage.patchedDependencies['ttsc@0.19.3']).
|
|
85
|
+
expect(rootPackage.patchedDependencies?.['ttsc@0.19.3']).toBeUndefined();
|
|
83
86
|
expect(rootPackage.workspaces).toContain('tooling');
|
|
84
87
|
expect(toolingPackage.name).toBe('@smoothbricks/tooling');
|
|
85
88
|
expect(toolingPackage.dependencies['@smoothbricks/cli']).toBe('workspace:*');
|
|
@@ -91,7 +94,7 @@ describe('tool configuration validation', () => {
|
|
|
91
94
|
}
|
|
92
95
|
});
|
|
93
96
|
|
|
94
|
-
it('accepts newer
|
|
97
|
+
it('accepts newer dependencies after removing even an empty obsolete ttsc patch key', async () => {
|
|
95
98
|
const root = await mkdtemp(join(tmpdir(), 'smoo-tool-validation-'));
|
|
96
99
|
try {
|
|
97
100
|
await writeJson(join(root, 'package.json'), {
|
|
@@ -108,12 +111,12 @@ describe('tool configuration validation', () => {
|
|
|
108
111
|
'eslint-stdout': await currentEslintStdoutRange(),
|
|
109
112
|
nx: '24.0.0',
|
|
110
113
|
prettier: '^3.7.0',
|
|
111
|
-
ttsc: '0.
|
|
114
|
+
ttsc: '0.25.0',
|
|
112
115
|
typescript: '^6.0.0',
|
|
113
116
|
'@typescript/native': 'npm:typescript@^7.0.2',
|
|
114
117
|
},
|
|
115
118
|
patchedDependencies: {
|
|
116
|
-
'ttsc@0.19.3': '
|
|
119
|
+
'ttsc@0.19.3': '',
|
|
117
120
|
},
|
|
118
121
|
});
|
|
119
122
|
await writeJson(join(root, 'tooling/package.json'), {
|
|
@@ -143,7 +146,11 @@ describe('tool configuration validation', () => {
|
|
|
143
146
|
`,
|
|
144
147
|
);
|
|
145
148
|
|
|
149
|
+
expect(await validateToolConfig(root)).toBe(1);
|
|
150
|
+
await applyToolConfigDefaults(root);
|
|
146
151
|
expect(await validateToolConfig(root)).toBe(0);
|
|
152
|
+
const rootPackage = JSON.parse(await readFile(join(root, 'package.json'), 'utf8'));
|
|
153
|
+
expect(rootPackage.patchedDependencies?.['ttsc@0.19.3']).toBeUndefined();
|
|
147
154
|
} finally {
|
|
148
155
|
await rm(root, { recursive: true, force: true });
|
|
149
156
|
}
|
|
@@ -210,7 +217,7 @@ describe('tool configuration validation', () => {
|
|
|
210
217
|
eslint: '^10.0.0',
|
|
211
218
|
nx: '24.0.0',
|
|
212
219
|
prettier: '^3.7.0',
|
|
213
|
-
ttsc: '0.
|
|
220
|
+
ttsc: '0.25.0',
|
|
214
221
|
typescript: '^6.0.0',
|
|
215
222
|
},
|
|
216
223
|
});
|
|
@@ -224,7 +231,7 @@ describe('tool configuration validation', () => {
|
|
|
224
231
|
expect(rootPackage.devDependencies['eslint-stdout']).toBe(await currentEslintStdoutRange());
|
|
225
232
|
expect(rootPackage.devDependencies['@smoothbricks/nx-plugin']).toBe(await currentNxPluginRange());
|
|
226
233
|
expect(rootPackage.devDependencies['@typescript/native']).toBe('npm:typescript@^7.0.2');
|
|
227
|
-
expect(rootPackage.patchedDependencies?.['ttsc@0.19.3']).
|
|
234
|
+
expect(rootPackage.patchedDependencies?.['ttsc@0.19.3']).toBeUndefined();
|
|
228
235
|
expect(toolingPackage.name).toBe('@fixture/tooling');
|
|
229
236
|
expect(toolingPackage.dependencies['@smoothbricks/cli']).toBe(await currentCliRange());
|
|
230
237
|
} finally {
|
|
@@ -56,8 +56,7 @@ const rootDevDependencies: RequiredDependency[] = [
|
|
|
56
56
|
},
|
|
57
57
|
{ name: 'nx', fallbackVersion: '23.1.0', minimumVersion: '23.1.0' },
|
|
58
58
|
{ name: 'prettier', fallbackVersion: '^3.6.1', minimumVersion: '3.6.0', prefix: '^' },
|
|
59
|
-
|
|
60
|
-
{ name: 'ttsc', fallbackVersion: '0.19.3' },
|
|
59
|
+
{ name: 'ttsc', fallbackVersion: '0.25.0' },
|
|
61
60
|
// Nx and typescript-eslint still load the TypeScript JS API (6.x).
|
|
62
61
|
// Compilation is exclusively delegated to ttsc by the Nx plugin targets.
|
|
63
62
|
{ name: 'typescript', fallbackVersion: '^6.0.3', minimumVersion: '6.0.0', prefix: '^' },
|
|
@@ -75,8 +74,7 @@ const requiredDevenvPackages = ['bun', 'git', 'git-format-staged', 'jq', 'alejan
|
|
|
75
74
|
// never against a version template here.
|
|
76
75
|
const nodePackagePattern = /(^|\s)nodejs(_\d+|_latest)?(\s|#|$)/m;
|
|
77
76
|
|
|
78
|
-
const
|
|
79
|
-
const TTSC_PATCH_PATH = 'patches/ttsc@0.19.3.patch';
|
|
77
|
+
const obsoleteTtscPatchedDependencyKey = 'ttsc@0.19.3';
|
|
80
78
|
|
|
81
79
|
export async function applyToolConfigDefaults(root: string): Promise<void> {
|
|
82
80
|
const context = await readToolContext(root);
|
|
@@ -89,7 +87,7 @@ export async function validateToolConfig(root: string): Promise<number> {
|
|
|
89
87
|
const context = await readToolContext(root);
|
|
90
88
|
return (
|
|
91
89
|
validateRootDevDependencies(context.policy, context.rootPackage) +
|
|
92
|
-
|
|
90
|
+
validateObsoleteTtscPatchRemoved(context.rootPackage) +
|
|
93
91
|
validateToolingPackage(root, context.policy) +
|
|
94
92
|
validateToolingWorkspace(context.rootPackage) +
|
|
95
93
|
validateDevenvPackages(root)
|
|
@@ -113,7 +111,7 @@ export async function applyRootDevDependencyDefaults(root: string, context: Tool
|
|
|
113
111
|
if (delete devDependencies['@smoothbricks/cli']) {
|
|
114
112
|
changed = true;
|
|
115
113
|
}
|
|
116
|
-
changed =
|
|
114
|
+
changed = removeObsoleteTtscPatch(pkg) || changed;
|
|
117
115
|
if (changed) {
|
|
118
116
|
writeJsonObject(join(root, 'package.json'), pkg);
|
|
119
117
|
console.log('updated package.json workspace tool dependencies');
|
|
@@ -129,7 +127,6 @@ async function applyRootPackageToolDefaults(root: string, context: ToolContext):
|
|
|
129
127
|
}
|
|
130
128
|
let dependencyChanged = false;
|
|
131
129
|
let workspaceChanged = false;
|
|
132
|
-
let patchChanged = false;
|
|
133
130
|
const devDependencies = ensureDependencyMap(pkg, 'devDependencies');
|
|
134
131
|
for (const dependency of rootDevDependencies) {
|
|
135
132
|
const current = devDependencies[dependency.name];
|
|
@@ -142,8 +139,8 @@ async function applyRootPackageToolDefaults(root: string, context: ToolContext):
|
|
|
142
139
|
dependencyChanged = true;
|
|
143
140
|
}
|
|
144
141
|
workspaceChanged = addWorkspacePattern(pkg, 'tooling');
|
|
145
|
-
|
|
146
|
-
if (dependencyChanged || workspaceChanged
|
|
142
|
+
dependencyChanged = removeObsoleteTtscPatch(pkg) || dependencyChanged;
|
|
143
|
+
if (dependencyChanged || workspaceChanged) {
|
|
147
144
|
writeJsonObject(join(root, 'package.json'), pkg);
|
|
148
145
|
}
|
|
149
146
|
console.log(
|
|
@@ -156,11 +153,6 @@ async function applyRootPackageToolDefaults(root: string, context: ToolContext):
|
|
|
156
153
|
? 'updated package.json tooling workspace'
|
|
157
154
|
: 'unchanged package.json tooling workspace',
|
|
158
155
|
);
|
|
159
|
-
console.log(
|
|
160
|
-
patchChanged
|
|
161
|
-
? 'updated package.json patchedDependencies for ttsc'
|
|
162
|
-
: 'unchanged package.json patchedDependencies for ttsc',
|
|
163
|
-
);
|
|
164
156
|
}
|
|
165
157
|
|
|
166
158
|
export function applyToolingPackageDefaults(root: string, policy: ToolPolicy): void {
|
|
@@ -252,22 +244,25 @@ export function validateRootDevDependencies(policy: ToolPolicy, rootPackage: Pac
|
|
|
252
244
|
return failures;
|
|
253
245
|
}
|
|
254
246
|
|
|
255
|
-
function
|
|
256
|
-
const
|
|
257
|
-
|
|
247
|
+
function removeObsoleteTtscPatch(pkg: PackageJson): boolean {
|
|
248
|
+
const patchedDependencies = pkg.patchedDependencies;
|
|
249
|
+
if (!patchedDependencies || !(obsoleteTtscPatchedDependencyKey in patchedDependencies)) {
|
|
250
|
+
return false;
|
|
251
|
+
}
|
|
252
|
+
delete patchedDependencies[obsoleteTtscPatchedDependencyKey];
|
|
253
|
+
if (Object.keys(patchedDependencies).length === 0) {
|
|
254
|
+
delete pkg.patchedDependencies;
|
|
255
|
+
}
|
|
256
|
+
return true;
|
|
258
257
|
}
|
|
259
258
|
|
|
260
|
-
function
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
}
|
|
264
|
-
const actual = rootPackage.patchedDependencies?.[TTSC_PATCHED_DEPENDENCY_KEY];
|
|
265
|
-
if (actual === TTSC_PATCH_PATH) {
|
|
259
|
+
function validateObsoleteTtscPatchRemoved(rootPackage: PackageJson | null): number {
|
|
260
|
+
const patchedDependencies = rootPackage?.patchedDependencies;
|
|
261
|
+
if (!patchedDependencies || !(obsoleteTtscPatchedDependencyKey in patchedDependencies)) {
|
|
266
262
|
return 0;
|
|
267
263
|
}
|
|
268
264
|
console.error(
|
|
269
|
-
`package.json patchedDependencies.${
|
|
270
|
-
(typeof actual === 'string' ? `; found ${actual}` : ' (required for declaration-emit fix)'),
|
|
265
|
+
`package.json patchedDependencies.${obsoleteTtscPatchedDependencyKey} must be removed; ttsc 0.25.0 emits typia declarations without the obsolete patch`,
|
|
271
266
|
);
|
|
272
267
|
return 1;
|
|
273
268
|
}
|
package/src/nx/index.test.ts
CHANGED
|
@@ -4,12 +4,12 @@ import {
|
|
|
4
4
|
formatProjectTargetLines,
|
|
5
5
|
nxCacheDirectories,
|
|
6
6
|
nxResetCommand,
|
|
7
|
-
nxShowProjectCommand,
|
|
8
|
-
projectNamesFromNxShowProjectsOutput,
|
|
9
7
|
projectNamesWithTarget,
|
|
10
8
|
projectRootFromNxProjectJson,
|
|
9
|
+
projectTargetsFromNxProjects,
|
|
11
10
|
targetDependenciesFromNxProjectJson,
|
|
12
11
|
targetNamesFromNxProjectJson,
|
|
12
|
+
targetNamesFromProjects,
|
|
13
13
|
targetOutputsFromNxProjectJson,
|
|
14
14
|
} from './index.js';
|
|
15
15
|
|
|
@@ -18,10 +18,6 @@ describe('Nx helper command construction', () => {
|
|
|
18
18
|
expect(nxResetCommand()).toEqual({ command: 'nx', args: ['reset'] });
|
|
19
19
|
});
|
|
20
20
|
|
|
21
|
-
it('builds explicit nx project metadata invocation', () => {
|
|
22
|
-
expect(nxShowProjectCommand('cli')).toEqual({ command: 'nx', args: ['show', 'project', 'cli', '--json'] });
|
|
23
|
-
});
|
|
24
|
-
|
|
25
21
|
it('selects only local Nx cache directories', () => {
|
|
26
22
|
expect(nxCacheDirectories('/repo')).toEqual([
|
|
27
23
|
join('/repo', '.nx/cache'),
|
|
@@ -54,14 +50,6 @@ describe('Nx helper output formatting', () => {
|
|
|
54
50
|
).toEqual(['cli', 'web']);
|
|
55
51
|
});
|
|
56
52
|
|
|
57
|
-
it('parses JSON project lists from nx show projects', () => {
|
|
58
|
-
expect(projectNamesFromNxShowProjectsOutput('["web","cli"]\n')).toEqual(['cli', 'web']);
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it('parses legacy newline project lists from nx show projects', () => {
|
|
62
|
-
expect(projectNamesFromNxShowProjectsOutput('web\ncli\n')).toEqual(['cli', 'web']);
|
|
63
|
-
});
|
|
64
|
-
|
|
65
53
|
it('extracts sorted target names from Nx project JSON', () => {
|
|
66
54
|
expect(targetNamesFromNxProjectJson({ targets: { test: {}, build: {}, lint: {} } })).toEqual([
|
|
67
55
|
'build',
|
|
@@ -113,4 +101,47 @@ describe('Nx helper output formatting', () => {
|
|
|
113
101
|
it('treats missing target metadata as an empty project', () => {
|
|
114
102
|
expect(targetNamesFromNxProjectJson({ name: 'cli' })).toEqual([]);
|
|
115
103
|
});
|
|
104
|
+
|
|
105
|
+
it('projects every resolved graph node without per-project CLI parsing', () => {
|
|
106
|
+
const projects = {
|
|
107
|
+
web: {
|
|
108
|
+
root: 'packages/web',
|
|
109
|
+
targets: {
|
|
110
|
+
test: {
|
|
111
|
+
executor: '@smoothbricks/nx-plugin:bounded-exec',
|
|
112
|
+
dependsOn: ['build'],
|
|
113
|
+
options: { command: 'bun test', timeoutMs: 120_000 },
|
|
114
|
+
},
|
|
115
|
+
build: { outputs: ['{projectRoot}/dist'] },
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
cli: { root: 'packages/cli', targets: { lint: {} } },
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
expect(targetNamesFromProjects(projects).sort()).toEqual(['build', 'lint', 'test']);
|
|
122
|
+
expect(projectTargetsFromNxProjects(projects)).toEqual([
|
|
123
|
+
{
|
|
124
|
+
project: 'cli',
|
|
125
|
+
root: 'packages/cli',
|
|
126
|
+
targets: ['lint'],
|
|
127
|
+
buildDependsOn: undefined,
|
|
128
|
+
targetDependencies: new Map(),
|
|
129
|
+
targetExecutors: new Map(),
|
|
130
|
+
targetOptions: new Map(),
|
|
131
|
+
targetOutputs: new Map(),
|
|
132
|
+
targetScripts: new Map(),
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
project: 'web',
|
|
136
|
+
root: 'packages/web',
|
|
137
|
+
targets: ['build', 'test'],
|
|
138
|
+
buildDependsOn: undefined,
|
|
139
|
+
targetDependencies: new Map([['test', ['build']]]),
|
|
140
|
+
targetExecutors: new Map([['test', '@smoothbricks/nx-plugin:bounded-exec']]),
|
|
141
|
+
targetOptions: new Map([['test', { command: 'bun test', timeoutMs: 120_000 }]]),
|
|
142
|
+
targetOutputs: new Map([['build', ['{projectRoot}/dist']]]),
|
|
143
|
+
targetScripts: new Map(),
|
|
144
|
+
},
|
|
145
|
+
]);
|
|
146
|
+
});
|
|
116
147
|
});
|