@bleedingdev/modern-js-ultramodern-create 3.8.3-ultramodern.2 → 3.8.3-ultramodern.3

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 (40) hide show
  1. package/dist/cjs/ultramodern-tooling/commands/mf-types.cjs +29 -3
  2. package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-modern-configs.cjs +11 -3
  3. package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-typescript.cjs +31 -3
  4. package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/package-cohort.cjs +13 -1
  5. package/dist/cjs/ultramodern-workspace/demo-components.cjs +90 -6
  6. package/dist/cjs/ultramodern-workspace/mf-validation/constants.cjs +7 -4
  7. package/dist/cjs/ultramodern-workspace/mf-validation/validate.cjs +3 -3
  8. package/dist/cjs/ultramodern-workspace/routes.cjs +6 -9
  9. package/dist/cjs/ultramodern-workspace/tsconfigs.cjs +11 -3
  10. package/dist/cjs/ultramodern-workspace/workspace-script-plan.cjs +32 -2
  11. package/dist/esm/ultramodern-tooling/commands/mf-types.js +29 -3
  12. package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-modern-configs.js +11 -3
  13. package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-typescript.js +32 -4
  14. package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/package-cohort.js +14 -2
  15. package/dist/esm/ultramodern-workspace/demo-components.js +88 -7
  16. package/dist/esm/ultramodern-workspace/mf-validation/constants.js +5 -2
  17. package/dist/esm/ultramodern-workspace/mf-validation/validate.js +4 -4
  18. package/dist/esm/ultramodern-workspace/routes.js +4 -10
  19. package/dist/esm/ultramodern-workspace/tsconfigs.js +11 -3
  20. package/dist/esm/ultramodern-workspace/workspace-script-plan.js +33 -3
  21. package/dist/esm-node/ultramodern-tooling/commands/mf-types.js +29 -3
  22. package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-modern-configs.js +11 -3
  23. package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-typescript.js +32 -4
  24. package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/package-cohort.js +14 -2
  25. package/dist/esm-node/ultramodern-workspace/demo-components.js +88 -7
  26. package/dist/esm-node/ultramodern-workspace/mf-validation/constants.js +5 -2
  27. package/dist/esm-node/ultramodern-workspace/mf-validation/validate.js +4 -4
  28. package/dist/esm-node/ultramodern-workspace/routes.js +4 -10
  29. package/dist/esm-node/ultramodern-workspace/tsconfigs.js +11 -3
  30. package/dist/esm-node/ultramodern-workspace/workspace-script-plan.js +33 -3
  31. package/dist/types/ultramodern-workspace/demo-components.d.ts +2 -1
  32. package/dist/types/ultramodern-workspace/mf-validation/constants.d.ts +4 -1
  33. package/dist/types/ultramodern-workspace/mf-validation/types.d.ts +2 -0
  34. package/dist/types/ultramodern-workspace/mf-validation/validate.d.ts +2 -2
  35. package/dist/types/ultramodern-workspace/mf-validation.d.ts +1 -1
  36. package/dist/types/ultramodern-workspace/routes.d.ts +1 -0
  37. package/dist/types/ultramodern-workspace/tsconfigs.d.ts +1 -0
  38. package/package.json +4 -4
  39. package/template-workspace/.modernjs/release-cohort.json +40 -40
  40. package/templates/workspace-scripts/validate-ultramodern-workspace.mjs.handlebars +15 -0
@@ -2,7 +2,7 @@ import node_fs from "node:fs";
2
2
  import node_path from "node:path";
3
3
  import { ULTRAMODERN_CREATE_PACKAGE } from "../../../ultramodern-package-source.js";
4
4
  import { createAppEnvDts, createAppRuntimeConfig } from "../../../ultramodern-workspace/app-files.js";
5
- import { createRemoteExposeFragmentPage, regenerateGeneratedNavigationSurface, remoteComponentOutputPath } from "../../../ultramodern-workspace/demo-components.js";
5
+ import { createRemoteExposeFragmentPage, regenerateGeneratedNavigationSurface, regenerateGeneratedProductRouteAdapter, remoteComponentOutputPath } from "../../../ultramodern-workspace/demo-components.js";
6
6
  import { appI18nNamespace, distributedSsrExposes, distributedSsrFragmentSlug, resolveRemoteRefs } from "../../../ultramodern-workspace/descriptors.js";
7
7
  import { createAppMfTypesTsConfig, createAppTsConfig, createSharedPackageTsConfig, createTsConfigBase } from "../../../ultramodern-workspace/package-json.js";
8
8
  import { allWorkspaceAppsFromToolingConfig } from "../../config.js";
