@bleedingdev/modern-js-create 3.2.0-ultramodern.99 → 3.4.0-ultramodern.1

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 (179) hide show
  1. package/README.md +281 -106
  2. package/bin/run.js +1 -0
  3. package/dist/cjs/create-package-root.cjs +63 -0
  4. package/dist/cjs/index.cjs +658 -0
  5. package/dist/cjs/locale/en.cjs +101 -0
  6. package/dist/cjs/locale/index.cjs +50 -0
  7. package/dist/cjs/locale/zh.cjs +101 -0
  8. package/dist/cjs/ultramodern-package-source.cjs +135 -0
  9. package/dist/cjs/ultramodern-workspace/add-vertical.cjs +603 -0
  10. package/dist/cjs/ultramodern-workspace/app-files.cjs +223 -0
  11. package/dist/cjs/ultramodern-workspace/codesmith.cjs +134 -0
  12. package/dist/cjs/ultramodern-workspace/contracts.cjs +837 -0
  13. package/dist/cjs/ultramodern-workspace/demo-components.cjs +422 -0
  14. package/dist/cjs/ultramodern-workspace/descriptors.cjs +222 -0
  15. package/dist/cjs/ultramodern-workspace/effect-api.cjs +952 -0
  16. package/dist/cjs/ultramodern-workspace/fs-io.cjs +191 -0
  17. package/dist/cjs/ultramodern-workspace/generation-result.cjs +158 -0
  18. package/dist/cjs/ultramodern-workspace/index.cjs +51 -0
  19. package/dist/cjs/ultramodern-workspace/locales.cjs +173 -0
  20. package/dist/cjs/ultramodern-workspace/module-federation.cjs +487 -0
  21. package/dist/cjs/ultramodern-workspace/naming.cjs +161 -0
  22. package/dist/cjs/ultramodern-workspace/overlays.cjs +120 -0
  23. package/dist/cjs/ultramodern-workspace/package-json.cjs +406 -0
  24. package/dist/cjs/ultramodern-workspace/package-source.cjs +59 -0
  25. package/dist/cjs/ultramodern-workspace/policy.cjs +248 -0
  26. package/dist/cjs/ultramodern-workspace/public-api.cjs +47 -0
  27. package/dist/cjs/ultramodern-workspace/public-surface.cjs +268 -0
  28. package/dist/cjs/ultramodern-workspace/routes.cjs +375 -0
  29. package/dist/cjs/ultramodern-workspace/types.cjs +61 -0
  30. package/dist/cjs/ultramodern-workspace/versions.cjs +161 -0
  31. package/dist/cjs/ultramodern-workspace/workspace-scripts.cjs +154 -0
  32. package/dist/cjs/ultramodern-workspace/write-workspace.cjs +197 -0
  33. package/dist/esm/create-package-root.js +14 -0
  34. package/dist/esm/index.js +621 -0
  35. package/dist/esm/locale/en.js +63 -0
  36. package/dist/esm/locale/index.js +9 -0
  37. package/dist/esm/locale/zh.js +63 -0
  38. package/dist/esm/ultramodern-package-source.js +63 -0
  39. package/dist/esm/ultramodern-workspace/add-vertical.js +511 -0
  40. package/dist/esm/ultramodern-workspace/app-files.js +149 -0
  41. package/dist/esm/ultramodern-workspace/codesmith.js +86 -0
  42. package/dist/esm/ultramodern-workspace/contracts.js +742 -0
  43. package/dist/esm/ultramodern-workspace/demo-components.js +363 -0
  44. package/dist/esm/ultramodern-workspace/descriptors.js +133 -0
  45. package/dist/esm/ultramodern-workspace/effect-api.js +854 -0
  46. package/dist/esm/ultramodern-workspace/fs-io.js +90 -0
  47. package/dist/esm/ultramodern-workspace/generation-result.js +102 -0
  48. package/dist/esm/ultramodern-workspace/index.js +3 -0
  49. package/dist/esm/ultramodern-workspace/locales.js +122 -0
  50. package/dist/esm/ultramodern-workspace/module-federation.js +415 -0
  51. package/dist/esm/ultramodern-workspace/naming.js +71 -0
  52. package/dist/esm/ultramodern-workspace/overlays.js +79 -0
  53. package/dist/esm/ultramodern-workspace/package-json.js +338 -0
  54. package/dist/esm/ultramodern-workspace/package-source.js +21 -0
  55. package/dist/esm/ultramodern-workspace/policy.js +183 -0
  56. package/dist/esm/ultramodern-workspace/public-api.js +2 -0
  57. package/dist/esm/ultramodern-workspace/public-surface.js +183 -0
  58. package/dist/esm/ultramodern-workspace/routes.js +280 -0
  59. package/dist/esm/ultramodern-workspace/types.js +16 -0
  60. package/dist/esm/ultramodern-workspace/versions.js +36 -0
  61. package/dist/esm/ultramodern-workspace/workspace-scripts.js +92 -0
  62. package/dist/esm/ultramodern-workspace/write-workspace.js +143 -0
  63. package/dist/esm-node/create-package-root.js +15 -0
  64. package/dist/esm-node/index.js +622 -0
  65. package/dist/esm-node/locale/en.js +64 -0
  66. package/dist/esm-node/locale/index.js +10 -0
  67. package/dist/esm-node/locale/zh.js +64 -0
  68. package/dist/esm-node/ultramodern-package-source.js +64 -0
  69. package/dist/esm-node/ultramodern-workspace/add-vertical.js +512 -0
  70. package/dist/esm-node/ultramodern-workspace/app-files.js +150 -0
  71. package/dist/esm-node/ultramodern-workspace/codesmith.js +87 -0
  72. package/dist/esm-node/ultramodern-workspace/contracts.js +743 -0
  73. package/dist/esm-node/ultramodern-workspace/demo-components.js +364 -0
  74. package/dist/esm-node/ultramodern-workspace/descriptors.js +134 -0
  75. package/dist/esm-node/ultramodern-workspace/effect-api.js +855 -0
  76. package/dist/esm-node/ultramodern-workspace/fs-io.js +91 -0
  77. package/dist/esm-node/ultramodern-workspace/generation-result.js +103 -0
  78. package/dist/esm-node/ultramodern-workspace/index.js +4 -0
  79. package/dist/esm-node/ultramodern-workspace/locales.js +123 -0
  80. package/dist/esm-node/ultramodern-workspace/module-federation.js +416 -0
  81. package/dist/esm-node/ultramodern-workspace/naming.js +72 -0
  82. package/dist/esm-node/ultramodern-workspace/overlays.js +80 -0
  83. package/dist/esm-node/ultramodern-workspace/package-json.js +339 -0
  84. package/dist/esm-node/ultramodern-workspace/package-source.js +22 -0
  85. package/dist/esm-node/ultramodern-workspace/policy.js +184 -0
  86. package/dist/esm-node/ultramodern-workspace/public-api.js +3 -0
  87. package/dist/esm-node/ultramodern-workspace/public-surface.js +184 -0
  88. package/dist/esm-node/ultramodern-workspace/routes.js +281 -0
  89. package/dist/esm-node/ultramodern-workspace/types.js +17 -0
  90. package/dist/esm-node/ultramodern-workspace/versions.js +37 -0
  91. package/dist/esm-node/ultramodern-workspace/workspace-scripts.js +93 -0
  92. package/dist/esm-node/ultramodern-workspace/write-workspace.js +144 -0
  93. package/dist/types/create-package-root.d.ts +1 -0
  94. package/dist/types/locale/en.d.ts +12 -8
  95. package/dist/types/locale/index.d.ts +125 -2
  96. package/dist/types/locale/zh.d.ts +12 -8
  97. package/dist/types/ultramodern-package-source.d.ts +28 -0
  98. package/dist/types/ultramodern-workspace/add-vertical.d.ts +36 -0
  99. package/dist/types/ultramodern-workspace/app-files.d.ts +14 -0
  100. package/dist/types/ultramodern-workspace/codesmith.d.ts +53 -0
  101. package/dist/types/ultramodern-workspace/contracts.d.ts +21 -0
  102. package/dist/types/ultramodern-workspace/demo-components.d.ts +9 -0
  103. package/dist/types/ultramodern-workspace/descriptors.d.ts +39 -0
  104. package/dist/types/ultramodern-workspace/effect-api.d.ts +73 -0
  105. package/dist/types/ultramodern-workspace/fs-io.d.ts +18 -0
  106. package/dist/types/ultramodern-workspace/generation-result.d.ts +14 -0
  107. package/dist/types/ultramodern-workspace/index.d.ts +4 -0
  108. package/dist/types/ultramodern-workspace/locales.d.ts +183 -0
  109. package/dist/types/ultramodern-workspace/module-federation.d.ts +16 -0
  110. package/dist/types/ultramodern-workspace/naming.d.ts +16 -0
  111. package/dist/types/ultramodern-workspace/overlays.d.ts +6 -0
  112. package/dist/types/ultramodern-workspace/package-json.d.ts +12 -0
  113. package/dist/types/ultramodern-workspace/package-source.d.ts +2 -0
  114. package/dist/types/ultramodern-workspace/policy.d.ts +60 -0
  115. package/dist/types/ultramodern-workspace/public-api.d.ts +3 -0
  116. package/dist/types/ultramodern-workspace/public-surface.d.ts +37 -0
  117. package/dist/types/ultramodern-workspace/routes.d.ts +25 -0
  118. package/dist/types/ultramodern-workspace/types.d.ts +197 -0
  119. package/dist/types/ultramodern-workspace/versions.d.ts +40 -0
  120. package/dist/types/ultramodern-workspace/workspace-scripts.d.ts +10 -0
  121. package/dist/types/ultramodern-workspace/write-workspace.d.ts +4 -0
  122. package/package.json +58 -15
  123. package/template-workspace/.github/workflows/ultramodern-workspace-gates.yml.handlebars +23 -10
  124. package/template-workspace/.mise.toml.handlebars +1 -0
  125. package/template-workspace/{AGENTS.md → AGENTS.md.handlebars} +20 -9
  126. package/template-workspace/README.md.handlebars +85 -14
  127. package/template-workspace/lefthook.yml +18 -4
  128. package/template-workspace/pnpm-workspace.yaml.handlebars +45 -0
  129. package/template-workspace/scripts/bootstrap-agent-skills.mjs +69 -75
  130. package/template-workspace/scripts/setup-agent-reference-repos.mjs +8 -6
  131. package/templates/app/shell-frame.tsx +49 -0
  132. package/templates/app/ultramodern-route-head.tsx.handlebars +142 -0
  133. package/templates/packages/shared-contracts-index.ts +466 -0
  134. package/templates/workspace-scripts/assert-mf-types.mjs.handlebars +69 -0
  135. package/templates/workspace-scripts/check-ultramodern-i18n-boundaries.mjs +9 -0
  136. package/templates/workspace-scripts/generate-public-surface-assets.mjs +529 -0
  137. package/templates/workspace-scripts/proof-cloudflare-version.mjs +125 -0
  138. package/templates/workspace-scripts/ultramodern-cloudflare-proof.mjs +851 -0
  139. package/templates/workspace-scripts/ultramodern-performance-readiness.config.mjs +7 -0
  140. package/templates/workspace-scripts/ultramodern-performance-readiness.mjs +223 -0
  141. package/templates/workspace-scripts/validate-ultramodern-workspace.mjs.handlebars +986 -0
  142. package/dist/index.js +0 -6625
  143. package/dist/types/ultramodern-workspace.d.ts +0 -31
  144. package/template/.agents/skills-lock.json +0 -34
  145. package/template/.browserslistrc +0 -4
  146. package/template/.codex/hooks.json +0 -16
  147. package/template/.github/renovate.json +0 -53
  148. package/template/.github/workflows/ultramodern-gates.yml.handlebars +0 -54
  149. package/template/.gitignore.handlebars +0 -30
  150. package/template/.mise.toml.handlebars +0 -2
  151. package/template/.nvmrc +0 -2
  152. package/template/AGENTS.md +0 -23
  153. package/template/README.md +0 -105
  154. package/template/api/effect/index.ts.handlebars +0 -34
  155. package/template/api/lambda/hello.ts.handlebars +0 -6
  156. package/template/config/public/locales/cs/translation.json +0 -39
  157. package/template/config/public/locales/en/translation.json +0 -39
  158. package/template/lefthook.yml +0 -10
  159. package/template/modern.config.ts.handlebars +0 -71
  160. package/template/oxfmt.config.ts +0 -15
  161. package/template/oxlint.config.ts +0 -19
  162. package/template/package.json.handlebars +0 -66
  163. package/template/pnpm-workspace.yaml +0 -29
  164. package/template/postcss.config.mjs.handlebars +0 -6
  165. package/template/rstest.config.mts +0 -5
  166. package/template/scripts/bootstrap-agent-skills.mjs +0 -228
  167. package/template/scripts/check-i18n-strings.mjs +0 -94
  168. package/template/scripts/validate-ultramodern.mjs.handlebars +0 -538
  169. package/template/shared/effect/api.ts.handlebars +0 -17
  170. package/template/src/modern-app-env.d.ts +0 -3
  171. package/template/src/modern.runtime.ts.handlebars +0 -23
  172. package/template/src/routes/[lang]/page.tsx.handlebars +0 -211
  173. package/template/src/routes/index.css.handlebars +0 -129
  174. package/template/src/routes/layout.tsx.handlebars +0 -10
  175. package/template/tailwind.config.ts.handlebars +0 -10
  176. package/template/tests/tsconfig.json +0 -7
  177. package/template/tests/ultramodern.contract.test.ts.handlebars +0 -78
  178. package/template/tsconfig.json +0 -120
  179. package/template-workspace/pnpm-workspace.yaml +0 -35
