@bleedingdev/modern-js-create 3.5.0-ultramodern.2 → 3.5.0-ultramodern.20

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.
Files changed (32) hide show
  1. package/README.md +20 -14
  2. package/dist/cjs/ultramodern-tooling/commands.cjs +357 -3
  3. package/dist/cjs/ultramodern-workspace/app-files.cjs +8 -11
  4. package/dist/cjs/ultramodern-workspace/module-federation.cjs +13 -6
  5. package/dist/cjs/ultramodern-workspace/package-json.cjs +15 -8
  6. package/dist/cjs/ultramodern-workspace/versions.cjs +5 -0
  7. package/dist/cjs/ultramodern-workspace/workspace-scripts.cjs +2 -0
  8. package/dist/cjs/ultramodern-workspace/write-workspace.cjs +2 -0
  9. package/dist/esm/ultramodern-tooling/commands.js +357 -3
  10. package/dist/esm/ultramodern-workspace/app-files.js +8 -11
  11. package/dist/esm/ultramodern-workspace/module-federation.js +13 -6
  12. package/dist/esm/ultramodern-workspace/package-json.js +5 -8
  13. package/dist/esm/ultramodern-workspace/versions.js +3 -1
  14. package/dist/esm/ultramodern-workspace/workspace-scripts.js +3 -1
  15. package/dist/esm/ultramodern-workspace/write-workspace.js +3 -1
  16. package/dist/esm-node/ultramodern-tooling/commands.js +357 -3
  17. package/dist/esm-node/ultramodern-workspace/app-files.js +8 -11
  18. package/dist/esm-node/ultramodern-workspace/module-federation.js +13 -6
  19. package/dist/esm-node/ultramodern-workspace/package-json.js +5 -8
  20. package/dist/esm-node/ultramodern-workspace/versions.js +3 -1
  21. package/dist/esm-node/ultramodern-workspace/workspace-scripts.js +3 -1
  22. package/dist/esm-node/ultramodern-workspace/write-workspace.js +3 -1
  23. package/dist/types/ultramodern-workspace/versions.d.ts +2 -0
  24. package/package.json +3 -3
  25. package/template-workspace/.gitignore.handlebars +3 -0
  26. package/template-workspace/README.md.handlebars +19 -13
  27. package/template-workspace/patches/@module-federation__bridge-react@2.6.0.patch +140 -0
  28. package/template-workspace/patches/@module-federation__modern-js-v3@2.6.0.patch +57 -0
  29. package/template-workspace/patches/drizzle-orm-ts7-strict-declarations.patch +452 -0
  30. package/template-workspace/patches/effect-schema-error-type-id.patch +18 -0
  31. package/template-workspace/pnpm-workspace.yaml.handlebars +8 -0
  32. package/templates/workspace-scripts/validate-ultramodern-workspace.mjs.handlebars +87 -5
@@ -14,7 +14,7 @@ import { runCodeSmithOverlays } from "./overlays.js";
14
14
  import { createAppMfTypesTsConfig, createAppPackage, createAppTsConfig, createRootPackageJson, createRootTsConfig, createSharedContractsIndex, createSharedPackage, createSharedPackageTsConfig, createTsConfigBase } from "./package-json.js";
15
15
  import { resolvePackageSource } from "./package-source.js";
16
16
  import { createPublicWebAppArtifacts } from "./public-surface.js";
17
- import { EFFECT_VERSION, EFFECT_VITEST_VERSION, NODE_FETCH_VERSION, NODE_VERSION, PNPM_VERSION, TANSTACK_ROUTER_CORE_VERSION, TANSTACK_ROUTER_VERSION, TYPESCRIPT_VERSION } from "./versions.js";
17
+ import { DRIZZLE_ORM_VERSION, EFFECT_VERSION, EFFECT_VITEST_VERSION, MODULE_FEDERATION_VERSION, NODE_FETCH_VERSION, NODE_VERSION, PNPM_VERSION, TANSTACK_ROUTER_CORE_VERSION, TANSTACK_ROUTER_VERSION, TYPESCRIPT_VERSION } from "./versions.js";
18
18
  import { writeGeneratedWorkspaceScripts } from "./workspace-scripts.js";
