@bleedingdev/modern-js-create 3.2.0-ultramodern.2 → 3.2.0-ultramodern.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/README.md +12 -0
  2. package/dist/index.js +676 -198
  3. package/package.json +5 -2
  4. package/template/.agents/skills-lock.json +34 -0
  5. package/template/.github/renovate.json +53 -0
  6. package/template/.github/workflows/ultramodern-gates.yml.handlebars +26 -4
  7. package/template/AGENTS.md +27 -0
  8. package/template/README.md +7 -3
  9. package/template/api/effect/index.ts.handlebars +7 -45
  10. package/template/config/public/locales/cs/translation.json +39 -0
  11. package/template/config/public/locales/en/translation.json +39 -0
  12. package/template/modern.config.ts.handlebars +44 -23
  13. package/template/oxfmt.config.ts +8 -0
  14. package/template/oxlint.config.ts +12 -0
  15. package/template/package.json.handlebars +56 -27
  16. package/template/pnpm-workspace.yaml +24 -0
  17. package/template/rstest.config.mts +7 -0
  18. package/template/scripts/bootstrap-agent-skills.mjs +95 -0
  19. package/template/scripts/check-i18n-strings.mjs +83 -0
  20. package/template/scripts/validate-ultramodern.mjs.handlebars +350 -16
  21. package/template/shared/effect/api.ts.handlebars +1 -2
  22. package/template/src/modern-app-env.d.ts +2 -0
  23. package/template/src/modern.runtime.ts.handlebars +17 -3
  24. package/template/src/routes/[lang]/page.tsx.handlebars +211 -0
  25. package/template/src/routes/index.css.handlebars +14 -3
  26. package/template/src/routes/layout.tsx.handlebars +1 -1
  27. package/template/tests/tsconfig.json +7 -0
  28. package/template/tests/ultramodern.contract.test.ts +67 -0
  29. package/template/tsconfig.json +106 -2
  30. package/template-workspace/.agents/agent-reference-repos.json +23 -0
  31. package/template-workspace/.agents/rstackjs-agent-skills-LICENSE +21 -0
  32. package/template-workspace/.agents/skills/rsbuild-best-practices/SKILL.md +57 -0
  33. package/template-workspace/.agents/skills/rsdoctor-analysis/SKILL.md +96 -0
  34. package/template-workspace/.agents/skills/rsdoctor-analysis/references/command-map.md +113 -0
  35. package/template-workspace/.agents/skills/rsdoctor-analysis/references/common-analysis-patterns.md +190 -0
  36. package/template-workspace/.agents/skills/rsdoctor-analysis/references/install-rsdoctor-common.md +88 -0
  37. package/template-workspace/.agents/skills/rsdoctor-analysis/references/install-rsdoctor-rspack.md +138 -0
  38. package/template-workspace/.agents/skills/rsdoctor-analysis/references/install-rsdoctor-webpack.md +71 -0
  39. package/template-workspace/.agents/skills/rsdoctor-analysis/references/install-rsdoctor.md +39 -0
  40. package/template-workspace/.agents/skills/rsdoctor-analysis/references/rsdoctor-data-types.md +103 -0
  41. package/template-workspace/.agents/skills/rslib-best-practices/SKILL.md +58 -0
  42. package/template-workspace/.agents/skills/rslib-modern-package/SKILL.md +173 -0
  43. package/template-workspace/.agents/skills/rspack-best-practices/SKILL.md +70 -0
  44. package/template-workspace/.agents/skills/rspack-tracing/SKILL.md +75 -0
  45. package/template-workspace/.agents/skills/rspack-tracing/references/bottlenecks.md +47 -0
  46. package/template-workspace/.agents/skills/rspack-tracing/references/tracing-guide.md +38 -0
  47. package/template-workspace/.agents/skills/rspack-tracing/scripts/analyze_trace.js +184 -0
  48. package/template-workspace/.agents/skills/rstest-best-practices/SKILL.md +133 -0
  49. package/template-workspace/.agents/skills-lock.json +95 -0
  50. package/template-workspace/.github/renovate.json +29 -0
  51. package/template-workspace/.github/workflows/ultramodern-workspace-gates.yml.handlebars +52 -0
  52. package/template-workspace/.gitignore.handlebars +6 -0
  53. package/template-workspace/AGENTS.md +61 -0
  54. package/template-workspace/README.md.handlebars +11 -1
  55. package/template-workspace/oxfmt.config.ts +16 -0
  56. package/template-workspace/oxlint.config.ts +19 -0
  57. package/template-workspace/pnpm-workspace.yaml +24 -6
  58. package/template-workspace/scripts/bootstrap-agent-skills.mjs +95 -0
  59. package/template-workspace/scripts/check-i18n-strings.mjs +83 -0
  60. package/template-workspace/scripts/setup-agent-reference-repos.mjs +217 -0
  61. package/template-workspace/scripts/validate-ultramodern-workspace.mjs.handlebars +397 -59
  62. package/template/biome.json +0 -41
  63. package/template/src/routes/page.tsx.handlebars +0 -119
@@ -4,52 +4,99 @@ import path from 'node:path';
4
4
  const root = process.cwd();