@@ -102,10 +102,13 @@ function manifestApps(manifest) {
102
102
  function packageManifest(io, appDirectory) {
103
103
  return readJsonObject(node_path.join(io.workspaceRoot, appDirectory, 'package.json'));
104
104
  }
105
- function shellSurfaceIsOwned(io, app, manifestApp) {
105
+ function appSurfaceIsOwned(io, app, manifestApp) {
106
106
  const packageJson = packageManifest(io, app.directory);
107
+ return manifestApp.path === app.directory && manifestApp.kind === app.kind && 'string' == typeof manifestApp.package && packageJson?.name === manifestApp.package;
108
+ }
109
+ function shellSurfaceIsOwned(io, app, manifestApp) {
107
110
  const moduleFederation = jsonObject(manifestApp.moduleFederation);
108
- return 'shell' === manifestApp.kind && manifestApp.path === app.directory && moduleFederation?.role === 'host' && 'string' == typeof manifestApp.package && packageJson?.name === manifestApp.package;
111
+ return appSurfaceIsOwned(io, app, manifestApp) && 'shell' === app.kind && moduleFederation?.role === 'host';
109
112
  }
110
113
  function deliveryUnitSurfaceIsOwned(io, config, app, manifestApp, expose, sourcePath) {
111
114
  const deliveryUnit = jsonObject(manifestApp.deliveryUnit);
@@ -116,6 +119,30 @@ function deliveryUnitSurfaceIsOwned(io, config, app, manifestApp, expose, source
116
119
  const packageName = manifestApp.package;
117
120
  return 'vertical' === manifestApp.kind && manifestApp.path === app.directory && 'string' == typeof packageName && packageJson?.name === packageName && deliveryUnit?.kind === 'microvertical-delivery-unit' && deliveryUnit.packageName === packageName && deliveryUnit.unitId === `${config.workspace.packageScope}/${app.id}` && exposedSurfaces.includes(expose) && packageExports?.[expose] === sourcePath;
118
121
  }
122
+ function updateGeneratedProductRouteAdapters(io, config) {
123
+ const manifest = generatedManifest(io, config);
124
+ if (void 0 === manifest) return false;
125
+ const appsById = new Map(manifestApps(manifest).map((app)=>[
126
+ String(app.id ?? ''),
127
+ app
128
+ ]));
129
+ let changed = false;
130
+ for (const app of allWorkspaceAppsFromToolingConfig(config)){
131
+ const manifestApp = appsById.get(app.id);
132
+ if ('decide' !== app.id && 'shell' !== app.kind || void 0 === manifestApp || !appSurfaceIsOwned(io, app, manifestApp)) continue;
133
+ const routeDirectory = node_path.join(io.workspaceRoot, app.directory, 'src/routes/[lang]/tractors/[slug]');
134
+ for (const fileName of [
135
+ 'page.search.ts',
136
+ 'page.tsx'
137
+ ]){
138
+ const filePath = node_path.join(routeDirectory, fileName);
139
+ if (!node_fs.existsSync(filePath)) continue;
140
+ const source = node_fs.readFileSync(filePath, 'utf-8');
141
+ changed = io.write(filePath, regenerateGeneratedProductRouteAdapter(source)) || changed;
142
+ }
143
+ }
144
+ return changed;
145
+ }
119
146
  function updateGeneratedNavigationSurfaces(io, config) {
120
147
  const manifest = generatedManifest(io, config);
121
148
  if (void 0 === manifest) return false;
@@ -144,7 +171,7 @@ function updateGeneratedNavigationSurfaces(io, config) {
144
171
  const filePath = node_path.join(io.workspaceRoot, relativePath);
145
172
  if (!node_fs.existsSync(filePath)) continue;
146
173
  const source = node_fs.readFileSync(filePath, 'utf-8');
147
- if (source.includes(`data-modern-boundary-id="${app.id}"`) && source.includes(`data-modern-mf-expose="${expose}"`)) changed = io.write(filePath, regenerateGeneratedNavigationSurface(source, 'demo-component')) || changed;
174
+ if (source.includes(`data-modern-boundary-id="${app.id}"`) && source.includes(`data-modern-mf-expose="${expose}"`)) changed = io.write(filePath, regenerateGeneratedNavigationSurface(source, './AddToCart' === expose ? 'checkout-add-to-cart' : 'checkout-page')) || changed;
148
175
  }
149
176
  }
150
177
  }
@@ -225,6 +252,7 @@ function updateGeneratedTypeScriptSurfaces(io, config) {
225
252
  }
226
253
  }
227
254
  updateGeneratedShellRuntimeSurfaces(io, config);
255
+ updateGeneratedProductRouteAdapters(io, config);
228
256
  updateGeneratedNavigationSurfaces(io, config);
229
257
  }
230
258
  export { updateGeneratedTypeScriptSurfaces };
@@ -1,4 +1,4 @@
1
- import { ULTRAMODERN_SINGLE_APP_MODERN_PACKAGES, ULTRAMODERN_WORKSPACE_MODERN_PACKAGES, modernPackageSpecifier } from "../../../ultramodern-package-source.js";
1
+ import { ULTRAMODERN_CREATE_PACKAGE, ULTRAMODERN_SINGLE_APP_MODERN_PACKAGES, ULTRAMODERN_WORKSPACE_MODERN_PACKAGES, modernPackageSpecifier } from "../../../ultramodern-package-source.js";
2
2
  import { ULTRAMODERN_PACKAGE_PINS } from "../../../ultramodern-workspace/policy.js";
3
3
  import { GENERATED_POSTINSTALL_SCRIPT, createWorkspaceAppPackageScripts, createWorkspaceRootPackageScripts } from "../../../ultramodern-workspace/workspace-script-plan.js";
4
4
  import { migratedWorkspaceScriptBasenames } from "../../../ultramodern-workspace/workspace-scripts.js";
@@ -25,12 +25,24 @@ function updateDeclaredDependencies(packageJson, pins) {
25
25
  return changed;
26
26
  }
27
27
  function updateModernDependencies(packageJson, packageSource) {
28
+ let changed = false;
29
+ for (const section of [
30
+ 'dependencies',
31
+ 'devDependencies'
32
+ ]){
33
+ const dependencies = packageJson[section];
34
+ if (dependencies && Object.hasOwn(dependencies, '@modern-js/create')) {
35
+ dependencies[ULTRAMODERN_CREATE_PACKAGE] = modernPackageSpecifier(ULTRAMODERN_CREATE_PACKAGE, packageSource);
36
+ delete dependencies['@modern-js/create'];
37
+ changed = true;
38
+ }
39
+ }
28
40
  return updateDeclaredDependencies(packageJson, new Map([
29
41
  ...modernPackageNames
30
42
  ].map((packageName)=>[
31
43
  packageName,
32
44
  modernPackageSpecifier(packageName, packageSource)
33
- ])));
45
+ ]))) || changed;
34
46
  }
