@bleedingdev/modern-js-create 3.5.0-ultramodern.3 → 3.5.0-ultramodern.30
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 +21 -17
- package/dist/cjs/ultramodern-tooling/commands.cjs +772 -75
- package/dist/cjs/ultramodern-tooling/config.cjs +149 -8
- package/dist/cjs/ultramodern-workspace/add-vertical.cjs +25 -0
- package/dist/cjs/ultramodern-workspace/api.cjs +44 -0
- package/dist/cjs/ultramodern-workspace/app-files.cjs +8 -11
- package/dist/cjs/ultramodern-workspace/backend-federation.cjs +282 -0
- package/dist/cjs/ultramodern-workspace/contracts.cjs +24 -8
- package/dist/cjs/ultramodern-workspace/delivery-unit-sync.cjs +157 -0
- package/dist/cjs/ultramodern-workspace/delivery-unit.cjs +88 -0
- package/dist/cjs/ultramodern-workspace/descriptors.cjs +24 -0
- package/dist/cjs/ultramodern-workspace/module-federation.cjs +161 -50
- package/dist/cjs/ultramodern-workspace/package-json.cjs +26 -43
- package/dist/cjs/ultramodern-workspace/pnpm-workspace-policy-plan.cjs +166 -0
- package/dist/cjs/ultramodern-workspace/tooling-command-catalog.cjs +153 -0
- package/dist/cjs/ultramodern-workspace/versions.cjs +8 -3
- package/dist/cjs/ultramodern-workspace/workspace-script-plan.cjs +175 -0
- package/dist/cjs/ultramodern-workspace/workspace-scripts.cjs +104 -63
- package/dist/cjs/ultramodern-workspace/workspace-validation-contract.cjs +97 -0
- package/dist/cjs/ultramodern-workspace/write-workspace.cjs +11 -3
- package/dist/cjs/ultramodern-workspace/zerops.cjs +100 -0
- package/dist/esm/ultramodern-tooling/commands.js +775 -78
- package/dist/esm/ultramodern-tooling/config.js +144 -9
- package/dist/esm/ultramodern-workspace/add-vertical.js +26 -1
- package/dist/esm/ultramodern-workspace/api.js +43 -2
- package/dist/esm/ultramodern-workspace/app-files.js +8 -11
- package/dist/esm/ultramodern-workspace/backend-federation.js +217 -0
- package/dist/esm/ultramodern-workspace/contracts.js +24 -8
- package/dist/esm/ultramodern-workspace/delivery-unit-sync.js +108 -0
- package/dist/esm/ultramodern-workspace/delivery-unit.js +31 -0
- package/dist/esm/ultramodern-workspace/descriptors.js +13 -1
- package/dist/esm/ultramodern-workspace/module-federation.js +160 -42
- package/dist/esm/ultramodern-workspace/package-json.js +16 -40
- package/dist/esm/ultramodern-workspace/pnpm-workspace-policy-plan.js +113 -0
- package/dist/esm/ultramodern-workspace/tooling-command-catalog.js +100 -0
- package/dist/esm/ultramodern-workspace/versions.js +6 -4
- package/dist/esm/ultramodern-workspace/workspace-script-plan.js +110 -0
- package/dist/esm/ultramodern-workspace/workspace-scripts.js +88 -65
- package/dist/esm/ultramodern-workspace/workspace-validation-contract.js +59 -0
- package/dist/esm/ultramodern-workspace/write-workspace.js +14 -6
- package/dist/esm/ultramodern-workspace/zerops.js +62 -0
- package/dist/esm-node/ultramodern-tooling/commands.js +775 -78
- package/dist/esm-node/ultramodern-tooling/config.js +144 -9
- package/dist/esm-node/ultramodern-workspace/add-vertical.js +26 -1
- package/dist/esm-node/ultramodern-workspace/api.js +43 -2
- package/dist/esm-node/ultramodern-workspace/app-files.js +8 -11
- package/dist/esm-node/ultramodern-workspace/backend-federation.js +218 -0
- package/dist/esm-node/ultramodern-workspace/contracts.js +24 -8
- package/dist/esm-node/ultramodern-workspace/delivery-unit-sync.js +109 -0
- package/dist/esm-node/ultramodern-workspace/delivery-unit.js +32 -0
- package/dist/esm-node/ultramodern-workspace/descriptors.js +13 -1
- package/dist/esm-node/ultramodern-workspace/module-federation.js +160 -42
- package/dist/esm-node/ultramodern-workspace/package-json.js +16 -40
- package/dist/esm-node/ultramodern-workspace/pnpm-workspace-policy-plan.js +114 -0
- package/dist/esm-node/ultramodern-workspace/tooling-command-catalog.js +101 -0
- package/dist/esm-node/ultramodern-workspace/versions.js +6 -4
- package/dist/esm-node/ultramodern-workspace/workspace-script-plan.js +111 -0
- package/dist/esm-node/ultramodern-workspace/workspace-scripts.js +88 -65
- package/dist/esm-node/ultramodern-workspace/workspace-validation-contract.js +60 -0
- package/dist/esm-node/ultramodern-workspace/write-workspace.js +14 -6
- package/dist/esm-node/ultramodern-workspace/zerops.js +63 -0
- package/dist/types/ultramodern-tooling/config.d.ts +6 -0
- package/dist/types/ultramodern-workspace/api.d.ts +1 -0
- package/dist/types/ultramodern-workspace/backend-federation.d.ts +11 -0
- package/dist/types/ultramodern-workspace/contracts.d.ts +1 -1
- package/dist/types/ultramodern-workspace/delivery-unit-sync.d.ts +6 -0
- package/dist/types/ultramodern-workspace/delivery-unit.d.ts +27 -0
- package/dist/types/ultramodern-workspace/descriptors.d.ts +4 -0
- package/dist/types/ultramodern-workspace/module-federation.d.ts +4 -9
- package/dist/types/ultramodern-workspace/package-json.d.ts +0 -1
- package/dist/types/ultramodern-workspace/pnpm-workspace-policy-plan.d.ts +39 -0
- package/dist/types/ultramodern-workspace/tooling-command-catalog.d.ts +18 -0
- package/dist/types/ultramodern-workspace/versions.d.ts +5 -3
- package/dist/types/ultramodern-workspace/workspace-script-plan.d.ts +72 -0
- package/dist/types/ultramodern-workspace/workspace-scripts.d.ts +13 -0
- package/dist/types/ultramodern-workspace/workspace-validation-contract.d.ts +42 -0
- package/dist/types/ultramodern-workspace/zerops.d.ts +2 -0
- package/package.json +3 -3
- package/template-workspace/.gitignore.handlebars +4 -0
- package/template-workspace/README.md.handlebars +18 -15
- package/template-workspace/patches/@module-federation__bridge-react@2.6.0.patch +140 -0
- package/template-workspace/patches/@module-federation__modern-js-v3@2.6.0.patch +57 -0
- package/template-workspace/patches/drizzle-orm-ts7-strict-declarations.patch +452 -0
- package/template-workspace/patches/effect-schema-error-type-id.patch +18 -0
- package/template-workspace/pnpm-workspace.yaml.handlebars +8 -0
- package/template-workspace/scripts/bootstrap-agent-skills.mjs +11 -3
- package/templates/workspace-scripts/check-ultramodern-api-boundaries.mts +40 -2
- package/templates/workspace-scripts/generate-node-backend-federation.mjs +270 -0
- package/templates/workspace-scripts/materialize-zerops-runtime.mjs +448 -0
- package/templates/workspace-scripts/proof-cloudflare-version.mjs +237 -9
- package/templates/workspace-scripts/proof-node-backend-federation.mjs +729 -0
- package/templates/workspace-scripts/ultramodern-cloudflare-proof.mjs +188 -2
- package/templates/workspace-scripts/validate-ultramodern-workspace.mjs.handlebars +434 -18
|
@@ -5,26 +5,31 @@ import node_path from "node:path";
|
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
import { resolveCreatePackageRoot } from "../create-package-root.js";
|
|
7
7
|
import { BLEEDINGDEV_PACKAGE_NAME_PREFIX, BLEEDINGDEV_PACKAGE_SCOPE, ULTRAMODERN_SINGLE_APP_MODERN_PACKAGES, ULTRAMODERN_WORKSPACE_MODERN_PACKAGES, WORKSPACE_PACKAGE_VERSION, modernPackageSpecifier } from "../ultramodern-package-source.js";
|
|
8
|
+
import { createAppEnvDts } from "../ultramodern-workspace/app-files.js";
|
|
9
|
+
import { createBackendFederationContractFile } from "../ultramodern-workspace/backend-federation.js";
|
|
10
|
+
import { runSyncDeliveryUnit } from "../ultramodern-workspace/delivery-unit-sync.js";
|
|
8
11
|
import { validateModuleFederationTypes } from "../ultramodern-workspace/mf-validation.js";
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
+
import { createAppModernConfig, createUltramodernBuildModule, createUltramodernBuildReexportModule } from "../ultramodern-workspace/module-federation.js";
|
|
13
|
+
import { createAppMfTypesTsConfig, createAppTsConfig, createSharedPackageTsConfig, createTsConfigBase } from "../ultramodern-workspace/package-json.js";
|
|
14
|
+
import { GENERATED_TOOLING_COMMANDS, generatedToolingCommandList, generatedToolingCommands } from "../ultramodern-workspace/tooling-command-catalog.js";
|
|
15
|
+
import { DRIZZLE_ORM_VERSION, EFFECT_TSGO_VERSION, EFFECT_VERSION, EFFECT_VITEST_VERSION, MODULE_FEDERATION_VERSION, OXFMT_VERSION, OXLINT_VERSION, TYPESCRIPT_NATIVE_PREVIEW_VERSION, WRANGLER_VERSION, ZEPHYR_AGENT_VERSION, ZEPHYR_RSPACK_PLUGIN_VERSION } from "../ultramodern-workspace/versions.js";
|
|
16
|
+
import { createWorkspaceAppPackageScripts, createWorkspaceRootPackageScripts } from "../ultramodern-workspace/workspace-script-plan.js";
|
|
17
|
+
import { createWorkspaceValidationScript, createZeropsRuntimeMaterializationScript, migratedWorkspaceScriptArtifacts, migratedWorkspaceScriptBasenames } from "../ultramodern-workspace/workspace-scripts.js";
|
|
18
|
+
import { createZeropsYaml } from "../ultramodern-workspace/zerops.js";
|
|
19
|
+
import { normalizeCompactUltramodernConfig, readUltramodernConfig, synthesizeCompactUltramodernConfig, workspaceAppsFromToolingConfig } from "./config.js";
|
|
12
20
|
const commands_dirname = node_path.dirname(fileURLToPath(import.meta.url));
|
|
13
21
|
const createPackageRoot = resolveCreatePackageRoot(commands_dirname);
|
|
14
22
|
function printHelp() {
|
|
23
|
+
const commands = [
|
|
24
|
+
...generatedToolingCommandList().map((command)=>` ${command}`),
|
|
25
|
+
' skills install',
|
|
26
|
+
' skills check'
|
|
27
|
+
].join('\n');
|
|
15
28
|
process.stdout.write(`Usage:
|
|
16
29
|
modern-js-create ultramodern <command> [args]
|
|
17
30
|
|
|
18
31
|
Commands:
|
|
19
|
-
|
|
20
|
-
typecheck
|
|
21
|
-
mf-types
|
|
22
|
-
migrate-strict-effect
|
|
23
|
-
public-surface
|
|
24
|
-
cloudflare-proof
|
|
25
|
-
performance-readiness
|
|
26
|
-
skills install
|
|
27
|
-
skills check
|
|
32
|
+
${commands}
|
|
28
33
|
`);
|
|
29
34
|
}
|
|
30
35
|
function spawnNodeScript(relativeScriptPath, args, context, options = {}) {
|
|
@@ -43,6 +48,13 @@ function spawnNodeScript(relativeScriptPath, args, context, options = {}) {
|
|
|
43
48
|
if (result.error) throw result.error;
|
|
44
49
|
return result.status ?? 1;
|
|
45
50
|
}
|
|
51
|
+
function runTemplateBackedToolingCommand(command, args, context) {
|
|
52
|
+
const toolingCommand = generatedToolingCommands.find((candidate)=>candidate.command === command);
|
|
53
|
+
if (!toolingCommand?.templatePath) return;
|
|
54
|
+
return spawnNodeScript(toolingCommand.templatePath, args, context, {
|
|
55
|
+
cwd: 'invocation' === toolingCommand.cwd ? context.invocationCwd : context.workspaceRoot
|
|
56
|
+
});
|
|
57
|
+
}
|
|
46
58
|
function runRenderedModule(source, context) {
|
|
47
59
|
const tempDir = node_fs.mkdtempSync(node_path.join(node_os.tmpdir(), 'ultramodern-tool-'));
|
|
48
60
|
const tempFile = node_path.join(tempDir, 'command.mjs');
|
|
@@ -89,11 +101,45 @@ const modernPackageNames = new Set([
|
|
|
89
101
|
...ULTRAMODERN_SINGLE_APP_MODERN_PACKAGES,
|
|
90
102
|
...ULTRAMODERN_WORKSPACE_MODERN_PACKAGES
|
|
91
103
|
]);
|
|
104
|
+
function createMigrationIo(workspaceRoot, dryRun) {
|
|
105
|
+
const plan = [];
|
|
106
|
+
const rel = (p)=>(node_path.relative(workspaceRoot, p) || node_path.basename(p)).split(node_path.sep).join('/');
|
|
107
|
+
return {
|
|
108
|
+
workspaceRoot,
|
|
109
|
+
dryRun,
|
|
110
|
+
plan,
|
|
111
|
+
write (filePath, content) {
|
|
112
|
+
if (node_fs.existsSync(filePath) && node_fs.readFileSync(filePath, 'utf-8') === content) return false;
|
|
113
|
+
if (dryRun) {
|
|
114
|
+
plan.push(`[dry-run] would write ${rel(filePath)}`);
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
node_fs.mkdirSync(node_path.dirname(filePath), {
|
|
118
|
+
recursive: true
|
|
119
|
+
});
|
|
120
|
+
node_fs.writeFileSync(filePath, content, 'utf-8');
|
|
121
|
+
return true;
|
|
122
|
+
},
|
|
123
|
+
remove (filePath) {
|
|
124
|
+
if (!node_fs.existsSync(filePath)) return false;
|
|
125
|
+
if (dryRun) {
|
|
126
|
+
plan.push(`[dry-run] would delete ${rel(filePath)}`);
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
node_fs.rmSync(filePath);
|
|
130
|
+
return true;
|
|
131
|
+
},
|
|
132
|
+
log (message) {
|
|
133
|
+
if (dryRun) plan.push(`[dry-run] ${message}`);
|
|
134
|
+
else process.stdout.write(`[ultramodern] ${message}\n`);
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
}
|
|
92
138
|
function readJsonFile(filePath) {
|
|
93
139
|
return JSON.parse(node_fs.readFileSync(filePath, 'utf-8'));
|
|
94
140
|
}
|
|
95
|
-
function writeJsonFile(filePath, value) {
|
|
96
|
-
|
|
141
|
+
function writeJsonFile(io, filePath, value) {
|
|
142
|
+
return io.write(filePath, `${JSON.stringify(value, null, 2)}\n`);
|
|
97
143
|
}
|
|
98
144
|
function readOption(args, name) {
|
|
99
145
|
const prefix = `${name}=`;
|
|
@@ -126,7 +172,7 @@ function listWorkspacePackageFiles(workspaceRoot) {
|
|
|
126
172
|
withFileTypes: true
|
|
127
173
|
})){
|
|
128
174
|
if (!entry.isDirectory()) continue;
|
|
129
|
-
const packageFile =
|
|
175
|
+
const packageFile = `${directory}/${entry.name}/package.json`;
|
|
130
176
|
if (node_fs.existsSync(node_path.join(workspaceRoot, packageFile))) packageFiles.push(packageFile);
|
|
131
177
|
}
|
|
132
178
|
}
|
|
@@ -182,6 +228,18 @@ const generatedToolingDependencyPins = new Map([
|
|
|
182
228
|
ZEPHYR_RSPACK_PLUGIN_VERSION
|
|
183
229
|
]
|
|
184
230
|
]);
|
|
231
|
+
const strictEffectPackageVersionPolicyExclusions = [
|
|
232
|
+
`effect@${EFFECT_VERSION}`,
|
|
233
|
+
`@effect/opentelemetry@${EFFECT_VERSION}`
|
|
234
|
+
];
|
|
235
|
+
const moduleFederationModernJsPatchPath = `patches/@module-federation__modern-js-v3@${MODULE_FEDERATION_VERSION}.patch`;
|
|
236
|
+
const moduleFederationModernJsPatchSourcePath = node_path.join(createPackageRoot, 'template-workspace', moduleFederationModernJsPatchPath);
|
|
237
|
+
const moduleFederationBridgeReactPatchPath = `patches/@module-federation__bridge-react@${MODULE_FEDERATION_VERSION}.patch`;
|
|
238
|
+
const moduleFederationBridgeReactPatchSourcePath = node_path.join(createPackageRoot, 'template-workspace', moduleFederationBridgeReactPatchPath);
|
|
239
|
+
const effectDeclarationPatchPath = 'patches/effect-schema-error-type-id.patch';
|
|
240
|
+
const effectDeclarationPatchSourcePath = node_path.join(createPackageRoot, 'template-workspace', effectDeclarationPatchPath);
|
|
241
|
+
const drizzleOrmDeclarationPatchPath = 'patches/drizzle-orm-ts7-strict-declarations.patch';
|
|
242
|
+
const drizzleOrmDeclarationPatchSourcePath = node_path.join(createPackageRoot, 'template-workspace', drizzleOrmDeclarationPatchPath);
|
|
185
243
|
function updateGeneratedToolingDependencies(packageJson) {
|
|
186
244
|
let changed = false;
|
|
187
245
|
for (const section of [
|
|
@@ -200,10 +258,117 @@ function updateGeneratedToolingDependencies(packageJson) {
|
|
|
200
258
|
}
|
|
201
259
|
return changed;
|
|
202
260
|
}
|
|
261
|
+
const cloudflareModernDeployCommand = 'ULTRAMODERN_ZEPHYR=false MODERNJS_DEPLOY=cloudflare modern deploy';
|
|
262
|
+
const cloudflareModernDeploySkipBuildCommand = `${cloudflareModernDeployCommand} --skip-build`;
|
|
263
|
+
const cloudflareWranglerDeployCommand = 'wrangler deploy --config .output/wrangler.json';
|
|
264
|
+
const cloudflareWranglerDeployInvalidSkipBuildCommand = `${cloudflareWranglerDeployCommand} --skip-build`;
|
|
265
|
+
function removeStaleBackendFederationCommandSegments(command) {
|
|
266
|
+
return command.replace(/\s+&&\s+node\s+\S*scripts\/generate-node-backend-federation\.m[ct]s(?:\s+--app\s+\S+)?(?:\s+--target\s+\S+)?(?=\s+&&|$)/gu, '');
|
|
267
|
+
}
|
|
268
|
+
const SHELL_ONLY_OMITTED_ROOT_SCRIPTS = [
|
|
269
|
+
'node:backend-federation:generate',
|
|
270
|
+
'node:proof',
|
|
271
|
+
'zerops:materialize'
|
|
272
|
+
];
|
|
273
|
+
const splitScriptSegments = (command)=>command.split('&&').map((segment)=>segment.trim()).filter((segment)=>segment.length > 0);
|
|
274
|
+
const scriptSegmentTarget = (segment)=>segment.replace(/^pnpm\s+/u, '').split(/\s+/u)[0] ?? segment;
|
|
275
|
+
const FRAMEWORK_CHECK_TARGETS = new Set([
|
|
276
|
+
'format:check',
|
|
277
|
+
'lint',
|
|
278
|
+
'typecheck',
|
|
279
|
+
'skills:check',
|
|
280
|
+
'i18n:boundaries',
|
|
281
|
+
'api:check',
|
|
282
|
+
'contract:check',
|
|
283
|
+
'node:backend-federation:generate',
|
|
284
|
+
'node:proof',
|
|
285
|
+
'performance:readiness',
|
|
286
|
+
'bridge:check'
|
|
287
|
+
]);
|
|
288
|
+
function mergeAggregateCheckScript(consumer, framework) {
|
|
289
|
+
const frameworkSegments = splitScriptSegments(framework);
|
|
290
|
+
const consumerExtras = splitScriptSegments(consumer).filter((segment)=>!FRAMEWORK_CHECK_TARGETS.has(scriptSegmentTarget(segment)));
|
|
291
|
+
return [
|
|
292
|
+
...consumerExtras,
|
|
293
|
+
...frameworkSegments
|
|
294
|
+
].join(' && ');
|
|
295
|
+
}
|
|
296
|
+
function rewriteMigratedScriptReferences(scripts) {
|
|
297
|
+
let changed = false;
|
|
298
|
+
const pattern = new RegExp(`(scripts/(?:${migratedWorkspaceScriptBasenames.join('|')}))\\.mjs`, 'gu');
|
|
299
|
+
for (const [name, value] of Object.entries(scripts)){
|
|
300
|
+
if ('string' != typeof value) continue;
|
|
301
|
+
const next = value.replace(pattern, '$1.mts');
|
|
302
|
+
if (next !== value) {
|
|
303
|
+
scripts[name] = next;
|
|
304
|
+
changed = true;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
return changed;
|
|
308
|
+
}
|
|
309
|
+
function updateGeneratedPackageScripts(packageJson, options = {}) {
|
|
310
|
+
const scripts = packageJson.scripts;
|
|
311
|
+
if (!scripts || 'object' != typeof scripts || Array.isArray(scripts)) return false;
|
|
312
|
+
let changed = false;
|
|
313
|
+
const apps = options.apps ?? [];
|
|
314
|
+
const shellOnly = options.shellOnly ?? false;
|
|
315
|
+
const app = apps.find((candidate)=>`${candidate.directory}/package.json` === options.relativePackageFile);
|
|
316
|
+
const isRootPackage = 'package.json' === options.relativePackageFile;
|
|
317
|
+
const expectedScripts = isRootPackage ? createWorkspaceRootPackageScripts(apps.filter((candidate)=>'shell' !== candidate.kind)) : app ? createWorkspaceAppPackageScripts(app) : void 0;
|
|
318
|
+
if (expectedScripts) for (const [name, value] of Object.entries(expectedScripts)){
|
|
319
|
+
if (isRootPackage && shellOnly && SHELL_ONLY_OMITTED_ROOT_SCRIPTS.includes(name)) {
|
|
320
|
+
if (name in scripts) {
|
|
321
|
+
delete scripts[name];
|
|
322
|
+
changed = true;
|
|
323
|
+
}
|
|
324
|
+
continue;
|
|
325
|
+
}
|
|
326
|
+
const nextValue = 'check' === name && 'string' == typeof scripts[name] ? mergeAggregateCheckScript(scripts[name], value) : value;
|
|
327
|
+
if (scripts[name] !== nextValue) {
|
|
328
|
+
scripts[name] = nextValue;
|
|
329
|
+
changed = true;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
if (rewriteMigratedScriptReferences(scripts)) changed = true;
|
|
333
|
+
const build = scripts.build;
|
|
334
|
+
if ('string' == typeof build) {
|
|
335
|
+
const nextBuild = removeStaleBackendFederationCommandSegments(build);
|
|
336
|
+
if (nextBuild !== build) {
|
|
337
|
+
scripts.build = nextBuild;
|
|
338
|
+
changed = true;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
const cloudflareBuild = scripts['cloudflare:build'];
|
|
342
|
+
if ('string' == typeof cloudflareBuild) {
|
|
343
|
+
let nextCloudflareBuild = removeStaleBackendFederationCommandSegments(cloudflareBuild);
|
|
344
|
+
if (nextCloudflareBuild.includes(cloudflareModernDeployCommand) && !nextCloudflareBuild.includes(cloudflareModernDeploySkipBuildCommand)) nextCloudflareBuild = nextCloudflareBuild.replace(cloudflareModernDeployCommand, cloudflareModernDeploySkipBuildCommand);
|
|
345
|
+
nextCloudflareBuild = nextCloudflareBuild.replace(/ && node \S*scripts\/generate-public-surface-assets\.m[ct]s --app [^&]+ --target dist(?= && ULTRAMODERN_ZEPHYR=false MODERNJS_DEPLOY=cloudflare modern deploy --skip-build)/u, '');
|
|
346
|
+
nextCloudflareBuild = nextCloudflareBuild.replace(/ && node \S*scripts\/verify-cloudflare-output\.m[ct]s(?: --app [^&]+)?/u, '');
|
|
347
|
+
if (nextCloudflareBuild !== cloudflareBuild) {
|
|
348
|
+
scripts['cloudflare:build'] = nextCloudflareBuild;
|
|
349
|
+
changed = true;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
const cloudflareDeploy = scripts['cloudflare:deploy'];
|
|
353
|
+
if ('string' == typeof cloudflareDeploy && cloudflareDeploy.includes(cloudflareWranglerDeployInvalidSkipBuildCommand)) {
|
|
354
|
+
scripts['cloudflare:deploy'] = cloudflareDeploy.replace(cloudflareWranglerDeployInvalidSkipBuildCommand, cloudflareWranglerDeployCommand);
|
|
355
|
+
changed = true;
|
|
356
|
+
}
|
|
357
|
+
return changed;
|
|
358
|
+
}
|
|
203
359
|
function normalizeStrictEffectApiMetadata(value) {
|
|
204
|
-
const api = value.api;
|
|
205
|
-
if (!api || 'object' != typeof api || Array.isArray(api)) return false;
|
|
206
360
|
let changed = false;
|
|
361
|
+
const backendFederation = value.backendFederation;
|
|
362
|
+
if (backendFederation && 'object' == typeof backendFederation && !Array.isArray(backendFederation) && Object.hasOwn(backendFederation, 'entry')) {
|
|
363
|
+
delete backendFederation.entry;
|
|
364
|
+
changed = true;
|
|
365
|
+
}
|
|
366
|
+
const api = value.api;
|
|
367
|
+
if (!api || 'object' != typeof api || Array.isArray(api)) return changed;
|
|
368
|
+
if (void 0 !== api.backendFederation) {
|
|
369
|
+
delete api.backendFederation;
|
|
370
|
+
changed = true;
|
|
371
|
+
}
|
|
207
372
|
const oldEffect = api.effect;
|
|
208
373
|
if (oldEffect && 'object' == typeof oldEffect && !Array.isArray(oldEffect)) {
|
|
209
374
|
if (void 0 === api.stem && 'string' == typeof oldEffect.stem) {
|
|
@@ -265,6 +430,38 @@ function normalizeStrictEffectApiMetadata(value) {
|
|
|
265
430
|
}
|
|
266
431
|
return changed;
|
|
267
432
|
}
|
|
433
|
+
function removeGeneratedFileIfExists(io, relativePath) {
|
|
434
|
+
return io.remove(node_path.join(io.workspaceRoot, relativePath));
|
|
435
|
+
}
|
|
436
|
+
function removeStaleBackendFederationArtifacts(io, config) {
|
|
437
|
+
let changed = false;
|
|
438
|
+
for (const relativePath of [
|
|
439
|
+
"scripts/generate-node-backend-federation.mts",
|
|
440
|
+
"scripts/proof-node-backend-federation.mts",
|
|
441
|
+
"scripts/verify-cloudflare-output.mts"
|
|
442
|
+
])changed = removeGeneratedFileIfExists(io, relativePath) || changed;
|
|
443
|
+
for (const app of workspaceAppsFromToolingConfig(config))changed = removeGeneratedFileIfExists(io, node_path.join(app.directory, 'api/backend-federation.ts')) || changed;
|
|
444
|
+
return changed;
|
|
445
|
+
}
|
|
446
|
+
function updateGeneratedZeropsArtifacts(io, config) {
|
|
447
|
+
const apps = workspaceAppsFromToolingConfig(config);
|
|
448
|
+
let changed = writeTextIfChanged(io, node_path.join(io.workspaceRoot, 'zerops.yaml'), `${createZeropsYaml(config.workspace.packageScope, apps)}\n`);
|
|
449
|
+
changed = writeTextIfChanged(io, node_path.join(io.workspaceRoot, "scripts/materialize-zerops-runtime.mjs"), createZeropsRuntimeMaterializationScript()) || changed;
|
|
450
|
+
return changed;
|
|
451
|
+
}
|
|
452
|
+
function updateGeneratedBuildIdentityModules(io, config) {
|
|
453
|
+
let changed = false;
|
|
454
|
+
for (const app of workspaceAppsFromToolingConfig(config)){
|
|
455
|
+
changed = writeTextIfChanged(io, node_path.join(io.workspaceRoot, app.directory, 'src/ultramodern-build.ts'), createUltramodernBuildReexportModule()) || changed;
|
|
456
|
+
changed = writeTextIfChanged(io, node_path.join(io.workspaceRoot, app.directory, 'shared/ultramodern-build.ts'), createUltramodernBuildModule(config.workspace.packageScope, app)) || changed;
|
|
457
|
+
}
|
|
458
|
+
return changed;
|
|
459
|
+
}
|
|
460
|
+
function updateGeneratedBackendFederationContractFiles(io, config) {
|
|
461
|
+
let changed = false;
|
|
462
|
+
for (const app of workspaceAppsFromToolingConfig(config))if (app.api) changed = writeTextIfChanged(io, node_path.join(io.workspaceRoot, app.directory, 'api/backend-federation.ts'), createBackendFederationContractFile(app)) || changed;
|
|
463
|
+
return changed;
|
|
464
|
+
}
|
|
268
465
|
function replaceYamlLine(source, pattern, replacement) {
|
|
269
466
|
const updated = source.replace(pattern, replacement);
|
|
270
467
|
return {
|
|
@@ -275,16 +472,17 @@ function replaceYamlLine(source, pattern, replacement) {
|
|
|
275
472
|
function ensureYamlListItem(source, key, item) {
|
|
276
473
|
const itemLine = ` - '${item}'`;
|
|
277
474
|
const headerPattern = new RegExp(`^${key}:\\n(?:(?: - .+\\n)*)`, 'mu');
|
|
278
|
-
const existingItemPattern = new RegExp(`^ - '${item.replace(/[.*+?^${}()|[\]\\]/gu, '\\$&')}'$`, 'mu');
|
|
279
|
-
if (existingItemPattern.test(source)) return {
|
|
280
|
-
source,
|
|
281
|
-
changed: false
|
|
282
|
-
};
|
|
283
475
|
const header = source.match(headerPattern);
|
|
284
|
-
if (header)
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
476
|
+
if (header) {
|
|
477
|
+
if (header[0].split('\n').includes(itemLine)) return {
|
|
478
|
+
source,
|
|
479
|
+
changed: false
|
|
480
|
+
};
|
|
481
|
+
return {
|
|
482
|
+
source: source.replace(headerPattern, `${header[0]}${itemLine}\n`),
|
|
483
|
+
changed: true
|
|
484
|
+
};
|
|
485
|
+
}
|
|
288
486
|
const block = `${key}:\n${itemLine}\n`;
|
|
289
487
|
const afterTrustPolicyIgnore = source.replace(/^(trustPolicyIgnoreAfter: .+\n)/mu, `$1${block}`);
|
|
290
488
|
if (afterTrustPolicyIgnore !== source) return {
|
|
@@ -296,43 +494,203 @@ function ensureYamlListItem(source, key, item) {
|
|
|
296
494
|
changed: true
|
|
297
495
|
};
|
|
298
496
|
}
|
|
299
|
-
function
|
|
300
|
-
const
|
|
497
|
+
function yamlEntryPattern(entryKey, scalar = false) {
|
|
498
|
+
const bareKey = entryKey.replace(/^['"]|['"]$/gu, '');
|
|
499
|
+
if (scalar) {
|
|
500
|
+
const esc = bareKey.replace(/[.*+?^${}()|[\]\\]/gu, '\\$&');
|
|
501
|
+
return new RegExp(`^ {2}(?:'${esc}'|"${esc}"|${esc}): .+$`, 'gmu');
|
|
502
|
+
}
|
|
503
|
+
const packageName = bareKey.includes('@') ? bareKey.slice(0, bareKey.lastIndexOf('@')) : bareKey;
|
|
504
|
+
const esc = packageName.replace(/[.*+?^${}()|[\]\\]/gu, '\\$&');
|
|
505
|
+
return new RegExp(`^ {2}(?:'${esc}@[^']+'|"${esc}@[^"]+"|${esc}@[^:'"\\s]+): .+$`, 'gmu');
|
|
506
|
+
}
|
|
507
|
+
function upsertYamlEntry(source, key, entryLine, pattern) {
|
|
508
|
+
const linePattern = new RegExp(pattern.source, 'u');
|
|
509
|
+
const lines = source.split('\n');
|
|
510
|
+
let seen = false;
|
|
511
|
+
let changed = false;
|
|
512
|
+
const out = [];
|
|
513
|
+
for (const line of lines)if (linePattern.test(line)) {
|
|
514
|
+
if (seen) {
|
|
515
|
+
changed = true;
|
|
516
|
+
continue;
|
|
517
|
+
}
|
|
518
|
+
seen = true;
|
|
519
|
+
if (line !== entryLine) changed = true;
|
|
520
|
+
out.push(entryLine);
|
|
521
|
+
} else out.push(line);
|
|
522
|
+
if (seen) return {
|
|
523
|
+
source: out.join('\n'),
|
|
524
|
+
changed
|
|
525
|
+
};
|
|
526
|
+
const headerPattern = new RegExp(`^${key}:\\n(?:(?: .+\\n)*)`, 'mu');
|
|
527
|
+
const header = source.match(headerPattern);
|
|
528
|
+
if (header) {
|
|
529
|
+
if (header[0].split('\n').includes(entryLine)) return {
|
|
530
|
+
source,
|
|
531
|
+
changed: false
|
|
532
|
+
};
|
|
533
|
+
return {
|
|
534
|
+
source: source.replace(headerPattern, `${header[0]}${entryLine}\n`),
|
|
535
|
+
changed: true
|
|
536
|
+
};
|
|
537
|
+
}
|
|
538
|
+
return {
|
|
539
|
+
source: `${source.trimEnd()}\n${key}:\n${entryLine}\n`,
|
|
540
|
+
changed: true
|
|
541
|
+
};
|
|
542
|
+
}
|
|
543
|
+
function ensureYamlMapEntry(source, key, entryKey, value) {
|
|
544
|
+
return upsertYamlEntry(source, key, ` '${entryKey}': ${value}`, yamlEntryPattern(entryKey));
|
|
545
|
+
}
|
|
546
|
+
function ensureYamlScalarMapEntry(source, key, entryKey, value) {
|
|
547
|
+
return upsertYamlEntry(source, key, ` ${entryKey}: ${value}`, yamlEntryPattern(entryKey, true));
|
|
548
|
+
}
|
|
549
|
+
function removeYamlMapEntry(source, entryKey) {
|
|
550
|
+
const linePattern = new RegExp(yamlEntryPattern(entryKey).source, 'u');
|
|
551
|
+
const lines = source.split('\n');
|
|
552
|
+
let changed = false;
|
|
553
|
+
const out = [];
|
|
554
|
+
for (const line of lines){
|
|
555
|
+
if (linePattern.test(line)) {
|
|
556
|
+
changed = true;
|
|
557
|
+
continue;
|
|
558
|
+
}
|
|
559
|
+
out.push(line);
|
|
560
|
+
}
|
|
561
|
+
return changed ? {
|
|
562
|
+
source: out.join('\n'),
|
|
563
|
+
changed
|
|
564
|
+
} : {
|
|
565
|
+
source,
|
|
566
|
+
changed
|
|
567
|
+
};
|
|
568
|
+
}
|
|
569
|
+
function ensureGeneratedPatchFile(io, relativePatchPath, sourcePatchPath) {
|
|
570
|
+
const targetPath = node_path.join(io.workspaceRoot, relativePatchPath);
|
|
571
|
+
const patch = node_fs.readFileSync(sourcePatchPath, 'utf-8');
|
|
572
|
+
return io.write(targetPath, patch);
|
|
573
|
+
}
|
|
574
|
+
function removeGeneratedPatchFileIfUnchanged(io, relativePatchPath, sourcePatchPath) {
|
|
575
|
+
const targetPath = node_path.join(io.workspaceRoot, relativePatchPath);
|
|
576
|
+
if (!node_fs.existsSync(targetPath)) return false;
|
|
577
|
+
const patch = node_fs.readFileSync(sourcePatchPath, 'utf-8');
|
|
578
|
+
if (node_fs.readFileSync(targetPath, 'utf-8') !== patch) return false;
|
|
579
|
+
return io.remove(targetPath);
|
|
580
|
+
}
|
|
581
|
+
function workspaceUsesDependency(workspaceRoot, packageName) {
|
|
582
|
+
const packageJsonPaths = [
|
|
583
|
+
node_path.join(workspaceRoot, 'package.json')
|
|
584
|
+
];
|
|
585
|
+
for (const workspaceDir of [
|
|
586
|
+
'apps',
|
|
587
|
+
'verticals',
|
|
588
|
+
'packages'
|
|
589
|
+
]){
|
|
590
|
+
const absoluteWorkspaceDir = node_path.join(workspaceRoot, workspaceDir);
|
|
591
|
+
if (node_fs.existsSync(absoluteWorkspaceDir)) for (const entry of node_fs.readdirSync(absoluteWorkspaceDir, {
|
|
592
|
+
withFileTypes: true
|
|
593
|
+
})){
|
|
594
|
+
if (!entry.isDirectory()) continue;
|
|
595
|
+
const packageJsonPath = node_path.join(absoluteWorkspaceDir, entry.name, 'package.json');
|
|
596
|
+
if (node_fs.existsSync(packageJsonPath)) packageJsonPaths.push(packageJsonPath);
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
for (const packageJsonPath of packageJsonPaths){
|
|
600
|
+
const packageJson = JSON.parse(node_fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
601
|
+
for (const field of [
|
|
602
|
+
'dependencies',
|
|
603
|
+
'devDependencies',
|
|
604
|
+
'peerDependencies',
|
|
605
|
+
'optionalDependencies'
|
|
606
|
+
]){
|
|
607
|
+
const dependencies = packageJson[field];
|
|
608
|
+
if (dependencies && 'object' == typeof dependencies) {
|
|
609
|
+
if (Object.prototype.hasOwnProperty.call(dependencies, packageName)) return true;
|
|
610
|
+
for (const specifier of Object.values(dependencies))if ('string' == typeof specifier && specifier.startsWith(`npm:${packageName}@`)) return true;
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
return false;
|
|
615
|
+
}
|
|
616
|
+
function ensureGeneratedDeclarationPatches(io, options) {
|
|
617
|
+
let changed = false;
|
|
618
|
+
changed = ensureGeneratedPatchFile(io, moduleFederationModernJsPatchPath, moduleFederationModernJsPatchSourcePath) || changed;
|
|
619
|
+
changed = ensureGeneratedPatchFile(io, moduleFederationBridgeReactPatchPath, moduleFederationBridgeReactPatchSourcePath) || changed;
|
|
620
|
+
changed = ensureGeneratedPatchFile(io, effectDeclarationPatchPath, effectDeclarationPatchSourcePath) || changed;
|
|
621
|
+
changed = options.includeDrizzleOrmPatch ? ensureGeneratedPatchFile(io, drizzleOrmDeclarationPatchPath, drizzleOrmDeclarationPatchSourcePath) || changed : removeGeneratedPatchFileIfUnchanged(io, drizzleOrmDeclarationPatchPath, drizzleOrmDeclarationPatchSourcePath) || changed;
|
|
622
|
+
return changed;
|
|
623
|
+
}
|
|
624
|
+
function updateGeneratedPnpmWorkspacePolicy(io) {
|
|
625
|
+
const workspaceFile = node_path.join(io.workspaceRoot, 'pnpm-workspace.yaml');
|
|
301
626
|
if (!node_fs.existsSync(workspaceFile)) return false;
|
|
302
627
|
let source = node_fs.readFileSync(workspaceFile, 'utf-8');
|
|
303
628
|
let changed = false;
|
|
629
|
+
const usesDrizzleOrm = workspaceUsesDependency(io.workspaceRoot, 'drizzle-orm');
|
|
304
630
|
const replacements = [
|
|
305
631
|
[
|
|
306
632
|
/^ {4}'@effect\/vitest>effect': .+$/mu,
|
|
307
633
|
` '@effect/vitest>effect': '${EFFECT_VERSION}'`
|
|
308
|
-
]
|
|
634
|
+
]
|
|
635
|
+
];
|
|
636
|
+
for (const [pattern, replacement] of replacements){
|
|
637
|
+
const result = replaceYamlLine(source, pattern, replacement);
|
|
638
|
+
source = result.source;
|
|
639
|
+
changed = result.changed || changed;
|
|
640
|
+
}
|
|
641
|
+
for (const [entryKey, version] of [
|
|
309
642
|
[
|
|
310
|
-
|
|
311
|
-
|
|
643
|
+
"'@effect/opentelemetry'",
|
|
644
|
+
EFFECT_VERSION
|
|
312
645
|
],
|
|
313
646
|
[
|
|
314
|
-
|
|
315
|
-
|
|
647
|
+
"'@effect/vitest'",
|
|
648
|
+
EFFECT_VITEST_VERSION
|
|
316
649
|
],
|
|
317
650
|
[
|
|
318
|
-
|
|
319
|
-
|
|
651
|
+
'effect',
|
|
652
|
+
EFFECT_VERSION
|
|
320
653
|
]
|
|
321
|
-
]
|
|
322
|
-
|
|
323
|
-
const result = replaceYamlLine(source, pattern, replacement);
|
|
654
|
+
]){
|
|
655
|
+
const result = ensureYamlScalarMapEntry(source, 'overrides', entryKey, version);
|
|
324
656
|
source = result.source;
|
|
325
657
|
changed = result.changed || changed;
|
|
326
658
|
}
|
|
327
|
-
const
|
|
328
|
-
source =
|
|
329
|
-
changed =
|
|
330
|
-
|
|
659
|
+
const parcelWatcherBuildPolicy = ensureYamlScalarMapEntry(source, 'allowBuilds', "'@parcel/watcher'", 'true');
|
|
660
|
+
source = parcelWatcherBuildPolicy.source;
|
|
661
|
+
changed = parcelWatcherBuildPolicy.changed || changed;
|
|
662
|
+
for (const item of strictEffectPackageVersionPolicyExclusions){
|
|
663
|
+
const packageName = item.slice(0, item.lastIndexOf('@'));
|
|
664
|
+
const escapedPackageName = packageName.replace(/[.*+?^${}()|[\]\\]/gu, '\\$&');
|
|
665
|
+
const currentVersion = replaceYamlLine(source, new RegExp(`^ {2}- '${escapedPackageName}@[^']+'$`, 'gmu'), ` - '${item}'`);
|
|
666
|
+
source = currentVersion.source;
|
|
667
|
+
changed = currentVersion.changed || changed;
|
|
668
|
+
for (const policyKey of [
|
|
669
|
+
'minimumReleaseAgeExclude',
|
|
670
|
+
'trustPolicyExclude'
|
|
671
|
+
]){
|
|
672
|
+
const policyExclude = ensureYamlListItem(source, policyKey, item);
|
|
673
|
+
source = policyExclude.source;
|
|
674
|
+
changed = policyExclude.changed || changed;
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
const effectPatch = ensureYamlMapEntry(source, 'patchedDependencies', `effect@${EFFECT_VERSION}`, effectDeclarationPatchPath);
|
|
678
|
+
source = effectPatch.source;
|
|
679
|
+
changed = effectPatch.changed || changed;
|
|
680
|
+
const moduleFederationModernJsPatch = ensureYamlMapEntry(source, 'patchedDependencies', `@module-federation/modern-js-v3@${MODULE_FEDERATION_VERSION}`, moduleFederationModernJsPatchPath);
|
|
681
|
+
source = moduleFederationModernJsPatch.source;
|
|
682
|
+
changed = moduleFederationModernJsPatch.changed || changed;
|
|
683
|
+
const moduleFederationBridgeReactPatch = ensureYamlMapEntry(source, 'patchedDependencies', `@module-federation/bridge-react@${MODULE_FEDERATION_VERSION}`, moduleFederationBridgeReactPatchPath);
|
|
684
|
+
source = moduleFederationBridgeReactPatch.source;
|
|
685
|
+
changed = moduleFederationBridgeReactPatch.changed || changed;
|
|
686
|
+
const drizzleOrmPatch = usesDrizzleOrm ? ensureYamlMapEntry(source, 'patchedDependencies', `drizzle-orm@${DRIZZLE_ORM_VERSION}`, drizzleOrmDeclarationPatchPath) : removeYamlMapEntry(source, `drizzle-orm@${DRIZZLE_ORM_VERSION}`);
|
|
687
|
+
source = drizzleOrmPatch.source;
|
|
688
|
+
changed = drizzleOrmPatch.changed || changed;
|
|
689
|
+
if (changed) io.write(workspaceFile, source);
|
|
331
690
|
return changed;
|
|
332
691
|
}
|
|
333
|
-
function updateUltramodernConfig(
|
|
334
|
-
const configPath = node_path.join(workspaceRoot, '.modernjs/ultramodern.json');
|
|
335
|
-
const config = readJsonFile(configPath);
|
|
692
|
+
function updateUltramodernConfig(io, config, packageSource) {
|
|
693
|
+
const configPath = node_path.join(io.workspaceRoot, '.modernjs/ultramodern.json');
|
|
336
694
|
config.packageSource = {
|
|
337
695
|
strategy: packageSource.strategy,
|
|
338
696
|
modernPackageVersion: packageSource.modernPackageVersion,
|
|
@@ -346,16 +704,147 @@ function updateUltramodernConfig(workspaceRoot, packageSource) {
|
|
|
346
704
|
aliasPackageNamePrefix: packageSource.aliasPackageNamePrefix
|
|
347
705
|
} : {}
|
|
348
706
|
};
|
|
707
|
+
if (config.generator && 'object' == typeof config.generator && !Array.isArray(config.generator) && 'string' == typeof config.generator.version) config.generator.version = packageSource.modernPackageVersion;
|
|
349
708
|
for (const app of config.topology?.apps ?? [])if (app && 'object' == typeof app && !Array.isArray(app)) normalizeStrictEffectApiMetadata(app);
|
|
350
|
-
writeJsonFile(configPath, config);
|
|
709
|
+
writeJsonFile(io, configPath, config);
|
|
710
|
+
}
|
|
711
|
+
function writeJsonIfChanged(io, filePath, value) {
|
|
712
|
+
return io.write(filePath, `${JSON.stringify(value, null, 2)}\n`);
|
|
713
|
+
}
|
|
714
|
+
function writeTextIfChanged(io, filePath, value) {
|
|
715
|
+
return io.write(filePath, value);
|
|
716
|
+
}
|
|
717
|
+
function ensureGeneratedIgnoreRules(io) {
|
|
718
|
+
const gitignorePath = node_path.join(io.workspaceRoot, '.gitignore');
|
|
719
|
+
const existing = node_fs.existsSync(gitignorePath) ? node_fs.readFileSync(gitignorePath, 'utf-8') : '';
|
|
720
|
+
const lines = 0 === existing.trimEnd().length ? [] : existing.trimEnd().split(/\r?\n/u);
|
|
721
|
+
let changed = false;
|
|
722
|
+
for (const rule of [
|
|
723
|
+
'.mf/',
|
|
724
|
+
'**/.mf/',
|
|
725
|
+
'dist-cloudflare/',
|
|
726
|
+
'.output/',
|
|
727
|
+
'**/.output/',
|
|
728
|
+
'.modern-js/',
|
|
729
|
+
'**/.modern-js/'
|
|
730
|
+
])if (!lines.includes(rule)) {
|
|
731
|
+
lines.push(rule);
|
|
732
|
+
changed = true;
|
|
733
|
+
}
|
|
734
|
+
if (!changed) return false;
|
|
735
|
+
return io.write(gitignorePath, `${lines.join('\n')}\n`);
|
|
736
|
+
}
|
|
737
|
+
function updateGeneratedTypeScriptSurfaces(io, config) {
|
|
738
|
+
let changed = false;
|
|
739
|
+
const apps = workspaceAppsFromToolingConfig(config);
|
|
740
|
+
const remotes = apps.filter((app)=>'shell' !== app.kind);
|
|
741
|
+
changed = writeJsonIfChanged(io, node_path.join(io.workspaceRoot, 'tsconfig.base.json'), createTsConfigBase()) || changed;
|
|
742
|
+
changed = ensureGeneratedIgnoreRules(io) || changed;
|
|
743
|
+
for (const sharedPackage of [
|
|
744
|
+
'packages/shared-contracts',
|
|
745
|
+
'packages/shared-design-tokens'
|
|
746
|
+
])changed = writeJsonIfChanged(io, node_path.join(io.workspaceRoot, sharedPackage, 'tsconfig.json'), createSharedPackageTsConfig(sharedPackage)) || changed;
|
|
747
|
+
for (const app of apps){
|
|
748
|
+
changed = writeJsonIfChanged(io, node_path.join(io.workspaceRoot, app.directory, 'tsconfig.json'), createAppTsConfig(app, remotes)) || changed;
|
|
749
|
+
changed = writeJsonIfChanged(io, node_path.join(io.workspaceRoot, app.directory, 'tsconfig.mf-types.json'), createAppMfTypesTsConfig(app)) || changed;
|
|
750
|
+
changed = writeTextIfChanged(io, node_path.join(io.workspaceRoot, app.directory, 'src/modern-app-env.d.ts'), createAppEnvDts(app, remotes)) || changed;
|
|
751
|
+
}
|
|
752
|
+
return changed;
|
|
753
|
+
}
|
|
754
|
+
function updateGeneratedModernConfigs(io, config) {
|
|
755
|
+
let changed = false;
|
|
756
|
+
const apps = workspaceAppsFromToolingConfig(config);
|
|
757
|
+
const remotes = apps.filter((app)=>'shell' !== app.kind);
|
|
758
|
+
for (const app of apps)changed = writeTextIfChanged(io, node_path.join(io.workspaceRoot, app.directory, 'modern.config.ts'), createAppModernConfig(config.workspace.packageScope, app, remotes)) || changed;
|
|
759
|
+
return changed;
|
|
760
|
+
}
|
|
761
|
+
function ensureGeneratedOxfmtIgnorePatterns(io) {
|
|
762
|
+
const configPath = node_path.join(io.workspaceRoot, 'oxfmt.config.ts');
|
|
763
|
+
if (!node_fs.existsSync(configPath)) return false;
|
|
764
|
+
const source = node_fs.readFileSync(configPath, 'utf-8');
|
|
765
|
+
const requiredPatterns = [
|
|
766
|
+
'.modernjs',
|
|
767
|
+
'.output',
|
|
768
|
+
'**/modern-tanstack/**',
|
|
769
|
+
'**/routeTree.gen.*'
|
|
770
|
+
];
|
|
771
|
+
const warnUnparseable = ()=>{
|
|
772
|
+
const message = `Could not update oxfmt.config.ts ignorePatterns automatically; add these entries manually: ${requiredPatterns.join(', ')}.`;
|
|
773
|
+
if (io.dryRun) io.log(message);
|
|
774
|
+
else process.stderr.write(`[ultramodern] ${message}\n`);
|
|
775
|
+
};
|
|
776
|
+
const anchor = source.indexOf('ignorePatterns:');
|
|
777
|
+
if (-1 === anchor) {
|
|
778
|
+
warnUnparseable();
|
|
779
|
+
return false;
|
|
780
|
+
}
|
|
781
|
+
const openBracket = source.indexOf('[', anchor);
|
|
782
|
+
if (-1 === openBracket) {
|
|
783
|
+
warnUnparseable();
|
|
784
|
+
return false;
|
|
785
|
+
}
|
|
786
|
+
let depth = 0;
|
|
787
|
+
let closeBracket = -1;
|
|
788
|
+
let stringQuote;
|
|
789
|
+
for(let index = openBracket; index < source.length; index += 1){
|
|
790
|
+
const char = source[index];
|
|
791
|
+
if (stringQuote) {
|
|
792
|
+
if ('\\' === char) index += 1;
|
|
793
|
+
else if (char === stringQuote) stringQuote = void 0;
|
|
794
|
+
continue;
|
|
795
|
+
}
|
|
796
|
+
if ("'" === char || '"' === char || '`' === char) stringQuote = char;
|
|
797
|
+
else if ('[' === char) depth += 1;
|
|
798
|
+
else if (']' === char) {
|
|
799
|
+
depth -= 1;
|
|
800
|
+
if (0 === depth) {
|
|
801
|
+
closeBracket = index;
|
|
802
|
+
break;
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
if (-1 === closeBracket) {
|
|
807
|
+
warnUnparseable();
|
|
808
|
+
return false;
|
|
809
|
+
}
|
|
810
|
+
const body = source.slice(openBracket + 1, closeBracket);
|
|
811
|
+
if (body.includes('...')) {
|
|
812
|
+
warnUnparseable();
|
|
813
|
+
return false;
|
|
814
|
+
}
|
|
815
|
+
const literalPattern = /(['"`])((?:\\.|(?!\1).)*)\1/g;
|
|
816
|
+
const existing = new Set();
|
|
817
|
+
for (const match of body.matchAll(literalPattern))existing.add(match[2]);
|
|
818
|
+
const missing = requiredPatterns.filter((pattern)=>!existing.has(pattern));
|
|
819
|
+
if (0 === missing.length) return false;
|
|
820
|
+
const bodyLines = body.split('\n');
|
|
821
|
+
let indent = ' ';
|
|
822
|
+
let quote = "'";
|
|
823
|
+
for(let index = bodyLines.length - 1; index >= 0; index -= 1){
|
|
824
|
+
const literal = bodyLines[index].match(/^(\s*)(['"`])/u);
|
|
825
|
+
if (literal) {
|
|
826
|
+
indent = literal[1];
|
|
827
|
+
quote = literal[2];
|
|
828
|
+
break;
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
const head = source.slice(0, closeBracket);
|
|
832
|
+
const rest = source.slice(closeBracket);
|
|
833
|
+
const tailMatch = head.match(/(\r?\n[ \t]*)$/u);
|
|
834
|
+
const tail = tailMatch ? tailMatch[1] : '\n';
|
|
835
|
+
let bodyContent = tailMatch ? head.slice(0, head.length - tail.length) : head;
|
|
836
|
+
if (!/[[,]\s*$/u.test(bodyContent)) bodyContent = `${bodyContent},`;
|
|
837
|
+
const insertionLines = missing.map((pattern)=>`${indent}${quote}${pattern}${quote},`).join('\n');
|
|
838
|
+
const nextSource = `${bodyContent}\n${insertionLines}${tail}${rest}`;
|
|
839
|
+
return io.write(configPath, nextSource);
|
|
351
840
|
}
|
|
352
|
-
function updateReferenceTopology(
|
|
353
|
-
const topologyPath = node_path.join(workspaceRoot, 'topology/reference-topology.json');
|
|
841
|
+
function updateReferenceTopology(io) {
|
|
842
|
+
const topologyPath = node_path.join(io.workspaceRoot, 'topology/reference-topology.json');
|
|
354
843
|
if (!node_fs.existsSync(topologyPath)) return false;
|
|
355
844
|
const topology = readJsonFile(topologyPath);
|
|
356
845
|
let changed = false;
|
|
357
846
|
for (const vertical of topology.verticals ?? [])if (vertical && 'object' == typeof vertical && !Array.isArray(vertical)) changed = normalizeStrictEffectApiMetadata(vertical) || changed;
|
|
358
|
-
if (changed) writeJsonFile(topologyPath, topology);
|
|
847
|
+
if (changed) writeJsonFile(io, topologyPath, topology);
|
|
359
848
|
return changed;
|
|
360
849
|
}
|
|
361
850
|
function createMigrationPackageSource(args, current) {
|
|
@@ -408,21 +897,69 @@ function runPnpmLockfileRefresh(context) {
|
|
|
408
897
|
function runMigrateStrictEffect(args, context) {
|
|
409
898
|
if (args.includes('--help') || args.includes('-h')) {
|
|
410
899
|
process.stdout.write(`Usage:
|
|
411
|
-
modern-js-create ultramodern migrate-strict-effect --version <version> [--skip-install]
|
|
900
|
+
modern-js-create ultramodern migrate-strict-effect --version <version> [--dry-run] [--skip-install]
|
|
412
901
|
|
|
413
902
|
Updates generated UltraModern package-source metadata, Modern package aliases,
|
|
414
903
|
framework-owned toolchain pins, direct Effect API topology metadata, strict
|
|
415
|
-
Effect pnpm overrides/trust policy,
|
|
416
|
-
has to pass pnpm api:check
|
|
904
|
+
Effect pnpm overrides/trust policy, framework-owned TypeScript config
|
|
905
|
+
surfaces, and the pnpm lockfile. Source code still has to pass pnpm api:check
|
|
906
|
+
and pnpm contract:check.
|
|
907
|
+
|
|
908
|
+
When the compact config is absent but legacy UltraModern 3.2 metadata is
|
|
909
|
+
present, the compact config is synthesized from it first. Shell-only
|
|
910
|
+
workspaces skip the backend-federation and Zerops runtime stages. Pass
|
|
911
|
+
--dry-run to print the planned filesystem changes without writing anything
|
|
912
|
+
(implies --skip-install).
|
|
417
913
|
`);
|
|
418
914
|
return 0;
|
|
419
915
|
}
|
|
420
|
-
const
|
|
916
|
+
const dryRun = hasFlag(args, '--dry-run');
|
|
917
|
+
const skipInstall = dryRun || hasFlag(args, '--skip-install');
|
|
918
|
+
const io = createMigrationIo(context.workspaceRoot, dryRun);
|
|
919
|
+
const compactPath = node_path.join(io.workspaceRoot, '.modernjs/ultramodern.json');
|
|
920
|
+
let raw;
|
|
921
|
+
if (node_fs.existsSync(compactPath)) raw = readJsonFile(compactPath);
|
|
922
|
+
else {
|
|
923
|
+
const synthesized = synthesizeCompactUltramodernConfig(io.workspaceRoot);
|
|
924
|
+
if (!synthesized) throw new Error("Missing .modernjs/ultramodern.json and no legacy UltraModern metadata (.modernjs/ultramodern-generated-contract.json) was found to synthesize it from.");
|
|
925
|
+
raw = synthesized.compact;
|
|
926
|
+
io.write(compactPath, `${JSON.stringify(raw, null, 2)}\n`);
|
|
927
|
+
io.log(`Synthesized .modernjs/ultramodern.json from legacy metadata: ${synthesized.sources.join(', ')}.`);
|
|
928
|
+
if (synthesized.missing.length > 0) io.log(`Legacy metadata not found (using defaults): ${synthesized.missing.join(', ')}.`);
|
|
929
|
+
}
|
|
930
|
+
const current = normalizeCompactUltramodernConfig(io.workspaceRoot, raw);
|
|
421
931
|
const packageSource = createMigrationPackageSource(args, current);
|
|
422
|
-
updateUltramodernConfig(
|
|
423
|
-
updateReferenceTopology(
|
|
424
|
-
|
|
425
|
-
|
|
932
|
+
updateUltramodernConfig(io, raw, packageSource);
|
|
933
|
+
updateReferenceTopology(io);
|
|
934
|
+
const migrated = normalizeCompactUltramodernConfig(io.workspaceRoot, raw);
|
|
935
|
+
const migratedApps = workspaceAppsFromToolingConfig(migrated);
|
|
936
|
+
const shellOnly = !migrated.topology.apps.some((app)=>app.api);
|
|
937
|
+
if (shellOnly) {
|
|
938
|
+
io.log('Shell-only workspace: skipping backend-federation and Zerops runtime stages.');
|
|
939
|
+
for (const relativePath of [
|
|
940
|
+
"scripts/generate-node-backend-federation.mts",
|
|
941
|
+
"scripts/generate-node-backend-federation.mjs",
|
|
942
|
+
"scripts/proof-node-backend-federation.mts",
|
|
943
|
+
"scripts/proof-node-backend-federation.mjs",
|
|
944
|
+
"scripts/materialize-zerops-runtime.mjs",
|
|
945
|
+
'zerops.yaml'
|
|
946
|
+
])removeGeneratedFileIfExists(io, relativePath);
|
|
947
|
+
} else {
|
|
948
|
+
removeStaleBackendFederationArtifacts(io, migrated);
|
|
949
|
+
updateGeneratedZeropsArtifacts(io, migrated);
|
|
950
|
+
updateGeneratedBackendFederationContractFiles(io, migrated);
|
|
951
|
+
}
|
|
952
|
+
for (const artifact of migratedWorkspaceScriptArtifacts({
|
|
953
|
+
shellOnly
|
|
954
|
+
})){
|
|
955
|
+
if (artifact.legacyPath) io.remove(node_path.join(io.workspaceRoot, artifact.legacyPath));
|
|
956
|
+
io.write(node_path.join(io.workspaceRoot, artifact.relativePath), artifact.content);
|
|
957
|
+
}
|
|
958
|
+
updateGeneratedBuildIdentityModules(io, migrated);
|
|
959
|
+
updateGeneratedTypeScriptSurfaces(io, migrated);
|
|
960
|
+
updateGeneratedModernConfigs(io, migrated);
|
|
961
|
+
for (const relativePackageFile of listWorkspacePackageFiles(io.workspaceRoot)){
|
|
962
|
+
const packageFile = node_path.join(io.workspaceRoot, relativePackageFile);
|
|
426
963
|
const packageJson = readJsonFile(packageFile);
|
|
427
964
|
if ('package.json' === relativePackageFile) {
|
|
428
965
|
packageJson.modernjs ??= {};
|
|
@@ -431,17 +968,29 @@ has to pass pnpm api:check and pnpm contract:check.
|
|
|
431
968
|
config: './.modernjs/ultramodern.json'
|
|
432
969
|
};
|
|
433
970
|
}
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
971
|
+
updateModernDependencies(packageJson, packageSource);
|
|
972
|
+
updateGeneratedToolingDependencies(packageJson);
|
|
973
|
+
updateGeneratedPackageScripts(packageJson, {
|
|
974
|
+
relativePackageFile,
|
|
975
|
+
apps: migratedApps,
|
|
976
|
+
shellOnly
|
|
977
|
+
});
|
|
978
|
+
writeJsonFile(io, packageFile, packageJson);
|
|
979
|
+
}
|
|
980
|
+
updateGeneratedPnpmWorkspacePolicy(io);
|
|
981
|
+
ensureGeneratedDeclarationPatches(io, {
|
|
982
|
+
includeDrizzleOrmPatch: workspaceUsesDependency(io.workspaceRoot, 'drizzle-orm')
|
|
983
|
+
});
|
|
984
|
+
ensureGeneratedOxfmtIgnorePatterns(io);
|
|
985
|
+
if (!skipInstall) {
|
|
442
986
|
const status = runPnpmLockfileRefresh(context);
|
|
443
987
|
if (0 !== status) return status;
|
|
444
988
|
}
|
|
989
|
+
if (dryRun) {
|
|
990
|
+
for (const line of io.plan)process.stdout.write(`${line}\n`);
|
|
991
|
+
process.stdout.write(`[dry-run] migrate-strict-effect would migrate UltraModern strict Effect metadata to ${packageSource.modernPackageVersion}.\n`);
|
|
992
|
+
return 0;
|
|
993
|
+
}
|
|
445
994
|
process.stdout.write(`UltraModern strict Effect metadata migrated to ${packageSource.modernPackageVersion}. Run pnpm api:check && pnpm contract:check next.\n`);
|
|
446
995
|
return 0;
|
|
447
996
|
}
|
|
@@ -454,6 +1003,154 @@ function runSkills(args, context) {
|
|
|
454
1003
|
], context);
|
|
455
1004
|
throw new Error('Usage: modern-js-create ultramodern skills <install|check>');
|
|
456
1005
|
}
|
|
1006
|
+
const resolveCloudflareOutputVerifyTargets = (args, context)=>{
|
|
1007
|
+
const outputDirectory = readOption(args, '--output');
|
|
1008
|
+
const appId = readOption(args, '--app');
|
|
1009
|
+
if (outputDirectory && appId) throw new Error('Use either --app or --output, not both.');
|
|
1010
|
+
const targets = outputDirectory ? [
|
|
1011
|
+
{
|
|
1012
|
+
label: outputDirectory,
|
|
1013
|
+
outputDirectory: node_path.resolve(context.invocationCwd, outputDirectory)
|
|
1014
|
+
}
|
|
1015
|
+
] : workspaceAppsFromToolingConfig(readUltramodernConfig(context.workspaceRoot)).filter((app)=>!appId || app.id === appId).map((app)=>({
|
|
1016
|
+
label: app.id,
|
|
1017
|
+
outputDirectory: node_path.join(context.workspaceRoot, app.directory, '.output')
|
|
1018
|
+
}));
|
|
1019
|
+
if (0 === targets.length) throw new Error(`No generated UltraModern app matched ${appId}.`);
|
|
1020
|
+
return targets;
|
|
1021
|
+
};
|
|
1022
|
+
const renderCloudflareOutputVerifyModule = ({ workspaceRoot, targets, scanRoots, importWorker })=>`
|
|
1023
|
+
import { createRequire } from 'node:module';
|
|
1024
|
+
import path from 'node:path';
|
|
1025
|
+
|
|
1026
|
+
const workspaceRoot = ${JSON.stringify(workspaceRoot)};
|
|
1027
|
+
const targets = ${JSON.stringify(targets, null, 2)};
|
|
1028
|
+
const scanRoots = ${JSON.stringify(scanRoots)};
|
|
1029
|
+
const verifierRequire = createRequire(path.join(workspaceRoot, 'package.json'));
|
|
1030
|
+
const {
|
|
1031
|
+
verifyCloudflareOutput,
|
|
1032
|
+
verifyCloudflareOutputMutationPolicy,
|
|
1033
|
+
} = verifierRequire('@modern-js/app-tools/cloudflare-output-verifier');
|
|
1034
|
+
|
|
1035
|
+
let failed = false;
|
|
1036
|
+
for (const target of targets) {
|
|
1037
|
+
const result = await verifyCloudflareOutput({
|
|
1038
|
+
outputDirectory: target.outputDirectory,
|
|
1039
|
+
importWorker: ${JSON.stringify(importWorker)},
|
|
1040
|
+
});
|
|
1041
|
+
if (result.ok) {
|
|
1042
|
+
console.log(\`[ultramodern] Cloudflare output verified: \${target.label}\`);
|
|
1043
|
+
} else {
|
|
1044
|
+
failed = true;
|
|
1045
|
+
console.error(\`[ultramodern] Cloudflare output failed: \${target.label}\`);
|
|
1046
|
+
for (const issue of result.issues) {
|
|
1047
|
+
console.error(\`- \${issue.code}: \${issue.message}\${issue.path ? \` (\${issue.path})\` : ''}\`);
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
if (scanRoots.length > 0) {
|
|
1053
|
+
const policyResult = await verifyCloudflareOutputMutationPolicy({ scanRoots });
|
|
1054
|
+
if (!policyResult.ok) {
|
|
1055
|
+
failed = true;
|
|
1056
|
+
console.error('[ultramodern] generated-output mutation policy failed');
|
|
1057
|
+
for (const issue of policyResult.issues) {
|
|
1058
|
+
console.error(\`- \${issue.code}: \${issue.message}\${issue.path ? \` (\${issue.path})\` : ''}\`);
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
process.exit(failed ? 1 : 0);
|
|
1064
|
+
`;
|
|
1065
|
+
function runCloudflareOutputVerify(args, context) {
|
|
1066
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
1067
|
+
process.stdout.write(`Usage:
|
|
1068
|
+
modern-js-create ultramodern cloudflare-output-verify [--app <id> | --output <dir>] [--no-import-worker] [--no-source-scan]
|
|
1069
|
+
|
|
1070
|
+
Verifies generated Cloudflare output against the UltraModern worker contract.
|
|
1071
|
+
Without --app or --output, every generated workspace app is verified.
|
|
1072
|
+
`);
|
|
1073
|
+
return 0;
|
|
1074
|
+
}
|
|
1075
|
+
const source = renderCloudflareOutputVerifyModule({
|
|
1076
|
+
workspaceRoot: context.workspaceRoot,
|
|
1077
|
+
targets: resolveCloudflareOutputVerifyTargets(args, context),
|
|
1078
|
+
scanRoots: hasFlag(args, '--no-source-scan') ? [] : [
|
|
1079
|
+
context.workspaceRoot
|
|
1080
|
+
],
|
|
1081
|
+
importWorker: !hasFlag(args, '--no-import-worker')
|
|
1082
|
+
});
|
|
1083
|
+
return runRenderedModule(source, context);
|
|
1084
|
+
}
|
|
1085
|
+
const resolveRoutesGenerateTargets = (args, context)=>{
|
|
1086
|
+
const appId = readOption(args, '--app');
|
|
1087
|
+
const targets = workspaceAppsFromToolingConfig(readUltramodernConfig(context.workspaceRoot)).filter((app)=>!appId || app.id === appId).map((app)=>({
|
|
1088
|
+
label: app.id,
|
|
1089
|
+
appDirectory: node_path.join(context.workspaceRoot, app.directory)
|
|
1090
|
+
}));
|
|
1091
|
+
if (0 === targets.length) throw new Error(`No generated UltraModern app matched ${appId ?? '<any>'}.`);
|
|
1092
|
+
return targets;
|
|
1093
|
+
};
|
|
1094
|
+
const renderRoutesGenerateModule = ({ workspaceRoot, targets })=>`
|
|
1095
|
+
import { createRequire } from 'node:module';
|
|
1096
|
+
import path from 'node:path';
|
|
1097
|
+
import { pathToFileURL } from 'node:url';
|
|
1098
|
+
|
|
1099
|
+
const workspaceRoot = ${JSON.stringify(workspaceRoot)};
|
|
1100
|
+
const targets = ${JSON.stringify(targets, null, 2)};
|
|
1101
|
+
const appRequire = createRequire(path.join(workspaceRoot, 'package.json'));
|
|
1102
|
+
const pluginUrl = pathToFileURL(
|
|
1103
|
+
appRequire.resolve('@modern-js/plugin-tanstack'),
|
|
1104
|
+
).href;
|
|
1105
|
+
const { generateTanstackRouteArtifacts } = await import(pluginUrl);
|
|
1106
|
+
|
|
1107
|
+
let failed = false;
|
|
1108
|
+
// Sequential — the app-tools cli singleton is not re-entrant.
|
|
1109
|
+
for (const target of targets) {
|
|
1110
|
+
try {
|
|
1111
|
+
await generateTanstackRouteArtifacts({ appDirectory: target.appDirectory });
|
|
1112
|
+
console.log(\`[ultramodern] TanStack route artifacts generated: \${target.label}\`);
|
|
1113
|
+
} catch (error) {
|
|
1114
|
+
failed = true;
|
|
1115
|
+
console.error(\`[ultramodern] TanStack route generation failed: \${target.label}\`);
|
|
1116
|
+
// Print the full underlying failure, including the cause chain. The
|
|
1117
|
+
// route-generate crash is often an opaque node error thrown deep inside
|
|
1118
|
+
// app-tools/plugin (e.g. a path TypeError), so surfacing only
|
|
1119
|
+
// \`error.message\` swallows the stack that points at the real culprit.
|
|
1120
|
+
let current = error;
|
|
1121
|
+
let depth = 0;
|
|
1122
|
+
while (current) {
|
|
1123
|
+
const label = depth === 0 ? '-' : ' caused by:';
|
|
1124
|
+
const detail =
|
|
1125
|
+
current instanceof Error
|
|
1126
|
+
? current.stack ?? \`\${current.name}: \${current.message}\`
|
|
1127
|
+
: String(current);
|
|
1128
|
+
console.error(\`\${label} \${detail}\`);
|
|
1129
|
+
current = current instanceof Error ? current.cause : undefined;
|
|
1130
|
+
depth += 1;
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
process.exit(failed ? 1 : 0);
|
|
1136
|
+
`;
|
|
1137
|
+
function runRoutesGenerate(args, context) {
|
|
1138
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
1139
|
+
process.stdout.write(`Usage:
|
|
1140
|
+
modern-js-create ultramodern routes-generate [--app <id>]
|
|
1141
|
+
|
|
1142
|
+
Regenerates TanStack route artifacts (router.gen.ts, register.gen.d.ts) for
|
|
1143
|
+
generated UltraModern apps without running dev or build. Without --app, every
|
|
1144
|
+
generated workspace app is regenerated.
|
|
1145
|
+
`);
|
|
1146
|
+
return 0;
|
|
1147
|
+
}
|
|
1148
|
+
const source = renderRoutesGenerateModule({
|
|
1149
|
+
workspaceRoot: context.workspaceRoot,
|
|
1150
|
+
targets: resolveRoutesGenerateTargets(args, context)
|
|
1151
|
+
});
|
|
1152
|
+
return runRenderedModule(source, context);
|
|
1153
|
+
}
|
|
457
1154
|
async function runUltramodernToolingCli(args, workspaceRoot = process.env.ULTRAMODERN_WORKSPACE_ROOT ?? process.cwd()) {
|
|
458
1155
|
try {
|
|
459
1156
|
const [command, ...rest] = args;
|
|
@@ -467,25 +1164,25 @@ async function runUltramodernToolingCli(args, workspaceRoot = process.env.ULTRAM
|
|
|
467
1164
|
case '-h':
|
|
468
1165
|
printHelp();
|
|
469
1166
|
return 0;
|
|
470
|
-
case
|
|
1167
|
+
case GENERATED_TOOLING_COMMANDS.validate.command:
|
|
471
1168
|
return runValidate(context);
|
|
472
|
-
case
|
|
473
|
-
return spawnNodeScript("templates/workspace-scripts/ultramodern-typecheck.mjs", rest, context, {
|
|
474
|
-
cwd: context.invocationCwd
|
|
475
|
-
});
|
|
476
|
-
case 'mf-types':
|
|
1169
|
+
case GENERATED_TOOLING_COMMANDS.mfTypes.command:
|
|
477
1170
|
return runMfTypes(rest, context);
|
|
478
|
-
case
|
|
1171
|
+
case GENERATED_TOOLING_COMMANDS.migrateStrictEffect.command:
|
|
479
1172
|
return runMigrateStrictEffect(rest, context);
|
|
480
|
-
case
|
|
481
|
-
return
|
|
482
|
-
case
|
|
483
|
-
return
|
|
484
|
-
case '
|
|
485
|
-
return
|
|
1173
|
+
case GENERATED_TOOLING_COMMANDS.cloudflareOutputVerify.command:
|
|
1174
|
+
return runCloudflareOutputVerify(rest, context);
|
|
1175
|
+
case GENERATED_TOOLING_COMMANDS.routesGenerate.command:
|
|
1176
|
+
return runRoutesGenerate(rest, context);
|
|
1177
|
+
case 'sync-delivery-unit':
|
|
1178
|
+
return runSyncDeliveryUnit(rest, context);
|
|
486
1179
|
case 'skills':
|
|
487
1180
|
return runSkills(rest, context);
|
|
488
1181
|
default:
|
|
1182
|
+
{
|
|
1183
|
+
const templateBackedStatus = runTemplateBackedToolingCommand(command ?? '', rest, context);
|
|
1184
|
+
if (void 0 !== templateBackedStatus) return templateBackedStatus;
|
|
1185
|
+
}
|
|
489
1186
|
throw new Error(`Unknown UltraModern command: ${command}`);
|
|
490
1187
|
}
|
|
491
1188
|
} catch (error) {
|