@@ -0,0 +1,416 @@
1
+ import "node:module";
2
+ import node_crypto from "node:crypto";
3
+ import { appHasEffectApi, createCloudflarePublicUrlEnv, createCloudflareWorkerName, createRemoteManifestEnv, effectApiPrefix, remoteDependencyAlias, resolveRemoteRefs, shellApp } from "./descriptors.js";
4
+ import { createRspackChunkLoadingGlobal, createRspackUniqueName, packageName } from "./naming.js";
5
+ import { createCloudflareSecurityContract, formatTsJsonValue } from "./policy.js";
6
+ import { sortJsonValue } from "./types.js";
7
+ import { CLOUDFLARE_COMPATIBILITY_DATE } from "./versions.js";
8
+ function createAppModernConfig(scope, app) {
9
+ const bffImport = appHasEffectApi(app) ? "import { bffPlugin } from '@modern-js/plugin-bff';\n" : '';
10
+ const bffConfig = appHasEffectApi(app) ? ` bff: {
11
+ effect: {
12
+ entry: './api/effect/index',
13
+ openapi: {
14
+ path: '/openapi.json',
15
+ },
16
+ },
17
+ prefix: '${effectApiPrefix(app)}',
18
+ runtimeFramework: 'effect',
19
+ },
20
+ ` : '';
21
+ const bffPluginEntry = appHasEffectApi(app) ? ' bffPlugin(),\n' : '';
22
+ return `// @effect-diagnostics processEnv:off
23
+ import {
24
+ appTools,
25
+ defineConfig,
26
+ presetUltramodern,
27
+ } from '@modern-js/app-tools';
28
+ ${bffImport}import { i18nPlugin } from '@modern-js/plugin-i18n';
29
+ import { tanstackRouterPlugin } from '@modern-js/plugin-tanstack';
30
+ import { moduleFederationPlugin } from '@module-federation/modern-js-v3';
31
+ import { withZephyr as withZephyrRspack } from 'zephyr-rspack-plugin';
32
+ import { ultramodernLocalisedUrls } from './src/routes/ultramodern-route-metadata';
33
+
34
+ type ZephyrRspackConfig = Parameters<ReturnType<typeof withZephyrRspack>>[0];
35
+
36
+ const zephyrEnabled = process.env['ULTRAMODERN_ZEPHYR'] !== 'false';
37
+ const cloudflareDeployEnabled =
38
+ process.env['MODERNJS_DEPLOY'] === 'cloudflare';
39
+
40
+ const zephyrRspackPlugin = () => ({
41
+ name: 'ultramodern-zephyr-rspack-plugin',
42
+ pre: ['@modern-js/plugin-module-federation-config'],
43
+ setup(api: {
44
+ modifyRspackConfig: (
45
+ handler: (
46
+ config: ZephyrRspackConfig,
47
+ ) => ZephyrRspackConfig | Promise<ZephyrRspackConfig>,
48
+ ) => void;
49
+ }) {
50
+ if (!zephyrEnabled) {
51
+ return;
52
+ }
53
+ api.modifyRspackConfig(config => withZephyrRspack()(config));
54
+ },
55
+ });
56
+
57
+ const appId = '${app.id}';
58
+ const cloudflareWorkerName = '${createCloudflareWorkerName(scope, app)}';
59
+ const port = Number(process.env['${app.portEnv}'] ?? ${app.port});
60
+ const envValue = (name: string) => {
61
+ const value = process.env[name]?.trim();
62
+ return value !== undefined && value.length > 0 ? value : undefined;
63
+ };
64
+ const configuredSiteUrl = envValue('MODERN_PUBLIC_SITE_URL');
65
+ const configuredCloudflareUrl = envValue('${createCloudflarePublicUrlEnv(app)}');
66
+ const configuredUltramodernAssetPrefix = envValue('ULTRAMODERN_ASSET_PREFIX');
67
+ const configuredModernAssetPrefix = envValue('MODERN_ASSET_PREFIX');
68
+ const cloudflareWorkersDevSubdomain = envValue(
69
+ 'ULTRAMODERN_CLOUDFLARE_WORKERS_DEV_SUBDOMAIN',
70
+ );
71
+ const inferredCloudflareUrl =
72
+ cloudflareDeployEnabled && cloudflareWorkersDevSubdomain !== undefined
73
+ ? \`https://\${cloudflareWorkerName}.\${cloudflareWorkersDevSubdomain}.workers.dev\`
74
+ : undefined;
75
+ // Site origin (SEO: canonical/hreflang URLs) prefers the site-wide public URL;
76
+ // the per-app deployment URL only fills in when no site origin is configured.
77
+ const siteUrl =
78
+ configuredSiteUrl ||
79
+ configuredCloudflareUrl ||
80
+ inferredCloudflareUrl ||
81
+ \`http://localhost:\${port}\`;
82
+ // Asset loading is intentionally independent from the canonical site URL and
83
+ // deployment public URL. Without an explicit asset prefix, assets stay
84
+ // origin-relative so self-hosted apps, tunnels, and reverse proxies never leak
85
+ // localhost URLs into production HTML.
86
+ const assetPrefix =
87
+ configuredModernAssetPrefix || configuredUltramodernAssetPrefix || '/';
88
+
89
+ if (
90
+ cloudflareDeployEnabled &&
91
+ process.env['ULTRAMODERN_CLOUDFLARE_REQUIRE_PUBLIC_URLS'] === 'true' &&
92
+ configuredCloudflareUrl === undefined &&
93
+ configuredSiteUrl === undefined &&
94
+ inferredCloudflareUrl === undefined
95
+ ) {
96
+ throw new Error(
97
+ \`Cloudflare deploy for \${appId} needs ${createCloudflarePublicUrlEnv(app)}, MODERN_PUBLIC_SITE_URL, or ULTRAMODERN_CLOUDFLARE_WORKERS_DEV_SUBDOMAIN.\`,
98
+ );
99
+ }
100
+
101
+ export default defineConfig(
102
+ presetUltramodern(
103
+ {
104
+ ${bffConfig} ...(cloudflareDeployEnabled
105
+ ? {
106
+ deploy: {
107
+ worker: {
108
+ compatibilityDate: '${CLOUDFLARE_COMPATIBILITY_DATE}',
109
+ name: cloudflareWorkerName,
110
+ security: ${formatTsJsonValue(sortJsonValue(createCloudflareSecurityContract()), 16)},
111
+ ssr: true,
112
+ },
113
+ },
114
+ }
115
+ : {}),
116
+ dev: {
117
+ // Keep dev assets origin-relative too; the default absolute
118
+ // http://localhost:<port> prefix breaks pages served through tunnels.
119
+ assetPrefix: '/',
120
+ },
121
+ html: {
122
+ outputStructure: 'flat',
123
+ },
124
+ output: {
125
+ assetPrefix,
126
+ disableTsChecker: true,
127
+ distPath: {
128
+ html: './',
129
+ },
130
+ polyfill: 'off',
131
+ splitRouteChunks: true,
132
+ },
133
+ performance: {
134
+ rsdoctor: {
135
+ disableClientServer: true,
136
+ enabled: process.env['ULTRAMODERN_RSDOCTOR'] === 'true',
137
+ },
138
+ },
139
+ plugins: [
140
+ appTools(),
141
+ tanstackRouterPlugin(),
142
+ i18nPlugin({
143
+ backend: {
144
+ enabled: true,
145
+ loadPath: '/locales/{{lng}}/{{ns}}.json',
146
+ },
147
+ localeDetection: {
148
+ fallbackLanguage: 'en',
149
+ ignoreRedirectRoutes: [
150
+ '/@mf-types',
151
+ '/assets',
152
+ '/bundles',
153
+ '${effectApiPrefix(app)}',
154
+ '/locales',
155
+ '/mf-manifest.json',
156
+ '/mf-stats.json',
157
+ '/remoteEntry.js',
158
+ '/robots.txt',
159
+ '/site.webmanifest',
160
+ '/sitemap.xml',
161
+ '/static',
162
+ '/zephyr-manifest.json',
163
+ ],
164
+ languages: ['en', 'cs'],
165
+ localePathRedirect: true,
166
+ localisedUrls: ultramodernLocalisedUrls as Record<string, Record<string, string>>,
167
+ },
168
+ reactI18next: false,
169
+ }),
170
+ ${bffPluginEntry} moduleFederationPlugin(),
171
+ zephyrRspackPlugin(),
172
+ ],
173
+ server: {
174
+ port,
175
+ publicDir: ['./locales', './assets'],
176
+ ssr: {
177
+ mode: 'string',
178
+ moduleFederationAppSSR: true,
179
+ },
180
+ },
181
+ source: {
182
+ globalVars: {
183
+ ULTRAMODERN_SITE_URL: siteUrl,
184
+ },
185
+ mainEntryName: 'index',
186
+ },
187
+ tools: {
188
+ autoprefixer: {
189
+ overrideBrowserslist: ['defaults'],
190
+ },
191
+ bundlerChain: chain => {
192
+ chain.output
193
+ .uniqueName('${createRspackUniqueName(app)}')
194
+ .chunkLoadingGlobal('${createRspackChunkLoadingGlobal(app)}');
195
+ chain.ignoreWarnings([
196
+ {
197
+ message: /the request of a dependency is an expression/u,
198
+ module: /modern-js-plugin-i18n/u,
199
+ },
200
+ ]);
201
+ },
202
+ },
203
+ },
204
+ {
205
+ appId,
206
+ enableBffRequestId: true,
207
+ enableModuleFederationSSR: true,
208
+ enableTelemetryExporters: true,
209
+ telemetryFailLoudStartup: false,
210
+ },
211
+ ),
212
+ );
213
+ `;
214
+ }
215
+ function createSharedModuleFederationConfig() {
216
+ return ` shared: {
217
+ '@modern-js/plugin-i18n/runtime': {
218
+ requiredVersion: pluginI18nVersion,
219
+ singleton: true,
220
+ treeShaking: false,
221
+ },
222
+ '@modern-js/plugin-tanstack/runtime': {
223
+ requiredVersion: pluginTanstackVersion,
224
+ singleton: true,
225
+ treeShaking: false,
226
+ },
227
+ '@modern-js/runtime': {
228
+ requiredVersion: runtimeVersion,
229
+ singleton: true,
230
+ treeShaking: false,
231
+ },
232
+ '@tanstack/react-router': {
233
+ requiredVersion: dependencies['@tanstack/react-router'],
234
+ singleton: true,
235
+ treeShaking: false,
236
+ },
237
+ react: {
238
+ requiredVersion: reactVersion,
239
+ singleton: true,
240
+ treeShaking: false,
241
+ },
242
+ 'react-dom': {
243
+ requiredVersion: reactDomVersion,
244
+ singleton: true,
245
+ treeShaking: false,
246
+ },
247
+ 'react-dom/client': {
248
+ requiredVersion: reactDomVersion,
249
+ singleton: true,
250
+ treeShaking: false,
251
+ },
252
+ }`;
253
+ }
254
+ function formatTsObjectLiteral(value) {
255
+ const entries = Object.entries(value).sort(([left], [right])=>left.localeCompare(right));
256
+ if (0 === entries.length) return '{}';
257
+ return `{
258
+ ${entries.map(([key, entryValue])=>` '${key}': '${entryValue}',`).join('\n')}
259
+ }`;
260
+ }
261
+ function createModuleFederationRemoteUrlHelpers(app, remotes = []) {
262
+ if (0 === resolveRemoteRefs(app, remotes).length) return '';
263
+ return `const cloudflareDeployEnabled =
264
+ process.env['MODERNJS_DEPLOY'] === 'cloudflare';
265
+ const cloudflareWorkersDevSubdomain =
266
+ process.env['ULTRAMODERN_CLOUDFLARE_WORKERS_DEV_SUBDOMAIN']?.trim();
267
+ const requireCloudflarePublicUrls =
268
+ process.env['ULTRAMODERN_CLOUDFLARE_REQUIRE_PUBLIC_URLS'] === 'true';
269
+
270
+ const createRemoteManifestUrl = (options: {
271
+ manifestEnv: string;
272
+ mfName: string;
273
+ port: number;
274
+ publicUrlEnv: string;
275
+ workerName: string;
276
+ }) => {
277
+ const configuredManifest = process.env[options.manifestEnv]?.trim();
278
+ if (configuredManifest !== undefined && configuredManifest.length > 0) {
279
+ return configuredManifest;
280
+ }
281
+
282
+ const configuredPublicUrl = process.env[options.publicUrlEnv]?.trim();
283
+ if (configuredPublicUrl !== undefined && configuredPublicUrl.length > 0) {
284
+ return \`\${options.mfName}@\${configuredPublicUrl.replace(/\\/+$/u, '')}/mf-manifest.json\`;
285
+ }
286
+
287
+ if (cloudflareDeployEnabled && cloudflareWorkersDevSubdomain !== undefined) {
288
+ return \`\${options.mfName}@https://\${options.workerName}.\${cloudflareWorkersDevSubdomain}.workers.dev/mf-manifest.json\`;
289
+ }
290
+
291
+ if (cloudflareDeployEnabled && requireCloudflarePublicUrls) {
292
+ throw new Error(
293
+ \`Cloudflare deploy needs \${options.publicUrlEnv}, \${options.manifestEnv}, or ULTRAMODERN_CLOUDFLARE_WORKERS_DEV_SUBDOMAIN for remote \${options.mfName}.\`,
294
+ );
295
+ }
296
+
297
+ return \`\${options.mfName}@http://localhost:\${options.port}/mf-manifest.json\`;
298
+ };
299
+
300
+ `;
301
+ }
302
+ function createModuleFederationRemotesConfig(scope, app, remotes = []) {
303
+ const remoteEntries = resolveRemoteRefs(app, remotes).toSorted((left, right)=>remoteDependencyAlias(left).localeCompare(remoteDependencyAlias(right))).map((remote)=>{
304
+ const key = remoteDependencyAlias(remote);
305
+ return ` ${key}: createRemoteManifestUrl({
306
+ manifestEnv: '${createRemoteManifestEnv(remote)}',
307
+ mfName: '${remote.mfName}',
308
+ port: ${remote.port},
309
+ publicUrlEnv: '${createCloudflarePublicUrlEnv(remote)}',
310
+ workerName: '${createCloudflareWorkerName(scope, remote)}',
311
+ }),`;
312
+ }).join('\n');
313
+ if (!remoteEntries) return '';
314
+ return ` remotes: {
315
+ ${remoteEntries}
316
+ },
317
+ `;
318
+ }
319
+ function createShellModuleFederationConfig(scope, remotes = []) {
320
+ const shellHost = {
321
+ ...shellApp,
322
+ verticalRefs: remotes.map((remote)=>remote.id)
323
+ };
324
+ return `// @effect-diagnostics nodeBuiltinImport:off processEnv:off
325
+ import { createRequire } from 'node:module';
326
+ import { createModuleFederationConfig } from '@module-federation/modern-js-v3';
327
+ import { dependencies } from './package.json';
328
+
329
+ const require = createRequire(import.meta.url);
330
+ const pluginI18nVersion = (require('@modern-js/plugin-i18n/package.json') as { version: string }).version;
331
+ const pluginTanstackVersion = (require('@modern-js/plugin-tanstack/package.json') as { version: string }).version;
332
+ const runtimeVersion = (require('@modern-js/runtime/package.json') as { version: string }).version;
333
+ const reactVersion = (require('react/package.json') as { version: string }).version;
334
+ const reactDomVersion = (require('react-dom/package.json') as { version: string }).version;
335
+
336
+ ${createModuleFederationRemoteUrlHelpers(shellHost, remotes)}
337
+ export default createModuleFederationConfig({
338
+ bridge: {
339
+ enableBridgeRouter: false,
340
+ },
341
+ dev: {
342
+ disableDynamicRemoteTypeHints: true,
343
+ },
344
+ dts: {
345
+ displayErrorInTerminal: true,
346
+ generateTypes: {
347
+ compilerInstance: 'tsgo',
348
+ },
349
+ },
350
+ filename: 'remoteEntry.js',
351
+ name: '${shellApp.mfName}',
352
+ ${createModuleFederationRemotesConfig(scope, shellHost, remotes)}${createSharedModuleFederationConfig()},
353
+ treeShakingSharedExcludePlugins: ['RspackModuleFederationPlugin'],
354
+ });
355
+ `;
356
+ }
357
+ function createBuildMarker(scope, app) {
358
+ return node_crypto.createHash('sha256').update(`${scope}:${app.packageSuffix}:${app.id}:0.1.0`).digest('hex').slice(0, 16);
359
+ }
360
+ function createUltramodernBuildModule(scope, app) {
361
+ return `export const ultramodernVerticalIdentity = {
362
+ appId: '${app.id}',
363
+ build: '${createBuildMarker(scope, app)}',
364
+ deployProfile: 'cloudflare-ssr-mf-effect-v1',
365
+ packageName: '${packageName(scope, app.packageSuffix)}',
366
+ version: '0.1.0',
367
+ } as const;
368
+
369
+ export const ultramodernUiMarker = {
370
+ ...ultramodernVerticalIdentity,
371
+ surface: 'ui',
372
+ } as const;
373
+
374
+ export const ultramodernApiMarker = {
375
+ ...ultramodernVerticalIdentity,
376
+ surface: 'effect-bff',
377
+ } as const;
378
+ `;
379
+ }
380
+ function createRemoteModuleFederationConfig(scope, app, remotes = []) {
381
+ const exposes = formatTsObjectLiteral(app.exposes ?? {});
382
+ return `// @effect-diagnostics nodeBuiltinImport:off
383
+ import { createRequire } from 'node:module';
384
+ import { createModuleFederationConfig } from '@module-federation/modern-js-v3';
385
+ import { dependencies } from './package.json';
386
+
387
+ const require = createRequire(import.meta.url);
388
+ const pluginI18nVersion = (require('@modern-js/plugin-i18n/package.json') as { version: string }).version;
389
+ const pluginTanstackVersion = (require('@modern-js/plugin-tanstack/package.json') as { version: string }).version;
390
+ const runtimeVersion = (require('@modern-js/runtime/package.json') as { version: string }).version;
391
+ const reactVersion = (require('react/package.json') as { version: string }).version;
392
+ const reactDomVersion = (require('react-dom/package.json') as { version: string }).version;
393
+
394
+ ${createModuleFederationRemoteUrlHelpers(app, remotes)}
395
+ export default createModuleFederationConfig({
396
+ bridge: {
397
+ enableBridgeRouter: false,
398
+ },
399
+ dev: {
400
+ disableDynamicRemoteTypeHints: true,
401
+ },
402
+ dts: {
403
+ displayErrorInTerminal: true,
404
+ generateTypes: {
405
+ compilerInstance: 'tsgo',
406
+ },
407
+ },
408
+ exposes: ${exposes},
409
+ filename: 'remoteEntry.js',
410
+ name: '${app.mfName}',
411
+ ${createModuleFederationRemotesConfig(scope, app, remotes)}${createSharedModuleFederationConfig()},
412
+ treeShakingSharedExcludePlugins: ['RspackModuleFederationPlugin'],
413
+ });
414
+ `;
415
+ }
416
+ export { createAppModernConfig, createBuildMarker, createModuleFederationRemoteUrlHelpers, createModuleFederationRemotesConfig, createRemoteModuleFederationConfig, createSharedModuleFederationConfig, createShellModuleFederationConfig, createUltramodernBuildModule, formatTsObjectLiteral };
@@ -0,0 +1,72 @@
1
+ import "node:module";
2
+ import node_path from "node:path";
3
+ const TAILWIND_PREFIX_DIGIT_WORDS = [
4
+ 'zero',
5
+ 'one',
6
+ 'two',
7
+ 'three',
8
+ 'four',
9
+ 'five',
10
+ 'six',
11
+ 'seven',
12
+ 'eight',
13
+ 'nine'
14
+ ];
15
+ function normalizePath(filePath) {
16
+ return filePath.split(node_path.sep).join('/');
17
+ }
18
+ function toPackageScope(packageName) {
19
+ const normalized = packageName.replace(/^@/, '').replace(/[\\/]+/g, '-').toLowerCase().replace(/[^a-z0-9._-]+/g, '-').replace(/^[._-]+|[._-]+$/g, '').replace(/-{2,}/g, '-');
20
+ return normalized || 'ultramodern-superapp';
21
+ }
22
+ function toKebabCase(value) {
23
+ const normalized = value.trim().replace(/([a-z0-9])([A-Z])/g, '$1-$2').replace(/[^a-zA-Z0-9._-]+/g, '-').replace(/[._]+/g, '-').toLowerCase().replace(/-+/g, '-').replace(/^-+|-+$/g, '');
24
+ return normalized;
25
+ }
26
+ function toCamelCase(value) {
27
+ const pascal = toPascalCase(value);
28
+ return `${pascal.charAt(0).toLowerCase()}${pascal.slice(1)}`;
29
+ }
30
+ function toEnvSegment(value) {
31
+ return toKebabCase(value).replace(/-/g, '_').toUpperCase();
32
+ }
33
+ function createRspackUniqueName(app) {
34
+ return app.mfName;
35
+ }
36
+ function createRspackChunkLoadingGlobal(app) {
37
+ return `__ULTRAMODERN_${toEnvSegment(app.mfName)}_LOADED_CHUNKS__`;
38
+ }
39
+ function naming_packageName(scope, suffix) {
40
+ return `@${scope}/${suffix}`;
41
+ }
42
+ function relativeRootFor(packageDir) {
43
+ return normalizePath(node_path.relative(packageDir, '.') || '.');
44
+ }
45
+ function createTailwindPrefix(raw) {
46
+ const normalized = raw.toLowerCase().replace(/[^a-z0-9]/gu, '');
47
+ if (!normalized) throw new Error(`Cannot derive a Tailwind prefix from ${raw}`);
48
+ return normalized.replace(/[0-9]/gu, (digit)=>TAILWIND_PREFIX_DIGIT_WORDS[Number(digit)]);
49
+ }
50
+ function tailwindPrefixForApp(app) {
51
+ if ('shell' === app.kind) return 'shell';
52
+ return createTailwindPrefix(app.domain ?? app.id);
53
+ }
54
+ function assertUniqueTailwindPrefixes(apps) {
55
+ const seen = new Map();
56
+ const entries = apps.map((app)=>[
57
+ app.id,
58
+ tailwindPrefixForApp(app)
59
+ ]);
60
+ for (const [id, prefix] of entries){
61
+ const previous = seen.get(prefix);
62
+ if (previous) throw new Error(`Tailwind prefix ${prefix} for ${id} collides with ${previous}`);
63
+ seen.set(prefix, id);
64
+ }
65
+ }
66
+ function createTw(prefix) {
67
+ return (classList)=>classList.split(/\s+/u).filter(Boolean).map((candidate)=>`${prefix}:${candidate.replace(/\[&&\]:/gu, '')}`).join(' ');
68
+ }
69
+ function toPascalCase(value) {
70
+ return value.split(/[-_]+/).filter(Boolean).map((part)=>`${part.charAt(0).toUpperCase()}${part.slice(1)}`).join('');
71
+ }
72
+ export { TAILWIND_PREFIX_DIGIT_WORDS, assertUniqueTailwindPrefixes, createRspackChunkLoadingGlobal, createRspackUniqueName, createTailwindPrefix, createTw, naming_packageName as packageName, normalizePath, relativeRootFor, tailwindPrefixForApp, toCamelCase, toEnvSegment, toKebabCase, toPackageScope, toPascalCase };
@@ -0,0 +1,80 @@
1
+ import "node:module";
2
+ import { spawnSync } from "node:child_process";
3
+ import { createRequire } from "node:module";
4
+ const overlays_require = createRequire(import.meta.url);
5
+ const overlayRunnerSource = `
6
+ const fs = require('node:fs');
7
+
8
+ (async () => {
9
+ const payload = JSON.parse(fs.readFileSync(0, 'utf-8'));
10
+ const { CodeSmith } = require(payload.codesmithModulePath);
11
+ const codesmith = new CodeSmith({});
12
+ await codesmith.forge({
13
+ pwd: payload.workspaceRoot,
14
+ tasks: [
15
+ {
16
+ generator: payload.generator,
17
+ config: payload.config,
18
+ },
19
+ ],
20
+ });
21
+ })().catch(error => {
22
+ console.error(error?.stack || error?.message || String(error));
23
+ process.exit(1);
24
+ });
25
+ `;
26
+ function runCodeSmithOverlays(options) {
27
+ if (!options.overlays?.length) return;
28
+ for (const overlay of options.overlays)runCodeSmithOverlay({
29
+ workspaceRoot: options.workspaceRoot,
30
+ overlay,
31
+ config: createOverlayRuntimeConfig(options.workspaceRoot, options.result)
32
+ });
33
+ }
34
+ function runCodeSmithOverlay(options) {
35
+ const result = spawnSync(process.execPath, [
36
+ '--eval',
37
+ overlayRunnerSource
38
+ ], {
39
+ cwd: options.workspaceRoot,
40
+ encoding: 'utf8',
41
+ input: JSON.stringify({
42
+ workspaceRoot: options.workspaceRoot,
43
+ generator: options.overlay.generator,
44
+ codesmithModulePath: overlays_require.resolve('@modern-js/codesmith'),
45
+ config: {
46
+ ...options.overlay.config ?? {},
47
+ ...options.config
48
+ }
49
+ })
50
+ });
51
+ if (0 !== result.status) {
52
+ const detail = [
53
+ result.stderr.trim(),
54
+ result.stdout.trim()
55
+ ].filter(Boolean).join('\n');
56
+ throw new Error([
57
+ `UltraModern CodeSmith overlay failed: ${options.overlay.generator}`,
58
+ detail
59
+ ].filter(Boolean).join('\n'));
60
+ }
61
+ }
62
+ function createOverlayRuntimeConfig(workspaceRoot, result) {
63
+ const generatedApp = result.createdApps[0];
64
+ return {
65
+ workspaceRoot,
66
+ packageScope: result.packageScope,
67
+ operation: result.operation,
68
+ generatedApp,
69
+ generatedApps: result.createdApps,
70
+ assignedPort: generatedApp ? result.assignedPorts[generatedApp.id] : void 0,
71
+ assignedPorts: result.assignedPorts,
72
+ moduleFederationName: generatedApp ? result.moduleFederationNames[generatedApp.id] : void 0,
73
+ moduleFederationNames: result.moduleFederationNames,
74
+ effectApiPrefix: generatedApp ? result.effectApiPrefixes[generatedApp.id] : void 0,
75
+ effectApiPrefixes: result.effectApiPrefixes,
76
+ packageSource: result.packageSource,
77
+ generationResult: result
78
+ };
79
+ }
80
+ export { runCodeSmithOverlays };