5
5
  const packageScope = '{{packageScope}}';
6
6
  const tanstackVersion = '1.170.1';
7
+ const rstackAgentSkillsCommit = '61c948b42512e223bad44b83af4080eba48b2677';
7
8
  const modernPackages = [
8
9
  '@modern-js/app-tools',
9
10
  '@modern-js/plugin-bff',
11
+ '@modern-js/plugin-i18n',
10
12
  '@modern-js/plugin-tanstack',
11
13
  '@modern-js/runtime',
12
14
  ];
15
+ const baselineAgentSkills = [
16
+ 'rsbuild-best-practices',
17
+ 'rspack-best-practices',
18
+ 'rspack-tracing',
19
+ 'rsdoctor-analysis',
20
+ 'rslib-best-practices',
21
+ 'rslib-modern-package',
22
+ 'rstest-best-practices',
23
+ ];
24
+ const privateAgentSkills = ['plan-graph', 'dag', 'subagent-graph', 'helm', 'debugger-mode'];
13
25
 
14
- function readText(relativePath) {
15
- return fs.readFileSync(path.join(root, relativePath), 'utf-8');
16
- }
17
-
18
- function readJson(relativePath) {
19
- return JSON.parse(readText(relativePath));
20
- }
21
-
22
- function assert(condition, message) {
26
+ const readText = (relativePath) => fs.readFileSync(path.join(root, relativePath), 'utf-8');
27
+ const readJson = (relativePath) => JSON.parse(readText(relativePath));
28
+ const assert = (condition, message) => {
23
29
  if (!condition) {
24
30
  throw new Error(message);
25
31
  }
26
- }
27
-
28
- function assertExists(relativePath) {
32
+ };
33
+ const assertExists = (relativePath) => {
29
34
  assert(fs.existsSync(path.join(root, relativePath)), `Missing ${relativePath}`);
30
- }
35
+ };
31
36
 