19
19
  function writeApp(targetDir, scope, app, packageSource, enableTailwind, remotes = [], bridge) {
20
20
  const resolvedApp = 'shell' === app.kind ? createShellHost(remotes) : app;
@@ -130,8 +130,10 @@ function generateUltramodernWorkspace(options) {
130
130
  nodeVersion: NODE_VERSION,
131
131
  pnpmVersion: PNPM_VERSION,
132
132
  nodeFetchVersion: NODE_FETCH_VERSION,
133
+ drizzleOrmVersion: DRIZZLE_ORM_VERSION,
133
134
  effectVersion: EFFECT_VERSION,
134
135
  effectVitestVersion: EFFECT_VITEST_VERSION,
136
+ moduleFederationVersion: MODULE_FEDERATION_VERSION,
135
137
  tanstackRouterCoreVersion: TANSTACK_ROUTER_CORE_VERSION,
136
138
  tanstackRouterVersion: TANSTACK_ROUTER_VERSION,
137
139
  typescriptVersion: TYPESCRIPT_VERSION,
@@ -6,7 +6,11 @@ import node_path from "node:path";
6
6
  import { fileURLToPath } from "node:url";
7
7
  import { resolveCreatePackageRoot } from "../create-package-root.js";
8
8
  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";
9
+ import { createAppEnvDts } from "../ultramodern-workspace/app-files.js";
9
10
  import { validateModuleFederationTypes } from "../ultramodern-workspace/mf-validation.js";
11
+ import { createAppModernConfig } from "../ultramodern-workspace/module-federation.js";
12
+ import { createAppMfTypesTsConfig, createAppTsConfig, createSharedPackageTsConfig, createTsConfigBase } from "../ultramodern-workspace/package-json.js";
13
+ 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";
10
14
  import { createWorkspaceValidationScript } from "../ultramodern-workspace/workspace-scripts.js";
11
15
  import { readUltramodernConfig, workspaceAppsFromToolingConfig } from "./config.js";
12
16
  const commands_dirname = node_path.dirname(fileURLToPath(import.meta.url));
@@ -152,6 +156,91 @@ function updateModernDependencies(packageJson, packageSource) {
152
156
  }
153
157
  return changed;
154
158
  }
159
+ const generatedToolingDependencyPins = new Map([
160
+ [
161
+ '@effect/tsgo',
162
+ EFFECT_TSGO_VERSION
163
+ ],
164
+ [
165
+ "@typescript/native-preview",
166
+ TYPESCRIPT_NATIVE_PREVIEW_VERSION
167
+ ],
168
+ [
169
+ 'oxfmt',
170
+ OXFMT_VERSION
171
+ ],
172
+ [
173
+ 'oxlint',
174
+ OXLINT_VERSION
175
+ ],
176
+ [
177
+ 'wrangler',
178
+ WRANGLER_VERSION
179
+ ],
180
+ [
181
+ 'zephyr-agent',
182
+ ZEPHYR_AGENT_VERSION
183
+ ],
184
+ [
185
+ 'zephyr-rspack-plugin',
186
+ ZEPHYR_RSPACK_PLUGIN_VERSION
187
+ ]
188
+ ]);
189
+ const strictEffectPackageVersionPolicyExclusions = [
190
+ `effect@${EFFECT_VERSION}`,
191
+ `@effect/opentelemetry@${EFFECT_VERSION}`
192
+ ];
193
+ const moduleFederationModernJsPatchPath = `patches/@module-federation__modern-js-v3@${MODULE_FEDERATION_VERSION}.patch`;
194
+ const moduleFederationModernJsPatchSourcePath = node_path.join(createPackageRoot, 'template-workspace', moduleFederationModernJsPatchPath);
195
+ const moduleFederationBridgeReactPatchPath = `patches/@module-federation__bridge-react@${MODULE_FEDERATION_VERSION}.patch`;
196
+ const moduleFederationBridgeReactPatchSourcePath = node_path.join(createPackageRoot, 'template-workspace', moduleFederationBridgeReactPatchPath);
197
+ const effectDeclarationPatchPath = 'patches/effect-schema-error-type-id.patch';
198
+ const effectDeclarationPatchSourcePath = node_path.join(createPackageRoot, 'template-workspace', effectDeclarationPatchPath);
199
+ const drizzleOrmDeclarationPatchPath = 'patches/drizzle-orm-ts7-strict-declarations.patch';
200
+ const drizzleOrmDeclarationPatchSourcePath = node_path.join(createPackageRoot, 'template-workspace', drizzleOrmDeclarationPatchPath);
201
+ function updateGeneratedToolingDependencies(packageJson) {
202
+ let changed = false;
203
+ for (const section of [
204
+ 'dependencies',
205
+ 'devDependencies',
206
+ 'peerDependencies',
207
+ 'optionalDependencies'
208
+ ]){
209
+ const dependencies = packageJson[section];
210
+ if (!(!dependencies || 'object' != typeof dependencies || Array.isArray(dependencies))) {
211
+ for (const [packageName, version] of generatedToolingDependencyPins)if (Object.prototype.hasOwnProperty.call(dependencies, packageName) && dependencies[packageName] !== version) {
212
+ dependencies[packageName] = version;
213
+ changed = true;
214
+ }
215
+ }
216
+ }
217
+ return changed;
218
+ }
219
+ const cloudflareModernDeployCommand = 'ULTRAMODERN_ZEPHYR=false MODERNJS_DEPLOY=cloudflare modern deploy';
220
+ const cloudflareModernDeploySkipBuildCommand = `${cloudflareModernDeployCommand} --skip-build`;
221
+ const cloudflareWranglerDeployCommand = 'wrangler deploy --config .output/wrangler.json';
222
+ const cloudflareWranglerDeployInvalidSkipBuildCommand = `${cloudflareWranglerDeployCommand} --skip-build`;
223
+ function updateGeneratedPackageScripts(packageJson) {
224
+ const scripts = packageJson.scripts;
225
+ if (!scripts || 'object' != typeof scripts || Array.isArray(scripts)) return false;
226
+ let changed = false;
227
+ const cloudflareBuild = scripts['cloudflare:build'];
228
+ if ('string' == typeof cloudflareBuild) {
229
+ let nextCloudflareBuild = cloudflareBuild;
230
+ if (nextCloudflareBuild.includes(cloudflareModernDeployCommand) && !nextCloudflareBuild.includes(cloudflareModernDeploySkipBuildCommand)) nextCloudflareBuild = nextCloudflareBuild.replace(cloudflareModernDeployCommand, cloudflareModernDeploySkipBuildCommand);
231
+ 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, '');
232
+ if (nextCloudflareBuild !== cloudflareBuild) {
233
+ scripts['cloudflare:build'] = nextCloudflareBuild;
234
+ changed = true;
235
+ }
236
+ }
237
+ const cloudflareDeploy = scripts['cloudflare:deploy'];
238
+ if ('string' == typeof cloudflareDeploy && cloudflareDeploy.includes(cloudflareWranglerDeployInvalidSkipBuildCommand)) {
239
+ scripts['cloudflare:deploy'] = cloudflareDeploy.replace(cloudflareWranglerDeployInvalidSkipBuildCommand, cloudflareWranglerDeployCommand);
240
+ changed = true;
241
+ }
242
+ return changed;
243
+ }
155
244
  function normalizeStrictEffectApiMetadata(value) {
156
245
  const api = value.api;
157
246
  if (!api || 'object' != typeof api || Array.isArray(api)) return false;
@@ -217,6 +306,200 @@ function normalizeStrictEffectApiMetadata(value) {
217
306
  }
218
307
  return changed;
219
308
  }
