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

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,63 @@
1
+ const ZH_LOCALE = {
2
+ prompt: {
3
+ projectName: '请输入项目名称: ',
4
+ legacyModernJsConfirmation: '输入 "{confirmation}" 以继续使用原始 Modern.js 初始化: '
5
+ },
6
+ error: {
7
+ projectNameEmpty: '错误: 项目名称不能为空',
8
+ directoryExists: '错误: 目录 "{projectName}" 已存在且不为空',
9
+ verticalNameMissing: '错误: 缺少 MicroVertical 名称。请使用 <名称> --vertical、--vertical=<名称> 或 --vertical-name <名称>。',
10
+ verticalNameAmbiguous: '错误: MicroVertical 名称不明确:{firstSource} 的 "{firstName}" 与 {secondSource} 的 "{secondName}" 不一致。',
11
+ legacyModernJsNotConfirmed: '已中止。UltraModern.js 仍是默认的免交互初始化方案。',
12
+ createFailed: '创建项目时出错:'
13
+ },
14
+ message: {
15
+ welcome: '🚀 欢迎使用 UltraModern.js',
16
+ success: '✨ 创建成功!',
17
+ nextSteps: '📋 下一步:',
18
+ step1: 'cd {projectName}',
19
+ step2: 'pnpm install',
20
+ step3: 'pnpm dev',
21
+ legacyModernJsWarning: "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n严重警告:你正在退出 ULTRAMODERN.JS 默认配置。\n免交互默认值是最佳 UltraModern.js 配置:\n结构化 SuperApp 工作区、presetUltramodern、TanStack Router、\nEffect BFF、Tailwind CSS v4,以及 BleedingDev 包版本队列。\n原始 Modern.js 初始化是危险的显式选择路径。\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
22
+ },
23
+ help: {
24
+ title: '🚀 UltraModern.js 项目创建工具',
25
+ description: '默认创建带完整质量基线的 UltraModern.js SuperApp 工作区',
26
+ usage: '📖 用法:',
27
+ usageExample: ' pnpm dlx @bleedingdev/modern-js-create [项目名称] [选项]',
28
+ options: '⚙️ 选项:',
29
+ optionHelp: ' -h, --help 显示帮助信息',
30
+ optionVersion: ' -v, --version 显示版本信息',
31
+ optionLang: ' -l, --lang 设置语言 (默认 en;zh 需显式选择)',
32
+ optionTailwind: ' --no-tailwind 禁用默认 Tailwind CSS v4 工作区样式',
33
+ optionBff: ' --bff 保留默认的 Effect BFF 脚手架(每个 MicroVertical 自带 Effect BFF)',
34
+ optionBffRuntime: ' --bff-runtime 选择 MicroVertical 脚手架的 BFF 运行时(支持: effect;默认: effect)',
35
+ optionWorkspace: ' --workspace 对 @modern-js 依赖使用 workspace 协议(用于本地 monorepo 联调)',
36
+ optionUltramodernPackageSource: ' --ultramodern-package-source 选择 UltraModern 依赖来源(workspace 或 install;BleedingDev 默认使用 install alias)',
37
+ optionUltramodernPackageVersion: ' --ultramodern-package-version 为 install 依赖来源固定精确的 BleedingDev 框架版本',
38
+ optionUltramodernPackageRegistry: ' --ultramodern-package-registry install 依赖来源使用的 npm registry 地址',
39
+ optionUltramodernPackageScope: ' --ultramodern-package-scope npm alias 安装使用的发布 scope(例如 bleedingdev)',
40
+ optionUltramodernPackageNamePrefix: ' --ultramodern-package-name-prefix npm alias 包名前缀(默认:modern-js-)',
41
+ optionVertical: ' --vertical[=<名称>] 修改当前已有的 UltraModern 工作区,并接入 MicroVertical',
42
+ optionVerticalName: ' --vertical-name <名称> 为自动化工作流显式指定 MicroVertical 名称',
43
+ optionDryRun: ' --dry-run 预览 MicroVertical 修改计划但不写入文件(与 --vertical 一起使用)',
44
+ optionCodeSmithOverlay: ' --codesmith-overlay <包或路径> 生成完成后运行显式配置的 CodeSmith overlay',
45
+ optionLegacyModernJs: ' --legacy-modern-js 在大型警告和输入确认后,选择原始 Modern.js 初始化',
46
+ examples: '💡 示例:',
47
+ example1: ' pnpm dlx @bleedingdev/modern-js-create my-workspace',
48
+ example2: ' pnpm dlx @bleedingdev/modern-js-create my-workspace --lang zh',
49
+ example3: ' pnpm dlx @bleedingdev/modern-js-create my-workspace --no-tailwind',
50
+ example4: ' pnpm dlx @bleedingdev/modern-js-create --help',
51
+ example5: ' pnpm dlx @bleedingdev/modern-js-create .',
52
+ example6: ' pnpm dlx @bleedingdev/modern-js-create my-workspace --workspace',
53
+ example7: ' pnpm dlx @bleedingdev/modern-js-create catalog --vertical',
54
+ example8: ' pnpm dlx @bleedingdev/modern-js-create --vertical=catalog',
55
+ example9: ' pnpm dlx @bleedingdev/modern-js-create catalog --vertical --dry-run',
56
+ example10: ' pnpm dlx @bleedingdev/modern-js-create catalog --vertical --codesmith-overlay ./overlay-generator',
57
+ moreInfo: '📚 更多信息: https://modernjs.dev'
58
+ },
59
+ version: {
60
+ message: '{name} 版本: {version}'
61
+ }
62
+ };
63
+ export { ZH_LOCALE };
@@ -0,0 +1,63 @@
1
+ const WORKSPACE_PACKAGE_VERSION = 'workspace:*';
2
+ const BLEEDINGDEV_CREATE_PACKAGE = '@bleedingdev/modern-js-create';
3
+ const BLEEDINGDEV_PACKAGE_SCOPE = 'bleedingdev';
4
+ const BLEEDINGDEV_PACKAGE_NAME_PREFIX = 'modern-js-';
5
+ const BLEEDINGDEV_FRAMEWORK_VERSION_ENV = 'MODERN_CREATE_ULTRAMODERN_FRAMEWORK_VERSION';
6
+ const ULTRAMODERN_SINGLE_APP_MODERN_PACKAGES = [
7
+ '@modern-js/create',
8
+ '@modern-js/code-tools',
9
+ '@modern-js/runtime',
10
+ '@modern-js/app-tools',
11
+ '@modern-js/tsconfig',
12
+ '@modern-js/plugin-i18n',
13
+ '@modern-js/plugin-tanstack',
14
+ '@modern-js/plugin-bff',
15
+ '@modern-js/adapter-rstest'
16
+ ];
17
+ const ULTRAMODERN_WORKSPACE_MODERN_PACKAGES = [
18
+ '@modern-js/create',
19
+ '@modern-js/code-tools',
20
+ '@modern-js/app-tools',
21
+ '@modern-js/plugin-bff',
22
+ '@modern-js/plugin-i18n',
23
+ '@modern-js/plugin-tanstack',
24
+ '@modern-js/runtime'
25
+ ];
26
+ function modernPackageVersion(packageSource) {
27
+ return 'install' === packageSource.strategy ? packageSource.modernPackageVersion : WORKSPACE_PACKAGE_VERSION;
28
+ }
29
+ function modernAliasPackageName(packageName, packageSource) {
30
+ if (!packageSource.aliasScope) return packageName;
31
+ const scope = packageSource.aliasScope.replace(/^@/, '');
32
+ const unscopedName = packageName.split('/').at(-1);
33
+ return `@${scope}/${packageSource.aliasPackageNamePrefix ?? ''}${unscopedName}`;
34
+ }
35
+ function modernPackageSpecifier(packageName, packageSource) {
36
+ if ('install' !== packageSource.strategy) return WORKSPACE_PACKAGE_VERSION;
37
+ if (!packageSource.aliasScope) return packageSource.modernPackageVersion;
38
+ return `npm:${modernAliasPackageName(packageName, packageSource)}@${packageSource.modernPackageVersion}`;
39
+ }
40
+ function modernPackageAliases(packageNames, packageSource) {
41
+ if (!packageSource.aliasScope) return;
42
+ return Object.fromEntries(packageNames.map((packageName)=>[
43
+ packageName,
44
+ modernAliasPackageName(packageName, packageSource)
45
+ ]));
46
+ }
47
+ function createModernPackagesMetadata(packageNames, packageSource, options = {}) {
48
+ const includeAliases = options.includeAliases ?? Boolean(packageSource.aliasScope);
49
+ const aliases = includeAliases ? modernPackageAliases(packageNames, packageSource) : void 0;
50
+ return {
51
+ packages: [
52
+ ...packageNames
53
+ ],
54
+ specifier: modernPackageVersion(packageSource),
55
+ ...packageSource.registry ? {
56
+ registry: packageSource.registry
57
+ } : {},
58
+ ...aliases ? {
59
+ aliases
60
+ } : {}
61
+ };
62
+ }
63
+ export { BLEEDINGDEV_CREATE_PACKAGE, BLEEDINGDEV_FRAMEWORK_VERSION_ENV, BLEEDINGDEV_PACKAGE_NAME_PREFIX, BLEEDINGDEV_PACKAGE_SCOPE, ULTRAMODERN_SINGLE_APP_MODERN_PACKAGES, ULTRAMODERN_WORKSPACE_MODERN_PACKAGES, WORKSPACE_PACKAGE_VERSION, createModernPackagesMetadata, modernAliasPackageName, modernPackageAliases, modernPackageSpecifier, modernPackageVersion };
@@ -0,0 +1,511 @@
1
+ import node_fs from "node:fs";
2
+ import node_os from "node:os";
3
+ import node_path from "node:path";
4
+ import { WORKSPACE_PACKAGE_VERSION } from "../ultramodern-package-source.js";
5
+ import { createAppEnvDts, createAppRuntimeConfig, createShellFrameComponent } from "./app-files.js";
6
+ import { createGeneratedContract } from "./contracts.js";
7
+ import { createShellPage, createShellRemoteComponents } from "./demo-components.js";
8
+ import { GENERATED_CONTRACT_PATH, appHasEffectApi, appI18nNamespace, createModuleFederationRemoteContracts, createNeutralOwnership, createRemoteManifestEnv, createShellHost, createVerticalDescriptor, effectApiPrefix, remoteDependencyAlias, shellApp, zephyrRemoteDependency } from "./descriptors.js";
9
+ import { createShellEffectClient, effectApiTopologyMetadata } from "./effect-api.js";
10
+ import { readJsonFile, writeFileReplacing, writeJsonFile } from "./fs-io.js";
11
+ import { createFileSnapshot, createGenerationResult, diffFileSnapshots } from "./generation-result.js";
12
+ import { createAppPublicLocaleMessages } from "./locales.js";
13
+ import { createShellModuleFederationConfig } from "./module-federation.js";
14
+ import { assertUniqueTailwindPrefixes, normalizePath, packageName, toEnvSegment, toKebabCase, toPackageScope, toPascalCase } from "./naming.js";
15
+ import { runCodeSmithOverlays } from "./overlays.js";
16
+ import { createAppPackage, createRootPackageJson } from "./package-json.js";
17
+ import { resolvePackageSource } from "./package-source.js";
18
+ import { createCloudflareDeployContract } from "./policy.js";
19
+ import { createPublicWebAppArtifacts, rewriteWorkspaceAssetsForApp } from "./public-surface.js";
20
+ import { isRecord } from "./types.js";
21
+ import { writeGeneratedWorkspaceScripts } from "./workspace-scripts.js";
22
+ import { writeApp } from "./write-workspace.js";
23
+ const FIRST_VERTICAL_PORT = 4101;
24
+ const TOPOLOGY_PATH = 'topology/reference-topology.json';
25
+ const OWNERSHIP_PATH = 'topology/ownership.json';
26
+ const DEVELOPMENT_OVERLAY_PATH = 'topology/local-overlays/development.json';
27
+ const PACKAGE_SOURCE_METADATA_PATH = '.modernjs/ultramodern-package-source.json';
28
+ function existingPackageSource(workspaceRoot, modernVersion, packageSource) {
29
+ if (packageSource) return resolvePackageSource({
30
+ targetDir: workspaceRoot,
31
+ packageName: node_path.basename(workspaceRoot),
32
+ modernVersion,
33
+ packageSource
34
+ });
35
+ const metadataPath = node_path.join(workspaceRoot, '.modernjs/ultramodern-package-source.json');
36
+ if (!node_fs.existsSync(metadataPath)) return resolvePackageSource({
37
+ targetDir: workspaceRoot,
38
+ packageName: node_path.basename(workspaceRoot),
39
+ modernVersion
40
+ });
41
+ const metadata = readJsonFile(metadataPath);
42
+ const aliases = metadata.modernPackages?.aliases ?? {};
43
+ const firstAlias = Object.values(aliases).find((value)=>'string' == typeof value);
44
+ const firstPackage = Object.keys(aliases)[0];
45
+ const aliasScope = firstAlias?.match(/^@([^/]+)\//)?.[1];
46
+ const unscopedName = firstPackage?.split('/').at(-1) ?? '';
47
+ const aliasUnscopedName = firstAlias?.split('/').at(-1) ?? '';
48
+ const aliasPackageNamePrefix = aliasUnscopedName && unscopedName && aliasUnscopedName.endsWith(unscopedName) ? aliasUnscopedName.slice(0, -unscopedName.length) : void 0;
49
+ return {
50
+ strategy: 'install' === metadata.strategy ? 'install' : 'workspace',
51
+ modernPackageVersion: 'string' == typeof metadata.modernPackages?.specifier ? metadata.modernPackages.specifier : modernVersion,
52
+ registry: metadata.modernPackages?.registry,
53
+ aliasScope,
54
+ aliasPackageNamePrefix
55
+ };
56
+ }
57
+ function existingTailwindEnabled(workspaceRoot) {
58
+ const contractPath = node_path.join(workspaceRoot, GENERATED_CONTRACT_PATH);
59
+ if (!node_fs.existsSync(contractPath)) return true;
60
+ const contract = readJsonFile(contractPath);
61
+ const apps = isRecord(contract) && Array.isArray(contract.apps) ? contract.apps : [];
62
+ const shell = apps.find((app)=>isRecord(app) && app.id === shellApp.id);
63
+ return shell?.styling && isRecord(shell.styling) ? false !== shell.styling.tailwind : true;
64
+ }
65
+ function assertValidVerticalName(name) {
66
+ const normalized = toKebabCase(name);
67
+ if (!normalized || normalized !== name) throw new Error(`Invalid Vertical name "${name}". Use lowercase kebab-case.`);
68
+ return normalized;
69
+ }
70
+ function nextAvailablePort(ports) {
71
+ const numericPorts = Object.values(ports).filter((value)=>'number' == typeof value && Number.isFinite(value));
72
+ return Math.max(FIRST_VERTICAL_PORT - 1, ...numericPorts) + 1;
73
+ }
74
+ function assertCanCreate(workspaceRoot, relativePath) {
75
+ if (node_fs.existsSync(node_path.join(workspaceRoot, relativePath))) throw new Error(`Refusing to overwrite existing path: ${relativePath}`);
76
+ }
77
+ function updateRootWorkspaceScripts(workspaceRoot, scope, packageSource, remotes) {
78
+ const packagePath = node_path.join(workspaceRoot, 'package.json');
79
+ const rootPackage = readJsonFile(packagePath);
80
+ const generatedRootPackage = createRootPackageJson(scope, packageSource, remotes);
81
+ rootPackage.scripts = generatedRootPackage.scripts;
82
+ writeJsonFile(packagePath, rootPackage);
83
+ }
84
+ function rewriteShellAppFiles(workspaceRoot, scope, packageSource, enableTailwind, remotes) {
85
+ const shellHost = createShellHost(remotes);
86
+ const publicWeb = createPublicWebAppArtifacts(shellHost);
87
+ writeJsonFile(node_path.join(workspaceRoot, `${shellApp.directory}/package.json`), createAppPackage(scope, shellHost, packageSource, enableTailwind, remotes));
88
+ writeFileReplacing(workspaceRoot, `${shellApp.directory}/src/modern-app-env.d.ts`, createAppEnvDts(shellHost, remotes));
89
+ writeFileReplacing(workspaceRoot, publicWeb.jsonLdHelperFile.path, publicWeb.jsonLdHelperFile.content);
90
+ writeFileReplacing(workspaceRoot, publicWeb.routeMetadataFile.path, publicWeb.routeMetadataFile.content);
91
+ writeFileReplacing(workspaceRoot, publicWeb.routeHeadFile.path, publicWeb.routeHeadFile.content);
92
+ for (const generatedFile of publicWeb.routeMetaFiles)writeFileReplacing(workspaceRoot, generatedFile.path, generatedFile.content);
93
+ rewriteWorkspaceAssetsForApp(workspaceRoot, shellHost);
94
+ writeFileReplacing(workspaceRoot, `${shellApp.directory}/src/modern.runtime.ts`, createAppRuntimeConfig(shellHost, scope, remotes));
95
+ writeJsonFile(node_path.join(workspaceRoot, `${shellApp.directory}/locales/en/translation.json`), createAppPublicLocaleMessages(shellHost, 'en', remotes));
96
+ writeJsonFile(node_path.join(workspaceRoot, `${shellApp.directory}/locales/en/${appI18nNamespace(shellHost)}.json`), createAppPublicLocaleMessages(shellHost, 'en', remotes));
97
+ writeJsonFile(node_path.join(workspaceRoot, `${shellApp.directory}/locales/cs/translation.json`), createAppPublicLocaleMessages(shellHost, 'cs', remotes));
98
+ writeJsonFile(node_path.join(workspaceRoot, `${shellApp.directory}/locales/cs/${appI18nNamespace(shellHost)}.json`), createAppPublicLocaleMessages(shellHost, 'cs', remotes));
99
+ writeFileReplacing(workspaceRoot, `${shellApp.directory}/module-federation.config.ts`, createShellModuleFederationConfig(scope, remotes));
100
+ writeFileReplacing(workspaceRoot, `${shellApp.directory}/src/routes/[lang]/page.tsx`, createShellPage(remotes));
101
+ writeFileReplacing(workspaceRoot, `${shellApp.directory}/src/routes/vertical-components.tsx`, createShellRemoteComponents(scope, remotes));
102
+ writeFileReplacing(workspaceRoot, `${shellApp.directory}/src/routes/shell-frame.tsx`, createShellFrameComponent());
103
+ writeFileReplacing(workspaceRoot, `${shellApp.directory}/src/effect/vertical-clients.ts`, createShellEffectClient(scope, remotes));
104
+ }
105
+ function addShellZephyrDependency(workspaceRoot, scope, remote) {
106
+ const packagePath = node_path.join(workspaceRoot, shellApp.directory, 'package.json');
107
+ const shellPackage = readJsonFile(packagePath);
108
+ shellPackage['zephyr:dependencies'] ??= {};
109
+ shellPackage['zephyr:dependencies'][remoteDependencyAlias(remote)] = zephyrRemoteDependency(scope, remote);
110
+ writeJsonFile(packagePath, shellPackage);
111
+ }
112
+ function addShellWorkspaceDependency(workspaceRoot, scope, remote) {
113
+ if (!appHasEffectApi(remote)) return;
114
+ const packagePath = node_path.join(workspaceRoot, shellApp.directory, 'package.json');
115
+ const shellPackage = readJsonFile(packagePath);
116
+ shellPackage.dependencies ??= {};
117
+ shellPackage.dependencies[packageName(scope, remote.packageSuffix)] = WORKSPACE_PACKAGE_VERSION;
118
+ writeJsonFile(packagePath, shellPackage);
119
+ }
120
+ function verticalTopologyEntry(scope, vertical, remotes = []) {
121
+ return {
122
+ id: vertical.id,
123
+ kind: vertical.kind,
124
+ domain: vertical.domain,
125
+ package: packageName(scope, vertical.packageSuffix),
126
+ path: vertical.directory,
127
+ moduleFederation: {
128
+ role: 'remote',
129
+ name: vertical.mfName,
130
+ manifestUrl: `http://localhost:${vertical.port}/mf-manifest.json`,
131
+ exposes: Object.keys(vertical.exposes ?? {}),
132
+ ...vertical.verticalRefs?.length ? {
133
+ verticalRefs: vertical.verticalRefs,
134
+ remotes: createModuleFederationRemoteContracts(vertical, remotes)
135
+ } : {},
136
+ ssr: true,
137
+ fallbackTelemetryEvent: 'modernjs:mv-runtime-parity',
138
+ sharedContractVersion: 'mf-ssr-contract-v1'
139
+ },
140
+ ...effectApiTopologyMetadata(vertical) ? {
141
+ api: effectApiTopologyMetadata(vertical)
142
+ } : {},
143
+ cloudflare: createCloudflareDeployContract(scope, vertical),
144
+ ownership: vertical.ownership
145
+ };
146
+ }
147
+ function ownershipEntry(scope, owner) {
148
+ return {
149
+ id: owner.id,
150
+ package: packageName(scope, owner.packageSuffix),
151
+ path: owner.directory,
152
+ ownership: owner.ownership
153
+ };
154
+ }
155
+ function verticalsFromTopology(topology, ports) {
156
+ return (topology.verticals ?? []).map((vertical)=>{
157
+ const domain = vertical.domain ?? String(vertical.id);
158
+ const packageSuffix = vertical.package?.split('/').at(-1) ?? domain;
159
+ const effectApi = vertical.api?.effect ? {
160
+ stem: 'string' == typeof vertical.api.effect.basePath ? vertical.api.effect.basePath.split('/').filter(Boolean).at(-1) ?? domain : domain,
161
+ prefix: vertical.api.effect.bff?.prefix ?? `/${domain}-api`,
162
+ consumedBy: Array.isArray(vertical.api.effect.consumedBy) ? vertical.api.effect.consumedBy : [
163
+ shellApp.id,
164
+ vertical.id
165
+ ]
166
+ } : void 0;
167
+ return {
168
+ id: vertical.id,
169
+ directory: 'string' == typeof vertical.path ? vertical.path : `verticals/${domain}`,
170
+ packageSuffix,
171
+ displayName: vertical.displayName ?? `${toPascalCase(domain)} Vertical`,
172
+ kind: 'vertical',
173
+ domain,
174
+ portEnv: `VERTICAL_${toEnvSegment(domain)}_PORT`,
175
+ port: 'number' == typeof ports[vertical.id] ? ports[vertical.id] : 0,
176
+ mfName: vertical.moduleFederation?.name ?? `vertical${toPascalCase(domain)}`,
177
+ ...Array.isArray(vertical.moduleFederation?.exposes) ? {
178
+ exposes: Object.fromEntries(vertical.moduleFederation.exposes.map((expose)=>[
179
+ expose,
180
+ './Route' === expose ? './src/federation-entry.tsx' : './Widget' === expose ? `./src/components/${domain}-widget.tsx` : ''
181
+ ]))
182
+ } : {},
183
+ ...Array.isArray(vertical.moduleFederation?.verticalRefs) ? {
184
+ verticalRefs: vertical.moduleFederation.verticalRefs
185
+ } : Array.isArray(vertical.moduleFederation?.remotes) ? {
186
+ verticalRefs: vertical.moduleFederation.remotes.map((entry)=>entry.id).filter((id)=>'string' == typeof id)
187
+ } : {},
188
+ ...effectApi ? {
189
+ effectApi
190
+ } : {},
191
+ ownership: vertical.ownership ?? createNeutralOwnership(vertical.id)
192
+ };
193
+ });
194
+ }
195
+ function prepareAddUltramodernVertical(options) {
196
+ const name = assertValidVerticalName(options.name);
197
+ const topologyPath = node_path.join(options.workspaceRoot, TOPOLOGY_PATH);
198
+ const ownershipPath = node_path.join(options.workspaceRoot, OWNERSHIP_PATH);
199
+ const overlayPath = node_path.join(options.workspaceRoot, DEVELOPMENT_OVERLAY_PATH);
200
+ const rootPackage = readRequiredJsonObject(node_path.join(options.workspaceRoot, 'package.json'));
201
+ const topology = readRequiredJsonObject(topologyPath);
202
+ const ownership = readRequiredJsonObject(ownershipPath);
203
+ const overlay = readRequiredJsonObject(overlayPath);
204
+ readRequiredJsonObject(node_path.join(options.workspaceRoot, GENERATED_CONTRACT_PATH));
205
+ readRequiredJsonObject(node_path.join(options.workspaceRoot, PACKAGE_SOURCE_METADATA_PATH));
206
+ assertOptionalJsonObject(topology.shell, 'topology.shell', topologyPath);
207
+ assertOptionalJsonArray(topology.verticals, 'topology.verticals', topologyPath);
208
+ assertOptionalJsonArray(ownership.owners, 'ownership.owners', ownershipPath);
209
+ assertOptionalJsonObject(overlay.ports, 'overlay.ports', overlayPath);
210
+ assertOptionalJsonObject(overlay.manifests, 'overlay.manifests', overlayPath);
211
+ assertOptionalJsonObject(overlay.apis, 'overlay.apis', overlayPath);
212
+ overlay.ports ??= {};
213
+ const scope = toPackageScope(String(rootPackage.name ?? node_path.basename(options.workspaceRoot)));
214
+ const packageSource = existingPackageSource(options.workspaceRoot, options.modernVersion, options.packageSource);
215
+ const enableTailwind = options.enableTailwind ?? existingTailwindEnabled(options.workspaceRoot);
216
+ const existingVerticals = verticalsFromTopology(topology, overlay.ports);
217
+ const port = nextAvailablePort(overlay.ports);
218
+ const vertical = createVerticalDescriptor(name, port);
219
+ const updatedVerticals = [
220
+ ...existingVerticals,
221
+ vertical
222
+ ];
223
+ const allApps = [
224
+ shellApp,
225
+ ...updatedVerticals
226
+ ];
227
+ assertCanCreate(options.workspaceRoot, vertical.directory);
228
+ validateWorkspaceAppDescriptors(allApps);
229
+ validateUniqueWorkspaceAppDescriptors(allApps);
230
+ assertUniqueTailwindPrefixes(allApps);
231
+ return {
232
+ name,
233
+ scope,
234
+ topologyPath,
235
+ ownershipPath,
236
+ overlayPath,
237
+ rootPackage,
238
+ topology,
239
+ ownership,
240
+ overlay,
241
+ packageSource,
242
+ enableTailwind,
243
+ vertical,
244
+ updatedVerticals
245
+ };
246
+ }
247
+ function readRequiredJsonObject(filePath) {
248
+ if (!node_fs.existsSync(filePath)) throw new Error(`Missing UltraModern workspace file: ${filePath}`);
249
+ const value = readJsonFile(filePath);
250
+ if (!isRecord(value)) throw new Error(`UltraModern workspace file must contain a JSON object: ${filePath}`);
251
+ return value;
252
+ }
253
+ function assertOptionalJsonObject(value, label, filePath) {
254
+ if (void 0 !== value && !isRecord(value)) throw new Error(`${label} in ${filePath} must be a JSON object`);
255
+ }
256
+ function assertOptionalJsonArray(value, label, filePath) {
257
+ if (void 0 !== value && !Array.isArray(value)) throw new Error(`${label} in ${filePath} must be a JSON array`);
258
+ }
259
+ function validateWorkspaceAppDescriptors(apps) {
260
+ for (const app of apps){
261
+ const appLabel = 'string' == typeof app.id && app.id ? app.id : '<unknown>';
262
+ assertNonEmptyString(app.id, `app id for ${appLabel}`);
263
+ assertNonEmptyString(app.directory, `directory for ${appLabel}`);
264
+ assertSafeOutputPath(app.directory, appLabel);
265
+ assertNonEmptyString(app.packageSuffix, `package suffix for ${appLabel}`);
266
+ assertNonEmptyString(app.displayName, `display name for ${appLabel}`);
267
+ if ('shell' !== app.kind && 'vertical' !== app.kind) throw new Error(`Invalid app kind for ${appLabel}: ${String(app.kind)}`);
268
+ assertNonEmptyString(app.portEnv, `port env for ${appLabel}`);
269
+ if ('number' != typeof app.port || !Number.isFinite(app.port) || app.port <= 0) throw new Error(`Invalid development port for ${appLabel}`);
270
+ assertNonEmptyString(app.mfName, `Module Federation name for ${appLabel}`);
271
+ if (app.effectApi) {
272
+ assertNonEmptyString(app.effectApi.prefix, `Effect API prefix for ${appLabel}`);
273
+ if (!app.effectApi.prefix.startsWith('/')) throw new Error(`Effect API prefix for ${appLabel} must start with "/"`);
274
+ }
275
+ }
276
+ }
277
+ function validateUniqueWorkspaceAppDescriptors(apps) {
278
+ assertUniqueAppField(apps, 'app id', (app)=>app.id);
279
+ assertUniqueAppField(apps, 'package suffix', (app)=>app.packageSuffix);
280
+ assertUniqueAppField(apps, 'output path', (app)=>normalizePath(app.directory));
281
+ assertUniqueAppField(apps, 'Module Federation name', (app)=>app.mfName);
282
+ assertUniqueAppField(apps, 'development port', (app)=>String(app.port));
283
+ assertUniqueAppField(apps, 'Effect API prefix', (app)=>app.effectApi?.prefix);
284
+ assertUniqueAppField(apps, 'manifest environment name', (app)=>'vertical' === app.kind ? createRemoteManifestEnv(app) : void 0);
285
+ }
286
+ function assertUniqueAppField(apps, label, readValue) {
287
+ const seen = new Map();
288
+ for (const app of apps){
289
+ const value = readValue(app);
290
+ if (!value) continue;
291
+ const previousId = seen.get(value);
292
+ if (previousId) throw new Error(`Duplicate ${label} "${value}" for ${previousId} and ${app.id}`);
293
+ seen.set(value, app.id);
294
+ }
295
+ }
296
+ function assertNonEmptyString(value, label) {
297
+ if ('string' != typeof value || 0 === value.length) throw new Error(`Invalid ${label}`);
298
+ }
299
+ function assertSafeOutputPath(relativePath, appId) {
300
+ if (node_path.isAbsolute(relativePath) || relativePath.split(/[\\/]+/u).includes('..')) throw new Error(`Unsafe output path for ${appId}: ${relativePath}`);
301
+ }
302
+ function planUltramodernVertical(options) {
303
+ const preflight = prepareAddUltramodernVertical(options);
304
+ const tempRoot = node_fs.mkdtempSync(node_path.join(node_os.tmpdir(), 'um-vertical-plan-'));
305
+ const copiedWorkspaceRoot = node_path.join(tempRoot, 'workspace');
306
+ try {
307
+ copyWorkspaceForPlan(options.workspaceRoot, copiedWorkspaceRoot);
308
+ const plannedResult = addUltramodernVertical({
309
+ ...options,
310
+ workspaceRoot: copiedWorkspaceRoot,
311
+ overlays: void 0
312
+ });
313
+ return createVerticalPlan(preflight, {
314
+ ...plannedResult,
315
+ workspaceRoot: options.workspaceRoot
316
+ });
317
+ } finally{
318
+ node_fs.rmSync(tempRoot, {
319
+ recursive: true,
320
+ force: true
321
+ });
322
+ }
323
+ }
324
+ function copyWorkspaceForPlan(workspaceRoot, copiedWorkspaceRoot) {
325
+ const skippedDirectories = new Set([
326
+ '.git',
327
+ '.nx',
328
+ '.output',
329
+ 'coverage',
330
+ 'dist',
331
+ 'node_modules'
332
+ ]);
333
+ node_fs.cpSync(workspaceRoot, copiedWorkspaceRoot, {
334
+ recursive: true,
335
+ filter: (sourcePath)=>!skippedDirectories.has(node_path.basename(sourcePath))
336
+ });
337
+ }
338
+ function createVerticalPlan(preflight, result) {
339
+ const { scope, vertical, updatedVerticals } = preflight;
340
+ const manifestUrl = `http://localhost:${vertical.port}/mf-manifest.json`;
341
+ return {
342
+ ...result,
343
+ dryRun: true,
344
+ selectedPort: vertical.port,
345
+ moduleFederationRemote: {
346
+ id: vertical.id,
347
+ name: vertical.mfName,
348
+ manifestUrl
349
+ },
350
+ ...vertical.effectApi ? {
351
+ effectApiPrefix: effectApiPrefix(vertical)
352
+ } : {},
353
+ jsonMutations: createDryRunJsonMutations(preflight, manifestUrl),
354
+ shellDependencyChanges: createShellDependencyChanges(scope, vertical),
355
+ generatedContractChanges: [
356
+ {
357
+ path: GENERATED_CONTRACT_PATH,
358
+ addedAppIds: [
359
+ vertical.id
360
+ ],
361
+ shellVerticalRefs: updatedVerticals.map((vertical)=>vertical.id)
362
+ }
363
+ ]
364
+ };
365
+ }
366
+ function createDryRunJsonMutations(preflight, manifestUrl) {
367
+ const { scope, vertical } = preflight;
368
+ const effectApiMutation = appHasEffectApi(vertical) ? [
369
+ {
370
+ path: DEVELOPMENT_OVERLAY_PATH,
371
+ pointer: `/apis/${vertical.id}`,
372
+ description: `Add local Effect API URL for ${vertical.id}`,
373
+ value: `http://localhost:${vertical.port}${effectApiPrefix(vertical)}`
374
+ }
375
+ ] : [];
376
+ return [
377
+ {
378
+ path: TOPOLOGY_PATH,
379
+ pointer: '/shell/verticalRefs/-',
380
+ description: `Add ${vertical.id} to the shell vertical references`,
381
+ value: vertical.id
382
+ },
383
+ {
384
+ path: TOPOLOGY_PATH,
385
+ pointer: '/shell/moduleFederation/remotes/-',
386
+ description: `Register ${vertical.id} as a Module Federation remote`,
387
+ value: {
388
+ id: vertical.id,
389
+ name: vertical.mfName,
390
+ manifestUrl
391
+ }
392
+ },
393
+ {
394
+ path: TOPOLOGY_PATH,
395
+ pointer: '/verticals/-',
396
+ description: `Add topology entry for ${vertical.id}`,
397
+ value: verticalTopologyEntry(scope, vertical)
398
+ },
399
+ {
400
+ path: OWNERSHIP_PATH,
401
+ pointer: '/owners/-',
402
+ description: `Add ownership entry for ${vertical.id}`,
403
+ value: ownershipEntry(scope, vertical)
404
+ },
405
+ {
406
+ path: DEVELOPMENT_OVERLAY_PATH,
407
+ pointer: `/ports/${vertical.id}`,
408
+ description: `Reserve development port ${vertical.port}`,
409
+ value: vertical.port
410
+ },
411
+ {
412
+ path: DEVELOPMENT_OVERLAY_PATH,
413
+ pointer: `/manifests/${vertical.id}`,
414
+ description: `Add local Module Federation manifest URL for ${vertical.id}`,
415
+ value: manifestUrl
416
+ },
417
+ ...effectApiMutation,
418
+ {
419
+ path: 'package.json',
420
+ pointer: "/scripts",
421
+ description: "Regenerate workspace scripts for the new vertical set"
422
+ },
423
+ {
424
+ path: `${shellApp.directory}/package.json`,
425
+ pointer: '/dependencies',
426
+ description: `Wire shell dependencies for ${vertical.id}`
427
+ },
428
+ {
429
+ path: GENERATED_CONTRACT_PATH,
430
+ pointer: '/apps',
431
+ description: `Regenerate contract with ${vertical.id}`
432
+ }
433
+ ];
434
+ }
435
+ function createShellDependencyChanges(scope, vertical) {
436
+ return [
437
+ {
438
+ path: `${shellApp.directory}/package.json`,
439
+ section: 'zephyr:dependencies',
440
+ packageName: remoteDependencyAlias(vertical),
441
+ version: zephyrRemoteDependency(scope, vertical)
442
+ },
443
+ ...appHasEffectApi(vertical) ? [
444
+ {
445
+ path: `${shellApp.directory}/package.json`,
446
+ section: 'dependencies',
447
+ packageName: packageName(scope, vertical.packageSuffix),
448
+ version: WORKSPACE_PACKAGE_VERSION
449
+ }
450
+ ] : []
451
+ ];
452
+ }
453
+ function addUltramodernVertical(options) {
454
+ const beforeFiles = createFileSnapshot(options.workspaceRoot);
455
+ const { scope, topologyPath, ownershipPath, overlayPath, topology, ownership, overlay, packageSource, enableTailwind, vertical, updatedVerticals } = prepareAddUltramodernVertical(options);
456
+ writeApp(options.workspaceRoot, scope, vertical, packageSource, enableTailwind);
457
+ topology.shell ??= {};
458
+ topology.shell.verticalRefs ??= [];
459
+ topology.shell.verticalRefs.push(vertical.id);
460
+ topology.shell.moduleFederation ??= {};
461
+ topology.shell.moduleFederation.remotes ??= [];
462
+ topology.shell.moduleFederation.remotes.push({
463
+ id: vertical.id,
464
+ name: vertical.mfName,
465
+ manifestUrl: `http://localhost:${vertical.port}/mf-manifest.json`
466
+ });
467
+ topology.verticals ??= [];
468
+ topology.verticals.push(verticalTopologyEntry(scope, vertical));
469
+ ownership.owners ??= [];
470
+ ownership.owners.push(ownershipEntry(scope, vertical));
471
+ overlay.ports[vertical.id] = vertical.port;
472
+ overlay.manifests ??= {};
473
+ overlay.manifests[vertical.id] = `http://localhost:${vertical.port}/mf-manifest.json`;
474
+ overlay.apis ??= {};
475
+ overlay.apis[vertical.id] = `http://localhost:${vertical.port}${effectApiPrefix(vertical)}`;
476
+ writeJsonFile(topologyPath, topology);
477
+ writeJsonFile(ownershipPath, ownership);
478
+ writeJsonFile(overlayPath, overlay);
479
+ writeJsonFile(node_path.join(options.workspaceRoot, GENERATED_CONTRACT_PATH), createGeneratedContract(scope, [
480
+ {
481
+ ...shellApp,
482
+ verticalRefs: updatedVerticals.map((vertical)=>vertical.id)
483
+ },
484
+ ...updatedVerticals
485
+ ], enableTailwind));
486
+ rewriteShellAppFiles(options.workspaceRoot, scope, packageSource, enableTailwind, updatedVerticals);
487
+ writeGeneratedWorkspaceScripts(options.workspaceRoot, scope, enableTailwind, updatedVerticals);
488
+ addShellZephyrDependency(options.workspaceRoot, scope, vertical);
489
+ addShellWorkspaceDependency(options.workspaceRoot, scope, vertical);
490
+ updateRootWorkspaceScripts(options.workspaceRoot, scope, packageSource, updatedVerticals);
491
+ const afterFiles = createFileSnapshot(options.workspaceRoot);
492
+ const { createdPaths, rewrittenPaths } = diffFileSnapshots(beforeFiles, afterFiles);
493
+ const result = createGenerationResult({
494
+ operation: 'vertical',
495
+ workspaceRoot: options.workspaceRoot,
496
+ packageScope: scope,
497
+ packageSource,
498
+ createdApps: [
499
+ vertical
500
+ ],
501
+ createdPaths,
502
+ rewrittenPaths
503
+ });
504
+ runCodeSmithOverlays({
505
+ workspaceRoot: options.workspaceRoot,
506
+ overlays: options.overlays,
507
+ result
508
+ });
509
+ return result;
510
+ }
511
+ export { addShellWorkspaceDependency, addShellZephyrDependency, addUltramodernVertical, assertCanCreate, assertValidVerticalName, existingPackageSource, existingTailwindEnabled, nextAvailablePort, ownershipEntry, planUltramodernVertical, prepareAddUltramodernVertical, rewriteShellAppFiles, updateRootWorkspaceScripts, verticalTopologyEntry, verticalsFromTopology };