@bleedingdev/modern-js-create 3.2.0-ultramodern.120 → 3.2.0-ultramodern.122

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 (118) hide show
  1. package/README.md +35 -12
  2. package/dist/cjs/create-package-root.cjs +7 -9
  3. package/dist/cjs/index.cjs +74 -44
  4. package/dist/cjs/locale/en.cjs +6 -7
  5. package/dist/cjs/locale/zh.cjs +6 -7
  6. package/dist/cjs/ultramodern-workspace/add-vertical.cjs +337 -0
  7. package/dist/cjs/ultramodern-workspace/app-files.cjs +223 -0
  8. package/dist/cjs/ultramodern-workspace/contracts.cjs +836 -0
  9. package/dist/cjs/ultramodern-workspace/demo-components.cjs +422 -0
  10. package/dist/cjs/ultramodern-workspace/descriptors.cjs +222 -0
  11. package/dist/cjs/ultramodern-workspace/effect-api.cjs +952 -0
  12. package/dist/cjs/ultramodern-workspace/fs-io.cjs +191 -0
  13. package/dist/cjs/ultramodern-workspace/index.cjs +48 -0
  14. package/dist/cjs/ultramodern-workspace/locales.cjs +173 -0
  15. package/dist/cjs/ultramodern-workspace/module-federation.cjs +487 -0
  16. package/dist/cjs/ultramodern-workspace/naming.cjs +161 -0
  17. package/dist/cjs/ultramodern-workspace/package-json.cjs +406 -0
  18. package/dist/cjs/ultramodern-workspace/package-source.cjs +59 -0
  19. package/dist/cjs/ultramodern-workspace/policy.cjs +248 -0
  20. package/dist/cjs/ultramodern-workspace/public-surface.cjs +268 -0
  21. package/dist/cjs/ultramodern-workspace/routes.cjs +375 -0
  22. package/dist/cjs/ultramodern-workspace/types.cjs +61 -0
  23. package/dist/cjs/ultramodern-workspace/versions.cjs +153 -0
  24. package/dist/cjs/ultramodern-workspace/workspace-scripts.cjs +153 -0
  25. package/dist/cjs/ultramodern-workspace/write-workspace.cjs +175 -0
  26. package/dist/esm/create-package-root.js +7 -9
  27. package/dist/esm/index.js +72 -42
  28. package/dist/esm/locale/en.js +6 -7
  29. package/dist/esm/locale/zh.js +6 -7
  30. package/dist/esm/ultramodern-workspace/add-vertical.js +252 -0
  31. package/dist/esm/ultramodern-workspace/app-files.js +149 -0
  32. package/dist/esm/ultramodern-workspace/contracts.js +741 -0
  33. package/dist/esm/ultramodern-workspace/demo-components.js +363 -0
  34. package/dist/esm/ultramodern-workspace/descriptors.js +133 -0
  35. package/dist/esm/ultramodern-workspace/effect-api.js +854 -0
  36. package/dist/esm/ultramodern-workspace/fs-io.js +90 -0
  37. package/dist/esm/ultramodern-workspace/index.js +3 -0
  38. package/dist/esm/ultramodern-workspace/locales.js +122 -0
  39. package/dist/esm/ultramodern-workspace/module-federation.js +415 -0
  40. package/dist/esm/ultramodern-workspace/naming.js +71 -0
  41. package/dist/esm/ultramodern-workspace/package-json.js +338 -0
  42. package/dist/esm/ultramodern-workspace/package-source.js +21 -0
  43. package/dist/esm/ultramodern-workspace/policy.js +183 -0
  44. package/dist/esm/ultramodern-workspace/public-surface.js +183 -0
  45. package/dist/esm/ultramodern-workspace/routes.js +280 -0
  46. package/dist/esm/ultramodern-workspace/types.js +16 -0
  47. package/dist/esm/ultramodern-workspace/versions.js +34 -0
  48. package/dist/esm/ultramodern-workspace/workspace-scripts.js +91 -0
  49. package/dist/esm/ultramodern-workspace/write-workspace.js +121 -0
  50. package/dist/esm-node/create-package-root.js +7 -9
  51. package/dist/esm-node/index.js +72 -42
  52. package/dist/esm-node/locale/en.js +6 -7
  53. package/dist/esm-node/locale/zh.js +6 -7
  54. package/dist/esm-node/ultramodern-workspace/add-vertical.js +253 -0
  55. package/dist/esm-node/ultramodern-workspace/app-files.js +150 -0
  56. package/dist/esm-node/ultramodern-workspace/contracts.js +742 -0
  57. package/dist/esm-node/ultramodern-workspace/demo-components.js +364 -0
  58. package/dist/esm-node/ultramodern-workspace/descriptors.js +134 -0
  59. package/dist/esm-node/ultramodern-workspace/effect-api.js +855 -0
  60. package/dist/esm-node/ultramodern-workspace/fs-io.js +91 -0
  61. package/dist/esm-node/ultramodern-workspace/index.js +4 -0
  62. package/dist/esm-node/ultramodern-workspace/locales.js +123 -0
  63. package/dist/esm-node/ultramodern-workspace/module-federation.js +416 -0
  64. package/dist/esm-node/ultramodern-workspace/naming.js +72 -0
  65. package/dist/esm-node/ultramodern-workspace/package-json.js +339 -0
  66. package/dist/esm-node/ultramodern-workspace/package-source.js +22 -0
  67. package/dist/esm-node/ultramodern-workspace/policy.js +184 -0
  68. package/dist/esm-node/ultramodern-workspace/public-surface.js +184 -0
  69. package/dist/esm-node/ultramodern-workspace/routes.js +281 -0
  70. package/dist/esm-node/ultramodern-workspace/types.js +17 -0
  71. package/dist/esm-node/ultramodern-workspace/versions.js +35 -0
  72. package/dist/esm-node/ultramodern-workspace/workspace-scripts.js +92 -0
  73. package/dist/esm-node/ultramodern-workspace/write-workspace.js +122 -0
  74. package/dist/types/locale/en.d.ts +4 -5
  75. package/dist/types/locale/index.d.ts +8 -10
  76. package/dist/types/locale/zh.d.ts +4 -5
  77. package/dist/types/ultramodern-workspace/add-vertical.d.ts +19 -0
  78. package/dist/types/ultramodern-workspace/app-files.d.ts +14 -0
  79. package/dist/types/ultramodern-workspace/contracts.d.ts +21 -0
  80. package/dist/types/ultramodern-workspace/demo-components.d.ts +9 -0
  81. package/dist/types/ultramodern-workspace/descriptors.d.ts +39 -0
  82. package/dist/types/ultramodern-workspace/effect-api.d.ts +73 -0
  83. package/dist/types/ultramodern-workspace/fs-io.d.ts +18 -0
  84. package/dist/types/ultramodern-workspace/index.d.ts +4 -0
  85. package/dist/types/ultramodern-workspace/locales.d.ts +183 -0
  86. package/dist/types/ultramodern-workspace/module-federation.d.ts +16 -0
  87. package/dist/types/ultramodern-workspace/naming.d.ts +16 -0
  88. package/dist/types/ultramodern-workspace/package-json.d.ts +12 -0
  89. package/dist/types/ultramodern-workspace/package-source.d.ts +2 -0
  90. package/dist/types/ultramodern-workspace/policy.d.ts +60 -0
  91. package/dist/types/ultramodern-workspace/public-surface.d.ts +37 -0
  92. package/dist/types/ultramodern-workspace/routes.d.ts +25 -0
  93. package/dist/types/ultramodern-workspace/types.d.ts +95 -0
  94. package/dist/types/ultramodern-workspace/versions.d.ts +38 -0
  95. package/dist/types/ultramodern-workspace/workspace-scripts.d.ts +10 -0
  96. package/dist/types/ultramodern-workspace/write-workspace.d.ts +4 -0
  97. package/package.json +4 -3
  98. package/template-workspace/.github/workflows/ultramodern-workspace-gates.yml.handlebars +1 -4
  99. package/template-workspace/.mise.toml.handlebars +1 -0
  100. package/template-workspace/{AGENTS.md → AGENTS.md.handlebars} +12 -7
  101. package/template-workspace/README.md.handlebars +40 -24
  102. package/template-workspace/{pnpm-workspace.yaml → pnpm-workspace.yaml.handlebars} +2 -2
  103. package/template-workspace/scripts/bootstrap-agent-skills.mjs +31 -51
  104. package/templates/app/shell-frame.tsx +49 -0
  105. package/templates/app/ultramodern-route-head.tsx.handlebars +142 -0
  106. package/templates/packages/shared-contracts-index.ts +466 -0
  107. package/templates/workspace-scripts/assert-mf-types.mjs.handlebars +69 -0
  108. package/templates/workspace-scripts/check-ultramodern-i18n-boundaries.mjs +9 -0
  109. package/templates/workspace-scripts/generate-public-surface-assets.mjs +529 -0
  110. package/templates/workspace-scripts/proof-cloudflare-version.mjs +125 -0
  111. package/templates/workspace-scripts/ultramodern-cloudflare-proof.mjs +851 -0
  112. package/templates/workspace-scripts/ultramodern-performance-readiness.config.mjs +7 -0
  113. package/templates/workspace-scripts/ultramodern-performance-readiness.mjs +223 -0
  114. package/templates/workspace-scripts/validate-ultramodern-workspace.mjs.handlebars +656 -0
  115. package/dist/cjs/ultramodern-workspace.cjs +0 -6797
  116. package/dist/esm/ultramodern-workspace.js +0 -6738
  117. package/dist/esm-node/ultramodern-workspace.js +0 -6739
  118. package/dist/types/ultramodern-workspace.d.ts +0 -29
