@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.
- package/README.md +35 -12
- package/dist/cjs/create-package-root.cjs +7 -9
- package/dist/cjs/index.cjs +74 -44
- package/dist/cjs/locale/en.cjs +6 -7
- package/dist/cjs/locale/zh.cjs +6 -7
- package/dist/cjs/ultramodern-workspace/add-vertical.cjs +337 -0
- package/dist/cjs/ultramodern-workspace/app-files.cjs +223 -0
- package/dist/cjs/ultramodern-workspace/contracts.cjs +836 -0
- package/dist/cjs/ultramodern-workspace/demo-components.cjs +422 -0
- package/dist/cjs/ultramodern-workspace/descriptors.cjs +222 -0
- package/dist/cjs/ultramodern-workspace/effect-api.cjs +952 -0
- package/dist/cjs/ultramodern-workspace/fs-io.cjs +191 -0
- package/dist/cjs/ultramodern-workspace/index.cjs +48 -0
- package/dist/cjs/ultramodern-workspace/locales.cjs +173 -0
- package/dist/cjs/ultramodern-workspace/module-federation.cjs +487 -0
- package/dist/cjs/ultramodern-workspace/naming.cjs +161 -0
- package/dist/cjs/ultramodern-workspace/package-json.cjs +406 -0
- package/dist/cjs/ultramodern-workspace/package-source.cjs +59 -0
- package/dist/cjs/ultramodern-workspace/policy.cjs +248 -0
- package/dist/cjs/ultramodern-workspace/public-surface.cjs +268 -0
- package/dist/cjs/ultramodern-workspace/routes.cjs +375 -0
- package/dist/cjs/ultramodern-workspace/types.cjs +61 -0
- package/dist/cjs/ultramodern-workspace/versions.cjs +153 -0
- package/dist/cjs/ultramodern-workspace/workspace-scripts.cjs +153 -0
- package/dist/cjs/ultramodern-workspace/write-workspace.cjs +175 -0
- package/dist/esm/create-package-root.js +7 -9
- package/dist/esm/index.js +72 -42
- package/dist/esm/locale/en.js +6 -7
- package/dist/esm/locale/zh.js +6 -7
- package/dist/esm/ultramodern-workspace/add-vertical.js +252 -0
- package/dist/esm/ultramodern-workspace/app-files.js +149 -0
- package/dist/esm/ultramodern-workspace/contracts.js +741 -0
- package/dist/esm/ultramodern-workspace/demo-components.js +363 -0
- package/dist/esm/ultramodern-workspace/descriptors.js +133 -0
- package/dist/esm/ultramodern-workspace/effect-api.js +854 -0
- package/dist/esm/ultramodern-workspace/fs-io.js +90 -0
- package/dist/esm/ultramodern-workspace/index.js +3 -0
- package/dist/esm/ultramodern-workspace/locales.js +122 -0
- package/dist/esm/ultramodern-workspace/module-federation.js +415 -0
- package/dist/esm/ultramodern-workspace/naming.js +71 -0
- package/dist/esm/ultramodern-workspace/package-json.js +338 -0
- package/dist/esm/ultramodern-workspace/package-source.js +21 -0
- package/dist/esm/ultramodern-workspace/policy.js +183 -0
- package/dist/esm/ultramodern-workspace/public-surface.js +183 -0
- package/dist/esm/ultramodern-workspace/routes.js +280 -0
- package/dist/esm/ultramodern-workspace/types.js +16 -0
- package/dist/esm/ultramodern-workspace/versions.js +34 -0
- package/dist/esm/ultramodern-workspace/workspace-scripts.js +91 -0
- package/dist/esm/ultramodern-workspace/write-workspace.js +121 -0
- package/dist/esm-node/create-package-root.js +7 -9
- package/dist/esm-node/index.js +72 -42
- package/dist/esm-node/locale/en.js +6 -7
- package/dist/esm-node/locale/zh.js +6 -7
- package/dist/esm-node/ultramodern-workspace/add-vertical.js +253 -0
- package/dist/esm-node/ultramodern-workspace/app-files.js +150 -0
- package/dist/esm-node/ultramodern-workspace/contracts.js +742 -0
- package/dist/esm-node/ultramodern-workspace/demo-components.js +364 -0
- package/dist/esm-node/ultramodern-workspace/descriptors.js +134 -0
- package/dist/esm-node/ultramodern-workspace/effect-api.js +855 -0
- package/dist/esm-node/ultramodern-workspace/fs-io.js +91 -0
- package/dist/esm-node/ultramodern-workspace/index.js +4 -0
- package/dist/esm-node/ultramodern-workspace/locales.js +123 -0
- package/dist/esm-node/ultramodern-workspace/module-federation.js +416 -0
- package/dist/esm-node/ultramodern-workspace/naming.js +72 -0
- package/dist/esm-node/ultramodern-workspace/package-json.js +339 -0
- package/dist/esm-node/ultramodern-workspace/package-source.js +22 -0
- package/dist/esm-node/ultramodern-workspace/policy.js +184 -0
- package/dist/esm-node/ultramodern-workspace/public-surface.js +184 -0
- package/dist/esm-node/ultramodern-workspace/routes.js +281 -0
- package/dist/esm-node/ultramodern-workspace/types.js +17 -0
- package/dist/esm-node/ultramodern-workspace/versions.js +35 -0
- package/dist/esm-node/ultramodern-workspace/workspace-scripts.js +92 -0
- package/dist/esm-node/ultramodern-workspace/write-workspace.js +122 -0
- package/dist/types/locale/en.d.ts +4 -5
- package/dist/types/locale/index.d.ts +8 -10
- package/dist/types/locale/zh.d.ts +4 -5
- package/dist/types/ultramodern-workspace/add-vertical.d.ts +19 -0
- package/dist/types/ultramodern-workspace/app-files.d.ts +14 -0
- package/dist/types/ultramodern-workspace/contracts.d.ts +21 -0
- package/dist/types/ultramodern-workspace/demo-components.d.ts +9 -0
- package/dist/types/ultramodern-workspace/descriptors.d.ts +39 -0
- package/dist/types/ultramodern-workspace/effect-api.d.ts +73 -0
- package/dist/types/ultramodern-workspace/fs-io.d.ts +18 -0
- package/dist/types/ultramodern-workspace/index.d.ts +4 -0
- package/dist/types/ultramodern-workspace/locales.d.ts +183 -0
- package/dist/types/ultramodern-workspace/module-federation.d.ts +16 -0
- package/dist/types/ultramodern-workspace/naming.d.ts +16 -0
- package/dist/types/ultramodern-workspace/package-json.d.ts +12 -0
- package/dist/types/ultramodern-workspace/package-source.d.ts +2 -0
- package/dist/types/ultramodern-workspace/policy.d.ts +60 -0
- package/dist/types/ultramodern-workspace/public-surface.d.ts +37 -0
- package/dist/types/ultramodern-workspace/routes.d.ts +25 -0
- package/dist/types/ultramodern-workspace/types.d.ts +95 -0
- package/dist/types/ultramodern-workspace/versions.d.ts +38 -0
- package/dist/types/ultramodern-workspace/workspace-scripts.d.ts +10 -0
- package/dist/types/ultramodern-workspace/write-workspace.d.ts +4 -0
- package/package.json +4 -3
- package/template-workspace/.github/workflows/ultramodern-workspace-gates.yml.handlebars +1 -4
- package/template-workspace/.mise.toml.handlebars +1 -0
- package/template-workspace/{AGENTS.md → AGENTS.md.handlebars} +12 -7
- package/template-workspace/README.md.handlebars +40 -24
- package/template-workspace/{pnpm-workspace.yaml → pnpm-workspace.yaml.handlebars} +2 -2
- package/template-workspace/scripts/bootstrap-agent-skills.mjs +31 -51
- package/templates/app/shell-frame.tsx +49 -0
- package/templates/app/ultramodern-route-head.tsx.handlebars +142 -0
- package/templates/packages/shared-contracts-index.ts +466 -0
- package/templates/workspace-scripts/assert-mf-types.mjs.handlebars +69 -0
- package/templates/workspace-scripts/check-ultramodern-i18n-boundaries.mjs +9 -0
- package/templates/workspace-scripts/generate-public-surface-assets.mjs +529 -0
- package/templates/workspace-scripts/proof-cloudflare-version.mjs +125 -0
- package/templates/workspace-scripts/ultramodern-cloudflare-proof.mjs +851 -0
- package/templates/workspace-scripts/ultramodern-performance-readiness.config.mjs +7 -0
- package/templates/workspace-scripts/ultramodern-performance-readiness.mjs +223 -0
- package/templates/workspace-scripts/validate-ultramodern-workspace.mjs.handlebars +656 -0
- package/dist/cjs/ultramodern-workspace.cjs +0 -6797
- package/dist/esm/ultramodern-workspace.js +0 -6738
- package/dist/esm-node/ultramodern-workspace.js +0 -6739
- package/dist/types/ultramodern-workspace.d.ts +0 -29
package/dist/esm/locale/zh.js
CHANGED
|
@@ -28,8 +28,12 @@ const ZH_LOCALE = {
|
|
|
28
28
|
optionVersion: ' -v, --version 显示版本信息',
|
|
29
29
|
optionLang: ' -l, --lang 设置语言 (默认 en;zh 需显式选择)',
|
|
30
30
|
optionTailwind: ' --no-tailwind 禁用默认 Tailwind CSS v4 工作区样式',
|
|
31
|
+
optionBff: ' --bff 保留默认的 Effect BFF 脚手架(每个 MicroVertical 自带 Effect BFF)',
|
|
32
|
+
optionBffRuntime: ' --bff-runtime 选择 MicroVertical 脚手架的 BFF 运行时(支持: effect;默认: effect)',
|
|
31
33
|
optionWorkspace: ' --workspace 对 @modern-js 依赖使用 workspace 协议(用于本地 monorepo 联调)',
|
|
32
34
|
optionUltramodernPackageSource: ' --ultramodern-package-source 选择 UltraModern 依赖来源(workspace 或 install;BleedingDev 默认使用 install alias)',
|
|
35
|
+
optionUltramodernPackageVersion: ' --ultramodern-package-version 为 install 依赖来源固定精确的 BleedingDev 框架版本',
|
|
36
|
+
optionUltramodernPackageRegistry: ' --ultramodern-package-registry install 依赖来源使用的 npm registry 地址',
|
|
33
37
|
optionUltramodernPackageScope: ' --ultramodern-package-scope npm alias 安装使用的发布 scope(例如 bleedingdev)',
|
|
34
38
|
optionUltramodernPackageNamePrefix: ' --ultramodern-package-name-prefix npm alias 包名前缀(默认:modern-js-)',
|
|
35
39
|
optionVertical: ' --vertical 修改当前已有的 UltraModern 工作区,并接入名为 <项目名称> 的 MicroVertical',
|
|
@@ -41,16 +45,11 @@ const ZH_LOCALE = {
|
|
|
41
45
|
example4: ' pnpm dlx @bleedingdev/modern-js-create --help',
|
|
42
46
|
example5: ' pnpm dlx @bleedingdev/modern-js-create .',
|
|
43
47
|
example6: ' pnpm dlx @bleedingdev/modern-js-create my-workspace --workspace',
|
|
44
|
-
example7: '',
|
|
45
|
-
example8: '',
|
|
46
|
-
example9: '',
|
|
47
|
-
example10: '',
|
|
48
|
-
example11: '',
|
|
49
|
-
example12: ' pnpm dlx @bleedingdev/modern-js-create catalog --vertical',
|
|
48
|
+
example7: ' pnpm dlx @bleedingdev/modern-js-create catalog --vertical',
|
|
50
49
|
moreInfo: '📚 更多信息: https://modernjs.dev'
|
|
51
50
|
},
|
|
52
51
|
version: {
|
|
53
|
-
message: '
|
|
52
|
+
message: '{name} 版本: {version}'
|
|
54
53
|
}
|
|
55
54
|
};
|
|
56
55
|
export { ZH_LOCALE };
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import node_fs from "node:fs";
|
|
2
|
+
import node_path from "node:path";
|
|
3
|
+
import { WORKSPACE_PACKAGE_VERSION } from "../ultramodern-package-source.js";
|
|
4
|
+
import { createAppEnvDts, createAppRuntimeConfig, createShellFrameComponent } from "./app-files.js";
|
|
5
|
+
import { createGeneratedContract } from "./contracts.js";
|
|
6
|
+
import { createShellPage, createShellRemoteComponents } from "./demo-components.js";
|
|
7
|
+
import { GENERATED_CONTRACT_PATH, appHasEffectApi, appI18nNamespace, createModuleFederationRemoteContracts, createNeutralOwnership, createShellHost, createVerticalDescriptor, effectApiPrefix, remoteDependencyAlias, shellApp, zephyrRemoteDependency } from "./descriptors.js";
|
|
8
|
+
import { createShellEffectClient, effectApiTopologyMetadata } from "./effect-api.js";
|
|
9
|
+
import { readJsonFile, writeFileReplacing, writeJsonFile } from "./fs-io.js";
|
|
10
|
+
import { createAppPublicLocaleMessages } from "./locales.js";
|
|
11
|
+
import { createShellModuleFederationConfig } from "./module-federation.js";
|
|
12
|
+
import { assertUniqueTailwindPrefixes, packageName, toEnvSegment, toKebabCase, toPackageScope, toPascalCase } from "./naming.js";
|
|
13
|
+
import { createAppPackage, createRootPackageJson } from "./package-json.js";
|
|
14
|
+
import { resolvePackageSource } from "./package-source.js";
|
|
15
|
+
import { createCloudflareDeployContract } from "./policy.js";
|
|
16
|
+
import { createPublicWebAppArtifacts, rewriteWorkspaceAssetsForApp } from "./public-surface.js";
|
|
17
|
+
import { isRecord } from "./types.js";
|
|
18
|
+
import { writeGeneratedWorkspaceScripts } from "./workspace-scripts.js";
|
|
19
|
+
import { writeApp } from "./write-workspace.js";
|
|
20
|
+
const FIRST_VERTICAL_PORT = 4101;
|
|
21
|
+
function existingPackageSource(workspaceRoot, modernVersion, packageSource) {
|
|
22
|
+
if (packageSource) return resolvePackageSource({
|
|
23
|
+
targetDir: workspaceRoot,
|
|
24
|
+
packageName: node_path.basename(workspaceRoot),
|
|
25
|
+
modernVersion,
|
|
26
|
+
packageSource
|
|
27
|
+
});
|
|
28
|
+
const metadataPath = node_path.join(workspaceRoot, '.modernjs/ultramodern-package-source.json');
|
|
29
|
+
if (!node_fs.existsSync(metadataPath)) return resolvePackageSource({
|
|
30
|
+
targetDir: workspaceRoot,
|
|
31
|
+
packageName: node_path.basename(workspaceRoot),
|
|
32
|
+
modernVersion
|
|
33
|
+
});
|
|
34
|
+
const metadata = readJsonFile(metadataPath);
|
|
35
|
+
const aliases = metadata.modernPackages?.aliases ?? {};
|
|
36
|
+
const firstAlias = Object.values(aliases).find((value)=>'string' == typeof value);
|
|
37
|
+
const firstPackage = Object.keys(aliases)[0];
|
|
38
|
+
const aliasScope = firstAlias?.match(/^@([^/]+)\//)?.[1];
|
|
39
|
+
const unscopedName = firstPackage?.split('/').at(-1) ?? '';
|
|
40
|
+
const aliasUnscopedName = firstAlias?.split('/').at(-1) ?? '';
|
|
41
|
+
const aliasPackageNamePrefix = aliasUnscopedName && unscopedName && aliasUnscopedName.endsWith(unscopedName) ? aliasUnscopedName.slice(0, -unscopedName.length) : void 0;
|
|
42
|
+
return {
|
|
43
|
+
strategy: 'install' === metadata.strategy ? 'install' : 'workspace',
|
|
44
|
+
modernPackageVersion: 'string' == typeof metadata.modernPackages?.specifier ? metadata.modernPackages.specifier : modernVersion,
|
|
45
|
+
registry: metadata.modernPackages?.registry,
|
|
46
|
+
aliasScope,
|
|
47
|
+
aliasPackageNamePrefix
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
function existingTailwindEnabled(workspaceRoot) {
|
|
51
|
+
const contractPath = node_path.join(workspaceRoot, GENERATED_CONTRACT_PATH);
|
|
52
|
+
if (!node_fs.existsSync(contractPath)) return true;
|
|
53
|
+
const contract = readJsonFile(contractPath);
|
|
54
|
+
const apps = isRecord(contract) && Array.isArray(contract.apps) ? contract.apps : [];
|
|
55
|
+
const shell = apps.find((app)=>isRecord(app) && app.id === shellApp.id);
|
|
56
|
+
return shell?.styling && isRecord(shell.styling) ? false !== shell.styling.tailwind : true;
|
|
57
|
+
}
|
|
58
|
+
function assertValidVerticalName(name) {
|
|
59
|
+
const normalized = toKebabCase(name);
|
|
60
|
+
if (!normalized || normalized !== name) throw new Error(`Invalid Vertical name "${name}". Use lowercase kebab-case.`);
|
|
61
|
+
return normalized;
|
|
62
|
+
}
|
|
63
|
+
function nextAvailablePort(ports) {
|
|
64
|
+
const numericPorts = Object.values(ports).filter((value)=>'number' == typeof value && Number.isFinite(value));
|
|
65
|
+
return Math.max(FIRST_VERTICAL_PORT - 1, ...numericPorts) + 1;
|
|
66
|
+
}
|
|
67
|
+
function assertCanCreate(workspaceRoot, relativePath) {
|
|
68
|
+
if (node_fs.existsSync(node_path.join(workspaceRoot, relativePath))) throw new Error(`Refusing to overwrite existing path: ${relativePath}`);
|
|
69
|
+
}
|
|
70
|
+
function updateRootWorkspaceScripts(workspaceRoot, scope, packageSource, remotes) {
|
|
71
|
+
const packagePath = node_path.join(workspaceRoot, 'package.json');
|
|
72
|
+
const rootPackage = readJsonFile(packagePath);
|
|
73
|
+
const generatedRootPackage = createRootPackageJson(scope, packageSource, remotes);
|
|
74
|
+
rootPackage.scripts = generatedRootPackage.scripts;
|
|
75
|
+
writeJsonFile(packagePath, rootPackage);
|
|
76
|
+
}
|
|
77
|
+
function rewriteShellAppFiles(workspaceRoot, scope, packageSource, enableTailwind, remotes) {
|
|
78
|
+
const shellHost = createShellHost(remotes);
|
|
79
|
+
const publicWeb = createPublicWebAppArtifacts(shellHost);
|
|
80
|
+
writeJsonFile(node_path.join(workspaceRoot, `${shellApp.directory}/package.json`), createAppPackage(scope, shellHost, packageSource, enableTailwind, remotes));
|
|
81
|
+
writeFileReplacing(workspaceRoot, `${shellApp.directory}/src/modern-app-env.d.ts`, createAppEnvDts(shellHost, remotes));
|
|
82
|
+
writeFileReplacing(workspaceRoot, publicWeb.jsonLdHelperFile.path, publicWeb.jsonLdHelperFile.content);
|
|
83
|
+
writeFileReplacing(workspaceRoot, publicWeb.routeMetadataFile.path, publicWeb.routeMetadataFile.content);
|
|
84
|
+
writeFileReplacing(workspaceRoot, publicWeb.routeHeadFile.path, publicWeb.routeHeadFile.content);
|
|
85
|
+
for (const generatedFile of publicWeb.routeMetaFiles)writeFileReplacing(workspaceRoot, generatedFile.path, generatedFile.content);
|
|
86
|
+
rewriteWorkspaceAssetsForApp(workspaceRoot, shellHost);
|
|
87
|
+
writeFileReplacing(workspaceRoot, `${shellApp.directory}/src/modern.runtime.ts`, createAppRuntimeConfig(shellHost, scope, remotes));
|
|
88
|
+
writeJsonFile(node_path.join(workspaceRoot, `${shellApp.directory}/locales/en/translation.json`), createAppPublicLocaleMessages(shellHost, 'en', remotes));
|
|
89
|
+
writeJsonFile(node_path.join(workspaceRoot, `${shellApp.directory}/locales/en/${appI18nNamespace(shellHost)}.json`), createAppPublicLocaleMessages(shellHost, 'en', remotes));
|
|
90
|
+
writeJsonFile(node_path.join(workspaceRoot, `${shellApp.directory}/locales/cs/translation.json`), createAppPublicLocaleMessages(shellHost, 'cs', remotes));
|
|
91
|
+
writeJsonFile(node_path.join(workspaceRoot, `${shellApp.directory}/locales/cs/${appI18nNamespace(shellHost)}.json`), createAppPublicLocaleMessages(shellHost, 'cs', remotes));
|
|
92
|
+
writeFileReplacing(workspaceRoot, `${shellApp.directory}/module-federation.config.ts`, createShellModuleFederationConfig(scope, remotes));
|
|
93
|
+
writeFileReplacing(workspaceRoot, `${shellApp.directory}/src/routes/[lang]/page.tsx`, createShellPage(remotes));
|
|
94
|
+
writeFileReplacing(workspaceRoot, `${shellApp.directory}/src/routes/vertical-components.tsx`, createShellRemoteComponents(scope, remotes));
|
|
95
|
+
writeFileReplacing(workspaceRoot, `${shellApp.directory}/src/routes/shell-frame.tsx`, createShellFrameComponent());
|
|
96
|
+
writeFileReplacing(workspaceRoot, `${shellApp.directory}/src/effect/vertical-clients.ts`, createShellEffectClient(scope, remotes));
|
|
97
|
+
}
|
|
98
|
+
function addShellZephyrDependency(workspaceRoot, scope, remote) {
|
|
99
|
+
const packagePath = node_path.join(workspaceRoot, shellApp.directory, 'package.json');
|
|
100
|
+
const shellPackage = readJsonFile(packagePath);
|
|
101
|
+
shellPackage['zephyr:dependencies'] ??= {};
|
|
102
|
+
shellPackage['zephyr:dependencies'][remoteDependencyAlias(remote)] = zephyrRemoteDependency(scope, remote);
|
|
103
|
+
writeJsonFile(packagePath, shellPackage);
|
|
104
|
+
}
|
|
105
|
+
function addShellWorkspaceDependency(workspaceRoot, scope, remote) {
|
|
106
|
+
if (!appHasEffectApi(remote)) return;
|
|
107
|
+
const packagePath = node_path.join(workspaceRoot, shellApp.directory, 'package.json');
|
|
108
|
+
const shellPackage = readJsonFile(packagePath);
|
|
109
|
+
shellPackage.dependencies ??= {};
|
|
110
|
+
shellPackage.dependencies[packageName(scope, remote.packageSuffix)] = WORKSPACE_PACKAGE_VERSION;
|
|
111
|
+
writeJsonFile(packagePath, shellPackage);
|
|
112
|
+
}
|
|
113
|
+
function verticalTopologyEntry(scope, vertical, remotes = []) {
|
|
114
|
+
return {
|
|
115
|
+
id: vertical.id,
|
|
116
|
+
kind: vertical.kind,
|
|
117
|
+
domain: vertical.domain,
|
|
118
|
+
package: packageName(scope, vertical.packageSuffix),
|
|
119
|
+
path: vertical.directory,
|
|
120
|
+
moduleFederation: {
|
|
121
|
+
role: 'remote',
|
|
122
|
+
name: vertical.mfName,
|
|
123
|
+
manifestUrl: `http://localhost:${vertical.port}/mf-manifest.json`,
|
|
124
|
+
exposes: Object.keys(vertical.exposes ?? {}),
|
|
125
|
+
...vertical.verticalRefs?.length ? {
|
|
126
|
+
verticalRefs: vertical.verticalRefs,
|
|
127
|
+
remotes: createModuleFederationRemoteContracts(vertical, remotes)
|
|
128
|
+
} : {},
|
|
129
|
+
ssr: true,
|
|
130
|
+
fallbackTelemetryEvent: 'modernjs:mv-runtime-parity',
|
|
131
|
+
sharedContractVersion: 'mf-ssr-contract-v1'
|
|
132
|
+
},
|
|
133
|
+
...effectApiTopologyMetadata(vertical) ? {
|
|
134
|
+
api: effectApiTopologyMetadata(vertical)
|
|
135
|
+
} : {},
|
|
136
|
+
cloudflare: createCloudflareDeployContract(scope, vertical),
|
|
137
|
+
ownership: vertical.ownership
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
function ownershipEntry(scope, owner) {
|
|
141
|
+
return {
|
|
142
|
+
id: owner.id,
|
|
143
|
+
package: packageName(scope, owner.packageSuffix),
|
|
144
|
+
path: owner.directory,
|
|
145
|
+
ownership: owner.ownership
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
function verticalsFromTopology(topology, ports) {
|
|
149
|
+
return (topology.verticals ?? []).map((vertical)=>{
|
|
150
|
+
const domain = vertical.domain ?? String(vertical.id);
|
|
151
|
+
const packageSuffix = vertical.package?.split('/').at(-1) ?? domain;
|
|
152
|
+
const effectApi = vertical.api?.effect ? {
|
|
153
|
+
stem: 'string' == typeof vertical.api.effect.basePath ? vertical.api.effect.basePath.split('/').filter(Boolean).at(-1) ?? domain : domain,
|
|
154
|
+
prefix: vertical.api.effect.bff?.prefix ?? `/${domain}-api`,
|
|
155
|
+
consumedBy: Array.isArray(vertical.api.effect.consumedBy) ? vertical.api.effect.consumedBy : [
|
|
156
|
+
shellApp.id,
|
|
157
|
+
vertical.id
|
|
158
|
+
]
|
|
159
|
+
} : void 0;
|
|
160
|
+
return {
|
|
161
|
+
id: vertical.id,
|
|
162
|
+
directory: 'string' == typeof vertical.path ? vertical.path : `verticals/${domain}`,
|
|
163
|
+
packageSuffix,
|
|
164
|
+
displayName: vertical.displayName ?? `${toPascalCase(domain)} Vertical`,
|
|
165
|
+
kind: 'vertical',
|
|
166
|
+
domain,
|
|
167
|
+
portEnv: `VERTICAL_${toEnvSegment(domain)}_PORT`,
|
|
168
|
+
port: 'number' == typeof ports[vertical.id] ? ports[vertical.id] : 0,
|
|
169
|
+
mfName: vertical.moduleFederation?.name ?? `vertical${toPascalCase(domain)}`,
|
|
170
|
+
...Array.isArray(vertical.moduleFederation?.exposes) ? {
|
|
171
|
+
exposes: Object.fromEntries(vertical.moduleFederation.exposes.map((expose)=>[
|
|
172
|
+
expose,
|
|
173
|
+
'./Route' === expose ? './src/federation-entry.tsx' : './Widget' === expose ? `./src/components/${domain}-widget.tsx` : ''
|
|
174
|
+
]))
|
|
175
|
+
} : {},
|
|
176
|
+
...Array.isArray(vertical.moduleFederation?.verticalRefs) ? {
|
|
177
|
+
verticalRefs: vertical.moduleFederation.verticalRefs
|
|
178
|
+
} : Array.isArray(vertical.moduleFederation?.remotes) ? {
|
|
179
|
+
verticalRefs: vertical.moduleFederation.remotes.map((entry)=>entry.id).filter((id)=>'string' == typeof id)
|
|
180
|
+
} : {},
|
|
181
|
+
...effectApi ? {
|
|
182
|
+
effectApi
|
|
183
|
+
} : {},
|
|
184
|
+
ownership: vertical.ownership ?? createNeutralOwnership(vertical.id)
|
|
185
|
+
};
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
function addUltramodernVertical(options) {
|
|
189
|
+
const name = assertValidVerticalName(options.name);
|
|
190
|
+
const rootPackage = readJsonFile(node_path.join(options.workspaceRoot, 'package.json'));
|
|
191
|
+
const scope = toPackageScope(String(rootPackage.name ?? node_path.basename(options.workspaceRoot)));
|
|
192
|
+
const topologyPath = node_path.join(options.workspaceRoot, 'topology/reference-topology.json');
|
|
193
|
+
const ownershipPath = node_path.join(options.workspaceRoot, 'topology/ownership.json');
|
|
194
|
+
const overlayPath = node_path.join(options.workspaceRoot, 'topology/local-overlays/development.json');
|
|
195
|
+
for (const requiredPath of [
|
|
196
|
+
topologyPath,
|
|
197
|
+
ownershipPath,
|
|
198
|
+
overlayPath
|
|
199
|
+
])if (!node_fs.existsSync(requiredPath)) throw new Error(`Missing UltraModern workspace file: ${requiredPath}`);
|
|
200
|
+
const topology = readJsonFile(topologyPath);
|
|
201
|
+
const ownership = readJsonFile(ownershipPath);
|
|
202
|
+
const overlay = readJsonFile(overlayPath);
|
|
203
|
+
overlay.ports ??= {};
|
|
204
|
+
const packageSource = existingPackageSource(options.workspaceRoot, options.modernVersion, options.packageSource);
|
|
205
|
+
const enableTailwind = options.enableTailwind ?? existingTailwindEnabled(options.workspaceRoot);
|
|
206
|
+
const port = nextAvailablePort(overlay.ports);
|
|
207
|
+
const vertical = createVerticalDescriptor(name, port);
|
|
208
|
+
assertCanCreate(options.workspaceRoot, vertical.directory);
|
|
209
|
+
if ((topology.verticals ?? []).some((entry)=>entry.id === vertical.id)) throw new Error(`Topology already contains ${vertical.id}`);
|
|
210
|
+
if (Object.values(overlay.ports).includes(vertical.port)) throw new Error(`Development port ${vertical.port} is already in use`);
|
|
211
|
+
writeApp(options.workspaceRoot, scope, vertical, packageSource, enableTailwind);
|
|
212
|
+
topology.shell ??= {};
|
|
213
|
+
topology.shell.verticalRefs ??= [];
|
|
214
|
+
topology.shell.verticalRefs.push(vertical.id);
|
|
215
|
+
topology.shell.moduleFederation ??= {};
|
|
216
|
+
topology.shell.moduleFederation.remotes ??= [];
|
|
217
|
+
topology.shell.moduleFederation.remotes.push({
|
|
218
|
+
id: vertical.id,
|
|
219
|
+
name: vertical.mfName,
|
|
220
|
+
manifestUrl: `http://localhost:${vertical.port}/mf-manifest.json`
|
|
221
|
+
});
|
|
222
|
+
topology.verticals ??= [];
|
|
223
|
+
topology.verticals.push(verticalTopologyEntry(scope, vertical));
|
|
224
|
+
ownership.owners ??= [];
|
|
225
|
+
ownership.owners.push(ownershipEntry(scope, vertical));
|
|
226
|
+
overlay.ports[vertical.id] = vertical.port;
|
|
227
|
+
overlay.manifests ??= {};
|
|
228
|
+
overlay.manifests[vertical.id] = `http://localhost:${vertical.port}/mf-manifest.json`;
|
|
229
|
+
overlay.apis ??= {};
|
|
230
|
+
overlay.apis[vertical.id] = `http://localhost:${vertical.port}${effectApiPrefix(vertical)}`;
|
|
231
|
+
writeJsonFile(topologyPath, topology);
|
|
232
|
+
writeJsonFile(ownershipPath, ownership);
|
|
233
|
+
writeJsonFile(overlayPath, overlay);
|
|
234
|
+
const updatedVerticals = verticalsFromTopology(topology, overlay.ports);
|
|
235
|
+
assertUniqueTailwindPrefixes([
|
|
236
|
+
shellApp,
|
|
237
|
+
...updatedVerticals
|
|
238
|
+
]);
|
|
239
|
+
writeJsonFile(node_path.join(options.workspaceRoot, GENERATED_CONTRACT_PATH), createGeneratedContract(scope, [
|
|
240
|
+
{
|
|
241
|
+
...shellApp,
|
|
242
|
+
verticalRefs: updatedVerticals.map((vertical)=>vertical.id)
|
|
243
|
+
},
|
|
244
|
+
...updatedVerticals
|
|
245
|
+
], enableTailwind));
|
|
246
|
+
rewriteShellAppFiles(options.workspaceRoot, scope, packageSource, enableTailwind, updatedVerticals);
|
|
247
|
+
writeGeneratedWorkspaceScripts(options.workspaceRoot, scope, enableTailwind, updatedVerticals);
|
|
248
|
+
addShellZephyrDependency(options.workspaceRoot, scope, vertical);
|
|
249
|
+
addShellWorkspaceDependency(options.workspaceRoot, scope, vertical);
|
|
250
|
+
updateRootWorkspaceScripts(options.workspaceRoot, scope, packageSource, updatedVerticals);
|
|
251
|
+
}
|
|
252
|
+
export { addShellWorkspaceDependency, addShellZephyrDependency, addUltramodernVertical, assertCanCreate, assertValidVerticalName, existingPackageSource, existingTailwindEnabled, nextAvailablePort, ownershipEntry, rewriteShellAppFiles, updateRootWorkspaceScripts, verticalTopologyEntry, verticalsFromTopology };
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { appI18nNamespace, remoteDependencyAlias, resolveRemoteRefs, shellApp } from "./descriptors.js";
|
|
2
|
+
import { readFileTemplate, renderFileTemplate } from "./fs-io.js";
|
|
3
|
+
import { packageName, tailwindPrefixForApp } from "./naming.js";
|
|
4
|
+
function createBoundaryDebugMetadata(scope, remotes = []) {
|
|
5
|
+
return {
|
|
6
|
+
appId: shellApp.id,
|
|
7
|
+
boundaries: [
|
|
8
|
+
shellApp,
|
|
9
|
+
...remotes
|
|
10
|
+
].map((app)=>({
|
|
11
|
+
appId: app.id,
|
|
12
|
+
label: app.displayName,
|
|
13
|
+
mfName: app.mfName,
|
|
14
|
+
ownerTeam: app.ownership.team,
|
|
15
|
+
packageName: packageName(scope, app.packageSuffix),
|
|
16
|
+
role: 'shell' === app.kind ? 'host' : 'vertical'
|
|
17
|
+
})),
|
|
18
|
+
schemaVersion: 1
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
function createAppEnvDts(app, remotes = []) {
|
|
22
|
+
const remoteModuleDeclarations = resolveRemoteRefs(app, remotes).flatMap((remote)=>Object.keys(remote.exposes ?? {}).filter((expose)=>'./Route' !== expose).map((expose)=>{
|
|
23
|
+
const moduleName = `${remoteDependencyAlias(remote)}/${expose.replace(/^\.\//u, '')}`;
|
|
24
|
+
return `declare module '${moduleName}' {
|
|
25
|
+
const Component: React.ComponentType<Record<string, never>>;
|
|
26
|
+
export default Component;
|
|
27
|
+
}
|
|
28
|
+
`;
|
|
29
|
+
})).join('\n');
|
|
30
|
+
const reactTypeReference = remoteModuleDeclarations ? "/// <reference types='react' />\n" : '';
|
|
31
|
+
const siteUrlDeclaration = 'declare const ULTRAMODERN_SITE_URL: string;';
|
|
32
|
+
return `${reactTypeReference}/// <reference types='@modern-js/app-tools/types' />
|
|
33
|
+
|
|
34
|
+
${siteUrlDeclaration}
|
|
35
|
+
declare module '*.svg' {
|
|
36
|
+
const url: string;
|
|
37
|
+
export default url;
|
|
38
|
+
}
|
|
39
|
+
declare module '*.css';
|
|
40
|
+
${remoteModuleDeclarations ? `\n${remoteModuleDeclarations}` : ''}`;
|
|
41
|
+
}
|
|
42
|
+
function createAppRuntimeConfig(app, scope, remotes = []) {
|
|
43
|
+
const pluginsConfig = 'shell' === app.kind ? ` plugins: [
|
|
44
|
+
ultramodernBoundaryDebuggerPlugin({
|
|
45
|
+
metadata: ${JSON.stringify(createBoundaryDebugMetadata(scope, remotes), null, 6).split('\n').join('\n ')},
|
|
46
|
+
}),
|
|
47
|
+
],
|
|
48
|
+
` : '';
|
|
49
|
+
return `import { defineRuntimeConfig } from '@modern-js/runtime';
|
|
50
|
+
${'shell' === app.kind ? "import { ultramodernBoundaryDebuggerPlugin } from '@modern-js/runtime/boundary-debugger';\n" : ''}import { createInstance } from 'i18next';
|
|
51
|
+
import csResource from '../locales/cs/${appI18nNamespace(app)}.json';
|
|
52
|
+
import enResource from '../locales/en/${appI18nNamespace(app)}.json';
|
|
53
|
+
import { ultramodernRouteNamespace } from './routes/ultramodern-route-metadata';
|
|
54
|
+
|
|
55
|
+
type LocaleResource = string | { readonly [key: string]: LocaleResource };
|
|
56
|
+
|
|
57
|
+
const flattenLocaleResource = (
|
|
58
|
+
resource: LocaleResource,
|
|
59
|
+
prefix = '',
|
|
60
|
+
): Record<string, string> => {
|
|
61
|
+
if (typeof resource === 'string') {
|
|
62
|
+
return prefix.length > 0 ? { [prefix]: resource } : {};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return Object.fromEntries(
|
|
66
|
+
Object.entries(resource).flatMap(([key, value]) => {
|
|
67
|
+
const nextKey = prefix.length > 0 ? \`\${prefix}.\${key}\` : key;
|
|
68
|
+
return typeof value === 'string'
|
|
69
|
+
? [[nextKey, value]]
|
|
70
|
+
: Object.entries(flattenLocaleResource(value, nextKey));
|
|
71
|
+
}),
|
|
72
|
+
);
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const i18nInstance = createInstance();
|
|
76
|
+
const resources = {
|
|
77
|
+
cs: { [ultramodernRouteNamespace]: flattenLocaleResource(csResource) },
|
|
78
|
+
en: { [ultramodernRouteNamespace]: flattenLocaleResource(enResource) },
|
|
79
|
+
} as const;
|
|
80
|
+
|
|
81
|
+
export default defineRuntimeConfig({
|
|
82
|
+
i18n: {
|
|
83
|
+
i18nInstance,
|
|
84
|
+
initOptions: {
|
|
85
|
+
defaultNS: ultramodernRouteNamespace,
|
|
86
|
+
fallbackLng: 'en',
|
|
87
|
+
interpolation: {
|
|
88
|
+
escapeValue: false,
|
|
89
|
+
},
|
|
90
|
+
ns: [ultramodernRouteNamespace, 'translation'],
|
|
91
|
+
resources,
|
|
92
|
+
supportedLngs: ['en', 'cs'],
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
${pluginsConfig}
|
|
96
|
+
router: {
|
|
97
|
+
framework: 'tanstack',
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
`;
|
|
101
|
+
}
|
|
102
|
+
function createCssTokenImport(scope) {
|
|
103
|
+
return `@import '${packageName(scope, 'shared-design-tokens')}/tokens.css';\n`;
|
|
104
|
+
}
|
|
105
|
+
function createTailwindImport(prefix) {
|
|
106
|
+
return `@import 'tailwindcss' prefix(${prefix}) source(none);\n@source '..';\n`;
|
|
107
|
+
}
|
|
108
|
+
function createShellStyles(enableTailwind, scope) {
|
|
109
|
+
return `${enableTailwind ? createTailwindImport(tailwindPrefixForApp(shellApp)) : ''}${createCssTokenImport(scope)}`;
|
|
110
|
+
}
|
|
111
|
+
function createRemoteStyles(enableTailwind, scope, app) {
|
|
112
|
+
return `${enableTailwind ? createTailwindImport(tailwindPrefixForApp(app)) : ''}${createCssTokenImport(scope)}`;
|
|
113
|
+
}
|
|
114
|
+
function createAppStyles(enableTailwind, scope, app) {
|
|
115
|
+
return 'shell' === app.kind ? createShellStyles(enableTailwind, scope) : createRemoteStyles(enableTailwind, scope, app);
|
|
116
|
+
}
|
|
117
|
+
function createPostcssConfig() {
|
|
118
|
+
return `export default {
|
|
119
|
+
plugins: {
|
|
120
|
+
'@tailwindcss/postcss': {},
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
`;
|
|
124
|
+
}
|
|
125
|
+
function createTailwindConfig() {
|
|
126
|
+
return `import type { Config } from 'tailwindcss';
|
|
127
|
+
|
|
128
|
+
export default {} satisfies Config;
|
|
129
|
+
`;
|
|
130
|
+
}
|
|
131
|
+
function createSharedDesignTokensCss() {
|
|
132
|
+
return `@theme {
|
|
133
|
+
--color-um-accent: #2f8f68;
|
|
134
|
+
--color-um-canvas: #f1eadc;
|
|
135
|
+
--color-um-foreground: #133225;
|
|
136
|
+
--color-um-link: #166b4b;
|
|
137
|
+
--color-um-surface: #f6fbf7;
|
|
138
|
+
}
|
|
139
|
+
`;
|
|
140
|
+
}
|
|
141
|
+
function createRouteHeadModule(app) {
|
|
142
|
+
return renderFileTemplate('app/ultramodern-route-head.tsx', {
|
|
143
|
+
appDisplayNameJson: JSON.stringify(app.displayName)
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
function createShellFrameComponent() {
|
|
147
|
+
return readFileTemplate('app/shell-frame.tsx');
|
|
148
|
+
}
|
|
149
|
+
export { createAppEnvDts, createAppRuntimeConfig, createAppStyles, createBoundaryDebugMetadata, createCssTokenImport, createPostcssConfig, createRemoteStyles, createRouteHeadModule, createSharedDesignTokensCss, createShellFrameComponent, createShellStyles, createTailwindConfig, createTailwindImport };
|