@bleedingdev/modern-js-create 3.4.0-ultramodern.12 → 3.4.0-ultramodern.14
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 +13 -11
- package/dist/cjs/index.cjs +36 -3
- package/dist/cjs/locale/en.cjs +10 -0
- package/dist/cjs/locale/zh.cjs +10 -0
- package/dist/cjs/ultramodern-tooling/commands.cjs +185 -0
- package/dist/cjs/ultramodern-tooling/config.cjs +248 -0
- package/dist/cjs/ultramodern-workspace/add-vertical.cjs +64 -25
- package/dist/cjs/ultramodern-workspace/bridge-config.cjs +327 -0
- package/dist/cjs/ultramodern-workspace/codesmith.cjs +1 -0
- package/dist/cjs/ultramodern-workspace/contracts.cjs +119 -653
- package/dist/cjs/ultramodern-workspace/descriptors.cjs +4 -0
- package/dist/cjs/ultramodern-workspace/fs-io.cjs +117 -0
- package/dist/cjs/ultramodern-workspace/generation-result.cjs +1 -1
- package/dist/cjs/ultramodern-workspace/index.cjs +4 -0
- package/dist/cjs/ultramodern-workspace/mf-validation.cjs +572 -0
- package/dist/cjs/ultramodern-workspace/module-federation.cjs +14 -0
- package/dist/cjs/ultramodern-workspace/package-json.cjs +26 -12
- package/dist/cjs/ultramodern-workspace/public-api.cjs +4 -0
- package/dist/cjs/ultramodern-workspace/workspace-scripts.cjs +74 -44
- package/dist/cjs/ultramodern-workspace/write-workspace.cjs +55 -15
- package/dist/esm/index.js +36 -3
- package/dist/esm/locale/en.js +10 -0
- package/dist/esm/locale/zh.js +10 -0
- package/dist/esm/ultramodern-tooling/commands.js +132 -0
- package/dist/esm/ultramodern-tooling/config.js +196 -0
- package/dist/esm/ultramodern-workspace/add-vertical.js +65 -29
- package/dist/esm/ultramodern-workspace/bridge-config.js +270 -0
- package/dist/esm/ultramodern-workspace/codesmith.js +1 -0
- package/dist/esm/ultramodern-workspace/contracts.js +122 -608
- package/dist/esm/ultramodern-workspace/descriptors.js +2 -1
- package/dist/esm/ultramodern-workspace/fs-io.js +116 -2
- package/dist/esm/ultramodern-workspace/generation-result.js +2 -2
- package/dist/esm/ultramodern-workspace/index.js +1 -0
- package/dist/esm/ultramodern-workspace/mf-validation.js +517 -0
- package/dist/esm/ultramodern-workspace/module-federation.js +14 -0
- package/dist/esm/ultramodern-workspace/package-json.js +26 -12
- package/dist/esm/ultramodern-workspace/public-api.js +1 -0
- package/dist/esm/ultramodern-workspace/workspace-scripts.js +75 -27
- package/dist/esm/ultramodern-workspace/write-workspace.js +56 -19
- package/dist/esm-node/index.js +36 -3
- package/dist/esm-node/locale/en.js +10 -0
- package/dist/esm-node/locale/zh.js +10 -0
- package/dist/esm-node/ultramodern-tooling/commands.js +133 -0
- package/dist/esm-node/ultramodern-tooling/config.js +197 -0
- package/dist/esm-node/ultramodern-workspace/add-vertical.js +65 -29
- package/dist/esm-node/ultramodern-workspace/bridge-config.js +271 -0
- package/dist/esm-node/ultramodern-workspace/codesmith.js +1 -0
- package/dist/esm-node/ultramodern-workspace/contracts.js +122 -608
- package/dist/esm-node/ultramodern-workspace/descriptors.js +2 -1
- package/dist/esm-node/ultramodern-workspace/fs-io.js +116 -2
- package/dist/esm-node/ultramodern-workspace/generation-result.js +2 -2
- package/dist/esm-node/ultramodern-workspace/index.js +1 -0
- package/dist/esm-node/ultramodern-workspace/mf-validation.js +518 -0
- package/dist/esm-node/ultramodern-workspace/module-federation.js +14 -0
- package/dist/esm-node/ultramodern-workspace/package-json.js +26 -12
- package/dist/esm-node/ultramodern-workspace/public-api.js +1 -0
- package/dist/esm-node/ultramodern-workspace/workspace-scripts.js +75 -27
- package/dist/esm-node/ultramodern-workspace/write-workspace.js +56 -19
- package/dist/types/locale/en.d.ts +10 -0
- package/dist/types/locale/index.d.ts +20 -0
- package/dist/types/locale/zh.d.ts +10 -0
- package/dist/types/ultramodern-tooling/commands.d.ts +1 -0
- package/dist/types/ultramodern-tooling/config.d.ts +43 -0
- package/dist/types/ultramodern-workspace/add-vertical.d.ts +5 -2
- package/dist/types/ultramodern-workspace/bridge-config.d.ts +64 -0
- package/dist/types/ultramodern-workspace/codesmith.d.ts +1 -0
- package/dist/types/ultramodern-workspace/contracts.d.ts +2 -17
- package/dist/types/ultramodern-workspace/descriptors.d.ts +1 -0
- package/dist/types/ultramodern-workspace/fs-io.d.ts +1 -0
- package/dist/types/ultramodern-workspace/index.d.ts +2 -0
- package/dist/types/ultramodern-workspace/mf-validation.d.ts +27 -0
- package/dist/types/ultramodern-workspace/package-json.d.ts +4 -3
- package/dist/types/ultramodern-workspace/public-api.d.ts +2 -0
- package/dist/types/ultramodern-workspace/types.d.ts +2 -0
- package/dist/types/ultramodern-workspace/workspace-scripts.d.ts +1 -7
- package/dist/types/ultramodern-workspace/write-workspace.d.ts +4 -2
- package/package.json +5 -3
- package/template-workspace/{.agents → .codex}/skills-lock.json +12 -12
- package/template-workspace/.gitignore.handlebars +3 -0
- package/template-workspace/AGENTS.md.handlebars +11 -5
- package/template-workspace/README.md.handlebars +15 -12
- package/template-workspace/oxfmt.config.ts +1 -0
- package/template-workspace/oxlint.config.ts +1 -0
- package/template-workspace/scripts/bootstrap-agent-skills.mjs +77 -67
- package/templates/workspace-scripts/assert-mf-types.mjs.handlebars +56 -3
- package/templates/workspace-scripts/generate-public-surface-assets.mjs +239 -4
- package/templates/workspace-scripts/proof-cloudflare-version.mjs +282 -4
- package/templates/workspace-scripts/ultramodern-performance-readiness.mjs +172 -1
- package/templates/workspace-scripts/ultramodern-typecheck.mjs +4 -2
- package/templates/workspace-scripts/validate-ultramodern-workspace.mjs.handlebars +783 -26
- /package/template-workspace/{.agents → .codex}/rstackjs-agent-skills-LICENSE +0 -0
- /package/template-workspace/{.agents → .codex}/skills/rsbuild-best-practices/SKILL.md +0 -0
- /package/template-workspace/{.agents → .codex}/skills/rsdoctor-analysis/SKILL.md +0 -0
- /package/template-workspace/{.agents → .codex}/skills/rsdoctor-analysis/references/command-map.md +0 -0
- /package/template-workspace/{.agents → .codex}/skills/rsdoctor-analysis/references/common-analysis-patterns.md +0 -0
- /package/template-workspace/{.agents → .codex}/skills/rsdoctor-analysis/references/install-rsdoctor-common.md +0 -0
- /package/template-workspace/{.agents → .codex}/skills/rsdoctor-analysis/references/install-rsdoctor-rspack.md +0 -0
- /package/template-workspace/{.agents → .codex}/skills/rsdoctor-analysis/references/install-rsdoctor-webpack.md +0 -0
- /package/template-workspace/{.agents → .codex}/skills/rsdoctor-analysis/references/install-rsdoctor.md +0 -0
- /package/template-workspace/{.agents → .codex}/skills/rsdoctor-analysis/references/rsdoctor-data-types.md +0 -0
- /package/template-workspace/{.agents → .codex}/skills/rslib-best-practices/SKILL.md +0 -0
- /package/template-workspace/{.agents → .codex}/skills/rslib-modern-package/SKILL.md +0 -0
- /package/template-workspace/{.agents → .codex}/skills/rspack-best-practices/SKILL.md +0 -0
- /package/template-workspace/{.agents → .codex}/skills/rspack-tracing/SKILL.md +0 -0
- /package/template-workspace/{.agents → .codex}/skills/rspack-tracing/references/bottlenecks.md +0 -0
- /package/template-workspace/{.agents → .codex}/skills/rspack-tracing/references/tracing-guide.md +0 -0
- /package/template-workspace/{.agents → .codex}/skills/rspack-tracing/scripts/analyze_trace.js +0 -0
- /package/template-workspace/{.agents → .codex}/skills/rstest-best-practices/SKILL.md +0 -0
|
@@ -0,0 +1,517 @@
|
|
|
1
|
+
import node_fs from "node:fs";
|
|
2
|
+
import node_path from "node:path";
|
|
3
|
+
const moduleFederationConfigFile = 'module-federation.config.ts';
|
|
4
|
+
const mfTypesArchive = 'dist/@mf-types.zip';
|
|
5
|
+
const generatedMetadataPaths = [
|
|
6
|
+
'.modernjs/ultramodern.json',
|
|
7
|
+
'.modernjs/ultramodern-generated-contract.json'
|
|
8
|
+
];
|
|
9
|
+
const defaultAppRootDirs = [
|
|
10
|
+
'apps',
|
|
11
|
+
'verticals'
|
|
12
|
+
];
|
|
13
|
+
const skippedScanDirs = new Set([
|
|
14
|
+
'.git',
|
|
15
|
+
'.modernjs',
|
|
16
|
+
'.nx',
|
|
17
|
+
'.turbo',
|
|
18
|
+
'coverage',
|
|
19
|
+
'dist',
|
|
20
|
+
'node_modules'
|
|
21
|
+
]);
|
|
22
|
+
function isRecord(value) {
|
|
23
|
+
return null !== value && 'object' == typeof value && !Array.isArray(value);
|
|
24
|
+
}
|
|
25
|
+
function toPosixPath(value) {
|
|
26
|
+
return value.split(node_path.sep).join('/');
|
|
27
|
+
}
|
|
28
|
+
function normalizeRelativePath(value) {
|
|
29
|
+
const normalized = value.replace(/\\/g, '/').replace(/^\.\/+/u, '');
|
|
30
|
+
const trimmed = normalized.replace(/\/+$/u, '');
|
|
31
|
+
return '' === trimmed ? '.' : trimmed;
|
|
32
|
+
}
|
|
33
|
+
function relativePath(root, target) {
|
|
34
|
+
return normalizeRelativePath(toPosixPath(node_path.relative(root, target)));
|
|
35
|
+
}
|
|
36
|
+
function readJsonIfExists(filePath) {
|
|
37
|
+
if (!node_fs.existsSync(filePath)) return;
|
|
38
|
+
return JSON.parse(node_fs.readFileSync(filePath, 'utf-8'));
|
|
39
|
+
}
|
|
40
|
+
function addMetadataAppDir(value, appDirs, hint = '') {
|
|
41
|
+
if (!isRecord(value)) return;
|
|
42
|
+
const pathValue = 'string' == typeof value.path ? value.path : 'string' == typeof value.directory ? value.directory : void 0;
|
|
43
|
+
const hasModuleFederationDeclaration = isRecord(value.moduleFederation) || 'string' == typeof value.moduleFederationName || 'apps' === hint || 'verticals' === hint || 'remotes' === hint || 'moduleFederation' === hint;
|
|
44
|
+
if (pathValue && hasModuleFederationDeclaration && !node_path.isAbsolute(pathValue)) appDirs.add(normalizeRelativePath(pathValue));
|
|
45
|
+
}
|
|
46
|
+
function collectMetadataAppDirs(value, appDirs, hint = '') {
|
|
47
|
+
if (Array.isArray(value)) {
|
|
48
|
+
for (const entry of value)collectMetadataAppDirs(entry, appDirs, hint);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
if (!isRecord(value)) return;
|
|
52
|
+
addMetadataAppDir(value, appDirs, hint);
|
|
53
|
+
for (const [key, entry] of Object.entries(value))collectMetadataAppDirs(entry, appDirs, key);
|
|
54
|
+
}
|
|
55
|
+
function literalRootFromPattern(pattern) {
|
|
56
|
+
const normalized = normalizeRelativePath(pattern);
|
|
57
|
+
if ('.' === normalized || node_path.isAbsolute(normalized) || normalized.startsWith('../')) return;
|
|
58
|
+
const segments = normalized.split('/');
|
|
59
|
+
const literalSegments = [];
|
|
60
|
+
for (const segment of segments){
|
|
61
|
+
if (/[*?[\]{}]/u.test(segment)) break;
|
|
62
|
+
literalSegments.push(segment);
|
|
63
|
+
}
|
|
64
|
+
return literalSegments.length > 0 ? literalSegments.join('/') : void 0;
|
|
65
|
+
}
|
|
66
|
+
function collectBridgeScanRoots(value, roots) {
|
|
67
|
+
if (Array.isArray(value)) {
|
|
68
|
+
for (const entry of value)collectBridgeScanRoots(entry, roots);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
if (!isRecord(value)) return;
|
|
72
|
+
const workspacePackages = value.bridge;
|
|
73
|
+
if (isRecord(workspacePackages)) {
|
|
74
|
+
const entries = workspacePackages.workspacePackages;
|
|
75
|
+
if (Array.isArray(entries)) {
|
|
76
|
+
for (const entry of entries)if (isRecord(entry) && 'string' == typeof entry.pattern) {
|
|
77
|
+
const root = literalRootFromPattern(entry.pattern);
|
|
78
|
+
if (root) roots.add(root);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
for (const entry of Object.values(value))collectBridgeScanRoots(entry, roots);
|
|
83
|
+
}
|
|
84
|
+
function readGeneratedMetadata(workspaceRoot) {
|
|
85
|
+
return generatedMetadataPaths.map((metadataPath)=>readJsonIfExists(node_path.join(workspaceRoot, metadataPath))).filter((metadata)=>void 0 !== metadata);
|
|
86
|
+
}
|
|
87
|
+
function firstSegment(appDir) {
|
|
88
|
+
if ('.' === appDir) return;
|
|
89
|
+
return appDir.split('/')[0];
|
|
90
|
+
}
|
|
91
|
+
function scanForModuleFederationConfigs(workspaceRoot, scanRoot, appDirs) {
|
|
92
|
+
const absoluteRoot = node_path.join(workspaceRoot, scanRoot);
|
|
93
|
+
if (!node_fs.existsSync(absoluteRoot) || !node_fs.statSync(absoluteRoot).isDirectory()) return;
|
|
94
|
+
const visit = (directory)=>{
|
|
95
|
+
for (const entry of node_fs.readdirSync(directory, {
|
|
96
|
+
withFileTypes: true
|
|
97
|
+
})){
|
|
98
|
+
if (entry.isDirectory()) {
|
|
99
|
+
if (!skippedScanDirs.has(entry.name)) visit(node_path.join(directory, entry.name));
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
if (entry.isFile() && entry.name === moduleFederationConfigFile) appDirs.add(relativePath(workspaceRoot, directory));
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
visit(absoluteRoot);
|
|
106
|
+
}
|
|
107
|
+
function discoverModuleFederationConfigs(options) {
|
|
108
|
+
const workspaceRoot = node_path.resolve(options.workspaceRoot);
|
|
109
|
+
const appDirs = new Set();
|
|
110
|
+
if (options.appDirs && options.appDirs.length > 0) for (const appDir of options.appDirs)appDirs.add(normalizeRelativePath(appDir));
|
|
111
|
+
else {
|
|
112
|
+
const metadata = readGeneratedMetadata(workspaceRoot);
|
|
113
|
+
const scanRoots = new Set(defaultAppRootDirs);
|
|
114
|
+
for (const metadataEntry of metadata){
|
|
115
|
+
collectMetadataAppDirs(metadataEntry, appDirs);
|
|
116
|
+
collectBridgeScanRoots(metadataEntry, scanRoots);
|
|
117
|
+
}
|
|
118
|
+
for (const appDir of appDirs){
|
|
119
|
+
const segment = firstSegment(appDir);
|
|
120
|
+
if (segment) scanRoots.add(segment);
|
|
121
|
+
}
|
|
122
|
+
if (node_fs.existsSync(node_path.join(workspaceRoot, moduleFederationConfigFile))) appDirs.add('.');
|
|
123
|
+
for (const scanRoot of scanRoots)scanForModuleFederationConfigs(workspaceRoot, scanRoot, appDirs);
|
|
124
|
+
}
|
|
125
|
+
return Array.from(appDirs).sort().map((appDir)=>({
|
|
126
|
+
appDir,
|
|
127
|
+
configPath: node_path.join(workspaceRoot, appDir, moduleFederationConfigFile)
|
|
128
|
+
}));
|
|
129
|
+
}
|
|
130
|
+
function isEscaped(source, index) {
|
|
131
|
+
let slashCount = 0;
|
|
132
|
+
for(let cursor = index - 1; cursor >= 0 && '\\' === source[cursor]; cursor -= 1)slashCount += 1;
|
|
133
|
+
return slashCount % 2 === 1;
|
|
134
|
+
}
|
|
135
|
+
function skipQuoted(source, start, quote) {
|
|
136
|
+
for(let index = start + 1; index < source.length; index += 1)if (source[index] === quote && !isEscaped(source, index)) return index + 1;
|
|
137
|
+
return source.length;
|
|
138
|
+
}
|
|
139
|
+
function skipLineComment(source, start) {
|
|
140
|
+
const newline = source.indexOf('\n', start + 2);
|
|
141
|
+
return -1 === newline ? source.length : newline + 1;
|
|
142
|
+
}
|
|
143
|
+
function skipBlockComment(source, start) {
|
|
144
|
+
const close = source.indexOf('*/', start + 2);
|
|
145
|
+
return -1 === close ? source.length : close + 2;
|
|
146
|
+
}
|
|
147
|
+
function skipTemplateExpression(source, start) {
|
|
148
|
+
let depth = 1;
|
|
149
|
+
for(let index = start + 1; index < source.length; index += 1){
|
|
150
|
+
const char = source[index];
|
|
151
|
+
const next = source[index + 1];
|
|
152
|
+
if ("'" === char) {
|
|
153
|
+
index = skipQuoted(source, index, "'") - 1;
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
if ('"' === char) {
|
|
157
|
+
index = skipQuoted(source, index, '"') - 1;
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
if ('`' === char) {
|
|
161
|
+
index = skipTemplate(source, index) - 1;
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
if ('/' === char && '/' === next) {
|
|
165
|
+
index = skipLineComment(source, index) - 1;
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
if ('/' === char && '*' === next) {
|
|
169
|
+
index = skipBlockComment(source, index) - 1;
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
if ('{' === char) {
|
|
173
|
+
depth += 1;
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
if ('}' === char) {
|
|
177
|
+
depth -= 1;
|
|
178
|
+
if (0 === depth) return index + 1;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return source.length;
|
|
182
|
+
}
|
|
183
|
+
function skipTemplate(source, start) {
|
|
184
|
+
for(let index = start + 1; index < source.length; index += 1){
|
|
185
|
+
const char = source[index];
|
|
186
|
+
const next = source[index + 1];
|
|
187
|
+
if ('`' === char && !isEscaped(source, index)) return index + 1;
|
|
188
|
+
if ('$' === char && '{' === next && !isEscaped(source, index)) index = skipTemplateExpression(source, index + 1) - 1;
|
|
189
|
+
}
|
|
190
|
+
return source.length;
|
|
191
|
+
}
|
|
192
|
+
function skipSyntax(source, index) {
|
|
193
|
+
const char = source[index];
|
|
194
|
+
const next = source[index + 1];
|
|
195
|
+
if ("'" === char) return skipQuoted(source, index, "'");
|
|
196
|
+
if ('"' === char) return skipQuoted(source, index, '"');
|
|
197
|
+
if ('`' === char) return skipTemplate(source, index);
|
|
198
|
+
if ('/' === char && '/' === next) return skipLineComment(source, index);
|
|
199
|
+
if ('/' === char && '*' === next) return skipBlockComment(source, index);
|
|
200
|
+
return index;
|
|
201
|
+
}
|
|
202
|
+
function findBalanced(source, start, open, close) {
|
|
203
|
+
let depth = 1;
|
|
204
|
+
for(let index = start + 1; index < source.length; index += 1){
|
|
205
|
+
const skipped = skipSyntax(source, index);
|
|
206
|
+
if (skipped !== index) {
|
|
207
|
+
index = skipped - 1;
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
if (source[index] === open) {
|
|
211
|
+
depth += 1;
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
if (source[index] === close) {
|
|
215
|
+
depth -= 1;
|
|
216
|
+
if (0 === depth) return index;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
function skipWhitespaceAndComments(source, start) {
|
|
221
|
+
let index = start;
|
|
222
|
+
while(index < source.length){
|
|
223
|
+
const char = source[index];
|
|
224
|
+
const next = source[index + 1];
|
|
225
|
+
if (/\s/u.test(char ?? '')) {
|
|
226
|
+
index += 1;
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
if ('/' === char && '/' === next) {
|
|
230
|
+
index = skipLineComment(source, index);
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
if ('/' === char && '*' === next) {
|
|
234
|
+
index = skipBlockComment(source, index);
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
break;
|
|
238
|
+
}
|
|
239
|
+
return index;
|
|
240
|
+
}
|
|
241
|
+
function hasIdentifierBoundary(source, start, end) {
|
|
242
|
+
return !/[$\w]/u.test(source[start - 1] ?? '') && !/[$\w]/u.test(source[end] ?? '');
|
|
243
|
+
}
|
|
244
|
+
function findCreateModuleFederationConfigObject(source) {
|
|
245
|
+
const callee = 'createModuleFederationConfig';
|
|
246
|
+
let offset = 0;
|
|
247
|
+
while(offset < source.length){
|
|
248
|
+
const index = source.indexOf(callee, offset);
|
|
249
|
+
if (-1 === index) break;
|
|
250
|
+
offset = index + callee.length;
|
|
251
|
+
if (!hasIdentifierBoundary(source, index, offset)) continue;
|
|
252
|
+
const parenIndex = skipWhitespaceAndComments(source, offset);
|
|
253
|
+
if ('(' !== source[parenIndex]) continue;
|
|
254
|
+
const argumentIndex = skipWhitespaceAndComments(source, parenIndex + 1);
|
|
255
|
+
if ('{' !== source[argumentIndex]) throw new Error('Module Federation config must pass a static object literal to createModuleFederationConfig.');
|
|
256
|
+
const closeIndex = findBalanced(source, argumentIndex, '{', '}');
|
|
257
|
+
if (void 0 === closeIndex) throw new Error('Module Federation config object literal is not closed.');
|
|
258
|
+
return source.slice(argumentIndex, closeIndex + 1);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
function findExportDefaultObject(source) {
|
|
262
|
+
const exportDefault = 'export default';
|
|
263
|
+
const index = source.indexOf(exportDefault);
|
|
264
|
+
if (-1 === index) return;
|
|
265
|
+
const objectStart = skipWhitespaceAndComments(source, index + exportDefault.length);
|
|
266
|
+
if ('{' !== source[objectStart]) return;
|
|
267
|
+
const objectEnd = findBalanced(source, objectStart, '{', '}');
|
|
268
|
+
if (void 0 === objectEnd) throw new Error('Module Federation export default object is not closed.');
|
|
269
|
+
return source.slice(objectStart, objectEnd + 1);
|
|
270
|
+
}
|
|
271
|
+
function getOuterBlock(source, open, close) {
|
|
272
|
+
const trimmed = source.trim();
|
|
273
|
+
if (trimmed[0] !== open) return;
|
|
274
|
+
const end = findBalanced(trimmed, 0, open, close);
|
|
275
|
+
if (void 0 === end) return;
|
|
276
|
+
const suffix = trimmed.slice(end + 1).trim();
|
|
277
|
+
if ('' !== suffix && !suffix.startsWith('as ') && !suffix.startsWith('satisfies ')) return;
|
|
278
|
+
return {
|
|
279
|
+
inner: trimmed.slice(1, end),
|
|
280
|
+
suffix
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
function splitTopLevelEntries(source) {
|
|
284
|
+
const entries = [];
|
|
285
|
+
let start = 0;
|
|
286
|
+
let braceDepth = 0;
|
|
287
|
+
let bracketDepth = 0;
|
|
288
|
+
let parenDepth = 0;
|
|
289
|
+
for(let index = 0; index < source.length; index += 1){
|
|
290
|
+
const skipped = skipSyntax(source, index);
|
|
291
|
+
if (skipped !== index) {
|
|
292
|
+
index = skipped - 1;
|
|
293
|
+
continue;
|
|
294
|
+
}
|
|
295
|
+
const char = source[index];
|
|
296
|
+
if ('{' === char) braceDepth += 1;
|
|
297
|
+
else if ('}' === char) braceDepth -= 1;
|
|
298
|
+
else if ('[' === char) bracketDepth += 1;
|
|
299
|
+
else if (']' === char) bracketDepth -= 1;
|
|
300
|
+
else if ('(' === char) parenDepth += 1;
|
|
301
|
+
else if (')' === char) parenDepth -= 1;
|
|
302
|
+
else if (',' === char && 0 === braceDepth && 0 === bracketDepth && 0 === parenDepth) {
|
|
303
|
+
const entry = source.slice(start, index).trim();
|
|
304
|
+
if (entry) entries.push(entry);
|
|
305
|
+
start = index + 1;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
const finalEntry = source.slice(start).trim();
|
|
309
|
+
if (finalEntry) entries.push(finalEntry);
|
|
310
|
+
return entries;
|
|
311
|
+
}
|
|
312
|
+
function findTopLevelColon(source) {
|
|
313
|
+
let braceDepth = 0;
|
|
314
|
+
let bracketDepth = 0;
|
|
315
|
+
let parenDepth = 0;
|
|
316
|
+
for(let index = 0; index < source.length; index += 1){
|
|
317
|
+
const skipped = skipSyntax(source, index);
|
|
318
|
+
if (skipped !== index) {
|
|
319
|
+
index = skipped - 1;
|
|
320
|
+
continue;
|
|
321
|
+
}
|
|
322
|
+
const char = source[index];
|
|
323
|
+
if ('{' === char) braceDepth += 1;
|
|
324
|
+
else if ('}' === char) braceDepth -= 1;
|
|
325
|
+
else if ('[' === char) bracketDepth += 1;
|
|
326
|
+
else if (']' === char) bracketDepth -= 1;
|
|
327
|
+
else if ('(' === char) parenDepth += 1;
|
|
328
|
+
else if (')' === char) parenDepth -= 1;
|
|
329
|
+
else if (':' === char && 0 === braceDepth && 0 === bracketDepth && 0 === parenDepth) return index;
|
|
330
|
+
}
|
|
331
|
+
return -1;
|
|
332
|
+
}
|
|
333
|
+
function stripConstAssertion(source) {
|
|
334
|
+
return source.trim().replace(/\s+as\s+const\s*$/u, '').trim();
|
|
335
|
+
}
|
|
336
|
+
function unescapeSingleQuotedString(value) {
|
|
337
|
+
return value.replace(/\\(['"\\nrtbfv0])/gu, (_, sequence)=>{
|
|
338
|
+
switch(sequence){
|
|
339
|
+
case 'n':
|
|
340
|
+
return '\n';
|
|
341
|
+
case 'r':
|
|
342
|
+
return '\r';
|
|
343
|
+
case 't':
|
|
344
|
+
return '\t';
|
|
345
|
+
case 'b':
|
|
346
|
+
return '\b';
|
|
347
|
+
case 'f':
|
|
348
|
+
return '\f';
|
|
349
|
+
case 'v':
|
|
350
|
+
return '\v';
|
|
351
|
+
case '0':
|
|
352
|
+
return '\0';
|
|
353
|
+
default:
|
|
354
|
+
return sequence;
|
|
355
|
+
}
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
function parseLiteralString(source) {
|
|
359
|
+
if (void 0 === source) return;
|
|
360
|
+
const trimmed = stripConstAssertion(source);
|
|
361
|
+
const quote = trimmed[0];
|
|
362
|
+
if ("'" !== quote && '"' !== quote && '`' !== quote) return;
|
|
363
|
+
if ('`' === quote) {
|
|
364
|
+
if (trimmed.includes('${')) return;
|
|
365
|
+
const close = trimmed.lastIndexOf('`');
|
|
366
|
+
return close > 0 ? trimmed.slice(1, close).replaceAll('\\`', '`') : void 0;
|
|
367
|
+
}
|
|
368
|
+
const close = trimmed.lastIndexOf(quote);
|
|
369
|
+
if (close <= 0 || '' !== trimmed.slice(close + 1).trim()) return;
|
|
370
|
+
const content = trimmed.slice(1, close);
|
|
371
|
+
if ('"' === quote) try {
|
|
372
|
+
return JSON.parse(trimmed);
|
|
373
|
+
} catch {
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
return unescapeSingleQuotedString(content);
|
|
377
|
+
}
|
|
378
|
+
function parseObjectKey(source) {
|
|
379
|
+
const trimmed = source.trim();
|
|
380
|
+
const literal = parseLiteralString(trimmed);
|
|
381
|
+
if (void 0 !== literal) return literal;
|
|
382
|
+
return /^[$A-Z_a-z][$\w]*$/u.test(trimmed) ? trimmed : void 0;
|
|
383
|
+
}
|
|
384
|
+
function parseObjectLiteral(source) {
|
|
385
|
+
if (void 0 === source) return;
|
|
386
|
+
const block = getOuterBlock(source, '{', '}');
|
|
387
|
+
if (!block) return;
|
|
388
|
+
const properties = new Map();
|
|
389
|
+
let hasSpread = false;
|
|
390
|
+
for (const entry of splitTopLevelEntries(block.inner)){
|
|
391
|
+
if (entry.startsWith('...')) {
|
|
392
|
+
hasSpread = true;
|
|
393
|
+
continue;
|
|
394
|
+
}
|
|
395
|
+
const colon = findTopLevelColon(entry);
|
|
396
|
+
if (-1 === colon) {
|
|
397
|
+
hasSpread = true;
|
|
398
|
+
continue;
|
|
399
|
+
}
|
|
400
|
+
const key = parseObjectKey(entry.slice(0, colon));
|
|
401
|
+
if (void 0 === key) {
|
|
402
|
+
hasSpread = true;
|
|
403
|
+
continue;
|
|
404
|
+
}
|
|
405
|
+
properties.set(key, entry.slice(colon + 1).trim());
|
|
406
|
+
}
|
|
407
|
+
return {
|
|
408
|
+
hasSpread,
|
|
409
|
+
properties
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
function parseArrayLiteral(source) {
|
|
413
|
+
if (void 0 === source) return;
|
|
414
|
+
const block = getOuterBlock(source, '[', ']');
|
|
415
|
+
if (!block) return;
|
|
416
|
+
const entries = splitTopLevelEntries(block.inner);
|
|
417
|
+
const exposes = [];
|
|
418
|
+
for (const entry of entries){
|
|
419
|
+
const expose = parseLiteralString(entry);
|
|
420
|
+
if (void 0 === expose) return;
|
|
421
|
+
exposes.push(expose);
|
|
422
|
+
}
|
|
423
|
+
return exposes;
|
|
424
|
+
}
|
|
425
|
+
function extractExposes(configPath, value) {
|
|
426
|
+
if (void 0 === value) return [];
|
|
427
|
+
const object = parseObjectLiteral(value);
|
|
428
|
+
if (object) {
|
|
429
|
+
if (object.hasSpread) throw new Error(`Cannot statically extract Module Federation exposes from ${configPath}; use a literal exposes object without spreads.`);
|
|
430
|
+
return Array.from(object.properties.keys()).sort();
|
|
431
|
+
}
|
|
432
|
+
const array = parseArrayLiteral(value);
|
|
433
|
+
if (array) return array.sort();
|
|
434
|
+
throw new Error(`Cannot statically extract Module Federation exposes from ${configPath}; use a literal exposes object or string array.`);
|
|
435
|
+
}
|
|
436
|
+
function extractDtsSettings(configPath, value) {
|
|
437
|
+
if (void 0 === value) return {};
|
|
438
|
+
const dts = parseObjectLiteral(value);
|
|
439
|
+
if (!dts || dts.hasSpread) throw new Error(`Cannot statically extract Module Federation DTS settings from ${configPath}; use a literal dts object.`);
|
|
440
|
+
const generateTypes = parseObjectLiteral(dts.properties.get('generateTypes'));
|
|
441
|
+
if (generateTypes?.hasSpread) throw new Error(`Cannot statically extract Module Federation generateTypes settings from ${configPath}; use a literal generateTypes object.`);
|
|
442
|
+
return {
|
|
443
|
+
compilerInstance: parseLiteralString(generateTypes?.properties.get('compilerInstance')),
|
|
444
|
+
tsConfigPath: parseLiteralString(dts.properties.get('tsConfigPath'))
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
function hasHostOnlyNoExposesDeclaration(source) {
|
|
448
|
+
return /@?ultramodern-mf\s*:?\s*(?:host-only|no-exposes)\b/iu.test(source);
|
|
449
|
+
}
|
|
450
|
+
function inspectModuleFederationConfigSource(source, appDir, configPath) {
|
|
451
|
+
const configObject = findCreateModuleFederationConfigObject(source) ?? findExportDefaultObject(source);
|
|
452
|
+
if (!configObject) throw new Error(`Cannot statically inspect Module Federation config ${configPath}; export or pass a literal config object.`);
|
|
453
|
+
const properties = parseObjectLiteral(configObject);
|
|
454
|
+
if (!properties) throw new Error(`Cannot statically inspect Module Federation config ${configPath}; expected a literal config object.`);
|
|
455
|
+
if (properties.hasSpread) throw new Error(`Cannot statically inspect Module Federation config ${configPath}; top-level config spreads are not supported.`);
|
|
456
|
+
const exposes = extractExposes(configPath, properties.properties.get('exposes'));
|
|
457
|
+
const hostOnlyNoExposes = hasHostOnlyNoExposesDeclaration(source);
|
|
458
|
+
if (hostOnlyNoExposes && exposes.length > 0) throw new Error(`Module Federation host-only/no-exposes declaration conflicts with actual exposes in ${configPath}.`);
|
|
459
|
+
return {
|
|
460
|
+
appDir,
|
|
461
|
+
configPath,
|
|
462
|
+
dts: extractDtsSettings(configPath, properties.properties.get('dts')),
|
|
463
|
+
exposes,
|
|
464
|
+
hostOnlyNoExposes
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
function assertExposedAppDtsSettings(app) {
|
|
468
|
+
if ('tsgo' !== app.dts.compilerInstance) throw new Error(`Module Federation DTS compilerInstance must be "tsgo" for ${app.appDir}.`);
|
|
469
|
+
if ('./tsconfig.mf-types.json' !== app.dts.tsConfigPath) throw new Error(`Module Federation DTS tsConfigPath must be "./tsconfig.mf-types.json" for ${app.appDir}.`);
|
|
470
|
+
}
|
|
471
|
+
function assertTypesArchive(workspaceRoot, appDir) {
|
|
472
|
+
const typesArchivePath = node_path.join(workspaceRoot, appDir, mfTypesArchive);
|
|
473
|
+
if (!node_fs.existsSync(typesArchivePath)) throw new Error(`Missing Module Federation DTS archive: ${relativePath(workspaceRoot, typesArchivePath)}`);
|
|
474
|
+
if (0 === node_fs.statSync(typesArchivePath).size) throw new Error(`Empty Module Federation DTS archive: ${relativePath(workspaceRoot, typesArchivePath)}`);
|
|
475
|
+
}
|
|
476
|
+
function validateModuleFederationTypes(options) {
|
|
477
|
+
const workspaceRoot = node_path.resolve(options.workspaceRoot);
|
|
478
|
+
const discoveredConfigs = discoverModuleFederationConfigs({
|
|
479
|
+
...options,
|
|
480
|
+
workspaceRoot
|
|
481
|
+
});
|
|
482
|
+
const apps = [];
|
|
483
|
+
const missingConfigPaths = [];
|
|
484
|
+
for (const discoveredConfig of discoveredConfigs){
|
|
485
|
+
if (!node_fs.existsSync(discoveredConfig.configPath)) {
|
|
486
|
+
missingConfigPaths.push(relativePath(workspaceRoot, discoveredConfig.configPath));
|
|
487
|
+
continue;
|
|
488
|
+
}
|
|
489
|
+
const configPath = relativePath(workspaceRoot, discoveredConfig.configPath);
|
|
490
|
+
apps.push(inspectModuleFederationConfigSource(node_fs.readFileSync(discoveredConfig.configPath, 'utf-8'), discoveredConfig.appDir, configPath));
|
|
491
|
+
}
|
|
492
|
+
if (missingConfigPaths.length > 0) throw new Error(`Missing Module Federation config: ${missingConfigPaths.join(', ')}`);
|
|
493
|
+
const noExposeApps = apps.filter((app)=>0 === app.exposes.length && !app.hostOnlyNoExposes);
|
|
494
|
+
if (noExposeApps.length > 0) {
|
|
495
|
+
const suffix = 0 === apps.filter((app)=>app.exposes.length > 0).length ? ' Validation would otherwise validate zero exposed apps.' : '';
|
|
496
|
+
throw new Error(`Module Federation configs declare no exposes without an explicit host-only/no-exposes declaration: ${noExposeApps.map((app)=>app.appDir).join(', ')}.${suffix}`);
|
|
497
|
+
}
|
|
498
|
+
let exposedAppCount = 0;
|
|
499
|
+
let hostOnlyAppCount = 0;
|
|
500
|
+
for (const app of apps){
|
|
501
|
+
if (app.hostOnlyNoExposes) {
|
|
502
|
+
hostOnlyAppCount += 1;
|
|
503
|
+
continue;
|
|
504
|
+
}
|
|
505
|
+
assertExposedAppDtsSettings(app);
|
|
506
|
+
assertTypesArchive(workspaceRoot, app.appDir);
|
|
507
|
+
exposedAppCount += 1;
|
|
508
|
+
}
|
|
509
|
+
if (apps.length > 0 && 0 === exposedAppCount && hostOnlyAppCount !== apps.length) throw new Error('Module Federation validation inspected configs but validated zero exposed apps. Declare host-only/no-exposes intent only for host apps without exposes.');
|
|
510
|
+
return {
|
|
511
|
+
apps,
|
|
512
|
+
configCount: apps.length,
|
|
513
|
+
exposedAppCount,
|
|
514
|
+
hostOnlyAppCount
|
|
515
|
+
};
|
|
516
|
+
}
|
|
517
|
+
export { discoverModuleFederationConfigs, inspectModuleFederationConfigSource, validateModuleFederationTypes };
|
|
@@ -64,6 +64,8 @@ const configuredSiteUrl = envValue('MODERN_PUBLIC_SITE_URL');
|
|
|
64
64
|
const configuredCloudflareUrl = envValue('${createCloudflarePublicUrlEnv(app)}');
|
|
65
65
|
const configuredUltramodernAssetPrefix = envValue('ULTRAMODERN_ASSET_PREFIX');
|
|
66
66
|
const configuredModernAssetPrefix = envValue('MODERN_ASSET_PREFIX');
|
|
67
|
+
const moduleFederationDevServerOrigin =
|
|
68
|
+
envValue('ULTRAMODERN_MF_DEV_ORIGIN') || 'http://localhost:${shellApp.port}';
|
|
67
69
|
const cloudflareWorkersDevSubdomain = envValue(
|
|
68
70
|
'ULTRAMODERN_CLOUDFLARE_WORKERS_DEV_SUBDOMAIN',
|
|
69
71
|
);
|
|
@@ -187,6 +189,9 @@ ${bffPluginEntry} moduleFederationPlugin(),
|
|
|
187
189
|
},
|
|
188
190
|
mainEntryName: 'index',
|
|
189
191
|
},
|
|
192
|
+
splitChunks: {
|
|
193
|
+
chunks: 'async',
|
|
194
|
+
},
|
|
190
195
|
tools: {
|
|
191
196
|
autoprefixer: {
|
|
192
197
|
overrideBrowserslist: ['defaults'],
|
|
@@ -196,6 +201,14 @@ ${bffPluginEntry} moduleFederationPlugin(),
|
|
|
196
201
|
.uniqueName('${createRspackUniqueName(app)}')
|
|
197
202
|
.chunkLoadingGlobal('${createRspackChunkLoadingGlobal(app)}');
|
|
198
203
|
},
|
|
204
|
+
devServer: {
|
|
205
|
+
headers: {
|
|
206
|
+
'Access-Control-Allow-Headers':
|
|
207
|
+
'Accept, Authorization, Content-Type, X-Requested-With',
|
|
208
|
+
'Access-Control-Allow-Methods': 'GET, HEAD, OPTIONS',
|
|
209
|
+
'Access-Control-Allow-Origin': moduleFederationDevServerOrigin,
|
|
210
|
+
},
|
|
211
|
+
},
|
|
199
212
|
},
|
|
200
213
|
},
|
|
201
214
|
{
|
|
@@ -319,6 +332,7 @@ function createShellModuleFederationConfig(scope, remotes = []) {
|
|
|
319
332
|
verticalRefs: remotes.map((remote)=>remote.id)
|
|
320
333
|
};
|
|
321
334
|
return `// @effect-diagnostics nodeBuiltinImport:off processEnv:off
|
|
335
|
+
// ultramodern-mf: host-only
|
|
322
336
|
import { createRequire } from 'node:module';
|
|
323
337
|
import { createModuleFederationConfig } from '@module-federation/modern-js-v3';
|
|
324
338
|
import { dependencies } from './package.json';
|
|
@@ -80,7 +80,7 @@ const effectDiagnostics = [
|
|
|
80
80
|
'unnecessaryPipe',
|
|
81
81
|
'unnecessaryPipeChain'
|
|
82
82
|
];
|
|
83
|
-
function appDependencies(scope, packageSource, app, remotes = []) {
|
|
83
|
+
function appDependencies(scope, packageSource, app, remotes = [], bridge) {
|
|
84
84
|
const dependencies = {
|
|
85
85
|
'@modern-js/plugin-tanstack': modernPackageSpecifier('@modern-js/plugin-tanstack', packageSource),
|
|
86
86
|
'@modern-js/plugin-i18n': modernPackageSpecifier('@modern-js/plugin-i18n', packageSource),
|
|
@@ -97,6 +97,7 @@ function appDependencies(scope, packageSource, app, remotes = []) {
|
|
|
97
97
|
'react-dom': REACT_DOM_VERSION,
|
|
98
98
|
'react-router': REACT_ROUTER_VERSION
|
|
99
99
|
};
|
|
100
|
+
for (const dependency of bridge?.dependencies ?? [])dependencies[dependency] = WORKSPACE_PACKAGE_VERSION;
|
|
100
101
|
if ('shell' === app.kind) {
|
|
101
102
|
dependencies['@modern-js/plugin-bff'] = modernPackageSpecifier('@modern-js/plugin-bff', packageSource);
|
|
102
103
|
for (const remote of verticalEffectApps(remotes))dependencies[packageName(scope, remote.packageSuffix)] = WORKSPACE_PACKAGE_VERSION;
|
|
@@ -123,12 +124,28 @@ function appDevDependencies(packageSource, enableTailwind) {
|
|
|
123
124
|
wrangler: WRANGLER_VERSION
|
|
124
125
|
};
|
|
125
126
|
}
|
|
126
|
-
function createRootPackageJson(scope, packageSource, remotes = []) {
|
|
127
|
+
function createRootPackageJson(scope, packageSource, remotes = [], bridge) {
|
|
127
128
|
const shellFilter = `--filter ${packageName(scope, shellApp.packageSuffix)}`;
|
|
128
129
|
const remoteFilters = remotes.map((remote)=>`--filter ${packageName(scope, remote.packageSuffix)}`);
|
|
129
130
|
const remoteBuildPrefix = remotes.length > 0 ? 'ULTRAMODERN_ZEPHYR=false pnpm -r --filter "./verticals/*" run build && ' : '';
|
|
130
131
|
const remoteCloudflareBuildPrefix = remotes.length > 0 ? 'pnpm -r --filter "./verticals/*" run cloudflare:build && ' : '';
|
|
131
132
|
const remoteCloudflareDeployPrefix = remotes.length > 0 ? 'pnpm -r --filter "./verticals/*" run cloudflare:deploy && ' : '';
|
|
133
|
+
const generatedPackageTypecheck = 'pnpm -r --filter "./apps/*" --filter "./verticals/*" --filter "./packages/*" run typecheck';
|
|
134
|
+
const typecheck = bridge ? generatedPackageTypecheck : "node ./scripts/ultramodern-typecheck.mjs --build tsconfig.json";
|
|
135
|
+
const bridgeScripts = bridge ? {
|
|
136
|
+
...Object.fromEntries(bridge.gates.map((gate)=>[
|
|
137
|
+
`bridge:${gate.name}`,
|
|
138
|
+
`${gate.cwd ? `cd ${gate.cwd} && ` : ''}${gate.command}`
|
|
139
|
+
])),
|
|
140
|
+
'bridge:check': bridge.gates.map((gate)=>`pnpm run bridge:${gate.name}`).join(' && ')
|
|
141
|
+
} : {};
|
|
142
|
+
const bridgeCheck = bridge ? ' && pnpm bridge:check' : '';
|
|
143
|
+
const workspacePackages = [
|
|
144
|
+
'apps/*',
|
|
145
|
+
'verticals/*',
|
|
146
|
+
'packages/*',
|
|
147
|
+
...bridge?.workspacePackages.map((entry)=>entry.pattern) ?? []
|
|
148
|
+
];
|
|
132
149
|
return {
|
|
133
150
|
private: true,
|
|
134
151
|
name: scope,
|
|
@@ -150,7 +167,7 @@ function createRootPackageJson(scope, packageSource, remotes = []) {
|
|
|
150
167
|
'format:check': "oxfmt --check . '!repos/**'",
|
|
151
168
|
lint: 'oxlint apps verticals packages',
|
|
152
169
|
'lint:fix': 'oxlint apps verticals packages --fix',
|
|
153
|
-
typecheck
|
|
170
|
+
typecheck,
|
|
154
171
|
'cloudflare:build': `${remoteCloudflareBuildPrefix}pnpm --filter "./apps/shell-super-app" run cloudflare:build && pnpm mf:types`,
|
|
155
172
|
'cloudflare:deploy': `${remoteCloudflareDeployPrefix}pnpm --filter "./apps/shell-super-app" run cloudflare:deploy`,
|
|
156
173
|
'cloudflare:proof': "node ./scripts/proof-cloudflare-version.mjs --out .codex/reports/cloudflare-version-proof/public-url-proof.json",
|
|
@@ -162,18 +179,15 @@ function createRootPackageJson(scope, packageSource, remotes = []) {
|
|
|
162
179
|
'performance:readiness': "node ./scripts/ultramodern-performance-readiness.mjs",
|
|
163
180
|
'contract:check': "node ./scripts/validate-ultramodern-workspace.mjs",
|
|
164
181
|
'i18n:boundaries': "node ./scripts/check-ultramodern-i18n-boundaries.mjs",
|
|
182
|
+
...bridgeScripts,
|
|
165
183
|
postinstall: "oxfmt . '!repos/**' && node ./scripts/bootstrap-agent-skills.mjs --postinstall",
|
|
166
|
-
check:
|
|
184
|
+
check: `pnpm format:check && pnpm lint && pnpm typecheck && pnpm skills:check && pnpm i18n:boundaries && pnpm contract:check && pnpm performance:readiness${bridgeCheck}`
|
|
167
185
|
},
|
|
168
186
|
engines: {
|
|
169
187
|
node: '>=26',
|
|
170
188
|
pnpm: '>=11'
|
|
171
189
|
},
|
|
172
|
-
workspaces:
|
|
173
|
-
'apps/*',
|
|
174
|
-
'verticals/*',
|
|
175
|
-
'packages/*'
|
|
176
|
-
],
|
|
190
|
+
workspaces: workspacePackages,
|
|
177
191
|
modernjs: {
|
|
178
192
|
preset: 'presetUltramodern',
|
|
179
193
|
workspace: 'ultramodern-superapp',
|
|
@@ -181,7 +195,7 @@ function createRootPackageJson(scope, packageSource, remotes = []) {
|
|
|
181
195
|
ownership: './topology/ownership.json',
|
|
182
196
|
packageSource: {
|
|
183
197
|
strategy: packageSource.strategy,
|
|
184
|
-
config: './.modernjs/ultramodern
|
|
198
|
+
config: './.modernjs/ultramodern.json#packageSource'
|
|
185
199
|
}
|
|
186
200
|
},
|
|
187
201
|
devDependencies: {
|
|
@@ -339,7 +353,7 @@ function createRootTsConfig(apps = []) {
|
|
|
339
353
|
]
|
|
340
354
|
};
|
|
341
355
|
}
|
|
342
|
-
function createAppPackage(scope, app, packageSource, enableTailwind, remotes = []) {
|
|
356
|
+
function createAppPackage(scope, app, packageSource, enableTailwind, remotes = [], bridge) {
|
|
343
357
|
const publicSurfaceBuildCommand = createPublicSurfaceGenerationCommand(app, 'dist');
|
|
344
358
|
const publicSurfaceCloudflareBuildCommand = createPublicSurfaceGenerationCommand(app, 'dist');
|
|
345
359
|
const publicSurfaceCloudflareOutputCommand = createPublicSurfaceGenerationCommand(app, 'cloudflare');
|
|
@@ -371,7 +385,7 @@ function createAppPackage(scope, app, packageSource, enableTailwind, remotes = [
|
|
|
371
385
|
} : {}
|
|
372
386
|
},
|
|
373
387
|
'zephyr:dependencies': createZephyrDependencies(scope, app, remotes),
|
|
374
|
-
dependencies: appDependencies(scope, packageSource, app, remotes),
|
|
388
|
+
dependencies: appDependencies(scope, packageSource, app, remotes, bridge),
|
|
375
389
|
devDependencies: appDevDependencies(packageSource, enableTailwind)
|
|
376
390
|
};
|
|
377
391
|
if (appHasEffectApi(app)) Object.assign(packageExports, {
|