35
47
  const generatedToolingDependencyPins = new Map(Object.entries({
36
48
  ...ULTRAMODERN_PACKAGE_PINS.appDependencies,
@@ -412,6 +412,21 @@ function ensureNamedImport(source, moduleName, names) {
412
412
  const importLine = `import { ${names.join(', ')} } from '${moduleName}';\n`;
413
413
  return firstImportEnd < 0 ? `${importLine}${source}` : `${source.slice(0, firstImportEnd + 1)}${importLine}${source.slice(firstImportEnd + 1)}`;
414
414
  }
415
+ function removeNamedImport(source, moduleName, name) {
416
+ const importEndMarker = `from '${moduleName}';`;
417
+ const importEnd = source.indexOf(importEndMarker);
418
+ if (importEnd < 0) return source;
419
+ const importStart = source.lastIndexOf('import ', importEnd);
420
+ const openBrace = source.indexOf('{', importStart);
421
+ const closeBrace = source.indexOf('}', openBrace);
422
+ if (openBrace < 0 || closeBrace < 0 || closeBrace > importEnd) return source;
423
+ const names = source.slice(openBrace + 1, closeBrace).split(',').map((importName)=>importName.trim()).filter((importName)=>'' !== importName && importName !== name);
424
+ if (0 === names.length) {
425
+ const lineEnd = source.indexOf('\n', importEnd + importEndMarker.length);
426
+ return `${source.slice(0, importStart)}${source.slice(lineEnd < 0 ? source.length : lineEnd + 1)}`;
427
+ }
428
+ return `${source.slice(0, openBrace + 1)} ${names.join(', ')} ${source.slice(closeBrace)}`;
429
+ }
415
430
  function ensureNavigateHook(source) {
416
431
  if (source.includes('const navigate = useNavigate();')) return source;
417
432
  const functionStart = source.indexOf('export default function ');
@@ -531,6 +546,43 @@ function removeSyntheticClickHandler(openingTag) {
531
546
  }
532
547
  return openingTag;
533
548
  }
549
+ function rewriteCheckoutCartLink(source) {
550
+ let changed = false;
551
+ const next = source.replace(/^(\s*)to=\{`\/\$\{language\}\/cart\?sku=\$\{sku\}`\},?$/gmu, (_match, indentation)=>{
552
+ changed = true;
553
+ return `${indentation}params={{ lang: language }}\n${indentation}search={{ sku }}\n${indentation}to="/$lang/cart"`;
554
+ });
555
+ return {
556
+ changed,
557
+ source: next
558
+ };
559
+ }
560
+ function rewriteCrossRemoteTractorsLink(source) {
561
+ let next = source;
562
+ let searchFrom = 0;
563
+ let changed = false;
564
+ while(true){
565
+ const openingTagStart = next.indexOf('<Link', searchFrom);
566
+ if (openingTagStart < 0) break;
567
+ const openingTagEnd = closingJsxTag(next, openingTagStart);
568
+ if (openingTagEnd < 0) break;
569
+ const openingTag = next.slice(openingTagStart, openingTagEnd + 1);
570
+ if (!openingTag.includes("to={`/${language}/tractors`}")) {
571
+ searchFrom = openingTagEnd + 1;
572
+ continue;
573
+ }
574
+ const closingTagStart = next.indexOf('</Link>', openingTagEnd + 1);
575
+ if (closingTagStart < 0) break;
576
+ const nativeOpeningTag = openingTag.replace('<Link', '<a').replace('to=', 'href=');
577
+ next = `${next.slice(0, openingTagStart)}${nativeOpeningTag}${next.slice(openingTagEnd + 1, closingTagStart)}</a>${next.slice(closingTagStart + 7)}`;
578
+ searchFrom = openingTagStart + nativeOpeningTag.length;
579
+ changed = true;
580
+ }
581
+ return {
582
+ changed,
583
+ source: next
584
+ };
585
+ }
534
586
  function rewriteInternalAnchors(source) {
535
587
  let next = source;
536
588
  let searchFrom = 0;
@@ -588,21 +640,49 @@ function rewriteJsxElementName(source, elementName, componentName) {
588
640
  source: next
589
641
  };
590
642
  }
643
+ function ensureNoPropsComponentContract(source) {
644
+ let changed = false;
645
+ let next = source.replace(/export default function ([A-Za-z_$][\w$]*)\(\) \{/u, (_match, componentName)=>{
646
+ changed = true;
647
+ return `export default function ${componentName}(\n props: Record<string, never>\n): JSX.Element {\n void props;`;
648
+ });
649
+ if (changed) next = ensureNamedImport(next, 'react', [
650
+ 'JSX'
651
+ ]);
652
+ return {
653
+ changed,
654
+ source: next
655
+ };
656
+ }
657
+ function regenerateGeneratedProductRouteAdapter(source) {
658
+ let next = source.replaceAll('search.sku', "search['sku']");
659
+ next = next.replace(/<(DecideProductPage|ProductPage) sku=\{sku\} slug=\{slug\} \/>/u, `<$1\n {...(sku === undefined ? {} : { sku })}\n {...(slug === undefined ? {} : { slug })}\n />`);
660
+ return next;
661
+ }
591
662
  function regenerateGeneratedNavigationSurface(source, kind) {
592
663
  const routerLocationSuffix = rewriteRouterLocationSuffix(source);
593
- const anchors = rewriteInternalAnchors(routerLocationSuffix.source);
594
- const locationAssignments = rewriteWindowLocationAssignments(anchors.source);
595
- const forms = 'demo-component' === kind ? rewriteJsxElementName(locationAssignments.source, 'form', 'Form') : {
664
+ const anchors = 'checkout-page' === kind ? rewriteCrossRemoteTractorsLink(routerLocationSuffix.source) : rewriteInternalAnchors(routerLocationSuffix.source);
665
+ const cartLink = 'checkout-add-to-cart' === kind ? rewriteCheckoutCartLink(anchors.source) : {
666
+ changed: false,
667
+ source: anchors.source
668
+ };
669
+ const locationAssignments = rewriteWindowLocationAssignments(cartLink.source);
670
+ const forms = 'demo-component' === kind || 'checkout-page' === kind ? rewriteJsxElementName(locationAssignments.source, 'form', 'Form') : {
596
671
  changed: false,
597
672
  source: locationAssignments.source
598
673
  };
599
- let next = forms.source;
600
- if (anchors.changed || locationAssignments.changed || forms.changed) {
674
+ const noProps = 'checkout-page' === kind ? ensureNoPropsComponentContract(forms.source) : {
675
+ changed: false,
676
+ source: forms.source
677
+ };
678
+ let next = noProps.source;
679
+ const usesLink = next.includes('<Link');
680
+ if (forms.changed || anchors.changed && usesLink || locationAssignments.changed) {
601
681
  const imports = [
602
682
  ...forms.changed ? [
603
683
  'Form'
604
684
  ] : [],
605
- ...anchors.changed ? [
685
+ ...anchors.changed && usesLink ? [
606
686
  'Link'
607
687
  ] : [],
608
688
  ...locationAssignments.changed ? [
@@ -611,7 +691,8 @@ function regenerateGeneratedNavigationSurface(source, kind) {
611
691
  ];
612
692
  next = ensureNamedImport(next, tanstackRuntimeModule, imports);
613
693
  }
694
+ if (!usesLink) next = removeNamedImport(next, tanstackRuntimeModule, 'Link');
614
695
  if (locationAssignments.changed) next = ensureNavigateHook(next);
615
696
  return next;
616
697
  }
617
- export { createFederatedComponentsRegistry, createLayout, createRemoteEntry, createRemoteExposeComponent, createRemoteExposeFragmentPage, createRemotePage, createShellPage, createShellRemoteComponents, createShellWorkerRemoteComponents, regenerateGeneratedNavigationSurface, remoteComponentOutputPath };
698
+ export { createFederatedComponentsRegistry, createLayout, createRemoteEntry, createRemoteExposeComponent, createRemoteExposeFragmentPage, createRemotePage, createShellPage, createShellRemoteComponents, createShellWorkerRemoteComponents, regenerateGeneratedNavigationSurface, regenerateGeneratedProductRouteAdapter, remoteComponentOutputPath };
@@ -1,5 +1,8 @@
1
1
  const moduleFederationConfigFile = 'module-federation.config.ts';
2
- const mfTypesArchive = 'dist/@mf-types.zip';
2
+ const mfTypesArchives = {
3
+ cloudflare: 'dist-cloudflare/@mf-types.zip',
4
+ node: 'dist/@mf-types.zip'
5
+ };
3
6
  const generatedMetadataPaths = [
4
7
  '.modernjs/ultramodern.json'
5
8
  ];
@@ -16,4 +19,4 @@ const skippedScanDirs = new Set([
16
19
  'dist',
17
20
  'node_modules'
18
21
  ]);
19
- export { defaultAppRootDirs, generatedMetadataPaths, mfTypesArchive, moduleFederationConfigFile, skippedScanDirs };
22
+ export { defaultAppRootDirs, generatedMetadataPaths, mfTypesArchives, moduleFederationConfigFile, skippedScanDirs };
@@ -1,6 +1,6 @@
1
1
  import node_fs from "node:fs";
2
2
  import node_path from "node:path";
3
- import { mfTypesArchive } from "./constants.js";
3
+ import { mfTypesArchives } from "./constants.js";
4
4
  import { discoverModuleFederationConfigs } from "./discovery.js";
5
5
  import { inspectModuleFederationConfigSource } from "./inspect.js";
6
6
  import { relativePath } from "./path-utils.js";
@@ -8,8 +8,8 @@ function assertExposedAppDtsSettings(app) {
8
8
  if ('effect-tsgo' !== app.dts.compilerInstance) throw new Error(`Module Federation DTS compilerInstance must resolve "@effect/tsgo" for ${app.appDir}.`);
9
9
  if ('./tsconfig.mf-types.json' !== app.dts.tsConfigPath) throw new Error(`Module Federation DTS tsConfigPath must be "./tsconfig.mf-types.json" for ${app.appDir}.`);
10
10
  }
11
- function assertTypesArchive(workspaceRoot, appDir) {
12
- const typesArchivePath = node_path.join(workspaceRoot, appDir, mfTypesArchive);
11
+ function assertTypesArchive(workspaceRoot, appDir, target = 'node') {
12
+ const typesArchivePath = node_path.join(workspaceRoot, appDir, mfTypesArchives[target]);
13
13
  if (!node_fs.existsSync(typesArchivePath)) throw new Error(`Missing Module Federation DTS archive: ${relativePath(workspaceRoot, typesArchivePath)}`);
14
14
  if (0 === node_fs.statSync(typesArchivePath).size) throw new Error(`Empty Module Federation DTS archive: ${relativePath(workspaceRoot, typesArchivePath)}`);
15
15
  }
@@ -43,7 +43,7 @@ function validateModuleFederationTypes(options) {
43
43
  continue;
44
44
  }
45
45
  assertExposedAppDtsSettings(app);
46
- assertTypesArchive(workspaceRoot, app.appDir);
46
+ assertTypesArchive(workspaceRoot, app.appDir, options.target);
47
47
  exposedAppCount += 1;
48
48
  }
49
49
  if (apps.length > 0 && 0 === exposedAppCount && hostOnlyAppCount !== apps.length) throw new Error('Module Federation validation inspected configs but validated zero exposed apps. Declare host-only/no-exposes intent only for host apps without exposes.');
@@ -44,16 +44,10 @@ function isPublicIndexableRoute(route) {
44
44
  return route.public && route.indexable;
45
45
  }
46
46
  function createLocalisedUrlsMapFromRoutes(routes) {
47
- return Object.fromEntries(routes.flatMap((route)=>{
48
- if ('/' === route.canonicalPath) return [];
49
- return Array.from(new Set([
47
+ return Object.fromEntries(routes.filter((route)=>'/' !== route.canonicalPath).map((route)=>[
50
48
  route.canonicalPath,
51
- ...Object.values(route.localisedPaths)
52
- ])).map((pathname)=>[
53
- pathname,
54
- route.localisedPaths
55
- ]);
56
- }));
49
+ route.localisedPaths
50
+ ]));
57
51
  }
58
52
  function createLocalisedUrlsMap(app) {
59
53
  return createLocalisedUrlsMapFromRoutes(createRouteOwnedI18nPaths(app));
@@ -143,4 +137,4 @@ function createRouteAliasPage(canonicalPath) {
143
137
  return `export { default } from '${rootPageImport}';
144
138
  `;
145
139
  }
146
- export { createJsonLdHelperModule, createLocalisedUrlsMap, createPublicRouteMetadata, createPublicRouteMetadataFromRoutes, createRouteAliasPage, createRouteMetaFilePath, createRouteMetaModule, createRouteMetadataModule, createRouteOwnedI18nPaths, createRoutePageFilePath, isConcretePublicPath, normalisePublicPath, routePathParamName, routeSegmentToDirectory, splitPublicPathSegments };
140
+ export { createJsonLdHelperModule, createLocalisedUrlsMap, createLocalisedUrlsMapFromRoutes, createPublicRouteMetadata, createPublicRouteMetadataFromRoutes, createRouteAliasPage, createRouteMetaFilePath, createRouteMetaModule, createRouteMetadataModule, createRouteOwnedI18nPaths, createRoutePageFilePath, isConcretePublicPath, normalisePublicPath, routePathParamName, routeSegmentToDirectory, splitPublicPathSegments };
@@ -84,6 +84,9 @@ function createPackageTsConfig(packageDir, options = {}) {
84
84
  emitDeclarationOnly: true,
85
85
  incremental: true,
86
86
  noEmit: false,
87
+ ...resolvedOptions.skipLibCheck ? {
88
+ skipLibCheck: true
89
+ } : {},
87
90
  outDir: createTsDeclarationOutDir(packageDir),
88
91
  tsBuildInfoFile: createTsBuildInfoFile(packageDir)
89
92
  },
@@ -93,14 +96,16 @@ function createPackageTsConfig(packageDir, options = {}) {
93
96
  return tsconfig;
94
97
  }
95
98
  function createAppTsConfig(app, remotes = []) {
99
+ const remoteRefs = 'shell' === app.kind ? [] : resolveRemoteRefs(app, remotes);
96
100
  const references = [
97
101
  ...sharedPackages.map((sharedPackage)=>sharedPackage.directory),
98
- ...'shell' === app.kind ? [] : resolveRemoteRefs(app, remotes).map((remote)=>remote.directory)
102
+ ...remoteRefs.map((remote)=>remote.directory)
99
103
  ];
100
104
  return createPackageTsConfig(app.directory, {
101
105
  includeApi: appHasApi(app),
102
106
  includeServer: true,
103
- references
107
+ references,
108
+ skipLibCheck: remoteRefs.length > 0
104
109
  });
105
110
  }
106
111
  function createAppMfTypesTsConfig(app) {
@@ -112,7 +117,10 @@ function createAppMfTypesTsConfig(app) {
112
117
  ...exposedFiles,
113
118
  'src/modern-app-env.d.ts'
114
119
  ])
115
- ]
120
+ ],
121
+ compilerOptions: {
122
+ skipLibCheck: true
123
+ }
116
124
  };
117
125
  }
118
126
  function createSharedPackageTsConfig(packageDir) {
@@ -1,4 +1,4 @@
1
- import { appHasApi, shellApp } from "./descriptors.js";
1
+ import { appHasApi, sharedPackages, shellApp } from "./descriptors.js";
2
2
  import { relativeRootFor } from "./naming.js";
3
3
  import { createPublicSurfaceGenerationCommand } from "./public-surface.js";
4
4
  import { GENERATED_TOOLING_COMMANDS } from "./tooling-command-catalog.js";
@@ -78,6 +78,29 @@ function createWorkspaceAppPackageScripts(app) {
78
78
  plan[planKey]
79
79
  ]));
80
80
  }
81
+ function createReferencedRemoteDeclarationBuilds(remotes) {
82
+ const remotesById = new Map(remotes.map((remote)=>[
83
+ remote.id,
84
+ remote
85
+ ]));
86
+ const referencedRemoteIds = new Set(remotes.flatMap((remote)=>remote.verticalRefs ?? []));
87
+ const orderedRemotes = [];
88
+ const visiting = new Set();
89
+ const visited = new Set();
90
+ const visit = (remote)=>{
91
+ if (visited.has(remote.id) || visiting.has(remote.id)) return;
92
+ visiting.add(remote.id);
93
+ for (const dependencyId of remote.verticalRefs ?? []){
94
+ const dependency = remotesById.get(dependencyId);
95
+ if (void 0 !== dependency) visit(dependency);
96
+ }
97
+ visiting.delete(remote.id);
98
+ visited.add(remote.id);
99
+ orderedRemotes.push(remote);
100
+ };
101
+ for (const remote of remotes)if (referencedRemoteIds.has(remote.id)) visit(remote);
102
+ return orderedRemotes.map((remote)=>`${rootToolingWrapperCommand('typecheck')} --project ${remote.directory}/tsconfig.json --skipLibCheck`).join(' && ');
103
+ }
81
104
  function createWorkspaceRootScriptPlan(remotes = [], options = {}) {
82
105
  const hasRemotes = remotes.length > 0;
83
106
  remotes.some(appHasApi);
@@ -91,13 +114,20 @@ function createWorkspaceRootScriptPlan(remotes = [], options = {}) {
91
114
  const performanceReadinessScript = rootToolingScriptName('performanceReadiness');
92
115
  const cloudflareOutputVerifyScript = rootToolingScriptName('cloudflareOutputVerify');
93
116
  const bridgeCheck = options.bridgeCheck ?? '';
117
+ const sharedDeclarationsBuild = sharedPackages.map((sharedPackage)=>`${rootToolingWrapperCommand('typecheck')} --build ${sharedPackage.directory}/tsconfig.json`).join(' && ');
118
+ const referencedRemoteDeclarationsBuild = createReferencedRemoteDeclarationBuilds(remotes);
119
+ const declarationBuild = [
120
+ sharedDeclarationsBuild,
121
+ referencedRemoteDeclarationsBuild
122
+ ].filter(Boolean).join(' && ');
123
+ const buildPrefix = `${declarationBuild} && `;
94
124
  const remoteBuildPrefix = hasRemotes ? 'pnpm -r --filter "./verticals/*" run build && ' : '';
95
125
  const remoteCloudflareBuildPrefix = hasRemotes ? 'pnpm -r --filter "./verticals/*" run cloudflare:build && ' : '';
96
126
  const remoteCloudflareDeployPrefix = hasRemotes ? 'pnpm -r --filter "./verticals/*" run cloudflare:deploy && ' : '';
97
127
  const cloudflareSsrProofSuffix = hasRemotes ? ' && pnpm cloudflare:ssr-proof' : '';
98
128
  return {
99
- build: `${remoteBuildPrefix}${shellBuild} && pnpm ${mfTypesScript} && pnpm ${performanceReadinessScript}`,
100
- cloudflareBuild: `${remoteCloudflareBuildPrefix}${shellCloudflareBuild} && pnpm ${mfTypesScript} && pnpm ${cloudflareOutputVerifyScript}${cloudflareSsrProofSuffix}`,
129
+ build: `${buildPrefix}${remoteBuildPrefix}${shellBuild} && pnpm ${mfTypesScript} && pnpm ${performanceReadinessScript}`,
130
+ cloudflareBuild: `${buildPrefix}${remoteCloudflareBuildPrefix}${shellCloudflareBuild} && pnpm ${mfTypesScript} --target cloudflare && pnpm ${cloudflareOutputVerifyScript}${cloudflareSsrProofSuffix}`,
101
131
  cloudflareDeploy: `${remoteCloudflareDeployPrefix}${shellCloudflareDeploy}`,
102
132
  cloudflareProof: `${rootToolingWrapperCommand('cloudflareProof')} --out .codex/reports/cloudflare-version-proof/public-url-proof.json`,
103
133
  cloudflareSsrProof: "node ./scripts/proof-workerd-ssr.mts",
@@ -1,17 +1,43 @@
1
1
  import "node:module";
2
2
  import { validateModuleFederationTypes } from "../../ultramodern-workspace/mf-validation.js";
3
+ function parseMfTypesArgs(args) {
4
+ const appDirs = [];
5
+ let target = 'node';
6
+ for(let index = 0; index < args.length; index += 1){
7
+ const arg = args[index];
8
+ if (void 0 === arg || '--' === arg) continue;
9
+ let targetValue;
10
+ if ('--target' === arg) {
11
+ targetValue = args[index + 1];
12
+ if (void 0 === targetValue || targetValue.startsWith('-')) throw new Error('--target needs a value.');
13
+ index += 1;
14
+ } else if (arg.startsWith('--target=')) targetValue = arg.slice(9);
15
+ else if (arg.startsWith('-')) throw new Error(`Unknown mf-types option: ${arg}`);
16
+ else appDirs.push(arg);
17
+ if (void 0 !== targetValue) {
18
+ if ('node' !== targetValue && 'cloudflare' !== targetValue) throw new Error(`Invalid mf-types target "${targetValue}". Expected "node" or "cloudflare".`);
19
+ target = targetValue;
20
+ }
21
+ }
22
+ return {
23
+ appDirs,
24
+ target
25
+ };
26
+ }
3
27
  function runMfTypes(args, context) {
4
28
  if (args.includes('--help') || args.includes('-h')) {
5
29
  process.stdout.write(`Usage:
6
- ultramodern-create ultramodern mf-types [app-dir...]
30
+ ultramodern-create ultramodern mf-types [--target node|cloudflare] [app-dir...]
7
31
 
8
- Checks real Module Federation config files and DTS archives for exposed apps.
32
+ Checks real Module Federation config files and target-specific DTS archives for exposed apps.
9
33
  `);
10
34
  return 0;
11
35
  }
36
+ const { appDirs, target } = parseMfTypesArgs(args);
12
37
  validateModuleFederationTypes({
13
38
  workspaceRoot: context.workspaceRoot,
14
- appDirs: args.length > 0 ? args : void 0
39
+ appDirs: appDirs.length > 0 ? appDirs : void 0,
40
+ target
15
41
  });
16
42
  return 0;
17
43
  }
@@ -63,6 +63,9 @@ function removeTsCheckerBuildOverride(source) {
63
63
  },
64
64
  `, '');
65
65
  }
66
+ function removeReleaseEnvelopePlugin(source) {
67
+ return source.replace(' ultramodernReleaseEnvelopePlugin,\n', '').replace(' ultramodernReleaseEnvelopePlugin(),\n', '');
68
+ }
66
69
  function updateGeneratedModernConfigs(io, config) {
67
70
  const apps = allWorkspaceAppsFromToolingConfig(config);
68
71
  const remotes = apps.filter((app)=>'shell' !== app.kind);
@@ -89,10 +92,15 @@ function updateGeneratedModernConfigs(io, config) {
89
92
  previousTailwindModernConfig,
90
93
  addLegacyGeneratedModernDefaults(previousTailwindModernConfig)
91
94
  ];
92
- const [generatedModernConfig, ...recognizedGeneratedModernConfigs] = formatGeneratedModernConfigCandidates(currentGeneratedModernConfigs.flatMap((source)=>[
95
+ const [generatedModernConfig, ...recognizedGeneratedModernConfigs] = formatGeneratedModernConfigCandidates(currentGeneratedModernConfigs.flatMap((source)=>{
96
+ const withoutReleaseEnvelope = removeReleaseEnvelopePlugin(source);
97
+ return [
93
98
  source,
94
- removeTsCheckerBuildOverride(source)
95
- ]));
99
+ removeTsCheckerBuildOverride(source),
100
+ withoutReleaseEnvelope,
101
+ removeTsCheckerBuildOverride(withoutReleaseEnvelope)
102
+ ];
103
+ }));
96
104
  if (node_fs.existsSync(modernConfigPath)) {
97
105
  const existingModernConfig = node_fs.readFileSync(modernConfigPath, 'utf-8');
98
106
  if (existingModernConfig === generatedModernConfig) io.writeGenerated(modernConfigPath, existingModernConfig);
@@ -3,7 +3,7 @@ import node_fs from "node:fs";
3
3
  import node_path from "node:path";
4
4
  import { ULTRAMODERN_CREATE_PACKAGE } from "../../../ultramodern-package-source.js";
5
5
  import { createAppEnvDts, createAppRuntimeConfig } from "../../../ultramodern-workspace/app-files.js";
6
- import { createRemoteExposeFragmentPage, regenerateGeneratedNavigationSurface, remoteComponentOutputPath } from "../../../ultramodern-workspace/demo-components.js";
6
+ import { createRemoteExposeFragmentPage, regenerateGeneratedNavigationSurface, regenerateGeneratedProductRouteAdapter, remoteComponentOutputPath } from "../../../ultramodern-workspace/demo-components.js";
7
7
  import { appI18nNamespace, distributedSsrExposes, distributedSsrFragmentSlug, resolveRemoteRefs } from "../../../ultramodern-workspace/descriptors.js";
8
8
  import { createAppMfTypesTsConfig, createAppTsConfig, createSharedPackageTsConfig, createTsConfigBase } from "../../../ultramodern-workspace/package-json.js";
9
9
  import { allWorkspaceAppsFromToolingConfig } from "../../config.js";
@@ -103,10 +103,13 @@ function manifestApps(manifest) {
103
103
  function packageManifest(io, appDirectory) {
104
104
  return readJsonObject(node_path.join(io.workspaceRoot, appDirectory, 'package.json'));
105
105
  }
106
- function shellSurfaceIsOwned(io, app, manifestApp) {
106
+ function appSurfaceIsOwned(io, app, manifestApp) {
107
107
  const packageJson = packageManifest(io, app.directory);
108
+ return manifestApp.path === app.directory && manifestApp.kind === app.kind && 'string' == typeof manifestApp.package && packageJson?.name === manifestApp.package;
109
+ }
110
+ function shellSurfaceIsOwned(io, app, manifestApp) {
108
111
  const moduleFederation = jsonObject(manifestApp.moduleFederation);
109
- return 'shell' === manifestApp.kind && manifestApp.path === app.directory && moduleFederation?.role === 'host' && 'string' == typeof manifestApp.package && packageJson?.name === manifestApp.package;
112
+ return appSurfaceIsOwned(io, app, manifestApp) && 'shell' === app.kind && moduleFederation?.role === 'host';
110
113
  }
111
114
  function deliveryUnitSurfaceIsOwned(io, config, app, manifestApp, expose, sourcePath) {
112
115
  const deliveryUnit = jsonObject(manifestApp.deliveryUnit);
@@ -117,6 +120,30 @@ function deliveryUnitSurfaceIsOwned(io, config, app, manifestApp, expose, source
117
120
  const packageName = manifestApp.package;
118
121
  return 'vertical' === manifestApp.kind && manifestApp.path === app.directory && 'string' == typeof packageName && packageJson?.name === packageName && deliveryUnit?.kind === 'microvertical-delivery-unit' && deliveryUnit.packageName === packageName && deliveryUnit.unitId === `${config.workspace.packageScope}/${app.id}` && exposedSurfaces.includes(expose) && packageExports?.[expose] === sourcePath;
119
122
  }
123
+ function updateGeneratedProductRouteAdapters(io, config) {
124
+ const manifest = generatedManifest(io, config);
125
+ if (void 0 === manifest) return false;
126
+ const appsById = new Map(manifestApps(manifest).map((app)=>[
127
+ String(app.id ?? ''),
128
+ app
129
+ ]));
130
+ let changed = false;
131
+ for (const app of allWorkspaceAppsFromToolingConfig(config)){
132
+ const manifestApp = appsById.get(app.id);
133
+ if ('decide' !== app.id && 'shell' !== app.kind || void 0 === manifestApp || !appSurfaceIsOwned(io, app, manifestApp)) continue;
134
+ const routeDirectory = node_path.join(io.workspaceRoot, app.directory, 'src/routes/[lang]/tractors/[slug]');
135
+ for (const fileName of [
136
+ 'page.search.ts',
137
+ 'page.tsx'
138
+ ]){
139
+ const filePath = node_path.join(routeDirectory, fileName);
140
+ if (!node_fs.existsSync(filePath)) continue;
141
+ const source = node_fs.readFileSync(filePath, 'utf-8');
142
+ changed = io.write(filePath, regenerateGeneratedProductRouteAdapter(source)) || changed;
143
+ }
144
+ }
145
+ return changed;
146
+ }
120
147
  function updateGeneratedNavigationSurfaces(io, config) {
121
148
  const manifest = generatedManifest(io, config);
122
149
  if (void 0 === manifest) return false;
@@ -145,7 +172,7 @@ function updateGeneratedNavigationSurfaces(io, config) {
145
172
  const filePath = node_path.join(io.workspaceRoot, relativePath);
146
173
  if (!node_fs.existsSync(filePath)) continue;
147
174
  const source = node_fs.readFileSync(filePath, 'utf-8');
148
- if (source.includes(`data-modern-boundary-id="${app.id}"`) && source.includes(`data-modern-mf-expose="${expose}"`)) changed = io.write(filePath, regenerateGeneratedNavigationSurface(source, 'demo-component')) || changed;
175
+ if (source.includes(`data-modern-boundary-id="${app.id}"`) && source.includes(`data-modern-mf-expose="${expose}"`)) changed = io.write(filePath, regenerateGeneratedNavigationSurface(source, './AddToCart' === expose ? 'checkout-add-to-cart' : 'checkout-page')) || changed;
149
176
  }
150
177
  }
151
178
  }
@@ -226,6 +253,7 @@ function updateGeneratedTypeScriptSurfaces(io, config) {
226
253
  }
227
254
  }
228
255
  updateGeneratedShellRuntimeSurfaces(io, config);
256
+ updateGeneratedProductRouteAdapters(io, config);
229
257
  updateGeneratedNavigationSurfaces(io, config);
230
258
  }
231
259
  export { updateGeneratedTypeScriptSurfaces };
@@ -1,5 +1,5 @@
1
1
  import "node:module";
2
- import { ULTRAMODERN_SINGLE_APP_MODERN_PACKAGES, ULTRAMODERN_WORKSPACE_MODERN_PACKAGES, modernPackageSpecifier } from "../../../ultramodern-package-source.js";
2
+ import { ULTRAMODERN_CREATE_PACKAGE, ULTRAMODERN_SINGLE_APP_MODERN_PACKAGES, ULTRAMODERN_WORKSPACE_MODERN_PACKAGES, modernPackageSpecifier } from "../../../ultramodern-package-source.js";
3
3
  import { ULTRAMODERN_PACKAGE_PINS } from "../../../ultramodern-workspace/policy.js";
4
4
  import { GENERATED_POSTINSTALL_SCRIPT, createWorkspaceAppPackageScripts, createWorkspaceRootPackageScripts } from "../../../ultramodern-workspace/workspace-script-plan.js";
5
5
  import { migratedWorkspaceScriptBasenames } from "../../../ultramodern-workspace/workspace-scripts.js";
@@ -26,12 +26,24 @@ function updateDeclaredDependencies(packageJson, pins) {
26
26
  return changed;
27
27
  }
28
28
  function updateModernDependencies(packageJson, packageSource) {
29
+ let changed = false;
30
+ for (const section of [
31
+ 'dependencies',
32
+ 'devDependencies'
33
+ ]){
34
+ const dependencies = packageJson[section];
35
+ if (dependencies && Object.hasOwn(dependencies, '@modern-js/create')) {
36
+ dependencies[ULTRAMODERN_CREATE_PACKAGE] = modernPackageSpecifier(ULTRAMODERN_CREATE_PACKAGE, packageSource);
37
+ delete dependencies['@modern-js/create'];
38
+ changed = true;
39
+ }
40
+ }
29
41
  return updateDeclaredDependencies(packageJson, new Map([
30
42
  ...modernPackageNames
31
43
  ].map((packageName)=>[
32
44
  packageName,
33
45
  modernPackageSpecifier(packageName, packageSource)
34
- ])));
46
+ ]))) || changed;
35
47
  }
36
48
  const generatedToolingDependencyPins = new Map(Object.entries({
37
49
  ...ULTRAMODERN_PACKAGE_PINS.appDependencies,