32
37
  const requiredPaths = [
38
+ 'AGENTS.md',
39
+ '.gitignore',
33
40
  'package.json',
34
41
  'pnpm-workspace.yaml',
35
42
  'tsconfig.base.json',
43
+ 'oxlint.config.ts',
44
+ 'oxfmt.config.ts',
45
+ '.github/renovate.json',
46
+ '.github/workflows/ultramodern-workspace-gates.yml',
47
+ '.agents/skills-lock.json',
48
+ '.agents/agent-reference-repos.json',
49
+ '.agents/rstackjs-agent-skills-LICENSE',
50
+ '.agents/skills/rsbuild-best-practices/SKILL.md',
51
+ '.agents/skills/rspack-best-practices/SKILL.md',
52
+ '.agents/skills/rspack-tracing/SKILL.md',
53
+ '.agents/skills/rspack-tracing/references/tracing-guide.md',
54
+ '.agents/skills/rspack-tracing/scripts/analyze_trace.js',
55
+ '.agents/skills/rsdoctor-analysis/SKILL.md',
56
+ '.agents/skills/rsdoctor-analysis/references/rsdoctor-data-types.md',
57
+ '.agents/skills/rslib-best-practices/SKILL.md',
58
+ '.agents/skills/rslib-modern-package/SKILL.md',
59
+ '.agents/skills/rstest-best-practices/SKILL.md',
36
60
  'topology/reference-topology.json',
37
61
  'topology/ownership.json',
38
62
  'topology/local-overlays/development.json',
39
63
  '.modernjs/ultramodern-workspace-template-manifest.json',
40
64
  '.modernjs/ultramodern-package-source.json',
65
+ 'scripts/bootstrap-agent-skills.mjs',
66
+ 'scripts/setup-agent-reference-repos.mjs',
67
+ 'scripts/check-i18n-strings.mjs',
41
68
  'apps/shell-super-app/package.json',
69
+ 'apps/shell-super-app/config/public/locales/en/translation.json',
70
+ 'apps/shell-super-app/config/public/locales/cs/translation.json',
42
71
  'apps/shell-super-app/modern.config.ts',
43
72
  'apps/shell-super-app/module-federation.config.ts',
73
+ 'apps/shell-super-app/src/modern-app-env.d.ts',
74
+ 'apps/shell-super-app/src/modern.runtime.ts',
75
+ 'apps/shell-super-app/src/routes/[lang]/page.tsx',
44
76
  'apps/remotes/remote-commerce/package.json',
77
+ 'apps/remotes/remote-commerce/config/public/locales/en/translation.json',
78
+ 'apps/remotes/remote-commerce/config/public/locales/cs/translation.json',
45
79
  'apps/remotes/remote-commerce/modern.config.ts',
46
80
  'apps/remotes/remote-commerce/module-federation.config.ts',
81
+ 'apps/remotes/remote-commerce/src/modern-app-env.d.ts',
82
+ 'apps/remotes/remote-commerce/src/modern.runtime.ts',
83
+ 'apps/remotes/remote-commerce/src/routes/[lang]/page.tsx',
47
84
  'apps/remotes/remote-identity/package.json',
85
+ 'apps/remotes/remote-identity/config/public/locales/en/translation.json',
86
+ 'apps/remotes/remote-identity/config/public/locales/cs/translation.json',
48
87
  'apps/remotes/remote-identity/modern.config.ts',
49
88
  'apps/remotes/remote-identity/module-federation.config.ts',
89
+ 'apps/remotes/remote-identity/src/modern-app-env.d.ts',
90
+ 'apps/remotes/remote-identity/src/modern.runtime.ts',
91
+ 'apps/remotes/remote-identity/src/routes/[lang]/page.tsx',
50
92
  'apps/remotes/remote-design-system/package.json',
93
+ 'apps/remotes/remote-design-system/config/public/locales/en/translation.json',
94
+ 'apps/remotes/remote-design-system/config/public/locales/cs/translation.json',
51
95
  'apps/remotes/remote-design-system/modern.config.ts',
52
96
  'apps/remotes/remote-design-system/module-federation.config.ts',
97
+ 'apps/remotes/remote-design-system/src/modern-app-env.d.ts',
98
+ 'apps/remotes/remote-design-system/src/modern.runtime.ts',
99
+ 'apps/remotes/remote-design-system/src/routes/[lang]/page.tsx',
53
100
  'services/service-recommendations-effect/package.json',
54
101
  'services/service-recommendations-effect/modern.config.ts',
55
102
  'services/service-recommendations-effect/api/effect/index.ts',
@@ -63,14 +110,30 @@ for (const requiredPath of requiredPaths) {
63
110
  assertExists(requiredPath);
64
111
  }
65
112
 
113
+ for (const appDirectory of [
114
+ 'apps/shell-super-app',
115
+ 'apps/remotes/remote-commerce',
116
+ 'apps/remotes/remote-identity',
117
+ 'apps/remotes/remote-design-system',
118
+ ]) {
119
+ assert(
120
+ !fs.existsSync(path.join(root, appDirectory, 'src/routes/page.tsx')),
121
+ `${appDirectory} must use src/routes/[lang]/page.tsx`,
122
+ );
123
+ }
124
+
66
125
  const rootPackage = readJson('package.json');
67
126
  const packageSource = readJson('.modernjs/ultramodern-package-source.json');
127
+ const skillsLock = readJson('.agents/skills-lock.json');
128
+ const agentReferenceRepos = readJson('.agents/agent-reference-repos.json');
129
+ const pnpmWorkspace = readText('pnpm-workspace.yaml');
130
+ const workflowContent = readText('.github/workflows/ultramodern-workspace-gates.yml');
131
+ const renovateConfig = readJson('.github/renovate.json');
132
+ const deprecatedTanstackRuntime = '@modern-js/runtime/' + 'tanstack-router';
68
133
  const expectedModernSpecifier =
69
- packageSource.strategy === 'install'
70
- ? packageSource.modernPackages?.specifier
71
- : 'workspace:*';
134
+ packageSource.strategy === 'install' ? packageSource.modernPackages?.specifier : 'workspace:*';
72
135
 
73
- function expectedModernDependency(packageName) {
136
+ const expectedModernDependency = (packageName) => {
74
137
  if (packageSource.strategy !== 'install') {
75
138
  return 'workspace:*';
76
139
  }
@@ -79,13 +142,74 @@ function expectedModernDependency(packageName) {
79
142
  return aliasPackageName
80
143
  ? `npm:${aliasPackageName}@${expectedModernSpecifier}`
81
144
  : expectedModernSpecifier;
82
- }
145
+ };
83
146
 
84
147
  assert(rootPackage.private === true, 'Root package must be private');
85
148
  assert(rootPackage.modernjs?.preset === 'presetUltramodern', 'Root must declare presetUltramodern');
149
+ assert(rootPackage.packageManager === 'pnpm@11.1.2', 'Root must pin pnpm 11.1.2');
150
+ assert(rootPackage.engines?.pnpm === '>=11.0.0', 'Root must require pnpm >=11');
151
+ for (const requiredSnippet of [
152
+ 'packages:\n - apps/*\n - apps/remotes/*\n - services/*\n - packages/*',
153
+ 'minimumReleaseAge: 1440',
154
+ 'minimumReleaseAgeStrict: true',
155
+ 'minimumReleaseAgeIgnoreMissingTime: false',
156
+ "minimumReleaseAgeExclude:\n - '@modern-js/*'\n - '@bleedingdev/*'\n - '@effect/tsgo'\n - '@effect/tsgo-*'\n - '@typescript/native-preview'\n - '@typescript/native-preview-*'",
157
+ 'trustPolicy: no-downgrade',
158
+ 'trustPolicyIgnoreAfter: 1440',
159
+ 'blockExoticSubdeps: true',
160
+ 'engineStrict: true',
161
+ 'pmOnFail: error',
162
+ 'verifyDepsBeforeRun: error',
163
+ 'strictDepBuilds: true',
164
+ "allowBuilds:\n '@swc/core': true\n core-js: true\n esbuild: true\n msgpackr-extract: true\n simple-git-hooks: true",
165
+ ]) {
166
+ assert(
167
+ pnpmWorkspace.includes(requiredSnippet),
168
+ `pnpm-workspace.yaml must retain ${requiredSnippet.split('\n')[0]}`,
169
+ );
170
+ }
171
+ assert(
172
+ !pnpmWorkspace.includes('onlyBuiltDependencies'),
173
+ 'pnpm-workspace.yaml must use pnpm 11 allowBuilds instead of onlyBuiltDependencies',
174
+ );
175
+ for (const requiredSnippet of [
176
+ 'permissions:\n contents: read',
177
+ 'pull_request:',
178
+ 'persist-credentials: false',
179
+ 'pnpm install --frozen-lockfile',
180
+ 'pnpm -r --filter "./apps/**" run build',
181
+ 'MODERN_PUBLIC_SITE_URL: http://localhost:8080',
182
+ 'timeout-minutes:',
183
+ 'egress-policy: audit',
184
+ ]) {
185
+ assert(
186
+ workflowContent.includes(requiredSnippet),
187
+ `Generated workspace workflow must retain ${requiredSnippet.split('\n')[0]}`,
188
+ );
189
+ }
190
+ assert(
191
+ !workflowContent.includes('pull_request_target'),
192
+ 'Generated workspace workflow must not use pull_request_target',
193
+ );
194
+ for (const match of workflowContent.matchAll(/^\s*uses:\s*([^@\s]+)@([^\s#]+)/gmu)) {
195
+ const [, actionName, actionRef] = match;
196
+ assert(
197
+ /^[a-f0-9]{40}$/u.test(actionRef),
198
+ `Generated workspace workflow must pin ${actionName}@${actionRef} to a commit SHA`,
199
+ );
200
+ }
201
+ assert(
202
+ renovateConfig.dependencyDashboard === true &&
203
+ renovateConfig.minimumReleaseAge === '1 day' &&
204
+ renovateConfig.extends?.includes('helpers:pinGitHubActionDigests') &&
205
+ renovateConfig.packageRules?.some(
206
+ (rule) =>
207
+ rule.dependencyDashboardApproval === true && rule.matchUpdateTypes?.includes('major'),
208
+ ),
209
+ 'Generated workspace Renovate config must retain dashboard, release-age, action pinning, and major-approval policy',
210
+ );
86
211
  assert(
87
- rootPackage.modernjs?.packageSource?.config ===
88
- './.modernjs/ultramodern-package-source.json',
212
+ rootPackage.modernjs?.packageSource?.config === './.modernjs/ultramodern-package-source.json',
89
213
  'Root must point to the UltraModern package source metadata',
90
214
  );
91
215
  assert(
@@ -101,21 +225,114 @@ assert(
101
225
  'Generated shared packages must keep workspace:* links',
102
226
  );
103
227
  assert(
104
- modernPackages.every(packageName =>
228
+ modernPackages.every((packageName) =>
105
229
  packageSource.modernPackages?.packages?.includes(packageName),
106
230
  ),
107
231
  'Package source metadata must list all Modern runtime/tooling packages',
108
232
  );
109
233
  assert(
110
- expectedModernSpecifier &&
111
- packageSource.modernPackages?.specifier === expectedModernSpecifier,
234
+ expectedModernSpecifier && packageSource.modernPackages?.specifier === expectedModernSpecifier,
112
235
  'Package source metadata must provide a Modern package specifier',
113
236
  );
237
+
238
+ const requiredRootScripts = {
239
+ format: 'oxfmt .',
240
+ 'format:check': 'oxfmt --check .',
241
+ 'i18n:check': 'node ./scripts/check-i18n-strings.mjs',
242
+ lint: 'oxlint .',
243
+ 'lint:fix': 'oxlint . --fix',
244
+ 'agents:refs:check': 'node ./scripts/setup-agent-reference-repos.mjs --check',
245
+ 'agents:refs:install': 'node ./scripts/setup-agent-reference-repos.mjs',
246
+ postinstall: 'node ./scripts/setup-agent-reference-repos.mjs',
247
+ 'skills:check': 'node ./scripts/bootstrap-agent-skills.mjs --check',
248
+ 'skills:install': 'node ./scripts/bootstrap-agent-skills.mjs',
249
+ typecheck: `pnpm -r --filter "@${packageScope}/*" typecheck`,
250
+ 'ultramodern:check': 'node ./scripts/validate-ultramodern-workspace.mjs',
251
+ };
252
+ for (const [scriptName, scriptCommand] of Object.entries(requiredRootScripts)) {
253
+ assert(rootPackage.scripts?.[scriptName] === scriptCommand, `Root must expose ${scriptName}`);
254
+ }
255
+
256
+ for (const dependency of [
257
+ '@effect/tsgo',
258
+ '@typescript/native-preview',
259
+ 'oxfmt',
260
+ 'oxlint',
261
+ 'ultracite',
262
+ ]) {
263
+ assert(rootPackage.devDependencies?.[dependency], `Root must depend on ${dependency}`);
264
+ }
265
+
266
+ const agentsInstructions = readText('AGENTS.md');
267
+ assert(
268
+ agentsInstructions.includes('UltraModern Agent Contract') &&
269
+ agentsInstructions.includes('Required Skill Baseline'),
270
+ 'Root AGENTS.md must document the UltraModern agent contract',
271
+ );
114
272
  assert(
115
- rootPackage.scripts?.['ultramodern:check'] ===
116
- 'node ./scripts/validate-ultramodern-workspace.mjs',
117
- 'Root must expose the ultramodern:check script',
273
+ skillsLock.source?.repository === 'https://github.com/rstackjs/agent-skills',
274
+ 'Agent skills lock must retain the Rstack skill source repository',
118
275
  );
276
+ assert(
277
+ skillsLock.source?.commit === rstackAgentSkillsCommit,
278
+ 'Agent skills lock must pin the expected Rstack skill commit',
279
+ );
280
+ assert(
281
+ skillsLock.installDir === '.agents/skills',
282
+ 'Agent skills lock must use .agents/skills as installDir',
283
+ );
284
+ assert(
285
+ agentReferenceRepos.defaultEnabled === true,
286
+ 'Agent reference repositories must be enabled by default',
287
+ );
288
+ assert(
289
+ agentReferenceRepos.repositories?.some(
290
+ (repo) =>
291
+ repo.id === 'effect' &&
292
+ repo.url === 'https://github.com/Effect-TS/effect.git' &&
293
+ repo.path === 'repos/effect',
294
+ ),
295
+ 'Agent reference repositories must include Effect',
296
+ );
297
+ assert(
298
+ agentReferenceRepos.repositories?.some(
299
+ (repo) =>
300
+ repo.id === 'ultramodern-js' &&
301
+ repo.url === 'https://github.com/BleedingDev/ultramodern.js.git' &&
302
+ repo.path === 'repos/ultramodern.js',
303
+ ),
304
+ 'Agent reference repositories must include UltraModern.js',
305
+ );
306
+ assert(
307
+ readText('scripts/setup-agent-reference-repos.mjs').includes(
308
+ 'ULTRAMODERN_SKIP_AGENT_REPOS',
309
+ ),
310
+ 'Agent reference repository setup must expose an opt-out environment variable',
311
+ );
312
+ for (const skillName of baselineAgentSkills) {
313
+ assert(
314
+ skillsLock.baseline?.some((skill) => skill.name === skillName),
315
+ `Agent skills lock must include ${skillName}`,
316
+ );
317
+ assert(
318
+ readText(`.agents/skills/${skillName}/SKILL.md`).includes(`name: ${skillName}`),
319
+ `${skillName} must contain matching skill metadata`,
320
+ );
321
+ }
322
+
323
+ const privateSource = skillsLock.sources?.find(
324
+ (source) => source.repository === 'https://github.com/TechsioCZ/skills',
325
+ );
326
+ assert(
327
+ privateSource?.install === 'clone-if-authorized',
328
+ 'Agent skills lock must configure TechsioCZ skills as clone-if-authorized',
329
+ );
330
+ for (const skillName of privateAgentSkills) {
331
+ assert(
332
+ privateSource.baseline?.some((skill) => skill.name === skillName),
333
+ `Agent skills lock must allowlist private skill ${skillName}`,
334
+ );
335
+ }
119
336
 
120
337
  const appPackagePaths = [
121
338
  'apps/shell-super-app/package.json',
@@ -126,31 +343,47 @@ const appPackagePaths = [
126
343
 
127
344
  for (const packagePath of appPackagePaths) {
128
345
  const packageJson = readJson(packagePath);
346
+ assert(
347
+ packageJson.dependencies?.['@modern-js/plugin-i18n'] ===
348
+ expectedModernDependency('@modern-js/plugin-i18n'),
349
+ `${packagePath} must use @modern-js/plugin-i18n through ${expectedModernDependency(
350
+ '@modern-js/plugin-i18n',
351
+ )}`,
352
+ );
129
353
  assert(
130
354
  packageJson.dependencies?.['@modern-js/plugin-tanstack'] ===
131
355
  expectedModernDependency('@modern-js/plugin-tanstack'),
132
- `${packagePath} must depend on @modern-js/plugin-tanstack through ${expectedModernDependency('@modern-js/plugin-tanstack')}`,
356
+ `${packagePath} must use @modern-js/plugin-tanstack through ${expectedModernDependency(
357
+ '@modern-js/plugin-tanstack',
358
+ )}`,
133
359
  );
134
360
  assert(
135
361
  packageJson.dependencies?.['@modern-js/runtime'] ===
136
362
  expectedModernDependency('@modern-js/runtime'),
137
- `${packagePath} must depend on @modern-js/runtime through ${expectedModernDependency('@modern-js/runtime')}`,
363
+ `${packagePath} must use @modern-js/runtime through ${expectedModernDependency(
364
+ '@modern-js/runtime',
365
+ )}`,
138
366
  );
139
367
  assert(
140
368
  packageJson.devDependencies?.['@modern-js/app-tools'] ===
141
369
  expectedModernDependency('@modern-js/app-tools'),
142
- `${packagePath} must depend on @modern-js/app-tools through ${expectedModernDependency('@modern-js/app-tools')}`,
370
+ `${packagePath} must use @modern-js/app-tools through ${expectedModernDependency(
371
+ '@modern-js/app-tools',
372
+ )}`,
143
373
  );
144
374
  assert(
145
- packageJson.dependencies?.[`@${packageScope}/shared-contracts`] ===
146
- 'workspace:*',
375
+ packageJson.dependencies?.[`@${packageScope}/shared-contracts`] === 'workspace:*',
147
376
  `${packagePath} must link generated shared contracts through workspace:*`,
148
377
  );
149
378
  assert(
150
- packageJson.dependencies?.[`@${packageScope}/shared-design-tokens`] ===
151
- 'workspace:*',
379
+ packageJson.dependencies?.[`@${packageScope}/shared-design-tokens`] === 'workspace:*',
152
380
  `${packagePath} must link generated shared design tokens through workspace:*`,
153
381
  );
382
+ assert(packageJson.dependencies?.i18next === '26.2.0', `${packagePath} must include i18next`);
383
+ assert(
384
+ packageJson.dependencies?.['react-i18next'] === '17.0.8',
385
+ `${packagePath} must include react-i18next`,
386
+ );
154
387
  assert(
155
388
  packageJson.dependencies?.['@tanstack/react-router'] === tanstackVersion,
156
389
  `${packagePath} must use @tanstack/react-router ${tanstackVersion}`,
@@ -159,6 +392,10 @@ for (const packagePath of appPackagePaths) {
159
392
  packageJson.dependencies?.['@module-federation/modern-js-v3'] === '2.4.0',
160
393
  `${packagePath} must include the Module Federation plugin`,
161
394
  );
395
+ assert(
396
+ packageJson.dependencies?.['zephyr-modernjs-plugin'] === '1.1.1',
397
+ `${packagePath} must include the official Zephyr Modern.js plugin`,
398
+ );
162
399
  assert(
163
400
  packageJson.modernjs?.preset === 'presetUltramodern',
164
401
  `${packagePath} must keep presetUltramodern metadata`,
@@ -173,57 +410,133 @@ for (const configPath of [
173
410
  ]) {
174
411
  const config = readText(configPath);
175
412
  assert(config.includes('presetUltramodern('), `${configPath} must use presetUltramodern`);
413
+ assert(config.includes('i18nPlugin('), `${configPath} must enable plugin-i18n`);
414
+ assert(config.includes('localePathRedirect: true'), `${configPath} must prefix localized URLs`);
415
+ assert(config.includes('ULTRAMODERN_SITE_URL'), `${configPath} must expose site URL metadata`);
416
+ assert(
417
+ config.includes('MODERN_PUBLIC_SITE_URL must be set for production builds'),
418
+ `${configPath} must require MODERN_PUBLIC_SITE_URL for production builds`,
419
+ );
176
420
  assert(config.includes('tanstackRouterPlugin()'), `${configPath} must enable plugin-tanstack`);
177
- assert(config.includes('moduleFederationPlugin()'), `${configPath} must enable Module Federation`);
421
+ assert(
422
+ config.includes('moduleFederationPlugin()'),
423
+ `${configPath} must enable Module Federation`,
424
+ );
425
+ assert(
426
+ config.includes("from 'zephyr-modernjs-plugin'"),
427
+ `${configPath} must import the official Zephyr Modern.js plugin`,
428
+ );
429
+ assert(config.includes('withZephyr()'), `${configPath} must enable Zephyr through plugins`);
430
+ assert(
431
+ config.includes("bundler: 'rspack'"),
432
+ `${configPath} must keep appTools on the rspack bundler for Zephyr`,
433
+ );
434
+ assert(
435
+ config.includes("html: './'"),
436
+ `${configPath} must keep Modern.js output.distPath.html compatible with Zephyr`,
437
+ );
438
+ assert(
439
+ config.includes("outputStructure: 'flat'"),
440
+ `${configPath} must keep Modern.js HTML output flat for Zephyr`,
441
+ );
442
+ assert(
443
+ config.includes("mainEntryName: 'index'"),
444
+ `${configPath} must keep Modern.js source.mainEntryName compatible with Zephyr`,
445
+ );
446
+ }
447
+
448
+ for (const routePath of [
449
+ 'apps/shell-super-app/src/routes/[lang]/page.tsx',
450
+ 'apps/remotes/remote-commerce/src/routes/[lang]/page.tsx',
451
+ 'apps/remotes/remote-identity/src/routes/[lang]/page.tsx',
452
+ 'apps/remotes/remote-design-system/src/routes/[lang]/page.tsx',
453
+ ]) {
454
+ const route = readText(routePath);
455
+ assert(route.includes('rel="canonical"'), `${routePath} must emit canonical metadata`);
456
+ assert(route.includes('rel="alternate"'), `${routePath} must emit alternate locale metadata`);
457
+ assert(route.includes('hrefLang="x-default"'), `${routePath} must emit x-default metadata`);
458
+ assert(route.includes('localizedPath('), `${routePath} must build localized URLs`);
459
+ assert(
460
+ route.includes('@modern-js/plugin-tanstack/runtime'),
461
+ `${routePath} must import TanStack runtime from @modern-js/plugin-tanstack/runtime`,
462
+ );
463
+ assert(
464
+ !route.includes(deprecatedTanstackRuntime),
465
+ `${routePath} must not import deprecated TanStack runtime path`,
466
+ );
178
467
  }
179
468
 
180
469
  const shellMf = readText('apps/shell-super-app/module-federation.config.ts');
181
470
  assert(shellMf.includes("name: 'shellSuperApp'"), 'Shell MF config must name the shell');
182
- assert(shellMf.includes('remoteCommerce@http://localhost:3021/mf-manifest.json'), 'Shell must reference commerce remote');
183
- assert(shellMf.includes('remoteIdentity@http://localhost:3022/mf-manifest.json'), 'Shell must reference identity remote');
184
- assert(shellMf.includes('remoteDesignSystem@http://localhost:3023/mf-manifest.json'), 'Shell must reference design-system remote');
471
+ assert(
472
+ shellMf.includes('remoteCommerce@http://localhost:3021/mf-manifest.json'),
473
+ 'Shell must reference commerce remote',
474
+ );
475
+ assert(
476
+ shellMf.includes('remoteIdentity@http://localhost:3022/mf-manifest.json'),
477
+ 'Shell must reference identity remote',
478
+ );
479
+ assert(
480
+ shellMf.includes('remoteDesignSystem@http://localhost:3023/mf-manifest.json'),
481
+ 'Shell must reference design-system remote',
482
+ );
185
483
 
186
484
  const commerceMf = readText('apps/remotes/remote-commerce/module-federation.config.ts');
187
485
  assert(commerceMf.includes("name: 'remoteCommerce'"), 'Commerce remote MF name is missing');
188
- assert(commerceMf.includes('"./Widget"'), 'Commerce remote must expose a widget');
486
+ assert(commerceMf.includes("'./Widget'"), 'Commerce remote must expose a widget');
189
487
 
190
488
  const designMf = readText('apps/remotes/remote-design-system/module-federation.config.ts');
191
489
  assert(designMf.includes("name: 'remoteDesignSystem'"), 'Design-system remote MF name is missing');
192
- assert(designMf.includes('"./Button"'), 'Design-system remote must expose Button');
193
- assert(designMf.includes('"./tokens"'), 'Design-system remote must expose tokens');
490
+ assert(designMf.includes("'./Button'"), 'Design-system remote must expose Button');
491
+ assert(designMf.includes("'./tokens'"), 'Design-system remote must expose tokens');
194
492
 
195
493
  const servicePackage = readJson('services/service-recommendations-effect/package.json');
196
494
  assert(
197
495
  servicePackage.dependencies?.['@modern-js/runtime'] ===
198
496
  expectedModernDependency('@modern-js/runtime'),
199
- `Effect service must use @modern-js/runtime through ${expectedModernDependency('@modern-js/runtime')}`,
497
+ `Effect service must use @modern-js/runtime through ${expectedModernDependency(
498
+ '@modern-js/runtime',
499
+ )}`,
200
500
  );
201
501
  assert(
202
502
  servicePackage.devDependencies?.['@modern-js/app-tools'] ===
203
503
  expectedModernDependency('@modern-js/app-tools'),
204
- `Effect service must use @modern-js/app-tools through ${expectedModernDependency('@modern-js/app-tools')}`,
504
+ `Effect service must use @modern-js/app-tools through ${expectedModernDependency(
505
+ '@modern-js/app-tools',
506
+ )}`,
205
507
  );
206
508
  assert(
207
509
  servicePackage.devDependencies?.['@modern-js/plugin-bff'] ===
208
510
  expectedModernDependency('@modern-js/plugin-bff'),
209
- `Effect service must use @modern-js/plugin-bff through ${expectedModernDependency('@modern-js/plugin-bff')}`,
511
+ `Effect service must use @modern-js/plugin-bff through ${expectedModernDependency(
512
+ '@modern-js/plugin-bff',
513
+ )}`,
210
514
  );
211
515
  assert(
212
- servicePackage.dependencies?.[`@${packageScope}/shared-effect-api`] ===
213
- 'workspace:*',
516
+ servicePackage.dependencies?.[`@${packageScope}/shared-effect-api`] === 'workspace:*',
214
517
  'Effect service must link generated shared Effect API through workspace:*',
215
518
  );
216
519
 
217
520
  const serviceConfig = readText('services/service-recommendations-effect/modern.config.ts');
218
- assert(serviceConfig.includes("runtimeFramework: 'effect'"), 'Effect service must use Effect runtime');
521
+ assert(
522
+ serviceConfig.includes("runtimeFramework: 'effect'"),
523
+ 'Effect service must use Effect runtime',
524
+ );
219
525
  assert(serviceConfig.includes('bffPlugin()'), 'Effect service must enable bffPlugin');
220
526
 
221
527
  const serviceEntry = readText('services/service-recommendations-effect/api/effect/index.ts');
222
- assert(serviceEntry.includes('defineEffectBff'), 'Effect service must expose defineEffectBff placeholder');
223
- assert(serviceEntry.includes('recommendationsEffectApi'), 'Effect service must use shared recommendations API');
224
-
225
- const serviceApi = readText('services/service-recommendations-effect/shared/effect/api.ts');
226
- assert(serviceApi.includes('HttpApi.make'), 'Effect shared API placeholder must define HttpApi');
528
+ assert(
529
+ serviceEntry.includes('defineEffectBff'),
530
+ 'Effect service must expose defineEffectBff placeholder',
531
+ );
532
+ assert(
533
+ serviceEntry.includes('recommendationsEffectApi'),
534
+ 'Effect service must use shared recommendations API',
535
+ );
536
+ assert(
537
+ readText('services/service-recommendations-effect/shared/effect/api.ts').includes('HttpApi.make'),
538
+ 'Effect shared API placeholder must define HttpApi',
539
+ );
227
540
 
228
541
  const topology = readJson('topology/reference-topology.json');
229
542
  assert(topology.preset === 'presetUltramodern', 'Topology must reference presetUltramodern');
@@ -232,27 +545,26 @@ assert(topology.shell?.remoteRefs?.length === 3, 'Topology shell must reference
232
545
  assert(topology.remotes?.length === 3, 'Topology must contain three remotes');
233
546
  assert(
234
547
  topology.remotes.some(
235
- remote =>
236
- remote.id === 'remote-design-system' &&
237
- remote.kind === 'horizontal-design-system',
548
+ (remote) => remote.id === 'remote-design-system' && remote.kind === 'horizontal-design-system',
238
549
  ),
239
550
  'Topology must contain the horizontal design-system remote',
240
551
  );
241
- assert(topology.effectServices?.[0]?.runtime === 'effect', 'Topology must contain an Effect service');
552
+ assert(
553
+ topology.effectServices?.[0]?.runtime === 'effect',
554
+ 'Topology must contain an Effect service',
555
+ );
242
556
  assert(topology.sharedPackages?.length === 3, 'Topology must contain shared package placeholders');
243
557
 
244
558
  const ownership = readJson('topology/ownership.json');
245
559
  assert(
246
560
  ownership.owners?.some(
247
- owner =>
248
- owner.id === 'remote-commerce' &&
249
- owner.ownership?.team === 'commerce-experience',
561
+ (owner) => owner.id === 'remote-commerce' && owner.ownership?.team === 'commerce-experience',
250
562
  ),
251
563
  'Ownership metadata must retain commerce owner',
252
564
  );
253
565
  assert(
254
566
  ownership.owners?.some(
255
- owner =>
567
+ (owner) =>
256
568
  owner.id === 'service-recommendations-effect' &&
257
569
  owner.package === `@${packageScope}/service-recommendations-effect`,
258
570
  ),
@@ -268,9 +580,35 @@ assert(
268
580
  manifest.packageSource?.strategy === packageSource.strategy,
269
581
  'Template manifest must retain the generated package source strategy',
270
582
  );
583
+ assert(
584
+ manifest.agentSkills?.source?.commit === rstackAgentSkillsCommit,
585
+ 'Template manifest must retain the Rstack agent skills commit',
586
+ );
587
+ assert(
588
+ baselineAgentSkills.every((skillName) => manifest.agentSkills?.baseline?.includes(skillName)),
589
+ 'Template manifest must list every baseline agent skill',
590
+ );
591
+ assert(
592
+ manifest.agentSkills?.privateSource?.repository === 'https://github.com/TechsioCZ/skills',
593
+ 'Template manifest must retain the private TechsioCZ skill source',
594
+ );
595
+ assert(
596
+ privateAgentSkills.every((skillName) =>
597
+ manifest.agentSkills?.privateSource?.baseline?.includes(skillName),
598
+ ),
599
+ 'Template manifest must list every private agent skill allowlist entry',
600
+ );
271
601
  assert(
272
602
  manifest.validation?.expectedCommands?.includes('pnpm run ultramodern:check'),
273
603
  'Template manifest must document the validation command',
274
604
  );
605
+ assert(
606
+ manifest.validation?.postMaterializationValidation?.includes('pnpm-11-policy-enforced'),
607
+ 'Template manifest must document pnpm 11 policy validation',
608
+ );
609
+ assert(
610
+ manifest.validation?.postMaterializationValidation?.includes('github-workflow-security-enforced'),
611
+ 'Template manifest must document generated workflow security validation',
612
+ );
275
613
 
276
614
  console.log('UltraModern workspace scaffold validated');
@@ -1,41 +0,0 @@
1
- {
2
- "root": false,
3
- "$schema": "https://biomejs.dev/schemas/2.4.15/schema.json",
4
- "vcs": {
5
- "enabled": true,
6
- "defaultBranch": "main",
7
- "clientKind": "git",
8
- "useIgnoreFile": true
9
- },
10
- "formatter": {
11
- "enabled": true,
12
- "indentStyle": "space"
13
- },
14
- "javascript": {
15
- "formatter": {
16
- "quoteStyle": "single",
17
- "arrowParentheses": "asNeeded",
18
- "jsxQuoteStyle": "double",
19
- "lineWidth": 80
20
- }
21
- },
22
- "linter": {
23
- "enabled": true,
24
- "rules": {
25
- "recommended": true,
26
- "suspicious": {
27
- "noDuplicateFontNames": "off"
28
- }
29
- }
30
- },
31
- "assist": { "actions": { "source": { "organizeImports": "on" } } },
32
- "files": {
33
- "ignoreUnknown": true,
34
- "includes": [
35
- "**",
36
- "!**/.vscode/**/*",
37
- "!**/node_modules/**/*",
38
- "!**/dist/**/*"
39
- ]
40
- }
41
- }