309
+ function replaceYamlLine(source, pattern, replacement) {
310
+ const updated = source.replace(pattern, replacement);
311
+ return {
312
+ source: updated,
313
+ changed: updated !== source
314
+ };
315
+ }
316
+ function ensureYamlListItem(source, key, item) {
317
+ const itemLine = ` - '${item}'`;
318
+ const headerPattern = new RegExp(`^${key}:\\n(?:(?: - .+\\n)*)`, 'mu');
319
+ const header = source.match(headerPattern);
320
+ if (header) {
321
+ if (header[0].split('\n').includes(itemLine)) return {
322
+ source,
323
+ changed: false
324
+ };
325
+ return {
326
+ source: source.replace(headerPattern, `${header[0]}${itemLine}\n`),
327
+ changed: true
328
+ };
329
+ }
330
+ const block = `${key}:\n${itemLine}\n`;
331
+ const afterTrustPolicyIgnore = source.replace(/^(trustPolicyIgnoreAfter: .+\n)/mu, `$1${block}`);
332
+ if (afterTrustPolicyIgnore !== source) return {
333
+ source: afterTrustPolicyIgnore,
334
+ changed: true
335
+ };
336
+ return {
337
+ source: `${source.trimEnd()}\n${block}`,
338
+ changed: true
339
+ };
340
+ }
341
+ function ensureYamlMapEntry(source, key, entryKey, value) {
342
+ const entryLine = ` '${entryKey}': ${value}`;
343
+ const packageName = entryKey.includes('@') ? entryKey.slice(0, entryKey.lastIndexOf('@')) : entryKey;
344
+ const escapedPackageName = packageName.replace(/[.*+?^${}()|[\]\\]/gu, '\\$&');
345
+ const currentEntryPattern = new RegExp(`^ {2}'${escapedPackageName}@[^']+': .+$`, 'mu');
346
+ const currentEntry = source.match(currentEntryPattern);
347
+ if (currentEntry) {
348
+ if (currentEntry[0] === entryLine) return {
349
+ source,
350
+ changed: false
351
+ };
352
+ return {
353
+ source: source.replace(currentEntryPattern, entryLine),
354
+ changed: true
355
+ };
356
+ }
357
+ const headerPattern = new RegExp(`^${key}:\\n(?:(?: .+\\n)*)`, 'mu');
358
+ const header = source.match(headerPattern);
359
+ if (header) {
360
+ if (header[0].split('\n').includes(entryLine)) return {
361
+ source,
362
+ changed: false
363
+ };
364
+ return {
365
+ source: source.replace(headerPattern, `${header[0]}${entryLine}\n`),
366
+ changed: true
367
+ };
368
+ }
369
+ return {
370
+ source: `${source.trimEnd()}\n${key}:\n${entryLine}\n`,
371
+ changed: true
372
+ };
373
+ }
374
+ function removeYamlMapEntry(source, entryKey) {
375
+ const packageName = entryKey.includes('@') ? entryKey.slice(0, entryKey.lastIndexOf('@')) : entryKey;
376
+ const escapedPackageName = packageName.replace(/[.*+?^${}()|[\]\\]/gu, '\\$&');
377
+ const currentEntryPattern = new RegExp(`^ {2}'${escapedPackageName}@[^']+': .+\\n?`, 'mu');
378
+ if (!currentEntryPattern.test(source)) return {
379
+ source,
380
+ changed: false
381
+ };
382
+ return {
383
+ source: source.replace(currentEntryPattern, ''),
384
+ changed: true
385
+ };
386
+ }
387
+ function ensureGeneratedPatchFile(workspaceRoot, relativePatchPath, sourcePatchPath) {
388
+ const targetPath = node_path.join(workspaceRoot, relativePatchPath);
389
+ const patch = node_fs.readFileSync(sourcePatchPath, 'utf-8');
390
+ if (node_fs.existsSync(targetPath) && node_fs.readFileSync(targetPath, 'utf-8') === patch) return false;
391
+ node_fs.mkdirSync(node_path.dirname(targetPath), {
392
+ recursive: true
393
+ });
394
+ node_fs.writeFileSync(targetPath, patch, 'utf-8');
395
+ return true;
396
+ }
397
+ function removeGeneratedPatchFileIfUnchanged(workspaceRoot, relativePatchPath, sourcePatchPath) {
398
+ const targetPath = node_path.join(workspaceRoot, relativePatchPath);
399
+ if (!node_fs.existsSync(targetPath)) return false;
400
+ const patch = node_fs.readFileSync(sourcePatchPath, 'utf-8');
401
+ if (node_fs.readFileSync(targetPath, 'utf-8') !== patch) return false;
402
+ node_fs.rmSync(targetPath);
403
+ return true;
404
+ }
405
+ function workspaceUsesDependency(workspaceRoot, packageName) {
406
+ const packageJsonPaths = [
407
+ node_path.join(workspaceRoot, 'package.json')
408
+ ];
409
+ for (const workspaceDir of [
410
+ 'apps',
411
+ 'verticals',
412
+ 'packages'
413
+ ]){
414
+ const absoluteWorkspaceDir = node_path.join(workspaceRoot, workspaceDir);
415
+ if (node_fs.existsSync(absoluteWorkspaceDir)) for (const entry of node_fs.readdirSync(absoluteWorkspaceDir, {
416
+ withFileTypes: true
417
+ })){
418
+ if (!entry.isDirectory()) continue;
419
+ const packageJsonPath = node_path.join(absoluteWorkspaceDir, entry.name, 'package.json');
420
+ if (node_fs.existsSync(packageJsonPath)) packageJsonPaths.push(packageJsonPath);
421
+ }
422
+ }
423
+ for (const packageJsonPath of packageJsonPaths){
424
+ const packageJson = JSON.parse(node_fs.readFileSync(packageJsonPath, 'utf-8'));
425
+ for (const field of [
426
+ 'dependencies',
427
+ 'devDependencies',
428
+ 'peerDependencies',
429
+ 'optionalDependencies'
430
+ ]){
431
+ const dependencies = packageJson[field];
432
+ if (dependencies && 'object' == typeof dependencies) {
433
+ if (Object.prototype.hasOwnProperty.call(dependencies, packageName)) return true;
434
+ for (const specifier of Object.values(dependencies))if ('string' == typeof specifier && specifier.startsWith(`npm:${packageName}@`)) return true;
435
+ }
436
+ }
437
+ }
438
+ return false;
439
+ }
440
+ function ensureGeneratedDeclarationPatches(workspaceRoot, options) {
441
+ let changed = false;
442
+ changed = ensureGeneratedPatchFile(workspaceRoot, moduleFederationModernJsPatchPath, moduleFederationModernJsPatchSourcePath) || changed;
443
+ changed = ensureGeneratedPatchFile(workspaceRoot, moduleFederationBridgeReactPatchPath, moduleFederationBridgeReactPatchSourcePath) || changed;
444
+ changed = ensureGeneratedPatchFile(workspaceRoot, effectDeclarationPatchPath, effectDeclarationPatchSourcePath) || changed;
445
+ changed = options.includeDrizzleOrmPatch ? ensureGeneratedPatchFile(workspaceRoot, drizzleOrmDeclarationPatchPath, drizzleOrmDeclarationPatchSourcePath) || changed : removeGeneratedPatchFileIfUnchanged(workspaceRoot, drizzleOrmDeclarationPatchPath, drizzleOrmDeclarationPatchSourcePath) || changed;
446
+ return changed;
447
+ }
448
+ function updateGeneratedPnpmWorkspacePolicy(workspaceRoot) {
449
+ const workspaceFile = node_path.join(workspaceRoot, 'pnpm-workspace.yaml');
450
+ if (!node_fs.existsSync(workspaceFile)) return false;
451
+ let source = node_fs.readFileSync(workspaceFile, 'utf-8');
452
+ let changed = false;
453
+ const usesDrizzleOrm = workspaceUsesDependency(workspaceRoot, 'drizzle-orm');
454
+ const replacements = [
455
+ [
456
+ /^ {4}'@effect\/vitest>effect': .+$/mu,
457
+ ` '@effect/vitest>effect': '${EFFECT_VERSION}'`
458
+ ],
459
+ [
460
+ /^ {2}'@effect\/vitest': .+$/mu,
461
+ ` '@effect/vitest': ${EFFECT_VITEST_VERSION}`
462
+ ],
463
+ [
464
+ /^ {2}effect: .+$/mu,
465
+ ` effect: ${EFFECT_VERSION}`
466
+ ]
467
+ ];
468
+ for (const [pattern, replacement] of replacements){
469
+ const result = replaceYamlLine(source, pattern, replacement);
470
+ source = result.source;
471
+ changed = result.changed || changed;
472
+ }
473
+ for (const item of strictEffectPackageVersionPolicyExclusions){
474
+ const packageName = item.slice(0, item.lastIndexOf('@'));
475
+ const escapedPackageName = packageName.replace(/[.*+?^${}()|[\]\\]/gu, '\\$&');
476
+ const currentVersion = replaceYamlLine(source, new RegExp(`^ {2}- '${escapedPackageName}@[^']+'$`, 'gmu'), ` - '${item}'`);
477
+ source = currentVersion.source;
478
+ changed = currentVersion.changed || changed;
479
+ for (const policyKey of [
480
+ 'minimumReleaseAgeExclude',
481
+ 'trustPolicyExclude'
482
+ ]){
483
+ const policyExclude = ensureYamlListItem(source, policyKey, item);
484
+ source = policyExclude.source;
485
+ changed = policyExclude.changed || changed;
486
+ }
487
+ }
488
+ const effectPatch = ensureYamlMapEntry(source, 'patchedDependencies', `effect@${EFFECT_VERSION}`, effectDeclarationPatchPath);
489
+ source = effectPatch.source;
490
+ changed = effectPatch.changed || changed;
491
+ const moduleFederationModernJsPatch = ensureYamlMapEntry(source, 'patchedDependencies', `@module-federation/modern-js-v3@${MODULE_FEDERATION_VERSION}`, moduleFederationModernJsPatchPath);
492
+ source = moduleFederationModernJsPatch.source;
493
+ changed = moduleFederationModernJsPatch.changed || changed;
494
+ const moduleFederationBridgeReactPatch = ensureYamlMapEntry(source, 'patchedDependencies', `@module-federation/bridge-react@${MODULE_FEDERATION_VERSION}`, moduleFederationBridgeReactPatchPath);
495
+ source = moduleFederationBridgeReactPatch.source;
496
+ changed = moduleFederationBridgeReactPatch.changed || changed;
497
+ const drizzleOrmPatch = usesDrizzleOrm ? ensureYamlMapEntry(source, 'patchedDependencies', `drizzle-orm@${DRIZZLE_ORM_VERSION}`, drizzleOrmDeclarationPatchPath) : removeYamlMapEntry(source, `drizzle-orm@${DRIZZLE_ORM_VERSION}`);
498
+ source = drizzleOrmPatch.source;
499
+ changed = drizzleOrmPatch.changed || changed;
500
+ if (changed) node_fs.writeFileSync(workspaceFile, source, 'utf-8');
501
+ return changed;
502
+ }
220
503
  function updateUltramodernConfig(workspaceRoot, packageSource) {
221
504
  const configPath = node_path.join(workspaceRoot, '.modernjs/ultramodern.json');
222
505
  const config = readJsonFile(configPath);
@@ -236,6 +519,64 @@ function updateUltramodernConfig(workspaceRoot, packageSource) {
236
519
  for (const app of config.topology?.apps ?? [])if (app && 'object' == typeof app && !Array.isArray(app)) normalizeStrictEffectApiMetadata(app);
237
520
  writeJsonFile(configPath, config);
238
521
  }
522
+ function writeJsonIfChanged(filePath, value) {
523
+ const next = `${JSON.stringify(value, null, 2)}\n`;
524
+ if (node_fs.existsSync(filePath) && node_fs.readFileSync(filePath, 'utf-8') === next) return false;
525
+ node_fs.mkdirSync(node_path.dirname(filePath), {
526
+ recursive: true
527
+ });
528
+ node_fs.writeFileSync(filePath, next, 'utf-8');
529
+ return true;
530
+ }
531
+ function writeTextIfChanged(filePath, value) {
532
+ if (node_fs.existsSync(filePath) && node_fs.readFileSync(filePath, 'utf-8') === value) return false;
533
+ node_fs.mkdirSync(node_path.dirname(filePath), {
534
+ recursive: true
535
+ });
536
+ node_fs.writeFileSync(filePath, value, 'utf-8');
537
+ return true;
538
+ }
539
+ function ensureGeneratedIgnoreRules(workspaceRoot) {
540
+ const gitignorePath = node_path.join(workspaceRoot, '.gitignore');
541
+ const existing = node_fs.existsSync(gitignorePath) ? node_fs.readFileSync(gitignorePath, 'utf-8') : '';
542
+ const lines = 0 === existing.trimEnd().length ? [] : existing.trimEnd().split(/\r?\n/u);
543
+ let changed = false;
544
+ for (const rule of [
545
+ '.mf/',
546
+ '**/.mf/',
547
+ 'dist-cloudflare/'
548
+ ])if (!lines.includes(rule)) {
549
+ lines.push(rule);
550
+ changed = true;
551
+ }
552
+ if (!changed) return false;
553
+ const next = `${lines.join('\n')}\n`;
554
+ node_fs.writeFileSync(gitignorePath, next, 'utf-8');
555
+ return true;
556
+ }
557
+ function updateGeneratedTypeScriptSurfaces(workspaceRoot, config) {
558
+ let changed = false;
559
+ const apps = workspaceAppsFromToolingConfig(config);
560
+ const remotes = apps.filter((app)=>'shell' !== app.kind);
561
+ changed = writeJsonIfChanged(node_path.join(workspaceRoot, 'tsconfig.base.json'), createTsConfigBase()) || changed;
562
+ changed = ensureGeneratedIgnoreRules(workspaceRoot) || changed;
563
+ for (const sharedPackage of [
564
+ 'packages/shared-contracts',
565
+ 'packages/shared-design-tokens'
566
+ ])changed = writeJsonIfChanged(node_path.join(workspaceRoot, sharedPackage, 'tsconfig.json'), createSharedPackageTsConfig(sharedPackage)) || changed;
567
+ for (const app of apps){
568
+ changed = writeJsonIfChanged(node_path.join(workspaceRoot, app.directory, 'tsconfig.json'), createAppTsConfig(app, remotes)) || changed;
569
+ changed = writeJsonIfChanged(node_path.join(workspaceRoot, app.directory, 'tsconfig.mf-types.json'), createAppMfTypesTsConfig(app)) || changed;
570
+ changed = writeTextIfChanged(node_path.join(workspaceRoot, app.directory, 'src/modern-app-env.d.ts'), createAppEnvDts(app, remotes)) || changed;
571
+ }
572
+ return changed;
573
+ }
574
+ function updateGeneratedModernConfigs(workspaceRoot, config) {
575
+ let changed = false;
576
+ const apps = workspaceAppsFromToolingConfig(config);
577
+ for (const app of apps)changed = writeTextIfChanged(node_path.join(workspaceRoot, app.directory, 'modern.config.ts'), createAppModernConfig(config.workspace.packageScope, app)) || changed;
578
+ return changed;
579
+ }
239
580
  function updateReferenceTopology(workspaceRoot) {
240
581
  const topologyPath = node_path.join(workspaceRoot, 'topology/reference-topology.json');
241
582
  if (!node_fs.existsSync(topologyPath)) return false;
@@ -298,8 +639,10 @@ function runMigrateStrictEffect(args, context) {
298
639
  modern-js-create ultramodern migrate-strict-effect --version <version> [--skip-install]
299
640
 
300
641
  Updates generated UltraModern package-source metadata, Modern package aliases,
301
- direct Effect API topology metadata, and the pnpm lockfile for strict Effect
302
- workspaces. Source code still has to pass pnpm api:check and pnpm contract:check.
642
+ framework-owned toolchain pins, direct Effect API topology metadata, strict
643
+ Effect pnpm overrides/trust policy, framework-owned TypeScript config
644
+ surfaces, and the pnpm lockfile. Source code still has to pass pnpm api:check
645
+ and pnpm contract:check.
303
646
  `);
304
647
  return 0;
305
648
  }
@@ -307,6 +650,9 @@ workspaces. Source code still has to pass pnpm api:check and pnpm contract:check
307
650
  const packageSource = createMigrationPackageSource(args, current);
308
651
  updateUltramodernConfig(context.workspaceRoot, packageSource);
309
652
  updateReferenceTopology(context.workspaceRoot);
653
+ const migrated = readUltramodernConfig(context.workspaceRoot);
654
+ updateGeneratedTypeScriptSurfaces(context.workspaceRoot, migrated);
655
+ updateGeneratedModernConfigs(context.workspaceRoot, migrated);
310
656
  for (const relativePackageFile of listWorkspacePackageFiles(context.workspaceRoot)){
311
657
  const packageFile = node_path.join(context.workspaceRoot, relativePackageFile);
312
658
  const packageJson = readJsonFile(packageFile);
@@ -317,9 +663,17 @@ workspaces. Source code still has to pass pnpm api:check and pnpm contract:check
317
663
  config: './.modernjs/ultramodern.json'
318
664
  };
319
665
  }
320
- if (updateModernDependencies(packageJson, packageSource)) writeJsonFile(packageFile, packageJson);
666
+ const modernDependenciesChanged = updateModernDependencies(packageJson, packageSource);
667
+ const toolingDependenciesChanged = updateGeneratedToolingDependencies(packageJson);
668
+ const generatedScriptsChanged = updateGeneratedPackageScripts(packageJson);
669
+ const changed = modernDependenciesChanged || toolingDependenciesChanged || generatedScriptsChanged;
670
+ if (changed) writeJsonFile(packageFile, packageJson);
321
671
  else if ('package.json' === relativePackageFile) writeJsonFile(packageFile, packageJson);
322
672
  }
673
+ updateGeneratedPnpmWorkspacePolicy(context.workspaceRoot);
674
+ ensureGeneratedDeclarationPatches(context.workspaceRoot, {
675
+ includeDrizzleOrmPatch: workspaceUsesDependency(context.workspaceRoot, 'drizzle-orm')
676
+ });
323
677
  if (!hasFlag(args, '--skip-install')) {
324
678
  const status = runPnpmLockfileRefresh(context);
325
679
  if (0 !== status) return status;
@@ -28,17 +28,14 @@ function createAppEnvDts(app, remotes = []) {
28
28
  }
29
29
  `;
30
30
  })).join('\n');
