@bleedingdev/modern-js-create 3.4.0-ultramodern.12 → 3.4.0-ultramodern.13
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 +39 -18
- 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/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 +37 -8
- 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 +39 -21
- 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/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 +38 -12
- 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 +39 -21
- 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/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 +38 -12
- 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/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 +3 -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
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { execFileSync } from 'node:child_process';
|
|
2
|
+
import crypto from 'node:crypto';
|
|
2
3
|
import fs from 'node:fs';
|
|
3
4
|
import path from 'node:path';
|
|
4
5
|
|
|
@@ -14,6 +15,21 @@ const expectedCloudflareBuildScript = {{expectedCloudflareBuildScriptJson}};
|
|
|
14
15
|
const expectedCloudflareDeployScript = {{expectedCloudflareDeployScriptJson}};
|
|
15
16
|
const expectedCloudflareSecurity = {{expectedCloudflareSecurityJson}};
|
|
16
17
|
const publicSurfaceManagedSourceAssetPaths = {{publicSurfaceManagedSourceAssetPathsJson}};
|
|
18
|
+
const compactConfigPath = '.modernjs/ultramodern.json';
|
|
19
|
+
const legacyGeneratedContractPath =
|
|
20
|
+
'.modernjs/ultramodern-generated-contract.json';
|
|
21
|
+
const legacyPackageSourcePath = '.modernjs/ultramodern-package-source.json';
|
|
22
|
+
const legacyTemplateManifestPath =
|
|
23
|
+
'.modernjs/ultramodern-workspace-template-manifest.json';
|
|
24
|
+
const modernPackageCohort = [
|
|
25
|
+
'@modern-js/create',
|
|
26
|
+
'@modern-js/code-tools',
|
|
27
|
+
'@modern-js/app-tools',
|
|
28
|
+
'@modern-js/plugin-bff',
|
|
29
|
+
'@modern-js/plugin-i18n',
|
|
30
|
+
'@modern-js/plugin-tanstack',
|
|
31
|
+
'@modern-js/runtime',
|
|
32
|
+
];
|
|
17
33
|
const expectedModernPackageSpecifier = packageName => {
|
|
18
34
|
if (packageSource.strategy === 'workspace') {
|
|
19
35
|
return 'workspace:*';
|
|
@@ -70,6 +86,682 @@ const assertArray = (value, contract, fixArea) => {
|
|
|
70
86
|
};
|
|
71
87
|
const findById = (entries, id) =>
|
|
72
88
|
Array.isArray(entries) ? entries.find(entry => entry?.id === id) : undefined;
|
|
89
|
+
const exists = relativePath => fs.existsSync(path.join(root, relativePath));
|
|
90
|
+
const hasCompactConfig = exists(compactConfigPath);
|
|
91
|
+
const generatedContractLabel = hasCompactConfig
|
|
92
|
+
? compactConfigPath
|
|
93
|
+
: legacyGeneratedContractPath;
|
|
94
|
+
const toKebabCase = value =>
|
|
95
|
+
String(value)
|
|
96
|
+
.trim()
|
|
97
|
+
.replace(/([a-z0-9])([A-Z])/gu, '$1-$2')
|
|
98
|
+
.replace(/[^a-zA-Z0-9._-]+/gu, '-')
|
|
99
|
+
.replace(/[._]+/gu, '-')
|
|
100
|
+
.toLowerCase()
|
|
101
|
+
.replace(/-+/gu, '-')
|
|
102
|
+
.replace(/^-+|-+$/gu, '');
|
|
103
|
+
const toPascalCase = value =>
|
|
104
|
+
toKebabCase(value)
|
|
105
|
+
.split('-')
|
|
106
|
+
.filter(Boolean)
|
|
107
|
+
.map(part => part.charAt(0).toUpperCase() + part.slice(1))
|
|
108
|
+
.join('');
|
|
109
|
+
const toCamelCase = value => {
|
|
110
|
+
const pascal = toPascalCase(value);
|
|
111
|
+
return `${pascal.charAt(0).toLowerCase()}${pascal.slice(1)}`;
|
|
112
|
+
};
|
|
113
|
+
const toEnvSegment = value => toKebabCase(value).replace(/-/gu, '_').toUpperCase();
|
|
114
|
+
const packageNameFor = (scope, suffix) => `@${scope}/${suffix}`;
|
|
115
|
+
const normalizeRelativePath = value =>
|
|
116
|
+
String(value ?? '').replace(/\\/gu, '/').replace(/^\.\/+/u, '');
|
|
117
|
+
const appNamespace = app => (app.kind === 'shell' ? 'shell' : (app.domain ?? app.id));
|
|
118
|
+
const tailwindPrefixFor = app =>
|
|
119
|
+
app.kind === 'shell'
|
|
120
|
+
? 'shell'
|
|
121
|
+
: String(app.domain ?? app.id).toLowerCase().replace(/[^a-z0-9]/gu, '');
|
|
122
|
+
const buildMarkerFor = app =>
|
|
123
|
+
crypto
|
|
124
|
+
.createHash('sha256')
|
|
125
|
+
.update(`${packageScope}:${app.packageSuffix}:${app.id}:0.1.0`)
|
|
126
|
+
.digest('hex')
|
|
127
|
+
.slice(0, 16);
|
|
128
|
+
const normalizeCompactApp = rawApp => {
|
|
129
|
+
const id = String(rawApp.id);
|
|
130
|
+
const kind = rawApp.kind === 'vertical' ? 'vertical' : 'shell';
|
|
131
|
+
const appPath =
|
|
132
|
+
typeof rawApp.path === 'string'
|
|
133
|
+
? normalizeRelativePath(rawApp.path)
|
|
134
|
+
: kind === 'shell'
|
|
135
|
+
? 'apps/shell-super-app'
|
|
136
|
+
: `verticals/${toKebabCase(id)}`;
|
|
137
|
+
const packageSuffix =
|
|
138
|
+
typeof rawApp.packageSuffix === 'string'
|
|
139
|
+
? rawApp.packageSuffix
|
|
140
|
+
: appPath.split('/').at(-1) ?? id;
|
|
141
|
+
const domain =
|
|
142
|
+
typeof rawApp.domain === 'string'
|
|
143
|
+
? rawApp.domain
|
|
144
|
+
: kind === 'vertical'
|
|
145
|
+
? packageSuffix
|
|
146
|
+
: undefined;
|
|
147
|
+
const moduleFederation =
|
|
148
|
+
rawApp.moduleFederation && typeof rawApp.moduleFederation === 'object'
|
|
149
|
+
? rawApp.moduleFederation
|
|
150
|
+
: {};
|
|
151
|
+
const effectApi =
|
|
152
|
+
rawApp.effectApi && typeof rawApp.effectApi === 'object'
|
|
153
|
+
? {
|
|
154
|
+
stem:
|
|
155
|
+
typeof rawApp.effectApi.stem === 'string'
|
|
156
|
+
? rawApp.effectApi.stem
|
|
157
|
+
: domain ?? id,
|
|
158
|
+
prefix:
|
|
159
|
+
typeof rawApp.effectApi.prefix === 'string'
|
|
160
|
+
? rawApp.effectApi.prefix
|
|
161
|
+
: `/${domain ?? id}-api`,
|
|
162
|
+
consumedBy: Array.isArray(rawApp.effectApi.consumedBy)
|
|
163
|
+
? rawApp.effectApi.consumedBy.filter(
|
|
164
|
+
consumer => typeof consumer === 'string',
|
|
165
|
+
)
|
|
166
|
+
: ['shell-super-app', id],
|
|
167
|
+
}
|
|
168
|
+
: undefined;
|
|
169
|
+
|
|
170
|
+
return {
|
|
171
|
+
id,
|
|
172
|
+
kind,
|
|
173
|
+
path: appPath,
|
|
174
|
+
package: typeof rawApp.package === 'string' ? rawApp.package : undefined,
|
|
175
|
+
packageSuffix,
|
|
176
|
+
domain,
|
|
177
|
+
port:
|
|
178
|
+
typeof rawApp.port === 'number'
|
|
179
|
+
? rawApp.port
|
|
180
|
+
: kind === 'shell'
|
|
181
|
+
? 3020
|
|
182
|
+
: 3030,
|
|
183
|
+
portEnv:
|
|
184
|
+
typeof rawApp.portEnv === 'string'
|
|
185
|
+
? rawApp.portEnv
|
|
186
|
+
: kind === 'shell'
|
|
187
|
+
? 'SHELL_SUPER_APP_PORT'
|
|
188
|
+
: `VERTICAL_${toEnvSegment(domain ?? id)}_PORT`,
|
|
189
|
+
mfName:
|
|
190
|
+
typeof moduleFederation.name === 'string'
|
|
191
|
+
? moduleFederation.name
|
|
192
|
+
: kind === 'shell'
|
|
193
|
+
? 'shellSuperApp'
|
|
194
|
+
: `vertical${toPascalCase(domain ?? id)}`,
|
|
195
|
+
exposes: Array.isArray(moduleFederation.exposes)
|
|
196
|
+
? moduleFederation.exposes.filter(expose => typeof expose === 'string')
|
|
197
|
+
: [],
|
|
198
|
+
moduleFederationSsr: moduleFederation.ssr !== false,
|
|
199
|
+
verticalRefs: Array.isArray(moduleFederation.verticalRefs)
|
|
200
|
+
? moduleFederation.verticalRefs.filter(ref => typeof ref === 'string')
|
|
201
|
+
: [],
|
|
202
|
+
effectApi,
|
|
203
|
+
};
|
|
204
|
+
};
|
|
205
|
+
const compactAppsFromConfig = config =>
|
|
206
|
+
Array.isArray(config.topology?.apps)
|
|
207
|
+
? config.topology.apps.map(normalizeCompactApp)
|
|
208
|
+
: [];
|
|
209
|
+
const remoteDependencyAliasFor = app => toCamelCase(app.domain ?? app.id.replace(/^remote-/u, ''));
|
|
210
|
+
const remoteContractsFor = (app, apps) =>
|
|
211
|
+
(app.verticalRefs ?? [])
|
|
212
|
+
.map(ref => apps.find(candidate => candidate.id === ref))
|
|
213
|
+
.filter(Boolean)
|
|
214
|
+
.map(remote => ({
|
|
215
|
+
id: remote.id,
|
|
216
|
+
alias: remoteDependencyAliasFor(remote),
|
|
217
|
+
name: remote.mfName,
|
|
218
|
+
manifestEnv: `VERTICAL_${toEnvSegment(remote.domain ?? remote.id)}_MF_MANIFEST`,
|
|
219
|
+
manifestUrl: `http://localhost:${remote.port}/mf-manifest.json`,
|
|
220
|
+
}));
|
|
221
|
+
const createRouteOwnedEntries = app => {
|
|
222
|
+
const namespace = appNamespace(app);
|
|
223
|
+
const base = {
|
|
224
|
+
descriptionKey: `${namespace}.seo.description`,
|
|
225
|
+
mfBoundaryId: app.mfName,
|
|
226
|
+
namespace,
|
|
227
|
+
ownerAppId: app.id,
|
|
228
|
+
public: false,
|
|
229
|
+
indexable: false,
|
|
230
|
+
publicSurface: 'private-app-screen',
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
return [
|
|
234
|
+
{
|
|
235
|
+
...base,
|
|
236
|
+
canonicalPath: '/',
|
|
237
|
+
id: app.kind === 'shell' ? 'shell-home' : `${app.id}-home`,
|
|
238
|
+
localisedPaths: {
|
|
239
|
+
cs: '/',
|
|
240
|
+
en: '/',
|
|
241
|
+
},
|
|
242
|
+
titleKey: app.kind === 'shell' ? 'shell.title' : `${namespace}.title`,
|
|
243
|
+
},
|
|
244
|
+
];
|
|
245
|
+
};
|
|
246
|
+
const createPublicRoutes = app =>
|
|
247
|
+
createRouteOwnedEntries(app)
|
|
248
|
+
.filter(route => route.public && route.indexable)
|
|
249
|
+
.map(route => ({
|
|
250
|
+
canonicalPath: route.canonicalPath,
|
|
251
|
+
id: route.id,
|
|
252
|
+
localisedPaths: route.localisedPaths,
|
|
253
|
+
namespace: route.namespace,
|
|
254
|
+
ownerAppId: route.ownerAppId,
|
|
255
|
+
descriptionKey: route.descriptionKey,
|
|
256
|
+
titleKey: route.titleKey,
|
|
257
|
+
}));
|
|
258
|
+
const createLocalisedUrls = app =>
|
|
259
|
+
Object.fromEntries(
|
|
260
|
+
createRouteOwnedEntries(app).flatMap(route => {
|
|
261
|
+
if (route.canonicalPath === '/') {
|
|
262
|
+
return [];
|
|
263
|
+
}
|
|
264
|
+
return Array.from(
|
|
265
|
+
new Set([route.canonicalPath, ...Object.values(route.localisedPaths)]),
|
|
266
|
+
).map(pathname => [pathname, route.localisedPaths]);
|
|
267
|
+
}),
|
|
268
|
+
);
|
|
269
|
+
const createPublicSurface = app => {
|
|
270
|
+
const publicRoutes = createPublicRoutes(app);
|
|
271
|
+
return {
|
|
272
|
+
authoring: 'colocated-route-meta',
|
|
273
|
+
artifactLifecycle: 'build-and-deploy-output',
|
|
274
|
+
generatedManifest: './src/routes/ultramodern-route-metadata',
|
|
275
|
+
source: 'route-owned-public-routes',
|
|
276
|
+
metadataExport: './src/routes/ultramodern-route-metadata',
|
|
277
|
+
generator: 'scripts/generate-public-surface-assets.mjs',
|
|
278
|
+
outputRoot: 'dist/public',
|
|
279
|
+
cloudflareOutputRoot: '.output/public',
|
|
280
|
+
privateRoutePolicy: 'omit-from-generated-public-surface',
|
|
281
|
+
files:
|
|
282
|
+
publicRoutes.length > 0
|
|
283
|
+
? ['robots.txt', 'sitemap.xml', 'site.webmanifest']
|
|
284
|
+
: ['robots.txt'],
|
|
285
|
+
omittedByDefault: ['api-catalog.json', 'llms.txt', 'security.txt'],
|
|
286
|
+
languages: ['en', 'cs'],
|
|
287
|
+
contentExpansion: {
|
|
288
|
+
authoring: 'route-owned-esm-provider',
|
|
289
|
+
defaultProviderFile: 'route.sitemap.mjs',
|
|
290
|
+
entryExport: 'default-or-entries',
|
|
291
|
+
paramsSource: 'params-or-localeParams',
|
|
292
|
+
draftPolicy: 'omit-draft-by-default',
|
|
293
|
+
indexablePolicy: 'omit-indexable-false',
|
|
294
|
+
lifecycle: 'executed-during-public-surface-generation',
|
|
295
|
+
},
|
|
296
|
+
contentSources: [],
|
|
297
|
+
publicRoutes,
|
|
298
|
+
routeEntries: [],
|
|
299
|
+
concreteUrlPaths: [],
|
|
300
|
+
};
|
|
301
|
+
};
|
|
302
|
+
const createPublicHead = () => ({
|
|
303
|
+
authoring: 'colocated-route-meta',
|
|
304
|
+
generator: './src/routes/ultramodern-route-head',
|
|
305
|
+
renderer: '@modern-js/runtime/head Helmet',
|
|
306
|
+
ssr: true,
|
|
307
|
+
title: {
|
|
308
|
+
required: true,
|
|
309
|
+
source: 'route.titleKey',
|
|
310
|
+
},
|
|
311
|
+
description: {
|
|
312
|
+
required: true,
|
|
313
|
+
source: 'route.descriptionKey',
|
|
314
|
+
},
|
|
315
|
+
canonical: {
|
|
316
|
+
publicIndexableOnly: true,
|
|
317
|
+
source: 'localized canonical route URL',
|
|
318
|
+
},
|
|
319
|
+
alternates: {
|
|
320
|
+
hreflang: ['en', 'cs'],
|
|
321
|
+
xDefault: 'en',
|
|
322
|
+
},
|
|
323
|
+
openGraph: {
|
|
324
|
+
publicIndexableOnly: true,
|
|
325
|
+
required: ['og:title', 'og:description', 'og:url', 'og:type'],
|
|
326
|
+
},
|
|
327
|
+
twitter: {
|
|
328
|
+
publicIndexableOnly: true,
|
|
329
|
+
required: ['twitter:card', 'twitter:title', 'twitter:description'],
|
|
330
|
+
},
|
|
331
|
+
structuredData: {
|
|
332
|
+
publicIndexableOnly: true,
|
|
333
|
+
optional: true,
|
|
334
|
+
source: 'route.jsonLd',
|
|
335
|
+
inference: false,
|
|
336
|
+
helperModule: './src/routes/ultramodern-jsonld',
|
|
337
|
+
helperTypes: [
|
|
338
|
+
'WebPage',
|
|
339
|
+
'WebApplication',
|
|
340
|
+
'SoftwareApplication',
|
|
341
|
+
'BreadcrumbList',
|
|
342
|
+
'FAQPage',
|
|
343
|
+
'Organization',
|
|
344
|
+
],
|
|
345
|
+
sanitizesHtmlOpenBracket: true,
|
|
346
|
+
},
|
|
347
|
+
privateRouteRobots: 'noindex, nofollow',
|
|
348
|
+
});
|
|
349
|
+
const createQualityGates = () => ({
|
|
350
|
+
publicRoutes: {
|
|
351
|
+
requireSitemapWhenPresent: true,
|
|
352
|
+
requireRobotsSitemapConsistency: true,
|
|
353
|
+
requireWebManifestWhenPresent: true,
|
|
354
|
+
},
|
|
355
|
+
statusCodes: {
|
|
356
|
+
notFoundRoute: '/__ultramodern-smoke-missing',
|
|
357
|
+
unknownRouteStatus: 404,
|
|
358
|
+
},
|
|
359
|
+
indexing: {
|
|
360
|
+
previewNoindex: true,
|
|
361
|
+
productionPublicRoutesIndexable: true,
|
|
362
|
+
},
|
|
363
|
+
assets: {
|
|
364
|
+
cssPreloadRequired: true,
|
|
365
|
+
cssResponseRequired: true,
|
|
366
|
+
cacheControlRequiredForCss: true,
|
|
367
|
+
sourcemapsPubliclyReferenced: false,
|
|
368
|
+
},
|
|
369
|
+
budgets: {
|
|
370
|
+
ssrHtmlMaxBytes: 250_000,
|
|
371
|
+
mfManifestMaxBytes: 500_000,
|
|
372
|
+
localeJsonMaxBytes: 100_000,
|
|
373
|
+
sitemapXmlMaxBytes: 500_000,
|
|
374
|
+
cssAssetMaxBytes: 750_000,
|
|
375
|
+
},
|
|
376
|
+
csp: {
|
|
377
|
+
finalMode: 'report-only-dogfood',
|
|
378
|
+
decision:
|
|
379
|
+
'Report-only remains the generated final mode until public smoke proof records MF SSR script/style/connect compatibility for the deployed surface.',
|
|
380
|
+
},
|
|
381
|
+
});
|
|
382
|
+
const createCloudflareRoutes = app => ({
|
|
383
|
+
ssr: '/en',
|
|
384
|
+
mfManifest: '/mf-manifest.json',
|
|
385
|
+
locale: `/locales/en/${appNamespace(app)}.json`,
|
|
386
|
+
...(app.effectApi
|
|
387
|
+
? {
|
|
388
|
+
effectReadiness: `${app.effectApi.prefix}/effect/${app.effectApi.stem}/readiness`,
|
|
389
|
+
}
|
|
390
|
+
: {}),
|
|
391
|
+
});
|
|
392
|
+
const createCloudflareDeploy = app => ({
|
|
393
|
+
target: 'cloudflare',
|
|
394
|
+
workerName: expectedWorkerName(app.packageSuffix),
|
|
395
|
+
publicUrlEnv: `ULTRAMODERN_PUBLIC_URL_${toEnvSegment(app.id)}`,
|
|
396
|
+
compatibilityDate: '{{cloudflareCompatibilityDate}}',
|
|
397
|
+
compatibilityFlags: ['nodejs_compat', 'global_fetch_strictly_public'],
|
|
398
|
+
assetsBinding: 'ASSETS',
|
|
399
|
+
routes: createCloudflareRoutes(app),
|
|
400
|
+
security: expectedCloudflareSecurity,
|
|
401
|
+
qualityGates: createQualityGates(),
|
|
402
|
+
evidence: {
|
|
403
|
+
proofScript: 'scripts/proof-cloudflare-version.mjs',
|
|
404
|
+
reportDefault:
|
|
405
|
+
'.codex/reports/cloudflare-version-proof/public-url-proof.json',
|
|
406
|
+
},
|
|
407
|
+
});
|
|
408
|
+
const createEffectReadiness = app => ({
|
|
409
|
+
endpoint: `/effect/${app.effectApi.stem}/readiness`,
|
|
410
|
+
marker: {
|
|
411
|
+
ui: 'ultramodernUiMarker',
|
|
412
|
+
api: 'ultramodernApiMarker',
|
|
413
|
+
skew: 'none',
|
|
414
|
+
},
|
|
415
|
+
checks: ['moduleFederation', 'ssr', 'translations', 'effectBff'],
|
|
416
|
+
});
|
|
417
|
+
const createEffectRequestContext = () => ({
|
|
418
|
+
propagatedHeaders: [
|
|
419
|
+
'accept-language',
|
|
420
|
+
'authorization',
|
|
421
|
+
'traceparent',
|
|
422
|
+
'x-correlation-id',
|
|
423
|
+
'x-tenant-id',
|
|
424
|
+
'x-ultramodern-env',
|
|
425
|
+
'x-vertical-version-id',
|
|
426
|
+
],
|
|
427
|
+
source: 'shell-to-vertical-effect-client',
|
|
428
|
+
});
|
|
429
|
+
const createEffectDomainOperations = app => {
|
|
430
|
+
const stem = app.effectApi.stem;
|
|
431
|
+
const group = toCamelCase(stem);
|
|
432
|
+
const basePath = `/effect/${stem}`;
|
|
433
|
+
return {
|
|
434
|
+
workspaceFeed: {
|
|
435
|
+
client: `list${toPascalCase(stem)}`,
|
|
436
|
+
method: 'GET',
|
|
437
|
+
path: basePath,
|
|
438
|
+
resource: 'workspace-items',
|
|
439
|
+
owner: app.id,
|
|
440
|
+
},
|
|
441
|
+
workspaceDetail: {
|
|
442
|
+
client: `get${toPascalCase(stem)}`,
|
|
443
|
+
method: 'GET',
|
|
444
|
+
path: `${basePath}/:id`,
|
|
445
|
+
resource: 'workspace-item',
|
|
446
|
+
owner: app.id,
|
|
447
|
+
},
|
|
448
|
+
workspaceCreate: {
|
|
449
|
+
client: `create${toPascalCase(stem)}`,
|
|
450
|
+
method: 'POST',
|
|
451
|
+
path: basePath,
|
|
452
|
+
resource: group,
|
|
453
|
+
owner: app.id,
|
|
454
|
+
},
|
|
455
|
+
};
|
|
456
|
+
};
|
|
457
|
+
const createEffectOperationContract = app => ({
|
|
458
|
+
group: toCamelCase(app.effectApi.stem),
|
|
459
|
+
operations: {
|
|
460
|
+
readiness: {
|
|
461
|
+
method: 'GET',
|
|
462
|
+
path: `/effect/${app.effectApi.stem}/readiness`,
|
|
463
|
+
source: 'generated-client',
|
|
464
|
+
},
|
|
465
|
+
},
|
|
466
|
+
});
|
|
467
|
+
const createAppConfigContract = app => ({
|
|
468
|
+
preset: 'presetUltramodern',
|
|
469
|
+
plugins: [
|
|
470
|
+
'appTools',
|
|
471
|
+
'tanstackRouterPlugin',
|
|
472
|
+
'i18nPlugin',
|
|
473
|
+
...(app.effectApi ? ['bffPlugin'] : []),
|
|
474
|
+
'moduleFederationPlugin',
|
|
475
|
+
'zephyrRspackPlugin',
|
|
476
|
+
],
|
|
477
|
+
dev: {
|
|
478
|
+
assetPrefix: '/',
|
|
479
|
+
},
|
|
480
|
+
output: {
|
|
481
|
+
assetPrefix: {
|
|
482
|
+
envFallbackOrder: ['MODERN_ASSET_PREFIX', 'ULTRAMODERN_ASSET_PREFIX'],
|
|
483
|
+
default: '/',
|
|
484
|
+
},
|
|
485
|
+
disableTsChecker: false,
|
|
486
|
+
},
|
|
487
|
+
performance: {
|
|
488
|
+
readinessDiagnostics: {
|
|
489
|
+
default: 'enabled',
|
|
490
|
+
optOut: {
|
|
491
|
+
env: 'ULTRAMODERN_PERFORMANCE_READINESS_DIAGNOSTICS=false',
|
|
492
|
+
config: 'scripts/ultramodern-performance-readiness.config.mjs',
|
|
493
|
+
},
|
|
494
|
+
failOn: 'framework-invariant',
|
|
495
|
+
},
|
|
496
|
+
},
|
|
497
|
+
rspack: {
|
|
498
|
+
output: {
|
|
499
|
+
uniqueName: app.mfName,
|
|
500
|
+
chunkLoadingGlobal: expectedChunkLoadingGlobal(app.mfName),
|
|
501
|
+
},
|
|
502
|
+
},
|
|
503
|
+
source: {
|
|
504
|
+
siteUrl: {
|
|
505
|
+
envFallbackOrder: [
|
|
506
|
+
'MODERN_PUBLIC_SITE_URL',
|
|
507
|
+
`ULTRAMODERN_PUBLIC_URL_${toEnvSegment(app.id)}`,
|
|
508
|
+
'ULTRAMODERN_CLOUDFLARE_WORKERS_DEV_SUBDOMAIN',
|
|
509
|
+
app.portEnv,
|
|
510
|
+
],
|
|
511
|
+
defaultLocalhostPort: app.port,
|
|
512
|
+
},
|
|
513
|
+
},
|
|
514
|
+
});
|
|
515
|
+
const cssDedupe = () => ({
|
|
516
|
+
strategy: 'shared-token-package-plus-css-content-hash',
|
|
517
|
+
sharedPackage: packageNameFor(packageScope, 'shared-design-tokens'),
|
|
518
|
+
sharedLayers: ['ultramodern-shared-tokens'],
|
|
519
|
+
runtimeLoad: 'once-per-content-hash',
|
|
520
|
+
duplicateBaseStylesAllowed: false,
|
|
521
|
+
});
|
|
522
|
+
const createStylingContract = app => {
|
|
523
|
+
const sharedTokenPackage = packageNameFor(packageScope, 'shared-design-tokens');
|
|
524
|
+
const ownedLayers =
|
|
525
|
+
app.kind === 'shell'
|
|
526
|
+
? ['ultramodern-shell-base', 'ultramodern-shell-overlay']
|
|
527
|
+
: [`ultramodern-vertical-${app.domain ?? app.id}`];
|
|
528
|
+
|
|
529
|
+
return {
|
|
530
|
+
tailwind: tailwindEnabled,
|
|
531
|
+
federation: {
|
|
532
|
+
owner: {
|
|
533
|
+
id: app.id,
|
|
534
|
+
package: app.package ?? packageNameFor(packageScope, app.packageSuffix),
|
|
535
|
+
},
|
|
536
|
+
role: app.kind === 'shell' ? 'shell-base-overlay' : 'vertical-css',
|
|
537
|
+
rootSelector: `[data-app-id="${app.id}"]`,
|
|
538
|
+
classPrefix: `${tailwindPrefixFor(app)}:`,
|
|
539
|
+
layers: {
|
|
540
|
+
shared: ['ultramodern-shared-tokens'],
|
|
541
|
+
owned: ownedLayers,
|
|
542
|
+
},
|
|
543
|
+
entrypoints: {
|
|
544
|
+
layoutImport: 'src/routes/layout.tsx',
|
|
545
|
+
css: ['src/routes/index.css'],
|
|
546
|
+
...(app.kind === 'shell'
|
|
547
|
+
? {}
|
|
548
|
+
: { federationEntry: 'src/federation-entry.tsx' }),
|
|
549
|
+
},
|
|
550
|
+
assets: {
|
|
551
|
+
shared: [`${sharedTokenPackage}/tokens.css`],
|
|
552
|
+
owned: ['src/routes/index.css'],
|
|
553
|
+
},
|
|
554
|
+
dedupe: cssDedupe(),
|
|
555
|
+
ssr: {
|
|
556
|
+
cloudflare: true,
|
|
557
|
+
firstPaintRequired: true,
|
|
558
|
+
verticalCss:
|
|
559
|
+
app.kind === 'shell'
|
|
560
|
+
? 'host-preloads-shell-and-shared-css'
|
|
561
|
+
: 'federated-manifest-owned-css',
|
|
562
|
+
},
|
|
563
|
+
},
|
|
564
|
+
};
|
|
565
|
+
};
|
|
566
|
+
const createAppContract = (app, apps) => ({
|
|
567
|
+
id: app.id,
|
|
568
|
+
package: app.package ?? packageNameFor(packageScope, app.packageSuffix),
|
|
569
|
+
path: app.path,
|
|
570
|
+
kind: app.kind,
|
|
571
|
+
config: createAppConfigContract(app),
|
|
572
|
+
styling: createStylingContract(app),
|
|
573
|
+
deploy: {
|
|
574
|
+
target: 'cloudflare',
|
|
575
|
+
cloudflare: createCloudflareDeploy(app),
|
|
576
|
+
worker: {
|
|
577
|
+
compatibilityDate: '{{cloudflareCompatibilityDate}}',
|
|
578
|
+
name: expectedWorkerName(app.packageSuffix),
|
|
579
|
+
security: expectedCloudflareSecurity,
|
|
580
|
+
ssr: true,
|
|
581
|
+
},
|
|
582
|
+
},
|
|
583
|
+
ssr: app.moduleFederationSsr
|
|
584
|
+
? {
|
|
585
|
+
mode: 'string',
|
|
586
|
+
moduleFederationAppSSR: true,
|
|
587
|
+
}
|
|
588
|
+
: undefined,
|
|
589
|
+
i18n: {
|
|
590
|
+
languages: ['en', 'cs'],
|
|
591
|
+
namespace: appNamespace(app),
|
|
592
|
+
localisedUrls: createLocalisedUrls(app),
|
|
593
|
+
},
|
|
594
|
+
routes: {
|
|
595
|
+
source: 'route-owned',
|
|
596
|
+
metadataAuthoring: 'colocated-route-meta',
|
|
597
|
+
generatedManifest: true,
|
|
598
|
+
metadataExport: './src/routes/ultramodern-route-metadata',
|
|
599
|
+
localisedUrls: createLocalisedUrls(app),
|
|
600
|
+
owned: createRouteOwnedEntries(app),
|
|
601
|
+
publicRoutes: createPublicRoutes(app),
|
|
602
|
+
privateByDefault: true,
|
|
603
|
+
publicnessDefault: 'private-app-screen',
|
|
604
|
+
publicHead: createPublicHead(),
|
|
605
|
+
publicSurface: createPublicSurface(app),
|
|
606
|
+
},
|
|
607
|
+
moduleFederation: {
|
|
608
|
+
name: app.mfName,
|
|
609
|
+
...(app.verticalRefs?.length
|
|
610
|
+
? {
|
|
611
|
+
verticalRefs: app.verticalRefs,
|
|
612
|
+
remotes: remoteContractsFor(app, apps),
|
|
613
|
+
}
|
|
614
|
+
: {}),
|
|
615
|
+
exposes: app.exposes,
|
|
616
|
+
dts: {
|
|
617
|
+
compilerInstance: 'tsgo',
|
|
618
|
+
displayErrorInTerminal: true,
|
|
619
|
+
tsConfigPath: './tsconfig.mf-types.json',
|
|
620
|
+
},
|
|
621
|
+
browserSafeExposesOnly: true,
|
|
622
|
+
},
|
|
623
|
+
marker: {
|
|
624
|
+
appId: app.id,
|
|
625
|
+
packageName: app.package ?? packageNameFor(packageScope, app.packageSuffix),
|
|
626
|
+
version: '0.1.0',
|
|
627
|
+
build: buildMarkerFor(app),
|
|
628
|
+
deployProfile: 'cloudflare-ssr-mf-effect-v1',
|
|
629
|
+
uiSurface: 'ui',
|
|
630
|
+
...(app.effectApi ? { apiSurface: 'effect-bff' } : {}),
|
|
631
|
+
},
|
|
632
|
+
...(app.effectApi
|
|
633
|
+
? {
|
|
634
|
+
effect: {
|
|
635
|
+
runtime: 'effect',
|
|
636
|
+
import: '@modern-js/plugin-bff/effect-edge',
|
|
637
|
+
prefix: app.effectApi.prefix,
|
|
638
|
+
openapi: '/openapi.json',
|
|
639
|
+
workerEntry: 'worker/__modern_bff_effect.js',
|
|
640
|
+
contract: './shared/effect/api',
|
|
641
|
+
client: './effect/client',
|
|
642
|
+
readiness: createEffectReadiness(app),
|
|
643
|
+
requestContext: createEffectRequestContext(),
|
|
644
|
+
domainOperations: createEffectDomainOperations(app),
|
|
645
|
+
...createEffectOperationContract(app),
|
|
646
|
+
},
|
|
647
|
+
}
|
|
648
|
+
: {}),
|
|
649
|
+
});
|
|
650
|
+
const createCssFederationContract = () => ({
|
|
651
|
+
sharedDesignTokens: {
|
|
652
|
+
owner: {
|
|
653
|
+
id: 'shared-design-tokens',
|
|
654
|
+
package: packageNameFor(packageScope, 'shared-design-tokens'),
|
|
655
|
+
},
|
|
656
|
+
role: 'shared-design-tokens',
|
|
657
|
+
rootSelector: ':root',
|
|
658
|
+
classPrefix: '--um-',
|
|
659
|
+
layers: {
|
|
660
|
+
owned: ['ultramodern-shared-tokens'],
|
|
661
|
+
},
|
|
662
|
+
entrypoints: {
|
|
663
|
+
css: ['packages/shared-design-tokens/src/tokens.css'],
|
|
664
|
+
},
|
|
665
|
+
assets: {
|
|
666
|
+
exports: ['./tokens.css'],
|
|
667
|
+
},
|
|
668
|
+
dedupe: cssDedupe(),
|
|
669
|
+
ssr: {
|
|
670
|
+
firstPaintRequired: true,
|
|
671
|
+
},
|
|
672
|
+
},
|
|
673
|
+
});
|
|
674
|
+
const createPerformanceReadinessContract = () => ({
|
|
675
|
+
default: 'enabled',
|
|
676
|
+
mode: 'diagnostic',
|
|
677
|
+
scope: 'ultramodern-generated-and-framework-owned',
|
|
678
|
+
report: {
|
|
679
|
+
script: 'scripts/ultramodern-performance-readiness.mjs',
|
|
680
|
+
config: 'scripts/ultramodern-performance-readiness.config.mjs',
|
|
681
|
+
deterministic: true,
|
|
682
|
+
},
|
|
683
|
+
optOut: {
|
|
684
|
+
env: 'ULTRAMODERN_PERFORMANCE_READINESS_DIAGNOSTICS=false',
|
|
685
|
+
},
|
|
686
|
+
signals: [
|
|
687
|
+
'bfcache',
|
|
688
|
+
'core-web-vitals-rum',
|
|
689
|
+
'duplicate-prefetch-warmup',
|
|
690
|
+
'cache-policy-sanity',
|
|
691
|
+
'save-data-behavior',
|
|
692
|
+
'cloudflare-ssr-cache-hints',
|
|
693
|
+
].map(id => ({ id })),
|
|
694
|
+
});
|
|
695
|
+
const createModernPackageAliases = packageSourceConfig => {
|
|
696
|
+
if (typeof packageSourceConfig?.aliasScope !== 'string') {
|
|
697
|
+
return undefined;
|
|
698
|
+
}
|
|
699
|
+
const scope = packageSourceConfig.aliasScope.replace(/^@/u, '');
|
|
700
|
+
const prefix =
|
|
701
|
+
typeof packageSourceConfig.aliasPackageNamePrefix === 'string'
|
|
702
|
+
? packageSourceConfig.aliasPackageNamePrefix
|
|
703
|
+
: '';
|
|
704
|
+
return Object.fromEntries(
|
|
705
|
+
modernPackageCohort.map(packageName => [
|
|
706
|
+
packageName,
|
|
707
|
+
`@${scope}/${prefix}${packageName.split('/').at(-1)}`,
|
|
708
|
+
]),
|
|
709
|
+
);
|
|
710
|
+
};
|
|
711
|
+
const synthesizePackageSourceFromCompact = config => {
|
|
712
|
+
const source = config.packageSource ?? {};
|
|
713
|
+
const strategy = source.strategy === 'install' ? 'install' : 'workspace';
|
|
714
|
+
const specifier =
|
|
715
|
+
strategy === 'install' && typeof source.modernPackageVersion === 'string'
|
|
716
|
+
? source.modernPackageVersion
|
|
717
|
+
: 'workspace:*';
|
|
718
|
+
const aliases = createModernPackageAliases(source);
|
|
719
|
+
return {
|
|
720
|
+
schemaVersion: 1,
|
|
721
|
+
strategy,
|
|
722
|
+
modernPackages: {
|
|
723
|
+
packages: modernPackageCohort,
|
|
724
|
+
specifier,
|
|
725
|
+
...(typeof source.registry === 'string'
|
|
726
|
+
? { registry: source.registry }
|
|
727
|
+
: {}),
|
|
728
|
+
...(aliases ? { aliases } : {}),
|
|
729
|
+
},
|
|
730
|
+
generatedWorkspacePackages: {
|
|
731
|
+
packages: [
|
|
732
|
+
packageNameFor(packageScope, 'shared-contracts'),
|
|
733
|
+
packageNameFor(packageScope, 'shared-design-tokens'),
|
|
734
|
+
],
|
|
735
|
+
specifier: 'workspace:*',
|
|
736
|
+
},
|
|
737
|
+
};
|
|
738
|
+
};
|
|
739
|
+
const synthesizeGeneratedContractFromCompact = config => {
|
|
740
|
+
const apps = compactAppsFromConfig(config);
|
|
741
|
+
return {
|
|
742
|
+
schemaVersion: 1,
|
|
743
|
+
profile: config.profile ?? 'cloudflare-ssr-mf-effect-v1',
|
|
744
|
+
node: {
|
|
745
|
+
version: config.workspace?.node?.version ?? expectedNodeVersion,
|
|
746
|
+
engineRange: '>=26',
|
|
747
|
+
},
|
|
748
|
+
performanceReadiness: createPerformanceReadinessContract(),
|
|
749
|
+
cssFederation: createCssFederationContract(),
|
|
750
|
+
apps: apps.map(app => createAppContract(app, apps)),
|
|
751
|
+
};
|
|
752
|
+
};
|
|
753
|
+
const readPackageSourceView = config => {
|
|
754
|
+
if (config) {
|
|
755
|
+
return synthesizePackageSourceFromCompact(config);
|
|
756
|
+
}
|
|
757
|
+
return readJson(legacyPackageSourcePath);
|
|
758
|
+
};
|
|
759
|
+
const readGeneratedContractView = config => {
|
|
760
|
+
if (config) {
|
|
761
|
+
return synthesizeGeneratedContractFromCompact(config);
|
|
762
|
+
}
|
|
763
|
+
return readJson(legacyGeneratedContractPath);
|
|
764
|
+
};
|
|
73
765
|
const expectedManifestUrl = vertical =>
|
|
74
766
|
`http://localhost:${vertical.port}/mf-manifest.json`;
|
|
75
767
|
const expectedApiUrl = vertical =>
|
|
@@ -177,7 +869,7 @@ const assertMicroVerticalContractGraph = ({
|
|
|
177
869
|
);
|
|
178
870
|
assertArray(
|
|
179
871
|
generatedContract.apps,
|
|
180
|
-
|
|
872
|
+
`${generatedContractLabel} apps`,
|
|
181
873
|
'regenerate the generated contract from the workspace topology',
|
|
182
874
|
);
|
|
183
875
|
|
|
@@ -202,26 +894,26 @@ const assertMicroVerticalContractGraph = ({
|
|
|
202
894
|
assertSameJson(
|
|
203
895
|
generatedContract.apps.map(app => app?.id),
|
|
204
896
|
expectedAppIds,
|
|
205
|
-
|
|
897
|
+
`${generatedContractLabel} apps`,
|
|
206
898
|
'regenerate the generated contract after topology changes',
|
|
207
899
|
);
|
|
208
900
|
|
|
209
901
|
const shellContract = findById(generatedContract.apps, 'shell-super-app');
|
|
210
902
|
assertObject(
|
|
211
903
|
shellContract,
|
|
212
|
-
|
|
904
|
+
`${generatedContractLabel} apps.shell-super-app`,
|
|
213
905
|
'regenerate the generated shell contract entry',
|
|
214
906
|
);
|
|
215
907
|
assertSameJson(
|
|
216
908
|
shellContract.moduleFederation?.verticalRefs ?? [],
|
|
217
909
|
expectedVerticalIds,
|
|
218
|
-
|
|
910
|
+
`${generatedContractLabel} shell moduleFederation.verticalRefs`,
|
|
219
911
|
'regenerate the generated shell Module Federation contract',
|
|
220
912
|
);
|
|
221
913
|
assertSameJson(
|
|
222
914
|
(shellContract.moduleFederation?.remotes ?? []).map(remoteContractSubset),
|
|
223
915
|
expectedShellRemotes,
|
|
224
|
-
|
|
916
|
+
`${generatedContractLabel} shell moduleFederation.remotes`,
|
|
225
917
|
'regenerate the generated shell Module Federation contract',
|
|
226
918
|
);
|
|
227
919
|
assertSameJson(
|
|
@@ -230,7 +922,7 @@ const assertMicroVerticalContractGraph = ({
|
|
|
230
922
|
mode: 'string',
|
|
231
923
|
moduleFederationAppSSR: true,
|
|
232
924
|
},
|
|
233
|
-
|
|
925
|
+
`${generatedContractLabel} shell SSR contract`,
|
|
234
926
|
'restore generated string SSR Module Federation settings',
|
|
235
927
|
);
|
|
236
928
|
|
|
@@ -343,7 +1035,7 @@ const assertMicroVerticalContractGraph = ({
|
|
|
343
1035
|
|
|
344
1036
|
assertObject(
|
|
345
1037
|
contractEntry,
|
|
346
|
-
|
|
1038
|
+
`${generatedContractLabel} apps.${vertical.id}`,
|
|
347
1039
|
'regenerate the generated MicroVertical contract entry',
|
|
348
1040
|
);
|
|
349
1041
|
assertSameJson(
|
|
@@ -386,7 +1078,7 @@ const assertMicroVerticalContractGraph = ({
|
|
|
386
1078
|
moduleFederationAppSSR: true,
|
|
387
1079
|
},
|
|
388
1080
|
},
|
|
389
|
-
|
|
1081
|
+
`${generatedContractLabel} apps.${vertical.id}`,
|
|
390
1082
|
'regenerate the generated MicroVertical contract entry',
|
|
391
1083
|
);
|
|
392
1084
|
}
|
|
@@ -770,15 +1462,12 @@ const requiredPaths = [
|
|
|
770
1462
|
'oxfmt.config.ts',
|
|
771
1463
|
'.github/renovate.json',
|
|
772
1464
|
'.github/workflows/ultramodern-workspace-gates.yml',
|
|
773
|
-
'.agents/skills-lock.json',
|
|
774
1465
|
'.agents/agent-reference-repos.json',
|
|
775
|
-
'.
|
|
1466
|
+
'.codex/skills-lock.json',
|
|
1467
|
+
'.codex/rstackjs-agent-skills-LICENSE',
|
|
776
1468
|
'topology/reference-topology.json',
|
|
777
1469
|
'topology/ownership.json',
|
|
778
1470
|
'topology/local-overlays/development.json',
|
|
779
|
-
'.modernjs/ultramodern-workspace-template-manifest.json',
|
|
780
|
-
'.modernjs/ultramodern-package-source.json',
|
|
781
|
-
'.modernjs/ultramodern-generated-contract.json',
|
|
782
1471
|
'scripts/assert-mf-types.mjs',
|
|
783
1472
|
'scripts/bootstrap-agent-skills.mjs',
|
|
784
1473
|
'scripts/check-ultramodern-i18n-boundaries.mjs',
|
|
@@ -788,6 +1477,7 @@ const requiredPaths = [
|
|
|
788
1477
|
'scripts/ultramodern-performance-readiness.config.mjs',
|
|
789
1478
|
'scripts/ultramodern-performance-readiness.mjs',
|
|
790
1479
|
'scripts/ultramodern-typecheck.mjs',
|
|
1480
|
+
'scripts/validate-ultramodern-workspace.mjs',
|
|
791
1481
|
'apps/shell-super-app/package.json',
|
|
792
1482
|
'apps/shell-super-app/tsconfig.json',
|
|
793
1483
|
'apps/shell-super-app/tsconfig.mf-types.json',
|
|
@@ -830,6 +1520,16 @@ if (tailwindEnabled) {
|
|
|
830
1520
|
);
|
|
831
1521
|
}
|
|
832
1522
|
|
|
1523
|
+
if (hasCompactConfig) {
|
|
1524
|
+
requiredPaths.push(compactConfigPath);
|
|
1525
|
+
} else {
|
|
1526
|
+
requiredPaths.push(
|
|
1527
|
+
legacyTemplateManifestPath,
|
|
1528
|
+
legacyPackageSourcePath,
|
|
1529
|
+
legacyGeneratedContractPath,
|
|
1530
|
+
);
|
|
1531
|
+
}
|
|
1532
|
+
|
|
833
1533
|
for (const vertical of fullStackVerticals) {
|
|
834
1534
|
requiredMicroVerticalPaths(vertical).forEach(
|
|
835
1535
|
assertRequiredVerticalFile(vertical),
|
|
@@ -841,9 +1541,21 @@ for (const requiredPath of requiredPaths) {
|
|
|
841
1541
|
for (const oldRemotePath of oldRemotePaths) {
|
|
842
1542
|
assertNotExists(oldRemotePath);
|
|
843
1543
|
}
|
|
1544
|
+
if (hasCompactConfig) {
|
|
1545
|
+
for (const retiredMetadataPath of [
|
|
1546
|
+
legacyTemplateManifestPath,
|
|
1547
|
+
legacyPackageSourcePath,
|
|
1548
|
+
legacyGeneratedContractPath,
|
|
1549
|
+
]) {
|
|
1550
|
+
assertNotExists(retiredMetadataPath);
|
|
1551
|
+
}
|
|
1552
|
+
}
|
|
844
1553
|
const rootPackage = readJson('package.json');
|
|
845
|
-
const
|
|
846
|
-
const
|
|
1554
|
+
const ultramodernConfig = hasCompactConfig ? readJson(compactConfigPath) : undefined;
|
|
1555
|
+
const bridgeConfig =
|
|
1556
|
+
ultramodernConfig?.bridge?.enabled === true ? ultramodernConfig.bridge : undefined;
|
|
1557
|
+
const packageSource = readPackageSourceView(ultramodernConfig);
|
|
1558
|
+
const generatedContract = readGeneratedContractView(ultramodernConfig);
|
|
847
1559
|
const topology = readJson('topology/reference-topology.json');
|
|
848
1560
|
const ownership = readJson('topology/ownership.json');
|
|
849
1561
|
const overlay = readJson('topology/local-overlays/development.json');
|
|
@@ -882,7 +1594,13 @@ assert(
|
|
|
882
1594
|
);
|
|
883
1595
|
assert(!workflowText.includes('FORCE_JAVASCRIPT_ACTIONS_TO_NODE24'), 'CI workflow must not carry the legacy Node 24 override');
|
|
884
1596
|
assert(rootPackage.modernjs?.preset === 'presetUltramodern', 'Root must declare presetUltramodern');
|
|
885
|
-
assert(
|
|
1597
|
+
assert(
|
|
1598
|
+
rootPackage.modernjs?.packageSource?.config ===
|
|
1599
|
+
(hasCompactConfig ? './.modernjs/ultramodern.json' : './.modernjs/ultramodern-package-source.json'),
|
|
1600
|
+
hasCompactConfig
|
|
1601
|
+
? 'Root must point at compact UltraModern config'
|
|
1602
|
+
: 'Root must point at package source metadata',
|
|
1603
|
+
);
|
|
886
1604
|
assert(rootPackage.modernjs?.packageSource?.strategy === packageSource.strategy, 'Root package source strategy must match metadata');
|
|
887
1605
|
assert(packageSource.strategy === 'workspace' || packageSource.strategy === 'install', 'Package source strategy must be workspace or install');
|
|
888
1606
|
assert(packageSource.strategy === 'install' || packageSource.modernPackages?.specifier === 'workspace:*', 'Workspace package source must be explicitly backed by workspace:*');
|
|
@@ -930,22 +1648,58 @@ assert(
|
|
|
930
1648
|
);
|
|
931
1649
|
assert(rootPackage.scripts?.['cloudflare:build'] === expectedCloudflareBuildScript, 'Root cloudflare:build script is incorrect');
|
|
932
1650
|
assert(!('ultramodern:check' in (rootPackage.scripts ?? {})), 'Root must not expose ultramodern:check');
|
|
933
|
-
|
|
1651
|
+
if (bridgeConfig) {
|
|
1652
|
+
assert(
|
|
1653
|
+
rootPackage.scripts?.typecheck ===
|
|
1654
|
+
'pnpm -r --filter "./apps/*" --filter "./verticals/*" --filter "./packages/*" run typecheck',
|
|
1655
|
+
'Bridge root typecheck must check generated package boundaries without building parent implementation sources',
|
|
1656
|
+
);
|
|
1657
|
+
assert(Array.isArray(bridgeConfig.workspacePackages), 'Bridge config must record workspace package patterns');
|
|
1658
|
+
for (const workspacePackage of bridgeConfig.workspacePackages) {
|
|
1659
|
+
assert(
|
|
1660
|
+
rootPackage.workspaces?.includes(workspacePackage.pattern),
|
|
1661
|
+
`Root workspaces must include bridge package pattern ${workspacePackage.pattern}`,
|
|
1662
|
+
);
|
|
1663
|
+
assert(
|
|
1664
|
+
readText('pnpm-workspace.yaml').includes(` - ${workspacePackage.pattern}`),
|
|
1665
|
+
`pnpm-workspace.yaml must include bridge package pattern ${workspacePackage.pattern}`,
|
|
1666
|
+
);
|
|
1667
|
+
}
|
|
1668
|
+
for (const gate of bridgeConfig.gates ?? []) {
|
|
1669
|
+
const expectedGateScript = `${gate.cwd ? `cd ${gate.cwd} && ` : ''}${gate.command}`;
|
|
1670
|
+
assert(
|
|
1671
|
+
rootPackage.scripts?.[`bridge:${gate.name}`] === expectedGateScript,
|
|
1672
|
+
`Bridge gate script bridge:${gate.name} is incorrect`,
|
|
1673
|
+
);
|
|
1674
|
+
}
|
|
1675
|
+
assert(rootPackage.scripts?.['bridge:check'], 'Bridge workspaces must expose bridge:check');
|
|
1676
|
+
} else {
|
|
1677
|
+
assert(rootPackage.scripts?.typecheck === 'node ./scripts/ultramodern-typecheck.mjs --build tsconfig.json', 'Root typecheck must run TS-Go build mode across project references');
|
|
1678
|
+
}
|
|
934
1679
|
assert(rootPackage.scripts?.['contract:check'] === 'node ./scripts/validate-ultramodern-workspace.mjs', 'Root must expose contract:check');
|
|
935
1680
|
assert(rootPackage.scripts?.['i18n:boundaries'] === 'node ./scripts/check-ultramodern-i18n-boundaries.mjs', 'Root must expose i18n:boundaries');
|
|
936
1681
|
assert(rootPackage.scripts?.['performance:readiness'] === 'node ./scripts/ultramodern-performance-readiness.mjs', 'Root must expose default-on performance readiness diagnostics');
|
|
937
|
-
assert(
|
|
1682
|
+
assert(
|
|
1683
|
+
bridgeConfig
|
|
1684
|
+
? rootPackage.scripts?.check?.endsWith('&& pnpm performance:readiness && pnpm bridge:check')
|
|
1685
|
+
: rootPackage.scripts?.check?.endsWith('&& pnpm performance:readiness'),
|
|
1686
|
+
'Root check must run default-on performance readiness diagnostics and bridge gates when configured',
|
|
1687
|
+
);
|
|
938
1688
|
const performanceReadinessConfig = readText('scripts/ultramodern-performance-readiness.config.mjs');
|
|
939
|
-
const
|
|
1689
|
+
const assertToolWrapper = (scriptPath, command) => {
|
|
1690
|
+
const source = readText(scriptPath);
|
|
1691
|
+
assert(source.includes('modern-js-create'), `${scriptPath} must delegate to modern-js-create`);
|
|
1692
|
+
assert(source.includes('ULTRAMODERN_CREATE_BIN'), `${scriptPath} must support local create-bin overrides for generated-workspace tests`);
|
|
1693
|
+
assert(source.includes("'ultramodern'") || source.includes('"ultramodern"'), `${scriptPath} must dispatch through the UltraModern tool surface`);
|
|
1694
|
+
assert(source.includes(command), `${scriptPath} must dispatch ${command}`);
|
|
1695
|
+
};
|
|
940
1696
|
assert(performanceReadinessConfig.includes('UltramodernPerformanceReadinessDiagnosticsConfig'), 'Performance readiness config must carry the typed opt-out surface');
|
|
941
1697
|
assert(performanceReadinessConfig.includes('enabled: true'), 'Performance readiness diagnostics must be default-on');
|
|
942
1698
|
assert(performanceReadinessConfig.includes("failOn: 'framework-invariant'"), 'Performance readiness diagnostics must only fail framework invariants by default');
|
|
943
|
-
|
|
944
|
-
|
|
1699
|
+
assertToolWrapper('scripts/validate-ultramodern-workspace.mjs', 'validate');
|
|
1700
|
+
assertToolWrapper('scripts/ultramodern-performance-readiness.mjs', 'performance-readiness');
|
|
945
1701
|
const i18nBoundaryScript = readText('scripts/check-ultramodern-i18n-boundaries.mjs');
|
|
946
|
-
|
|
947
|
-
assert(ultramodernTypecheckScript.includes("'--checkers'") && ultramodernTypecheckScript.includes("'--builders'"), 'Root typecheck script must drive TS-Go checker and builder parallelism');
|
|
948
|
-
assert(ultramodernTypecheckScript.includes('effect-tsgo') && ultramodernTypecheckScript.includes('get-exe-path'), 'Root typecheck script must resolve the @effect/tsgo native-preview binary');
|
|
1702
|
+
assertToolWrapper('scripts/ultramodern-typecheck.mjs', 'typecheck');
|
|
949
1703
|
assert(
|
|
950
1704
|
i18nBoundaryScript.includes("from '@modern-js/code-tools'") &&
|
|
951
1705
|
i18nBoundaryScript.includes('runWorkspaceSourceCheck'),
|
|
@@ -956,10 +1710,13 @@ assert(rootPackage.scripts?.['cloudflare:deploy'] === expectedCloudflareDeploySc
|
|
|
956
1710
|
assert(rootPackage.scripts?.['cloudflare:proof'] === 'node ./scripts/proof-cloudflare-version.mjs --out .codex/reports/cloudflare-version-proof/public-url-proof.json', 'Root must expose cloudflare:proof');
|
|
957
1711
|
assert(rootPackage.scripts?.['skills:install'] === 'node ./scripts/bootstrap-agent-skills.mjs', 'Root must expose skills:install');
|
|
958
1712
|
assert(rootPackage.scripts?.['skills:check'] === 'node ./scripts/bootstrap-agent-skills.mjs --check', 'Root must expose skills:check');
|
|
959
|
-
assert(rootPackage.scripts?.postinstall === "oxfmt . '!repos/**' && node ./scripts/bootstrap-agent-skills.mjs --postinstall", 'Root postinstall must
|
|
1713
|
+
assert(rootPackage.scripts?.postinstall === "oxfmt . '!repos/**' && node ./scripts/bootstrap-agent-skills.mjs --postinstall", 'Root postinstall must format, run the default-on Codex skills bootstrap, and leave reference repository installs explicit');
|
|
960
1714
|
assert(rootPackage.scripts?.['agents:refs:install'] === 'node ./scripts/setup-agent-reference-repos.mjs', 'Root must expose agents:refs:install as the explicit reference repo installer');
|
|
961
1715
|
const agentSkillsBootstrap = readText('scripts/bootstrap-agent-skills.mjs');
|
|
962
|
-
assert
|
|
1716
|
+
assertToolWrapper('scripts/assert-mf-types.mjs', 'mf-types');
|
|
1717
|
+
assertToolWrapper('scripts/generate-public-surface-assets.mjs', 'public-surface');
|
|
1718
|
+
assertToolWrapper('scripts/proof-cloudflare-version.mjs', 'cloudflare-proof');
|
|
1719
|
+
assertToolWrapper('scripts/bootstrap-agent-skills.mjs', 'skills');
|
|
963
1720
|
assert(!agentSkillsBootstrap.includes("run('brew'") && !agentSkillsBootstrap.includes('runShell('), 'Agent skills bootstrap must never invoke system package managers');
|
|
964
1721
|
const agentReferenceRepoSetup = readText('scripts/setup-agent-reference-repos.mjs');
|
|
965
1722
|
assert(agentReferenceRepoSetup.includes("['commit', '--no-verify', '-m', message]"), 'Agent reference repo installer commits must skip hooks during postinstall');
|