@bleedingdev/modern-js-create 3.4.0-ultramodern.1 → 3.4.0-ultramodern.2
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/cjs/ultramodern-workspace/add-vertical.cjs +18 -0
- package/dist/cjs/ultramodern-workspace/contracts.cjs +3 -3
- package/dist/cjs/ultramodern-workspace/descriptors.cjs +2 -2
- package/dist/cjs/ultramodern-workspace/module-federation.cjs +10 -1
- package/dist/cjs/ultramodern-workspace/package-json.cjs +74 -13
- package/dist/cjs/ultramodern-workspace/versions.cjs +1 -5
- package/dist/cjs/ultramodern-workspace/workspace-scripts.cjs +7 -0
- package/dist/cjs/ultramodern-workspace/write-workspace.cjs +3 -7
- package/dist/esm/ultramodern-workspace/add-vertical.js +19 -1
- package/dist/esm/ultramodern-workspace/contracts.js +4 -4
- package/dist/esm/ultramodern-workspace/descriptors.js +2 -2
- package/dist/esm/ultramodern-workspace/module-federation.js +10 -1
- package/dist/esm/ultramodern-workspace/package-json.js +62 -10
- package/dist/esm/ultramodern-workspace/versions.js +2 -3
- package/dist/esm/ultramodern-workspace/workspace-scripts.js +5 -1
- package/dist/esm/ultramodern-workspace/write-workspace.js +5 -9
- package/dist/esm-node/ultramodern-workspace/add-vertical.js +19 -1
- package/dist/esm-node/ultramodern-workspace/contracts.js +4 -4
- package/dist/esm-node/ultramodern-workspace/descriptors.js +2 -2
- package/dist/esm-node/ultramodern-workspace/module-federation.js +10 -1
- package/dist/esm-node/ultramodern-workspace/package-json.js +62 -10
- package/dist/esm-node/ultramodern-workspace/versions.js +2 -3
- package/dist/esm-node/ultramodern-workspace/workspace-scripts.js +5 -1
- package/dist/esm-node/ultramodern-workspace/write-workspace.js +5 -9
- package/dist/types/ultramodern-workspace/package-json.d.ts +11 -2
- package/dist/types/ultramodern-workspace/versions.d.ts +1 -2
- package/dist/types/ultramodern-workspace/workspace-scripts.d.ts +1 -0
- package/package.json +4 -4
- package/template-workspace/AGENTS.md.handlebars +2 -1
- package/template-workspace/README.md.handlebars +7 -0
- package/template-workspace/oxfmt.config.ts +3 -1
- package/template-workspace/oxlint.config.ts +3 -1
- package/template-workspace/scripts/bootstrap-agent-skills.mjs +23 -8
- package/templates/app/ultramodern-route-head.tsx.handlebars +20 -8
- package/templates/packages/shared-contracts-index.ts +206 -272
- package/templates/workspace-scripts/ultramodern-typecheck.mjs +197 -0
- package/templates/workspace-scripts/validate-ultramodern-workspace.mjs.handlebars +104 -2
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { spawnSync } from 'node:child_process';
|
|
4
|
+
import {
|
|
5
|
+
accessSync,
|
|
6
|
+
chmodSync,
|
|
7
|
+
constants,
|
|
8
|
+
existsSync,
|
|
9
|
+
mkdirSync,
|
|
10
|
+
} from 'node:fs';
|
|
11
|
+
import os from 'node:os';
|
|
12
|
+
import { dirname, join } from 'node:path';
|
|
13
|
+
import { fileURLToPath } from 'node:url';
|
|
14
|
+
|
|
15
|
+
const args = process.argv.slice(2);
|
|
16
|
+
const workspaceRoot = join(dirname(fileURLToPath(import.meta.url)), '..');
|
|
17
|
+
const cpuCount = Math.max(
|
|
18
|
+
1,
|
|
19
|
+
typeof os.availableParallelism === 'function'
|
|
20
|
+
? os.availableParallelism()
|
|
21
|
+
: os.cpus().length,
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
function fail(message) {
|
|
25
|
+
console.error(message);
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function parsePositiveInt(value, label) {
|
|
30
|
+
const parsed = Number(value);
|
|
31
|
+
if (!Number.isInteger(parsed) || parsed < 1) {
|
|
32
|
+
fail(`${label} must be a positive integer.`);
|
|
33
|
+
}
|
|
34
|
+
return parsed;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function envPositiveInt(name, fallback) {
|
|
38
|
+
const value = process.env[name]?.trim();
|
|
39
|
+
return value ? parsePositiveInt(value, name) : fallback;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function readArgs() {
|
|
43
|
+
let buildTarget;
|
|
44
|
+
let projectTarget;
|
|
45
|
+
let checkers;
|
|
46
|
+
let builders;
|
|
47
|
+
const passthrough = [];
|
|
48
|
+
|
|
49
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
50
|
+
const arg = args[index];
|
|
51
|
+
const next = args[index + 1];
|
|
52
|
+
|
|
53
|
+
if (arg === '--') {
|
|
54
|
+
passthrough.push(...args.slice(index + 1));
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (arg === '--build' || arg === '-b') {
|
|
59
|
+
buildTarget = next && !next.startsWith('-') ? next : 'tsconfig.json';
|
|
60
|
+
if (buildTarget === next) {
|
|
61
|
+
index += 1;
|
|
62
|
+
}
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (arg === '--project' || arg === '-p') {
|
|
67
|
+
if (!next || next.startsWith('-')) {
|
|
68
|
+
fail(`${arg} requires a tsconfig path.`);
|
|
69
|
+
}
|
|
70
|
+
projectTarget = next;
|
|
71
|
+
index += 1;
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (arg === '--checkers') {
|
|
76
|
+
if (!next || next.startsWith('-')) {
|
|
77
|
+
fail('--checkers requires a positive integer.');
|
|
78
|
+
}
|
|
79
|
+
checkers = parsePositiveInt(next, '--checkers');
|
|
80
|
+
index += 1;
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (arg === '--builders') {
|
|
85
|
+
if (!next || next.startsWith('-')) {
|
|
86
|
+
fail('--builders requires a positive integer.');
|
|
87
|
+
}
|
|
88
|
+
builders = parsePositiveInt(next, '--builders');
|
|
89
|
+
index += 1;
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
passthrough.push(arg);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (buildTarget && projectTarget) {
|
|
97
|
+
fail('Choose either --build or --project, not both.');
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return {
|
|
101
|
+
mode: buildTarget ? 'build' : 'project',
|
|
102
|
+
target: buildTarget ?? projectTarget ?? 'tsconfig.json',
|
|
103
|
+
checkers,
|
|
104
|
+
builders,
|
|
105
|
+
passthrough,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function resolveTsgoBinary() {
|
|
110
|
+
const explicitBinary = process.env.EFFECT_TSGO_BIN || process.env.TSGO_BIN;
|
|
111
|
+
if (explicitBinary) {
|
|
112
|
+
return explicitBinary;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const cli = process.env.EFFECT_TSGO_CLI || 'effect-tsgo';
|
|
116
|
+
const result = spawnSync(cli, ['get-exe-path'], {
|
|
117
|
+
encoding: 'utf8',
|
|
118
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
if (result.error) {
|
|
122
|
+
fail(
|
|
123
|
+
`Unable to run ${cli}. Run pnpm install or set EFFECT_TSGO_BIN to the native-preview tsgo binary.`,
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
if (result.status !== 0) {
|
|
127
|
+
fail(
|
|
128
|
+
result.stderr?.trim() ||
|
|
129
|
+
`${cli} get-exe-path exited with status ${result.status}.`,
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return result.stdout.trim() || cli;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const parsed = readArgs();
|
|
137
|
+
const defaultBuilders = Math.min(8, Math.max(1, Math.floor(cpuCount / 2)));
|
|
138
|
+
const builders =
|
|
139
|
+
parsed.builders ??
|
|
140
|
+
envPositiveInt('ULTRAMODERN_TSGO_BUILDERS', defaultBuilders);
|
|
141
|
+
const defaultCheckers =
|
|
142
|
+
parsed.mode === 'build'
|
|
143
|
+
? Math.min(4, Math.max(1, Math.floor(cpuCount / builders)))
|
|
144
|
+
: Math.min(8, Math.max(2, cpuCount - 1));
|
|
145
|
+
const checkers =
|
|
146
|
+
parsed.checkers ??
|
|
147
|
+
envPositiveInt('ULTRAMODERN_TSGO_CHECKERS', defaultCheckers);
|
|
148
|
+
const tsgoBin = resolveTsgoBinary();
|
|
149
|
+
|
|
150
|
+
if (!existsSync(tsgoBin)) {
|
|
151
|
+
fail(`TS-Go binary not found at ${tsgoBin}. Run pnpm install.`);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
try {
|
|
155
|
+
accessSync(tsgoBin, constants.X_OK);
|
|
156
|
+
} catch {
|
|
157
|
+
chmodSync(tsgoBin, 0o755);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
mkdirSync(join(workspaceRoot, 'node_modules/.cache/tsgo'), {
|
|
161
|
+
recursive: true,
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
const tsgoArgs =
|
|
165
|
+
parsed.mode === 'build'
|
|
166
|
+
? [
|
|
167
|
+
'--build',
|
|
168
|
+
parsed.target,
|
|
169
|
+
'--pretty',
|
|
170
|
+
'false',
|
|
171
|
+
'--checkers',
|
|
172
|
+
String(checkers),
|
|
173
|
+
'--builders',
|
|
174
|
+
String(builders),
|
|
175
|
+
'--stopBuildOnErrors',
|
|
176
|
+
...parsed.passthrough,
|
|
177
|
+
]
|
|
178
|
+
: [
|
|
179
|
+
'--project',
|
|
180
|
+
parsed.target,
|
|
181
|
+
'--noEmit',
|
|
182
|
+
'--pretty',
|
|
183
|
+
'false',
|
|
184
|
+
'--checkers',
|
|
185
|
+
String(checkers),
|
|
186
|
+
...parsed.passthrough,
|
|
187
|
+
];
|
|
188
|
+
|
|
189
|
+
const result = spawnSync(tsgoBin, tsgoArgs, {
|
|
190
|
+
stdio: 'inherit',
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
if (result.error) {
|
|
194
|
+
throw result.error;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
process.exit(result.status ?? 1);
|
|
@@ -91,6 +91,7 @@ const expectedRemoteSubsetsForRefs = refs =>
|
|
|
91
91
|
.map(expectedRemoteContractSubset);
|
|
92
92
|
const requiredMicroVerticalPaths = vertical => [
|
|
93
93
|
`${vertical.path}/package.json`,
|
|
94
|
+
`${vertical.path}/tsconfig.json`,
|
|
94
95
|
`${vertical.path}/modern.config.ts`,
|
|
95
96
|
`${vertical.path}/module-federation.config.ts`,
|
|
96
97
|
`${vertical.path}/api/effect/index.ts`,
|
|
@@ -222,6 +223,15 @@ const assertMicroVerticalContractGraph = ({
|
|
|
222
223
|
'.modernjs/ultramodern-generated-contract.json shell moduleFederation.remotes',
|
|
223
224
|
'regenerate the generated shell Module Federation contract',
|
|
224
225
|
);
|
|
226
|
+
assertSameJson(
|
|
227
|
+
shellContract.ssr,
|
|
228
|
+
{
|
|
229
|
+
mode: 'string',
|
|
230
|
+
moduleFederationAppSSR: true,
|
|
231
|
+
},
|
|
232
|
+
'.modernjs/ultramodern-generated-contract.json shell SSR contract',
|
|
233
|
+
'restore generated string SSR Module Federation settings',
|
|
234
|
+
);
|
|
225
235
|
|
|
226
236
|
for (const vertical of fullStackVerticals) {
|
|
227
237
|
const expectedRefs = vertical.verticalRefs ?? [];
|
|
@@ -353,6 +363,7 @@ const assertMicroVerticalContractGraph = ({
|
|
|
353
363
|
contract: contractEntry.effect?.contract,
|
|
354
364
|
client: contractEntry.effect?.client,
|
|
355
365
|
},
|
|
366
|
+
ssr: contractEntry.ssr,
|
|
356
367
|
},
|
|
357
368
|
{
|
|
358
369
|
kind: 'vertical',
|
|
@@ -369,6 +380,10 @@ const assertMicroVerticalContractGraph = ({
|
|
|
369
380
|
contract: './shared/effect/api',
|
|
370
381
|
client: './effect/client',
|
|
371
382
|
},
|
|
383
|
+
ssr: {
|
|
384
|
+
mode: 'string',
|
|
385
|
+
moduleFederationAppSSR: true,
|
|
386
|
+
},
|
|
372
387
|
},
|
|
373
388
|
`.modernjs/ultramodern-generated-contract.json apps.${vertical.id}`,
|
|
374
389
|
'regenerate the generated MicroVertical contract entry',
|
|
@@ -511,10 +526,79 @@ const assertCloudflareQualityGates = (appId, qualityGates) => {
|
|
|
511
526
|
assert(qualityGates?.csp?.finalMode === 'report-only-dogfood', `${appId} CSP final mode decision is missing`);
|
|
512
527
|
};
|
|
513
528
|
const extractAssetPrefixExpression = modernConfig => {
|
|
514
|
-
const match = /const
|
|
529
|
+
const match = /const\s+assetPrefix\s*=\s*(?<expression>[\s\S]*?);/u.exec(modernConfig);
|
|
515
530
|
assert(match?.groups?.expression, 'modern.config.ts must assign assetPrefix');
|
|
516
531
|
return match.groups.expression;
|
|
517
532
|
};
|
|
533
|
+
const stripYamlInlineComment = value => {
|
|
534
|
+
let quote = undefined;
|
|
535
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
536
|
+
const character = value[index];
|
|
537
|
+
if (quote) {
|
|
538
|
+
if (character === quote && value[index - 1] !== '\\') {
|
|
539
|
+
quote = undefined;
|
|
540
|
+
}
|
|
541
|
+
continue;
|
|
542
|
+
}
|
|
543
|
+
if (character === '"' || character === "'") {
|
|
544
|
+
quote = character;
|
|
545
|
+
continue;
|
|
546
|
+
}
|
|
547
|
+
if (character === '#' && (index === 0 || /\s/u.test(value[index - 1]))) {
|
|
548
|
+
return value.slice(0, index).trimEnd();
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
return value.trimEnd();
|
|
552
|
+
};
|
|
553
|
+
const normalizeYamlScalar = value => {
|
|
554
|
+
const trimmed = stripYamlInlineComment(value).trim();
|
|
555
|
+
if (
|
|
556
|
+
((trimmed.startsWith('"') && trimmed.endsWith('"')) ||
|
|
557
|
+
(trimmed.startsWith("'") && trimmed.endsWith("'"))) &&
|
|
558
|
+
trimmed.length >= 2
|
|
559
|
+
) {
|
|
560
|
+
return trimmed.slice(1, -1);
|
|
561
|
+
}
|
|
562
|
+
return trimmed;
|
|
563
|
+
};
|
|
564
|
+
const extractWorkflowNodeVersions = workflowText => {
|
|
565
|
+
const versions = [];
|
|
566
|
+
const lines = workflowText.split(/\r?\n/u);
|
|
567
|
+
for (let lineIndex = 0; lineIndex < lines.length; lineIndex += 1) {
|
|
568
|
+
const match = /^(?<indent>\s*)node-version\s*:\s*(?<value>.*)$/u.exec(
|
|
569
|
+
lines[lineIndex],
|
|
570
|
+
);
|
|
571
|
+
if (!match?.groups) {
|
|
572
|
+
continue;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
let value = normalizeYamlScalar(match.groups.value);
|
|
576
|
+
if (value === '' || value === '|' || value === '>') {
|
|
577
|
+
const currentIndent = match.groups.indent.length;
|
|
578
|
+
for (
|
|
579
|
+
let nextLineIndex = lineIndex + 1;
|
|
580
|
+
nextLineIndex < lines.length;
|
|
581
|
+
nextLineIndex += 1
|
|
582
|
+
) {
|
|
583
|
+
const nextLine = lines[nextLineIndex];
|
|
584
|
+
const nextTrimmed = nextLine.trim();
|
|
585
|
+
if (nextTrimmed === '' || nextTrimmed.startsWith('#')) {
|
|
586
|
+
continue;
|
|
587
|
+
}
|
|
588
|
+
const nextIndent = nextLine.match(/^\s*/u)?.[0]?.length ?? 0;
|
|
589
|
+
if (nextIndent <= currentIndent) {
|
|
590
|
+
break;
|
|
591
|
+
}
|
|
592
|
+
value = normalizeYamlScalar(nextTrimmed);
|
|
593
|
+
break;
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
if (value !== '') {
|
|
597
|
+
versions.push(value);
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
return versions;
|
|
601
|
+
};
|
|
518
602
|
const expectedWorkerName = packageSuffix => `${packageScope}-${packageSuffix}`.slice(0, 63);
|
|
519
603
|
const expectedChunkLoadingGlobal = mfName =>
|
|
520
604
|
`__ULTRAMODERN_${mfName
|
|
@@ -559,6 +643,7 @@ const requiredPaths = [
|
|
|
559
643
|
'.gitignore',
|
|
560
644
|
'package.json',
|
|
561
645
|
'pnpm-workspace.yaml',
|
|
646
|
+
'tsconfig.json',
|
|
562
647
|
'tsconfig.base.json',
|
|
563
648
|
'oxlint.config.ts',
|
|
564
649
|
'oxfmt.config.ts',
|
|
@@ -581,7 +666,9 @@ const requiredPaths = [
|
|
|
581
666
|
'scripts/setup-agent-reference-repos.mjs',
|
|
582
667
|
'scripts/ultramodern-performance-readiness.config.mjs',
|
|
583
668
|
'scripts/ultramodern-performance-readiness.mjs',
|
|
669
|
+
'scripts/ultramodern-typecheck.mjs',
|
|
584
670
|
'apps/shell-super-app/package.json',
|
|
671
|
+
'apps/shell-super-app/tsconfig.json',
|
|
585
672
|
'apps/shell-super-app/modern.config.ts',
|
|
586
673
|
'apps/shell-super-app/module-federation.config.ts',
|
|
587
674
|
'apps/shell-super-app/src/modern-app-env.d.ts',
|
|
@@ -597,9 +684,13 @@ const requiredPaths = [
|
|
|
597
684
|
'apps/shell-super-app/src/routes/ultramodern-route-metadata.ts',
|
|
598
685
|
'apps/shell-super-app/src/routes/[lang]/page.tsx',
|
|
599
686
|
...{{shellRouteMetaPathsJson}},
|
|
687
|
+
'packages/shared-contracts/package.json',
|
|
600
688
|
'packages/shared-contracts/src/index.ts',
|
|
689
|
+
'packages/shared-contracts/tsconfig.json',
|
|
690
|
+
'packages/shared-design-tokens/package.json',
|
|
601
691
|
'packages/shared-design-tokens/src/index.ts',
|
|
602
692
|
'packages/shared-design-tokens/src/tokens.css',
|
|
693
|
+
'packages/shared-design-tokens/tsconfig.json',
|
|
603
694
|
];
|
|
604
695
|
|
|
605
696
|
for (const vertical of fullStackVerticals) {
|
|
@@ -660,7 +751,12 @@ assert(generatedContract.node?.engineRange === '>=26', 'Generated contract must
|
|
|
660
751
|
assert(readText('.mise.toml').includes(`node = "${expectedNodeVersion}"`), 'mise must pin the generated Node version');
|
|
661
752
|
assert(readText('.mise.toml').includes(`pnpm = "${packageManagerPnpmVersion}"`), 'mise must pin the generated pnpm version');
|
|
662
753
|
const workflowText = readText('.github/workflows/ultramodern-workspace-gates.yml');
|
|
663
|
-
|
|
754
|
+
const workflowNodeVersions = extractWorkflowNodeVersions(workflowText);
|
|
755
|
+
assert(workflowNodeVersions.length > 0, 'CI workflow must configure setup-node node-version');
|
|
756
|
+
assert(
|
|
757
|
+
workflowNodeVersions.every(nodeVersion => nodeVersion === expectedNodeVersion),
|
|
758
|
+
`CI workflow must pin the generated Node version ${expectedNodeVersion}; found ${workflowNodeVersions.join(', ')}`,
|
|
759
|
+
);
|
|
664
760
|
assert(!workflowText.includes('FORCE_JAVASCRIPT_ACTIONS_TO_NODE24'), 'CI workflow must not carry the legacy Node 24 override');
|
|
665
761
|
assert(rootPackage.modernjs?.preset === 'presetUltramodern', 'Root must declare presetUltramodern');
|
|
666
762
|
assert(rootPackage.modernjs?.packageSource?.config === './.modernjs/ultramodern-package-source.json', 'Root must point at package source metadata');
|
|
@@ -711,6 +807,7 @@ assert(
|
|
|
711
807
|
);
|
|
712
808
|
assert(rootPackage.scripts?.['cloudflare:build'] === expectedCloudflareBuildScript, 'Root cloudflare:build script is incorrect');
|
|
713
809
|
assert(!('ultramodern:check' in (rootPackage.scripts ?? {})), 'Root must not expose ultramodern:check');
|
|
810
|
+
assert(rootPackage.scripts?.typecheck === 'node ./scripts/ultramodern-typecheck.mjs --build tsconfig.json', 'Root typecheck must run TS-Go build mode across project references');
|
|
714
811
|
assert(rootPackage.scripts?.['contract:check'] === 'node ./scripts/validate-ultramodern-workspace.mjs', 'Root must expose contract:check');
|
|
715
812
|
assert(rootPackage.scripts?.['i18n:boundaries'] === 'node ./scripts/check-ultramodern-i18n-boundaries.mjs', 'Root must expose i18n:boundaries');
|
|
716
813
|
assert(rootPackage.scripts?.['performance:readiness'] === 'node ./scripts/ultramodern-performance-readiness.mjs', 'Root must expose default-on performance readiness diagnostics');
|
|
@@ -723,6 +820,9 @@ assert(performanceReadinessConfig.includes("failOn: 'framework-invariant'"), 'Pe
|
|
|
723
820
|
assert(performanceReadinessScript.includes("ULTRAMODERN_PERFORMANCE_READINESS_DIAGNOSTICS"), 'Performance readiness script must support env opt-out');
|
|
724
821
|
assert(performanceReadinessScript.includes('ultramodern-performance-readiness-diagnostics-v1'), 'Performance readiness script must emit the deterministic report profile');
|
|
725
822
|
const i18nBoundaryScript = readText('scripts/check-ultramodern-i18n-boundaries.mjs');
|
|
823
|
+
const ultramodernTypecheckScript = readText('scripts/ultramodern-typecheck.mjs');
|
|
824
|
+
assert(ultramodernTypecheckScript.includes("'--checkers'") && ultramodernTypecheckScript.includes("'--builders'"), 'Root typecheck script must drive TS-Go checker and builder parallelism');
|
|
825
|
+
assert(ultramodernTypecheckScript.includes('effect-tsgo') && ultramodernTypecheckScript.includes('get-exe-path'), 'Root typecheck script must resolve the @effect/tsgo native-preview binary');
|
|
726
826
|
assert(
|
|
727
827
|
i18nBoundaryScript.includes("from '@modern-js/code-tools'") &&
|
|
728
828
|
i18nBoundaryScript.includes('runWorkspaceSourceCheck'),
|
|
@@ -819,6 +919,7 @@ assert(shellModernConfig.includes('assetPrefix,'), 'Shell modern.config.ts must
|
|
|
819
919
|
assert(shellContract?.config?.dev?.assetPrefix === '/', 'Shell dev asset prefix must stay origin-relative');
|
|
820
920
|
assert(shellContract?.config?.output?.assetPrefix?.default === '/', 'Shell asset prefix must default to origin-relative paths');
|
|
821
921
|
assert(JSON.stringify(shellContract?.config?.output?.assetPrefix?.envFallbackOrder) === JSON.stringify(['MODERN_ASSET_PREFIX', 'ULTRAMODERN_ASSET_PREFIX']), 'Shell asset prefix env fallback order is incorrect');
|
|
922
|
+
assert(shellContract?.config?.output?.disableTsChecker === false, 'Shell must keep the framework TypeScript checker enabled');
|
|
822
923
|
assert(shellContract?.config?.performance?.readinessDiagnostics?.default === 'enabled', 'Shell performance readiness diagnostics must be default-on');
|
|
823
924
|
assert(shellContract?.config?.performance?.readinessDiagnostics?.failOn === 'framework-invariant', 'Shell performance readiness diagnostics must only fail framework invariants by default');
|
|
824
925
|
assert(shellContract?.config?.performance?.readinessDiagnostics?.optOut?.env === 'ULTRAMODERN_PERFORMANCE_READINESS_DIAGNOSTICS=false', 'Shell performance readiness env opt-out is incorrect');
|
|
@@ -913,6 +1014,7 @@ for (const vertical of fullStackVerticals) {
|
|
|
913
1014
|
assert(contractEntry?.config?.dev?.assetPrefix === '/', `${vertical.id} dev asset prefix must stay origin-relative`);
|
|
914
1015
|
assert(contractEntry?.config?.output?.assetPrefix?.default === '/', `${vertical.id} asset prefix must default to origin-relative paths`);
|
|
915
1016
|
assert(JSON.stringify(contractEntry?.config?.output?.assetPrefix?.envFallbackOrder) === JSON.stringify(['MODERN_ASSET_PREFIX', 'ULTRAMODERN_ASSET_PREFIX']), `${vertical.id} asset prefix env fallback order is incorrect`);
|
|
1017
|
+
assert(contractEntry?.config?.output?.disableTsChecker === false, `${vertical.id} must keep the framework TypeScript checker enabled`);
|
|
916
1018
|
assert(contractEntry?.config?.performance?.readinessDiagnostics?.default === 'enabled', `${vertical.id} performance readiness diagnostics must be default-on`);
|
|
917
1019
|
assert(contractEntry?.config?.performance?.readinessDiagnostics?.failOn === 'framework-invariant', `${vertical.id} performance readiness diagnostics must only fail framework invariants by default`);
|
|
918
1020
|
assert(contractEntry?.config?.performance?.readinessDiagnostics?.optOut?.config === 'scripts/ultramodern-performance-readiness.config.mjs', `${vertical.id} performance readiness opt-out config is incorrect`);
|