@@ -0,0 +1,90 @@
1
+ import node_crypto from "node:crypto";
2
+ import node_fs from "node:fs";
3
+ import node_path from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import { resolveCreatePackageRoot } from "../create-package-root.js";
6
+ import { normalizePath } from "./naming.js";
7
+ const fs_io_dirname = node_path.dirname(fileURLToPath(import.meta.url));
8
+ const createPackageRoot = resolveCreatePackageRoot(fs_io_dirname);
9
+ const workspaceTemplateDir = node_path.join(createPackageRoot, 'template-workspace');
10
+ const fileTemplatesDir = node_path.join(createPackageRoot, 'templates');
11
+ function readFileTemplate(relativePath) {
12
+ return node_fs.readFileSync(node_path.join(fileTemplatesDir, relativePath), 'utf-8');
13
+ }
14
+ function renderFileTemplate(relativePath, data) {
15
+ return renderTemplate(readFileTemplate(`${relativePath}.handlebars`), data);
16
+ }
17
+ function assertSafeRelativePath(relativePath) {
18
+ if (0 === relativePath.length || node_path.isAbsolute(relativePath) || relativePath.split(/[\\/]+/).includes('..')) throw new Error(`Unsafe workspace template path: ${relativePath}`);
19
+ }
20
+ function ensureInsideRoot(root, targetPath) {
21
+ const relativePath = node_path.relative(root, targetPath);
22
+ if (relativePath.startsWith('..') || node_path.isAbsolute(relativePath)) throw new Error(`Refusing to write outside workspace root: ${targetPath}`);
23
+ }
24
+ function writeFile(targetDir, relativePath, content) {
25
+ assertSafeRelativePath(relativePath);
26
+ const filePath = node_path.join(targetDir, relativePath);
27
+ ensureInsideRoot(targetDir, filePath);
28
+ if (node_fs.existsSync(filePath)) throw new Error(`Refusing to overwrite generated workspace file: ${relativePath}`);
29
+ node_fs.mkdirSync(node_path.dirname(filePath), {
30
+ recursive: true
31
+ });
32
+ node_fs.writeFileSync(filePath, content, 'utf-8');
33
+ }
34
+ function writeFileReplacing(targetDir, relativePath, content) {
35
+ assertSafeRelativePath(relativePath);
36
+ const filePath = node_path.join(targetDir, relativePath);
37
+ ensureInsideRoot(targetDir, filePath);
38
+ node_fs.mkdirSync(node_path.dirname(filePath), {
39
+ recursive: true
40
+ });
41
+ node_fs.writeFileSync(filePath, content, 'utf-8');
42
+ }
43
+ function writeJson(targetDir, relativePath, value) {
44
+ writeFile(targetDir, relativePath, `${JSON.stringify(value, null, 2)}\n`);
45
+ }
46
+ function renderTemplate(template, data) {
47
+ return template.replace(/\{\{(\w+)\}\}/g, (match, key)=>data[key] ?? match);
48
+ }
49
+ function collectTemplateFiles(dir) {
50
+ const files = [];
51
+ function collect(currentDir) {
52
+ for (const entry of node_fs.readdirSync(currentDir, {
53
+ withFileTypes: true
54
+ }).sort((a, b)=>a.name.localeCompare(b.name))){
55
+ const entryPath = node_path.join(currentDir, entry.name);
56
+ if (entry.isDirectory()) collect(entryPath);
57
+ else if (entry.isFile()) files.push(normalizePath(node_path.relative(dir, entryPath)));
58
+ }
59
+ }
60
+ collect(dir);
61
+ return files;
62
+ }
63
+ function hashFile(filePath) {
64
+ return node_crypto.createHash('sha256').update(node_fs.readFileSync(filePath)).digest('hex');
65
+ }
66
+ function hashTemplateTree(dir) {
67
+ const hash = node_crypto.createHash('sha256');
68
+ for (const relativePath of collectTemplateFiles(dir)){
69
+ hash.update(relativePath);
70
+ hash.update('\0');
71
+ hash.update(hashFile(node_path.join(dir, relativePath)));
72
+ hash.update('\0');
73
+ }
74
+ return hash.digest('hex');
75
+ }
76
+ function copyRootTemplate(targetDir, data) {
77
+ for (const relativePath of collectTemplateFiles(workspaceTemplateDir)){
78
+ const sourcePath = node_path.join(workspaceTemplateDir, relativePath);
79
+ const outputPath = relativePath.replace(/\.handlebars$/, '');
80
+ const content = relativePath.endsWith('.handlebars') ? renderTemplate(node_fs.readFileSync(sourcePath, 'utf-8'), data) : node_fs.readFileSync(sourcePath, 'utf-8');
81
+ writeFile(targetDir, outputPath, content);
82
+ }
83
+ }
84
+ function readJsonFile(filePath) {
85
+ return JSON.parse(node_fs.readFileSync(filePath, 'utf-8'));
86
+ }
87
+ function writeJsonFile(filePath, value) {
88
+ node_fs.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`, 'utf-8');
89
+ }
90
+ export { assertSafeRelativePath, collectTemplateFiles, copyRootTemplate, createPackageRoot, ensureInsideRoot, fileTemplatesDir, hashFile, hashTemplateTree, readFileTemplate, readJsonFile, renderFileTemplate, renderTemplate, workspaceTemplateDir, writeFile, writeFileReplacing, writeJson, writeJsonFile };
@@ -0,0 +1,3 @@
1
+ export { addUltramodernVertical } from "./add-vertical.js";
2
+ export { ultramodernWorkspaceVersions } from "./versions.js";
3
+ export { generateUltramodernWorkspace } from "./write-workspace.js";
@@ -0,0 +1,122 @@
1
+ const commonLocaleMessages = {
2
+ cs: {
3
+ language: {
4
+ cs: 'Čeština',
5
+ en: 'Angličtina',
6
+ switcher: 'Jazyk'
7
+ },
8
+ routes: {
9
+ home: 'Domů'
10
+ },
11
+ seo: {
12
+ description: 'Route-owned UltraModern plocha s lokalizovaným SSR a frameworkem řízenými public metadata.'
13
+ }
14
+ },
15
+ en: {
16
+ language: {
17
+ cs: 'Czech',
18
+ en: 'English',
19
+ switcher: 'Language'
20
+ },
21
+ routes: {
22
+ home: 'Home'
23
+ },
24
+ seo: {
25
+ description: 'Route-owned UltraModern surface with localized SSR and framework-owned public metadata.'
26
+ }
27
+ }
28
+ };
29
+ const generatedLocaleResources = {
30
+ cs: {
31
+ shell: {
32
+ boundaries: {
33
+ toggle: 'zobrazit hranice týmů'
34
+ },
35
+ hero: {
36
+ cardOne: 'Přidejte první business vertical příkazem create <domain> --vertical, až ho opravdu potřebujete.',
37
+ cardOneKicker: 'Verticaly',
38
+ cardTwo: 'Plný markup, styly a lokalizovaný obsah se vykreslí před hydratací.',
39
+ cardTwoKicker: 'Vykreslení',
40
+ empty: 'Zatím nejsou připojené žádné MicroVerticaly.',
41
+ eyebrow: 'Shell SuperApp starter',
42
+ lede: 'Začněte s produkčně připraveným shellem. MicroVerticaly přidávejte až podle skutečných business domén.',
43
+ primary: 'Shell je připraven',
44
+ secondary: 'Přidejte vertical, až bude potřeba'
45
+ },
46
+ language: commonLocaleMessages.cs.language,
47
+ remoteUnavailable: 'Remote vertical je nedostupný',
48
+ remotes: {},
49
+ routes: {
50
+ home: commonLocaleMessages.cs.routes.home
51
+ },
52
+ seo: {
53
+ description: 'UltraModern shell SuperApp s lokalizovaným SSR, Module Federation a frameworkem řízenými public metadata.'
54
+ },
55
+ title: 'UltraModern Workspace'
56
+ }
57
+ },
58
+ en: {
59
+ shell: {
60
+ boundaries: {
61
+ toggle: 'show team boundaries'
62
+ },
63
+ hero: {
64
+ cardOne: 'Add the first business vertical with create <domain> --vertical when the product needs one.',
65
+ cardOneKicker: 'Verticals',
66
+ cardTwo: 'Full page markup, styles, and localized content render before hydration.',
67
+ cardTwoKicker: 'Rendering',
68
+ empty: 'No MicroVerticals are connected yet.',
69
+ eyebrow: 'Shell SuperApp starter',
70
+ lede: 'Start with a production-ready shell. Add MicroVerticals later for real business domains.',
71
+ primary: 'Shell ready',
72
+ secondary: 'Add a vertical when needed'
73
+ },
74
+ language: commonLocaleMessages.en.language,
75
+ remoteUnavailable: 'Remote vertical unavailable',
76
+ remotes: {},
77
+ routes: {
78
+ home: commonLocaleMessages.en.routes.home
79
+ },
80
+ seo: {
81
+ description: 'UltraModern shell SuperApp with localized SSR, Module Federation, and framework-owned public metadata.'
82
+ },
83
+ title: 'UltraModern Workspace'
84
+ }
85
+ }
86
+ };
87
+ const verticalLocaleCopy = {
88
+ cs: {
89
+ federatedSurface: 'Federovaná plocha vlastněná tímto verticalem.',
90
+ remoteUnavailable: 'Remote vertical je nedostupný',
91
+ routeSurface: 'Routovaná plocha vlastněná tímto verticalem.',
92
+ widgetBody: 'Vlastní routovanou plochu verticalu.'
93
+ },
94
+ en: {
95
+ federatedSurface: 'Federated surface owned by this vertical.',
96
+ remoteUnavailable: 'Remote vertical unavailable',
97
+ routeSurface: 'Route surface owned by this vertical.',
98
+ widgetBody: 'Owns a vertical route surface.'
99
+ }
100
+ };
101
+ const createFallbackLocaleMessages = (app, language)=>({
102
+ ...commonLocaleMessages[language],
103
+ federatedSurface: verticalLocaleCopy[language].federatedSurface,
104
+ remoteUnavailable: verticalLocaleCopy[language].remoteUnavailable,
105
+ role: app.domain ?? app.kind,
106
+ routeSurface: verticalLocaleCopy[language].routeSurface,
107
+ title: app.displayName,
108
+ widgetBody: verticalLocaleCopy[language].widgetBody
109
+ });
110
+ function createAppLocaleMessages(app, language) {
111
+ const domain = app.domain ?? app.id;
112
+ const messageKey = 'shell' === app.kind ? 'shell' : domain;
113
+ const messages = 'shell' === app.kind ? generatedLocaleResources[language].shell : createFallbackLocaleMessages(app, language);
114
+ return {
115
+ [messageKey]: messages
116
+ };
117
+ }
118
+ function createAppPublicLocaleMessages(app, language, remotes = []) {
119
+ if ('shell' !== app.kind) return createAppLocaleMessages(app, language);
120
+ return Object.assign({}, createAppLocaleMessages(app, language), ...remotes.map((remote)=>createAppLocaleMessages(remote, language)));
121
+ }
122
+ export { commonLocaleMessages, createAppLocaleMessages, createAppPublicLocaleMessages, createFallbackLocaleMessages, generatedLocaleResources };
@@ -0,0 +1,415 @@
1
+ import node_crypto from "node:crypto";
2
+ import { appHasEffectApi, createCloudflarePublicUrlEnv, createCloudflareWorkerName, createRemoteManifestEnv, effectApiPrefix, remoteDependencyAlias, resolveRemoteRefs, shellApp } from "./descriptors.js";
3
+ import { createRspackChunkLoadingGlobal, createRspackUniqueName, packageName } from "./naming.js";
4
+ import { createCloudflareSecurityContract, formatTsJsonValue } from "./policy.js";
5
+ import { sortJsonValue } from "./types.js";
6
+ import { CLOUDFLARE_COMPATIBILITY_DATE } from "./versions.js";
7
+ function createAppModernConfig(scope, app) {
8
+ const bffImport = appHasEffectApi(app) ? "import { bffPlugin } from '@modern-js/plugin-bff';\n" : '';
9
+ const bffConfig = appHasEffectApi(app) ? ` bff: {
10
+ effect: {
11
+ entry: './api/effect/index',
12
+ openapi: {
13
+ path: '/openapi.json',
14
+ },
15
+ },
16
+ prefix: '${effectApiPrefix(app)}',
17
+ runtimeFramework: 'effect',
18
+ },
19
+ ` : '';
20
+ const bffPluginEntry = appHasEffectApi(app) ? ' bffPlugin(),\n' : '';
21
+ return `// @effect-diagnostics processEnv:off
22
+ import {
23
+ appTools,
24
+ defineConfig,
25
+ presetUltramodern,
26
+ } from '@modern-js/app-tools';
27
+ ${bffImport}import { i18nPlugin } from '@modern-js/plugin-i18n';
28
+ import { tanstackRouterPlugin } from '@modern-js/plugin-tanstack';
29
+ import { moduleFederationPlugin } from '@module-federation/modern-js-v3';
30
+ import { withZephyr as withZephyrRspack } from 'zephyr-rspack-plugin';
31
+ import { ultramodernLocalisedUrls } from './src/routes/ultramodern-route-metadata';
32
+
33
+ type ZephyrRspackConfig = Parameters<ReturnType<typeof withZephyrRspack>>[0];
34
+
35
+ const zephyrEnabled = process.env['ULTRAMODERN_ZEPHYR'] !== 'false';
36
+ const cloudflareDeployEnabled =
37
+ process.env['MODERNJS_DEPLOY'] === 'cloudflare';
38
+
39
+ const zephyrRspackPlugin = () => ({
40
+ name: 'ultramodern-zephyr-rspack-plugin',
41
+ pre: ['@modern-js/plugin-module-federation-config'],
42
+ setup(api: {
43
+ modifyRspackConfig: (
44
+ handler: (
45
+ config: ZephyrRspackConfig,
46
+ ) => ZephyrRspackConfig | Promise<ZephyrRspackConfig>,
47
+ ) => void;
48
+ }) {
49
+ if (!zephyrEnabled) {
50
+ return;
51
+ }
52
+ api.modifyRspackConfig(config => withZephyrRspack()(config));
53
+ },
54
+ });
55
+
56
+ const appId = '${app.id}';
57
+ const cloudflareWorkerName = '${createCloudflareWorkerName(scope, app)}';
58
+ const port = Number(process.env['${app.portEnv}'] ?? ${app.port});
59
+ const envValue = (name: string) => {
60
+ const value = process.env[name]?.trim();
61
+ return value !== undefined && value.length > 0 ? value : undefined;
62
+ };
63
+ const configuredSiteUrl = envValue('MODERN_PUBLIC_SITE_URL');
64
+ const configuredCloudflareUrl = envValue('${createCloudflarePublicUrlEnv(app)}');
65
+ const configuredUltramodernAssetPrefix = envValue('ULTRAMODERN_ASSET_PREFIX');
66
+ const configuredModernAssetPrefix = envValue('MODERN_ASSET_PREFIX');
67
+ const cloudflareWorkersDevSubdomain = envValue(
68
+ 'ULTRAMODERN_CLOUDFLARE_WORKERS_DEV_SUBDOMAIN',
69
+ );
70
+ const inferredCloudflareUrl =
71
+ cloudflareDeployEnabled && cloudflareWorkersDevSubdomain !== undefined
72
+ ? \`https://\${cloudflareWorkerName}.\${cloudflareWorkersDevSubdomain}.workers.dev\`
73
+ : undefined;
74
+ // Site origin (SEO: canonical/hreflang URLs) prefers the site-wide public URL;
75
+ // the per-app deployment URL only fills in when no site origin is configured.
76
+ const siteUrl =
77
+ configuredSiteUrl ||
78
+ configuredCloudflareUrl ||
79
+ inferredCloudflareUrl ||
80
+ \`http://localhost:\${port}\`;
81
+ // Asset loading is intentionally independent from the canonical site URL and
82
+ // deployment public URL. Without an explicit asset prefix, assets stay
83
+ // origin-relative so self-hosted apps, tunnels, and reverse proxies never leak
84
+ // localhost URLs into production HTML.
85
+ const assetPrefix =
86
+ configuredModernAssetPrefix || configuredUltramodernAssetPrefix || '/';
87
+
88
+ if (
89
+ cloudflareDeployEnabled &&
90
+ process.env['ULTRAMODERN_CLOUDFLARE_REQUIRE_PUBLIC_URLS'] === 'true' &&
91
+ configuredCloudflareUrl === undefined &&
92
+ configuredSiteUrl === undefined &&
93
+ inferredCloudflareUrl === undefined
94
+ ) {
95
+ throw new Error(
96
+ \`Cloudflare deploy for \${appId} needs ${createCloudflarePublicUrlEnv(app)}, MODERN_PUBLIC_SITE_URL, or ULTRAMODERN_CLOUDFLARE_WORKERS_DEV_SUBDOMAIN.\`,
97
+ );
98
+ }
99
+
100
+ export default defineConfig(
101
+ presetUltramodern(
102
+ {
103
+ ${bffConfig} ...(cloudflareDeployEnabled
104
+ ? {
105
+ deploy: {
106
+ worker: {
107
+ compatibilityDate: '${CLOUDFLARE_COMPATIBILITY_DATE}',
108
+ name: cloudflareWorkerName,
109
+ security: ${formatTsJsonValue(sortJsonValue(createCloudflareSecurityContract()), 16)},
110
+ ssr: true,
111
+ },
112
+ },
113
+ }
114
+ : {}),
115
+ dev: {
116
+ // Keep dev assets origin-relative too; the default absolute
117
+ // http://localhost:<port> prefix breaks pages served through tunnels.
118
+ assetPrefix: '/',
119
+ },
120
+ html: {
121
+ outputStructure: 'flat',
122
+ },
123
+ output: {
124
+ assetPrefix,
125
+ disableTsChecker: true,
126
+ distPath: {
127
+ html: './',
128
+ },
129
+ polyfill: 'off',
130
+ splitRouteChunks: true,
131
+ },
132
+ performance: {
133
+ rsdoctor: {
134
+ disableClientServer: true,
135
+ enabled: process.env['ULTRAMODERN_RSDOCTOR'] === 'true',
136
+ },
137
+ },
138
+ plugins: [
139
+ appTools(),
140
+ tanstackRouterPlugin(),
141
+ i18nPlugin({
142
+ backend: {
143
+ enabled: true,
144
+ loadPath: '/locales/{{lng}}/{{ns}}.json',
145
+ },
146
+ localeDetection: {
147
+ fallbackLanguage: 'en',
148
+ ignoreRedirectRoutes: [
149
+ '/@mf-types',
150
+ '/assets',
151
+ '/bundles',
152
+ '${effectApiPrefix(app)}',
153
+ '/locales',
154
+ '/mf-manifest.json',
155
+ '/mf-stats.json',
156
+ '/remoteEntry.js',
157
+ '/robots.txt',
158
+ '/site.webmanifest',
159
+ '/sitemap.xml',
160
+ '/static',
161
+ '/zephyr-manifest.json',
162
+ ],
163
+ languages: ['en', 'cs'],
164
+ localePathRedirect: true,
165
+ localisedUrls: ultramodernLocalisedUrls as Record<string, Record<string, string>>,
166
+ },
167
+ reactI18next: false,
168
+ }),
169
+ ${bffPluginEntry} moduleFederationPlugin(),
170
+ zephyrRspackPlugin(),
171
+ ],
172
+ server: {
173
+ port,
174
+ publicDir: ['./locales', './assets'],
175
+ ssr: {
176
+ mode: 'string',
177
+ moduleFederationAppSSR: true,
178
+ },
179
+ },
180
+ source: {
181
+ globalVars: {
182
+ ULTRAMODERN_SITE_URL: siteUrl,
183
+ },
184
+ mainEntryName: 'index',
185
+ },
186
+ tools: {
187
+ autoprefixer: {
188
+ overrideBrowserslist: ['defaults'],
189
+ },
190
+ bundlerChain: chain => {
191
+ chain.output
192
+ .uniqueName('${createRspackUniqueName(app)}')
193
+ .chunkLoadingGlobal('${createRspackChunkLoadingGlobal(app)}');
194
+ chain.ignoreWarnings([
195
+ {
196
+ message: /the request of a dependency is an expression/u,
197
+ module: /modern-js-plugin-i18n/u,
198
+ },
199
+ ]);
200
+ },
201
+ },
202
+ },
203
+ {
204
+ appId,
205
+ enableBffRequestId: true,
206
+ enableModuleFederationSSR: true,
207
+ enableTelemetryExporters: true,
208
+ telemetryFailLoudStartup: false,
209
+ },
210
+ ),
211
+ );
212
+ `;
213
+ }
214
+ function createSharedModuleFederationConfig() {
215
+ return ` shared: {
216
+ '@modern-js/plugin-i18n/runtime': {
217
+ requiredVersion: pluginI18nVersion,
218
+ singleton: true,
219
+ treeShaking: false,
220
+ },
221
+ '@modern-js/plugin-tanstack/runtime': {
222
+ requiredVersion: pluginTanstackVersion,
223
+ singleton: true,
224
+ treeShaking: false,
225
+ },
226
+ '@modern-js/runtime': {
227
+ requiredVersion: runtimeVersion,
228
+ singleton: true,
229
+ treeShaking: false,
230
+ },
231
+ '@tanstack/react-router': {
232
+ requiredVersion: dependencies['@tanstack/react-router'],
233
+ singleton: true,
234
+ treeShaking: false,
235
+ },
236
+ react: {
237
+ requiredVersion: reactVersion,
238
+ singleton: true,
239
+ treeShaking: false,
240
+ },
241
+ 'react-dom': {
242
+ requiredVersion: reactDomVersion,
243
+ singleton: true,
244
+ treeShaking: false,
245
+ },
246
+ 'react-dom/client': {
247
+ requiredVersion: reactDomVersion,
248
+ singleton: true,
249
+ treeShaking: false,
250
+ },
251
+ }`;
252
+ }
253
+ function formatTsObjectLiteral(value) {
254
+ const entries = Object.entries(value).sort(([left], [right])=>left.localeCompare(right));
255
+ if (0 === entries.length) return '{}';
256
+ return `{
257
+ ${entries.map(([key, entryValue])=>` '${key}': '${entryValue}',`).join('\n')}
258
+ }`;
259
+ }
260
+ function createModuleFederationRemoteUrlHelpers(app, remotes = []) {
261
+ if (0 === resolveRemoteRefs(app, remotes).length) return '';
262
+ return `const cloudflareDeployEnabled =
263
+ process.env['MODERNJS_DEPLOY'] === 'cloudflare';
264
+ const cloudflareWorkersDevSubdomain =
265
+ process.env['ULTRAMODERN_CLOUDFLARE_WORKERS_DEV_SUBDOMAIN']?.trim();
266
+ const requireCloudflarePublicUrls =
267
+ process.env['ULTRAMODERN_CLOUDFLARE_REQUIRE_PUBLIC_URLS'] === 'true';
268
+
269
+ const createRemoteManifestUrl = (options: {
270
+ manifestEnv: string;
271
+ mfName: string;
272
+ port: number;
273
+ publicUrlEnv: string;
274
+ workerName: string;
275
+ }) => {
276
+ const configuredManifest = process.env[options.manifestEnv]?.trim();
277
+ if (configuredManifest !== undefined && configuredManifest.length > 0) {
278
+ return configuredManifest;
279
+ }
280
+
281
+ const configuredPublicUrl = process.env[options.publicUrlEnv]?.trim();
282
+ if (configuredPublicUrl !== undefined && configuredPublicUrl.length > 0) {
283
+ return \`\${options.mfName}@\${configuredPublicUrl.replace(/\\/+$/u, '')}/mf-manifest.json\`;
284
+ }
285
+
286
+ if (cloudflareDeployEnabled && cloudflareWorkersDevSubdomain !== undefined) {
287
+ return \`\${options.mfName}@https://\${options.workerName}.\${cloudflareWorkersDevSubdomain}.workers.dev/mf-manifest.json\`;
288
+ }
289
+
290
+ if (cloudflareDeployEnabled && requireCloudflarePublicUrls) {
291
+ throw new Error(
292
+ \`Cloudflare deploy needs \${options.publicUrlEnv}, \${options.manifestEnv}, or ULTRAMODERN_CLOUDFLARE_WORKERS_DEV_SUBDOMAIN for remote \${options.mfName}.\`,
293
+ );
294
+ }
295
+
296
+ return \`\${options.mfName}@http://localhost:\${options.port}/mf-manifest.json\`;
297
+ };
298
+
299
+ `;
300
+ }
301
+ function createModuleFederationRemotesConfig(scope, app, remotes = []) {
302
+ const remoteEntries = resolveRemoteRefs(app, remotes).toSorted((left, right)=>remoteDependencyAlias(left).localeCompare(remoteDependencyAlias(right))).map((remote)=>{
303
+ const key = remoteDependencyAlias(remote);
304
+ return ` ${key}: createRemoteManifestUrl({
305
+ manifestEnv: '${createRemoteManifestEnv(remote)}',
306
+ mfName: '${remote.mfName}',
307
+ port: ${remote.port},
308
+ publicUrlEnv: '${createCloudflarePublicUrlEnv(remote)}',
309
+ workerName: '${createCloudflareWorkerName(scope, remote)}',
310
+ }),`;
311
+ }).join('\n');
312
+ if (!remoteEntries) return '';
313
+ return ` remotes: {
314
+ ${remoteEntries}
315
+ },
316
+ `;
317
+ }
318
+ function createShellModuleFederationConfig(scope, remotes = []) {
319
+ const shellHost = {
320
+ ...shellApp,
321
+ verticalRefs: remotes.map((remote)=>remote.id)
322
+ };
323
+ return `// @effect-diagnostics nodeBuiltinImport:off processEnv:off
324
+ import { createRequire } from 'node:module';
325
+ import { createModuleFederationConfig } from '@module-federation/modern-js-v3';
326
+ import { dependencies } from './package.json';
327
+
328
+ const require = createRequire(import.meta.url);
329
+ const pluginI18nVersion = (require('@modern-js/plugin-i18n/package.json') as { version: string }).version;
330
+ const pluginTanstackVersion = (require('@modern-js/plugin-tanstack/package.json') as { version: string }).version;
331
+ const runtimeVersion = (require('@modern-js/runtime/package.json') as { version: string }).version;
332
+ const reactVersion = (require('react/package.json') as { version: string }).version;
333
+ const reactDomVersion = (require('react-dom/package.json') as { version: string }).version;
334
+
335
+ ${createModuleFederationRemoteUrlHelpers(shellHost, remotes)}
336
+ export default createModuleFederationConfig({
337
+ bridge: {
338
+ enableBridgeRouter: false,
339
+ },
340
+ dev: {
341
+ disableDynamicRemoteTypeHints: true,
342
+ },
343
+ dts: {
344
+ displayErrorInTerminal: true,
345
+ generateTypes: {
346
+ compilerInstance: 'tsgo',
347
+ },
348
+ },
349
+ filename: 'remoteEntry.js',
350
+ name: '${shellApp.mfName}',
351
+ ${createModuleFederationRemotesConfig(scope, shellHost, remotes)}${createSharedModuleFederationConfig()},
352
+ treeShakingSharedExcludePlugins: ['RspackModuleFederationPlugin'],
353
+ });
354
+ `;
355
+ }
356
+ function createBuildMarker(scope, app) {
357
+ return node_crypto.createHash('sha256').update(`${scope}:${app.packageSuffix}:${app.id}:0.1.0`).digest('hex').slice(0, 16);
358
+ }
359
+ function createUltramodernBuildModule(scope, app) {
360
+ return `export const ultramodernVerticalIdentity = {
361
+ appId: '${app.id}',
362
+ build: '${createBuildMarker(scope, app)}',
363
+ deployProfile: 'cloudflare-ssr-mf-effect-v1',
364
+ packageName: '${packageName(scope, app.packageSuffix)}',
365
+ version: '0.1.0',
366
+ } as const;
367
+
368
+ export const ultramodernUiMarker = {
369
+ ...ultramodernVerticalIdentity,
370
+ surface: 'ui',
371
+ } as const;
372
+
373
+ export const ultramodernApiMarker = {
374
+ ...ultramodernVerticalIdentity,
375
+ surface: 'effect-bff',
376
+ } as const;
377
+ `;
378
+ }
379
+ function createRemoteModuleFederationConfig(scope, app, remotes = []) {
380
+ const exposes = formatTsObjectLiteral(app.exposes ?? {});
381
+ return `// @effect-diagnostics nodeBuiltinImport:off
382
+ import { createRequire } from 'node:module';
383
+ import { createModuleFederationConfig } from '@module-federation/modern-js-v3';
384
+ import { dependencies } from './package.json';
385
+
386
+ const require = createRequire(import.meta.url);
387
+ const pluginI18nVersion = (require('@modern-js/plugin-i18n/package.json') as { version: string }).version;
388
+ const pluginTanstackVersion = (require('@modern-js/plugin-tanstack/package.json') as { version: string }).version;
389
+ const runtimeVersion = (require('@modern-js/runtime/package.json') as { version: string }).version;
390
+ const reactVersion = (require('react/package.json') as { version: string }).version;
391
+ const reactDomVersion = (require('react-dom/package.json') as { version: string }).version;
392
+
393
+ ${createModuleFederationRemoteUrlHelpers(app, remotes)}
394
+ export default createModuleFederationConfig({
395
+ bridge: {
396
+ enableBridgeRouter: false,
397
+ },
398
+ dev: {
399
+ disableDynamicRemoteTypeHints: true,
400
+ },
401
+ dts: {
402
+ displayErrorInTerminal: true,
403
+ generateTypes: {
404
+ compilerInstance: 'tsgo',
405
+ },
406
+ },
407
+ exposes: ${exposes},
408
+ filename: 'remoteEntry.js',
409
+ name: '${app.mfName}',
410
+ ${createModuleFederationRemotesConfig(scope, app, remotes)}${createSharedModuleFederationConfig()},
411
+ treeShakingSharedExcludePlugins: ['RspackModuleFederationPlugin'],
412
+ });
413
+ `;
414
+ }
415
+ export { createAppModernConfig, createBuildMarker, createModuleFederationRemoteUrlHelpers, createModuleFederationRemotesConfig, createRemoteModuleFederationConfig, createSharedModuleFederationConfig, createShellModuleFederationConfig, createUltramodernBuildModule, formatTsObjectLiteral };