31
- const reactTypeReference = remoteModuleDeclarations ? "/// <reference types='react' />\n" : '';
32
- const siteUrlDeclaration = 'declare const ULTRAMODERN_SITE_URL: string;';
33
- return `${reactTypeReference}/// <reference types='@modern-js/app-tools/types' />
34
-
35
- ${siteUrlDeclaration}
36
- declare module '*.svg' {
37
- const url: string;
38
- export default url;
39
- }
40
- declare module '*.css';
41
- ${remoteModuleDeclarations ? `\n${remoteModuleDeclarations}` : ''}`;
31
+ const reactTypeImport = remoteModuleDeclarations ? "import type React from 'react';\n" : '';
32
+ return [
33
+ `import '@modern-js/app-tools/types';\n${reactTypeImport}`.trimEnd(),
34
+ `declare global {
35
+ const ULTRAMODERN_SITE_URL: string;
36
+ }`,
37
+ remoteModuleDeclarations.trimEnd()
38
+ ].filter((section)=>section.length > 0).join('\n\n').concat('\n');
42
39
  }
43
40
  function createAppRuntimeConfig(app, scope, remotes = []) {
44
41
  const pluginsConfig = 'shell' === app.kind ? ` plugins: [
@@ -26,6 +26,11 @@ function createAppModernConfig(scope, app) {
26
26
  (cloudflareDeployEnabled ? '/' : \`http://localhost:\${port}\`);
27
27
  const defaultRemoteAssetPrefix = \`\${remoteAssetOrigin.replace(/\\/+$/u, '')}/\`;
28
28
  const defaultAssetPrefix = defaultRemoteAssetPrefix;`;
29
+ const devAssetPrefixSource = 'shell' === app.kind ? ` // Keep shell dev assets origin-relative so the shell works through
30
+ // tunnels and local previews without rewriting its own chunks.
31
+ assetPrefix: '/',` : ` // Remote dev manifests must publish an absolute publicPath so host
32
+ // shells load remoteEntry.js and exposed chunks from this dev server.
33
+ assetPrefix,`;
29
34
  return `// @effect-diagnostics processEnv:off
30
35
  import {
31
36
  appTools,
@@ -94,8 +99,10 @@ ${defaultAssetPrefixSource}
94
99
  // load remoteEntry.js and exposed chunks from the remote origin, not the host.
95
100
  const assetPrefix =
96
101
  configuredModernAssetPrefix || configuredUltramodernAssetPrefix || defaultAssetPrefix;
97
- const buildCacheTarget = cloudflareDeployEnabled ? 'cloudflare' : 'web';
98
- const buildCacheDirectory = \`node_modules/.cache/rspack-\${appId}-\${buildCacheTarget}\`;
102
+ const buildTarget = cloudflareDeployEnabled ? 'cloudflare' : 'web';
103
+ const buildOutputRoot = cloudflareDeployEnabled ? 'dist-cloudflare' : 'dist';
104
+ const buildTempDirectory = \`node_modules/.modern-js-\${appId}-\${buildTarget}\`;
105
+ const buildCacheDirectory = \`node_modules/.cache/rspack-\${appId}-\${buildTarget}\`;
99
106
 
100
107
  if (
101
108
  cloudflareDeployEnabled &&
@@ -125,9 +132,7 @@ ${bffConfig} ...(cloudflareDeployEnabled
125
132
  }
126
133
  : {}),
127
134
  dev: {
128
- // Keep dev assets origin-relative too; the default absolute
129
- // http://localhost:<port> prefix breaks pages served through tunnels.
130
- assetPrefix: '/',
135
+ ${devAssetPrefixSource}
131
136
  },
132
137
  html: {
133
138
  outputStructure: 'flat',
@@ -137,13 +142,15 @@ ${bffConfig} ...(cloudflareDeployEnabled
137
142
  disableTsChecker: false,
138
143
  distPath: {
139
144
  html: './',
145
+ root: buildOutputRoot,
140
146
  },
141
147
  polyfill: 'off',
142
148
  splitRouteChunks: true,
149
+ tempDir: buildTempDirectory,
143
150
  },
144
151
  performance: {
145
152
  buildCache: {
146
- cacheDigest: [appId, buildCacheTarget],
153
+ cacheDigest: [appId, buildTarget],
147
154
  cacheDirectory: buildCacheDirectory,
148
155
  },
149
156
  rsdoctor: {
@@ -1,4 +1,5 @@
1
1
  import "node:module";
2
+ import node_path from "node:path";
2
3
  import { WORKSPACE_PACKAGE_VERSION, modernPackageSpecifier } from "../ultramodern-package-source.js";
3
4
  import { appHasApi, remoteDependencyAlias, resolveRemoteRefs, sharedPackages, shellApp, verticalApiApps, zephyrRemoteDependency } from "./descriptors.js";
4
5
  import { readFileTemplate } from "./fs-io.js";
@@ -249,7 +250,6 @@ function createTsConfigBase() {
249
250
  noFallthroughCasesInSwitch: true,
250
251
  noPropertyAccessFromIndexSignature: true,
251
252
  noImplicitReturns: true,
252
- skipLibCheck: true,
253
253
  resolveJsonModule: true,
254
254
  plugins: [
255
255
  {
@@ -281,7 +281,7 @@ function createReferences(packageDir, references) {
281
281
  return [
282
282
  ...new Set(references)
283
283
  ].filter((reference)=>reference !== packageDir).map((reference)=>({
284
- path: `${relativeRootFor(packageDir)}/${reference}`
284
+ path: node_path.relative(packageDir, reference).split(node_path.sep).join('/')
285
285
  }));
286
286
  }
287
287
  function createPackageTsConfig(packageDir, options = {}) {
@@ -291,8 +291,6 @@ function createPackageTsConfig(packageDir, options = {}) {
291
291
  const include = resolvedOptions.include ?? [
292
292
  'src',
293
293
  'locales/**/*.json',
294
- 'modern.config.ts',
295
- 'module-federation.config.ts',
296
294
  'package.json',
297
295
  'shared'
298
296
  ];
@@ -326,9 +324,9 @@ function createAppTsConfig(app, remotes = []) {
326
324
  });
327
325
  }
328
326
  function createAppMfTypesTsConfig(app) {
329
- const exposedFiles = Object.values(app.exposes ?? {}).map((exposePath)=>exposePath.replace(/^\.\//u, ''));
327
+ const exposedFiles = Object.entries(app.exposes ?? {}).sort(([left], [right])=>'./Route' === left ? -1 : './Route' === right ? 1 : 0).map(([, exposePath])=>exposePath.replace(/^\.\//u, ''));
330
328
  return {
331
- extends: './tsconfig.json',
329
+ extends: `${relativeRootFor(app.directory)}/tsconfig.base.json`,
332
330
  include: [
333
331
  ...new Set([
334
332
  ...exposedFiles,
@@ -359,7 +357,6 @@ function createRootTsConfig(apps = []) {
359
357
  }
360
358
  function createAppPackage(scope, app, packageSource, enableTailwind, remotes = [], bridge) {
361
359
  const publicSurfaceBuildCommand = createPublicSurfaceGenerationCommand(app, 'dist');
362
- const publicSurfaceCloudflareBuildCommand = createPublicSurfaceGenerationCommand(app, 'dist');
363
360
  const publicSurfaceCloudflareOutputCommand = createPublicSurfaceGenerationCommand(app, 'cloudflare');
364
361
  const packageExports = Object.fromEntries(Object.entries(app.exposes ?? {}).map(([expose, source])=>[
365
362
  expose,
@@ -372,7 +369,7 @@ function createAppPackage(scope, app, packageSource, enableTailwind, remotes = [
372
369
  scripts: {
373
370
  dev: 'modern dev',
374
371
  build: app.exposes ? `ULTRAMODERN_ZEPHYR=false modern build && ${publicSurfaceBuildCommand} && node ${relativeRootFor(app.directory)}/scripts/assert-mf-types.mts` : `ULTRAMODERN_ZEPHYR=false modern build && ${publicSurfaceBuildCommand}`,
375
- 'cloudflare:build': `ULTRAMODERN_ZEPHYR=false MODERNJS_DEPLOY=cloudflare modern build && ${publicSurfaceCloudflareBuildCommand} && ULTRAMODERN_ZEPHYR=false MODERNJS_DEPLOY=cloudflare modern deploy && ${publicSurfaceCloudflareOutputCommand}`,
372
+ 'cloudflare:build': `ULTRAMODERN_ZEPHYR=false MODERNJS_DEPLOY=cloudflare modern build && ULTRAMODERN_ZEPHYR=false MODERNJS_DEPLOY=cloudflare modern deploy --skip-build && ${publicSurfaceCloudflareOutputCommand}`,
376
373
  'cloudflare:deploy': 'ULTRAMODERN_CLOUDFLARE_REQUIRE_PUBLIC_URLS=true pnpm run cloudflare:build && wrangler deploy --config .output/wrangler.json',
377
374
  'cloudflare:preview': 'pnpm run cloudflare:build && wrangler dev --config .output/wrangler.json',
378
375
  'cloudflare:proof': `node ${relativeRootFor(app.directory)}/scripts/proof-cloudflare-version.mts --app ${app.id}`,
@@ -12,6 +12,7 @@ const POSTCSS_VERSION = '8.5.15';
12
12
  const EFFECT_VERSION = '4.0.0-beta.91';
13
13
  const EFFECT_VITEST_VERSION = '4.0.0-beta.91';
14
14
  const EFFECT_TSGO_VERSION = '0.14.6';
15
+ const DRIZZLE_ORM_VERSION = '1.0.0-rc.4';
15
16
  const TYPESCRIPT_STABLE_VERSION = '6.0.3';
16
17
  const TYPESCRIPT_NATIVE_PREVIEW_VERSION = '7.0.0-dev.20260628.1';
17
18
  const TYPESCRIPT_VERSION = TYPESCRIPT_STABLE_VERSION;
@@ -36,7 +37,8 @@ const ultramodernWorkspaceVersions = {
36
37
  moduleFederation: MODULE_FEDERATION_VERSION,
37
38
  effect: EFFECT_VERSION,
38
39
  effectVitest: EFFECT_VITEST_VERSION,
40
+ drizzleOrm: DRIZZLE_ORM_VERSION,
39
41
  tailwind: TAILWIND_VERSION,
40
42
  tailwindPostcss: TAILWIND_POSTCSS_VERSION
41
43
  };
42
- export { CLOUDFLARE_COMPATIBILITY_DATE, EFFECT_TSGO_VERSION, EFFECT_VERSION, EFFECT_VITEST_VERSION, I18NEXT_VERSION, LEFTHOOK_VERSION, MODULE_FEDERATION_AGENT_SKILLS_COMMIT, MODULE_FEDERATION_VERSION, NODE_FETCH_VERSION, NODE_VERSION, OXFMT_VERSION, OXLINT_VERSION, PNPM_VERSION, POSTCSS_VERSION, REACT_DOM_VERSION, REACT_ROUTER_VERSION, REACT_VERSION, RSTACK_AGENT_SKILLS_COMMIT, TAILWIND_POSTCSS_VERSION, TAILWIND_VERSION, TANSTACK_ROUTER_CORE_VERSION, TANSTACK_ROUTER_VERSION, TYPESCRIPT_NATIVE_PREVIEW_VERSION, TYPESCRIPT_STABLE_VERSION, TYPESCRIPT_VERSION, TYPES_REACT_DOM_VERSION, TYPES_REACT_VERSION, ULTRACITE_VERSION, WRANGLER_VERSION, ZEPHYR_AGENT_VERSION, ZEPHYR_RSPACK_PLUGIN_VERSION, ultramodernWorkspaceVersions };
44
+ export { CLOUDFLARE_COMPATIBILITY_DATE, DRIZZLE_ORM_VERSION, EFFECT_TSGO_VERSION, EFFECT_VERSION, EFFECT_VITEST_VERSION, I18NEXT_VERSION, LEFTHOOK_VERSION, MODULE_FEDERATION_AGENT_SKILLS_COMMIT, MODULE_FEDERATION_VERSION, NODE_FETCH_VERSION, NODE_VERSION, OXFMT_VERSION, OXLINT_VERSION, PNPM_VERSION, POSTCSS_VERSION, REACT_DOM_VERSION, REACT_ROUTER_VERSION, REACT_VERSION, RSTACK_AGENT_SKILLS_COMMIT, TAILWIND_POSTCSS_VERSION, TAILWIND_VERSION, TANSTACK_ROUTER_CORE_VERSION, TANSTACK_ROUTER_VERSION, TYPESCRIPT_NATIVE_PREVIEW_VERSION, TYPESCRIPT_STABLE_VERSION, TYPESCRIPT_VERSION, TYPES_REACT_DOM_VERSION, TYPES_REACT_VERSION, ULTRACITE_VERSION, WRANGLER_VERSION, ZEPHYR_AGENT_VERSION, ZEPHYR_RSPACK_PLUGIN_VERSION, ultramodernWorkspaceVersions };
@@ -9,7 +9,7 @@ import { packageName, tailwindPrefixForApp } from "./naming.js";
9
9
  import { createCloudflareSecurityContract } from "./policy.js";
10
10
  import { publicSurfaceManagedSourceAssetPaths } from "./public-surface.js";
11
11
  import { createLocalisedUrlsMap, createRouteMetaFilePath, createRouteOwnedI18nPaths, createRoutePageFilePath } from "./routes.js";
12
- import { CLOUDFLARE_COMPATIBILITY_DATE, NODE_VERSION, PNPM_VERSION } from "./versions.js";
12
+ import { CLOUDFLARE_COMPATIBILITY_DATE, EFFECT_VERSION, MODULE_FEDERATION_VERSION, NODE_VERSION, PNPM_VERSION } from "./versions.js";
13
13
  function createToolWrapperScript(command, extraArgs = []) {
14
14
  const commandJson = JSON.stringify(command);
15
15
  const extraArgsJson = JSON.stringify(extraArgs);
@@ -128,6 +128,8 @@ function createWorkspaceValidationScript(scope, enableTailwind, remotes = []) {
128
128
  const expectedCloudflareSecurity = createCloudflareSecurityContract();
129
129
  return renderFileTemplate("workspace-scripts/validate-ultramodern-workspace.mjs", {
130
130
  packageScope: scope,
131
+ effectVersion: EFFECT_VERSION,
132
+ moduleFederationVersion: MODULE_FEDERATION_VERSION,
131
133
  nodeVersion: NODE_VERSION,
132
134
  pnpmVersion: PNPM_VERSION,
133
135
  tailwindEnabledJson: JSON.stringify(enableTailwind),
@@ -15,7 +15,7 @@ import { runCodeSmithOverlays } from "./overlays.js";
15
15
  import { createAppMfTypesTsConfig, createAppPackage, createAppTsConfig, createRootPackageJson, createRootTsConfig, createSharedContractsIndex, createSharedPackage, createSharedPackageTsConfig, createTsConfigBase } from "./package-json.js";
16
16
  import { resolvePackageSource } from "./package-source.js";
17
17
  import { createPublicWebAppArtifacts } from "./public-surface.js";
18
- import { EFFECT_VERSION, EFFECT_VITEST_VERSION, NODE_FETCH_VERSION, NODE_VERSION, PNPM_VERSION, TANSTACK_ROUTER_CORE_VERSION, TANSTACK_ROUTER_VERSION, TYPESCRIPT_VERSION } from "./versions.js";
18
+ import { DRIZZLE_ORM_VERSION, EFFECT_VERSION, EFFECT_VITEST_VERSION, MODULE_FEDERATION_VERSION, NODE_FETCH_VERSION, NODE_VERSION, PNPM_VERSION, TANSTACK_ROUTER_CORE_VERSION, TANSTACK_ROUTER_VERSION, TYPESCRIPT_VERSION } from "./versions.js";
19
19
  import { writeGeneratedWorkspaceScripts } from "./workspace-scripts.js";
20
20
  function writeApp(targetDir, scope, app, packageSource, enableTailwind, remotes = [], bridge) {
21
21
  const resolvedApp = 'shell' === app.kind ? createShellHost(remotes) : app;
@@ -131,8 +131,10 @@ function generateUltramodernWorkspace(options) {
131
131
  nodeVersion: NODE_VERSION,
132
132
  pnpmVersion: PNPM_VERSION,
133
133
  nodeFetchVersion: NODE_FETCH_VERSION,
134
+ drizzleOrmVersion: DRIZZLE_ORM_VERSION,
134
135
  effectVersion: EFFECT_VERSION,
135
136
  effectVitestVersion: EFFECT_VITEST_VERSION,
137
+ moduleFederationVersion: MODULE_FEDERATION_VERSION,
136
138
  tanstackRouterCoreVersion: TANSTACK_ROUTER_CORE_VERSION,
137
139
  tanstackRouterVersion: TANSTACK_ROUTER_VERSION,
138
140
  typescriptVersion: TYPESCRIPT_VERSION,
@@ -16,6 +16,7 @@ export declare const POSTCSS_VERSION = "8.5.15";
16
16
  export declare const EFFECT_VERSION = "4.0.0-beta.91";
17
17
  export declare const EFFECT_VITEST_VERSION = "4.0.0-beta.91";
18
18
  export declare const EFFECT_TSGO_VERSION = "0.14.6";
19
+ export declare const DRIZZLE_ORM_VERSION = "1.0.0-rc.4";
19
20
  export declare const TYPESCRIPT_STABLE_VERSION = "6.0.3";
20
21
  export declare const TYPESCRIPT_NATIVE_PREVIEW_VERSION = "7.0.0-dev.20260628.1";
21
22
  export declare const TYPESCRIPT_VERSION = "6.0.3";
@@ -40,6 +41,7 @@ export declare const ultramodernWorkspaceVersions: {
40
41
  moduleFederation: string;
41
42
  effect: string;
42
43
  effectVitest: string;
44
+ drizzleOrm: string;
43
45
  tailwind: string;
44
46
  tailwindPostcss: string;
45
47
  };