@bleedingdev/modern-js-create 3.2.0-ultramodern.10 → 3.2.0-ultramodern.101
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 +152 -35
- package/bin/run.js +0 -0
- package/dist/index.js +4745 -604
- package/dist/types/locale/en.d.ts +3 -0
- package/dist/types/locale/zh.d.ts +3 -0
- package/dist/types/ultramodern-workspace.d.ts +11 -0
- package/package.json +6 -6
- package/template/.codex/hooks.json +16 -0
- package/template/.github/renovate.json +53 -0
- package/template/.github/workflows/ultramodern-gates.yml.handlebars +34 -10
- package/template/.mise.toml.handlebars +2 -0
- package/template/AGENTS.md +9 -6
- package/template/README.md +60 -34
- package/template/api/effect/index.ts.handlebars +20 -9
- package/template/api/lambda/hello.ts.handlebars +5 -5
- package/template/config/public/locales/cs/translation.json +39 -0
- package/template/config/public/locales/en/translation.json +39 -0
- package/template/lefthook.yml +10 -0
- package/template/modern.config.ts.handlebars +27 -1
- package/template/oxfmt.config.ts +8 -1
- package/template/oxlint.config.ts +8 -1
- package/template/package.json.handlebars +30 -26
- package/template/pnpm-workspace.yaml +29 -0
- package/template/rstest.config.mts +5 -0
- package/template/scripts/bootstrap-agent-skills.mjs +148 -15
- package/template/scripts/check-i18n-strings.mjs +94 -0
- package/template/scripts/validate-ultramodern.mjs.handlebars +374 -2
- package/template/src/modern-app-env.d.ts +2 -0
- package/template/src/modern.runtime.ts.handlebars +17 -1
- package/template/src/routes/[lang]/page.tsx.handlebars +211 -0
- package/template/src/routes/layout.tsx.handlebars +2 -1
- package/template/tailwind.config.ts.handlebars +1 -1
- package/template/tests/tsconfig.json +7 -0
- package/template/tests/ultramodern.contract.test.ts.handlebars +78 -0
- package/template/tsconfig.json +1 -0
- package/template-workspace/.agents/agent-reference-repos.json +24 -0
- package/template-workspace/.agents/skills-lock.json +19 -0
- package/template-workspace/.codex/hooks.json +16 -0
- package/template-workspace/.github/renovate.json +29 -0
- package/template-workspace/.github/workflows/ultramodern-workspace-gates.yml.handlebars +54 -0
- package/template-workspace/.gitignore.handlebars +5 -0
- package/template-workspace/.mise.toml.handlebars +2 -0
- package/template-workspace/AGENTS.md +36 -5
- package/template-workspace/README.md.handlebars +61 -11
- package/template-workspace/lefthook.yml +10 -0
- package/template-workspace/oxfmt.config.ts +13 -3
- package/template-workspace/oxlint.config.ts +12 -4
- package/template-workspace/pnpm-workspace.yaml +26 -8
- package/template-workspace/scripts/bootstrap-agent-skills.mjs +184 -26
- package/template-workspace/scripts/setup-agent-reference-repos.mjs +368 -0
- package/template/src/routes/page.tsx.handlebars +0 -136
- package/template-workspace/scripts/validate-ultramodern-workspace.mjs.handlebars +0 -403
|
@@ -1,8 +1,41 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
1
2
|
import fs from 'node:fs';
|
|
2
3
|
import path from 'node:path';
|
|
3
4
|
|
|
4
5
|
const configPath = path.resolve(process.cwd(), 'modern.config.ts');
|
|
5
6
|
const templateManifestPath = path.resolve(process.cwd(), '.modernjs/mv-template-manifest.json');
|
|
7
|
+
const packageSourcePath = path.resolve(
|
|
8
|
+
process.cwd(),
|
|
9
|
+
'.modernjs/ultramodern-package-source.json',
|
|
10
|
+
);
|
|
11
|
+
const readPnpmConfig = (key) => {
|
|
12
|
+
const env = Object.fromEntries(
|
|
13
|
+
Object.entries(process.env).filter(
|
|
14
|
+
([envKey]) => !/^(?:npm|pnpm)_config_/iu.test(envKey),
|
|
15
|
+
),
|
|
16
|
+
);
|
|
17
|
+
const output = execFileSync('pnpm', ['config', 'get', key, '--json'], {
|
|
18
|
+
cwd: process.cwd(),
|
|
19
|
+
encoding: 'utf-8',
|
|
20
|
+
env,
|
|
21
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
22
|
+
}).trim();
|
|
23
|
+
return output ? JSON.parse(output) : undefined;
|
|
24
|
+
};
|
|
25
|
+
const enableTailwind = {{enableTailwind}};
|
|
26
|
+
const expectedPnpmVersion = '{{pnpmVersion}}';
|
|
27
|
+
const activePnpmVersion = execFileSync('pnpm', ['--version'], {
|
|
28
|
+
cwd: process.cwd(),
|
|
29
|
+
encoding: 'utf-8',
|
|
30
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
31
|
+
}).trim();
|
|
32
|
+
|
|
33
|
+
if (activePnpmVersion !== expectedPnpmVersion) {
|
|
34
|
+
console.error(
|
|
35
|
+
`Generated app requires pnpm ${expectedPnpmVersion}; active pnpm is ${activePnpmVersion}. Run mise install, then rerun pnpm from the activated shell`,
|
|
36
|
+
);
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
6
39
|
|
|
7
40
|
if (!fs.existsSync(configPath)) {
|
|
8
41
|
console.error('modern.config.ts not found');
|
|
@@ -16,6 +49,11 @@ const requiredTokens = [
|
|
|
16
49
|
'enableModuleFederationSSR',
|
|
17
50
|
'enableBffRequestId',
|
|
18
51
|
'enableTelemetryExporters',
|
|
52
|
+
'i18nPlugin(',
|
|
53
|
+
'localePathRedirect: true',
|
|
54
|
+
'ULTRAMODERN_SITE_URL',
|
|
55
|
+
'MODERN_PUBLIC_SITE_URL must be set for production builds',
|
|
56
|
+
'globalVars',
|
|
19
57
|
];
|
|
20
58
|
const missing = requiredTokens.filter((token) => !content.includes(token));
|
|
21
59
|
|
|
@@ -32,7 +70,6 @@ if (!fs.existsSync(templateManifestPath)) {
|
|
|
32
70
|
const templateManifest = JSON.parse(fs.readFileSync(templateManifestPath, 'utf-8'));
|
|
33
71
|
const requiredDeniedPaths = [
|
|
34
72
|
'.git/**',
|
|
35
|
-
'.github/**',
|
|
36
73
|
'.npmrc',
|
|
37
74
|
'.yarnrc',
|
|
38
75
|
'.env',
|
|
@@ -42,15 +79,41 @@ const requiredDeniedPaths = [
|
|
|
42
79
|
];
|
|
43
80
|
const requiredPostMaterialization = [
|
|
44
81
|
'ultramodern-contract-check',
|
|
45
|
-
'
|
|
82
|
+
'agent-skill-postinstall-allowed',
|
|
83
|
+
'github-workflow-security-enforced',
|
|
84
|
+
'package-source-retained',
|
|
85
|
+
'pnpm-11-policy-enforced',
|
|
86
|
+
'rstest-smoke-tests',
|
|
46
87
|
'template-manifest-retained',
|
|
47
88
|
];
|
|
48
89
|
const requiredPaths = [
|
|
90
|
+
{{#unless isSubproject}}
|
|
49
91
|
'AGENTS.md',
|
|
50
92
|
'.agents/skills-lock.json',
|
|
93
|
+
'.codex/hooks.json',
|
|
94
|
+
'.github/renovate.json',
|
|
95
|
+
'.github/workflows/ultramodern-gates.yml',
|
|
96
|
+
'lefthook.yml',
|
|
51
97
|
'oxlint.config.ts',
|
|
52
98
|
'oxfmt.config.ts',
|
|
53
99
|
'scripts/bootstrap-agent-skills.mjs',
|
|
100
|
+
{{/unless}}
|
|
101
|
+
'.mise.toml',
|
|
102
|
+
'.modernjs/ultramodern-package-source.json',
|
|
103
|
+
'pnpm-workspace.yaml',
|
|
104
|
+
'rstest.config.mts',
|
|
105
|
+
'scripts/check-i18n-strings.mjs',
|
|
106
|
+
{{#if enableTailwind}}
|
|
107
|
+
'postcss.config.mjs',
|
|
108
|
+
'tailwind.config.ts',
|
|
109
|
+
{{/if}}
|
|
110
|
+
'config/public/locales/en/translation.json',
|
|
111
|
+
'config/public/locales/cs/translation.json',
|
|
112
|
+
'src/modern-app-env.d.ts',
|
|
113
|
+
'src/routes/index.css',
|
|
114
|
+
'src/routes/layout.tsx',
|
|
115
|
+
'src/routes/[lang]/page.tsx',
|
|
116
|
+
'tests/ultramodern.contract.test.ts',
|
|
54
117
|
];
|
|
55
118
|
const manifestErrors = [];
|
|
56
119
|
|
|
@@ -61,6 +124,70 @@ for (const requiredPath of requiredPaths) {
|
|
|
61
124
|
}
|
|
62
125
|
}
|
|
63
126
|
|
|
127
|
+
if (fs.existsSync(path.resolve(process.cwd(), 'src/routes/page.tsx'))) {
|
|
128
|
+
console.error('src/routes/page.tsx must move under src/routes/[lang]/page.tsx');
|
|
129
|
+
process.exit(1);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (
|
|
133
|
+
!enableTailwind &&
|
|
134
|
+
(fs.existsSync(path.resolve(process.cwd(), 'postcss.config.mjs')) ||
|
|
135
|
+
fs.existsSync(path.resolve(process.cwd(), 'tailwind.config.ts')))
|
|
136
|
+
) {
|
|
137
|
+
console.error('Tailwind config files must not be written when Tailwind is disabled');
|
|
138
|
+
process.exit(1);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
{{#unless isSubproject}}
|
|
142
|
+
const workflowContent = fs.readFileSync(
|
|
143
|
+
path.resolve(process.cwd(), '.github/workflows/ultramodern-gates.yml'),
|
|
144
|
+
'utf-8',
|
|
145
|
+
);
|
|
146
|
+
const renovateConfig = JSON.parse(
|
|
147
|
+
fs.readFileSync(path.resolve(process.cwd(), '.github/renovate.json'), 'utf-8'),
|
|
148
|
+
);
|
|
149
|
+
for (const requiredSnippet of [
|
|
150
|
+
'permissions:\n contents: read',
|
|
151
|
+
'pull_request:',
|
|
152
|
+
'persist-credentials: false',
|
|
153
|
+
'jdx/mise-action',
|
|
154
|
+
'pnpm install --frozen-lockfile',
|
|
155
|
+
'pnpm run ultramodern:check',
|
|
156
|
+
'pnpm run build',
|
|
157
|
+
'MODERN_PUBLIC_SITE_URL: http://localhost:8080',
|
|
158
|
+
'timeout-minutes:',
|
|
159
|
+
'egress-policy: audit',
|
|
160
|
+
]) {
|
|
161
|
+
if (!workflowContent.includes(requiredSnippet)) {
|
|
162
|
+
console.error(`Generated workflow must retain ${requiredSnippet.split('\n')[0]}`);
|
|
163
|
+
process.exit(1);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
if (workflowContent.includes('pull_request_target')) {
|
|
167
|
+
console.error('Generated workflow must not use pull_request_target');
|
|
168
|
+
process.exit(1);
|
|
169
|
+
}
|
|
170
|
+
for (const match of workflowContent.matchAll(/^\s*uses:\s*([^@\s]+)@([^\s#]+)/gmu)) {
|
|
171
|
+
const [, actionName, actionRef] = match;
|
|
172
|
+
if (!/^[a-f0-9]{40}$/u.test(actionRef)) {
|
|
173
|
+
console.error(`Generated workflow must pin ${actionName}@${actionRef} to a commit SHA`);
|
|
174
|
+
process.exit(1);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
if (
|
|
178
|
+
renovateConfig.dependencyDashboard !== true ||
|
|
179
|
+
renovateConfig.minimumReleaseAge !== '1 day' ||
|
|
180
|
+
!renovateConfig.extends?.includes('helpers:pinGitHubActionDigests') ||
|
|
181
|
+
!renovateConfig.packageRules?.some(
|
|
182
|
+
(rule) =>
|
|
183
|
+
rule.dependencyDashboardApproval === true && rule.matchUpdateTypes?.includes('major'),
|
|
184
|
+
)
|
|
185
|
+
) {
|
|
186
|
+
console.error('Generated Renovate config must retain dashboard, release-age, action pinning, and major-approval policy');
|
|
187
|
+
process.exit(1);
|
|
188
|
+
}
|
|
189
|
+
{{/unless}}
|
|
190
|
+
|
|
64
191
|
if (templateManifest.schemaVersion !== 1) {
|
|
65
192
|
manifestErrors.push('schemaVersion');
|
|
66
193
|
}
|
|
@@ -90,6 +217,12 @@ for (const deniedPath of requiredDeniedPaths) {
|
|
|
90
217
|
if (templateManifest.lifecyclePolicy?.denyByDefault !== true) {
|
|
91
218
|
manifestErrors.push('lifecyclePolicy.denyByDefault');
|
|
92
219
|
}
|
|
220
|
+
if (
|
|
221
|
+
JSON.stringify(templateManifest.lifecyclePolicy?.allowedScripts) !==
|
|
222
|
+
JSON.stringify(['postinstall'])
|
|
223
|
+
) {
|
|
224
|
+
manifestErrors.push('lifecyclePolicy.allowedScripts');
|
|
225
|
+
}
|
|
93
226
|
|
|
94
227
|
for (const token of requiredPostMaterialization) {
|
|
95
228
|
if (!templateManifest.validation?.postMaterializationValidation?.includes(token)) {
|
|
@@ -107,21 +240,35 @@ if (manifestErrors.length > 0) {
|
|
|
107
240
|
const packageJson = JSON.parse(
|
|
108
241
|
fs.readFileSync(path.resolve(process.cwd(), 'package.json'), 'utf-8'),
|
|
109
242
|
);
|
|
243
|
+
const packageSource = JSON.parse(fs.readFileSync(packageSourcePath, 'utf-8'));
|
|
110
244
|
const unresolvedTemplateMarker = String.fromCodePoint(123, 123);
|
|
111
245
|
if (JSON.stringify(packageJson).includes(unresolvedTemplateMarker)) {
|
|
112
246
|
console.error('package.json contains unresolved template markers');
|
|
113
247
|
process.exit(1);
|
|
114
248
|
}
|
|
249
|
+
if (JSON.stringify(packageSource).includes(unresolvedTemplateMarker)) {
|
|
250
|
+
console.error('package source metadata contains unresolved template markers');
|
|
251
|
+
process.exit(1);
|
|
252
|
+
}
|
|
253
|
+
{{#unless isSubproject}}
|
|
115
254
|
const skillsLock = JSON.parse(
|
|
116
255
|
fs.readFileSync(path.resolve(process.cwd(), '.agents/skills-lock.json'), 'utf-8'),
|
|
117
256
|
);
|
|
257
|
+
{{/unless}}
|
|
118
258
|
const requiredScripts = {
|
|
259
|
+
{{#unless isSubproject}}
|
|
119
260
|
format: 'oxfmt .',
|
|
120
261
|
'format:check': 'oxfmt --check .',
|
|
262
|
+
{{/unless}}
|
|
263
|
+
'i18n:check': 'node ./scripts/check-i18n-strings.mjs',
|
|
264
|
+
{{#unless isSubproject}}
|
|
121
265
|
lint: 'oxlint .',
|
|
122
266
|
'lint:fix': 'oxlint . --fix',
|
|
267
|
+
postinstall: 'oxfmt . && node ./scripts/bootstrap-agent-skills.mjs',
|
|
123
268
|
'skills:check': 'node ./scripts/bootstrap-agent-skills.mjs --check',
|
|
124
269
|
'skills:install': 'node ./scripts/bootstrap-agent-skills.mjs',
|
|
270
|
+
{{/unless}}
|
|
271
|
+
test: 'rstest run',
|
|
125
272
|
};
|
|
126
273
|
|
|
127
274
|
for (const [scriptName, scriptCommand] of Object.entries(requiredScripts)) {
|
|
@@ -139,12 +286,215 @@ if (
|
|
|
139
286
|
process.exit(1);
|
|
140
287
|
}
|
|
141
288
|
|
|
289
|
+
if (!packageJson.scripts?.['ultramodern:check']?.includes('pnpm test')) {
|
|
290
|
+
console.error('ultramodern:check must run the generated Rstest suite');
|
|
291
|
+
process.exit(1);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
if (packageJson.private !== true) {
|
|
295
|
+
console.error('Generated app package must be private by default');
|
|
296
|
+
process.exit(1);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const miseConfig = fs.readFileSync(path.resolve(process.cwd(), '.mise.toml'), 'utf-8');
|
|
300
|
+
if (!miseConfig.includes(`pnpm = "${expectedPnpmVersion}"`)) {
|
|
301
|
+
console.error(`Generated app must pin pnpm ${expectedPnpmVersion} in .mise.toml`);
|
|
302
|
+
process.exit(1);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (packageJson.packageManager !== `pnpm@${expectedPnpmVersion}`) {
|
|
306
|
+
console.error(`Generated app package must pin pnpm@${expectedPnpmVersion}`);
|
|
307
|
+
process.exit(1);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
if (packageJson.engines?.pnpm !== `>=${expectedPnpmVersion} <11.6.0`) {
|
|
311
|
+
console.error(`Generated app package must require pnpm >=${expectedPnpmVersion} <11.6.0`);
|
|
312
|
+
process.exit(1);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
if (packageJson.pnpm !== undefined) {
|
|
316
|
+
console.error('Generated app must keep pnpm policy in pnpm-workspace.yaml, not package.json');
|
|
317
|
+
process.exit(1);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
if (readPnpmConfig('minimumReleaseAge') !== 1440) {
|
|
321
|
+
console.error('pnpm minimumReleaseAge must be 1440');
|
|
322
|
+
process.exit(1);
|
|
323
|
+
}
|
|
324
|
+
if (readPnpmConfig('minimumReleaseAgeStrict') !== true) {
|
|
325
|
+
console.error('pnpm minimumReleaseAgeStrict must be true');
|
|
326
|
+
process.exit(1);
|
|
327
|
+
}
|
|
328
|
+
if (readPnpmConfig('minimumReleaseAgeIgnoreMissingTime') !== false) {
|
|
329
|
+
console.error('pnpm minimumReleaseAgeIgnoreMissingTime must be false');
|
|
330
|
+
process.exit(1);
|
|
331
|
+
}
|
|
332
|
+
if (
|
|
333
|
+
JSON.stringify(readPnpmConfig('minimumReleaseAgeExclude')) !==
|
|
334
|
+
JSON.stringify(['@bleedingdev/modern-js-*'])
|
|
335
|
+
) {
|
|
336
|
+
console.error('pnpm minimumReleaseAgeExclude must allow just-published BleedingDev Modern cohorts only');
|
|
337
|
+
process.exit(1);
|
|
338
|
+
}
|
|
339
|
+
if (readPnpmConfig('trustPolicy') !== 'no-downgrade') {
|
|
340
|
+
console.error('pnpm trustPolicy must be no-downgrade');
|
|
341
|
+
process.exit(1);
|
|
342
|
+
}
|
|
343
|
+
for (const [key, expected] of [
|
|
344
|
+
['trustPolicyIgnoreAfter', 1440],
|
|
345
|
+
['blockExoticSubdeps', true],
|
|
346
|
+
['engineStrict', true],
|
|
347
|
+
['pmOnFail', 'error'],
|
|
348
|
+
['verifyDepsBeforeRun', 'error'],
|
|
349
|
+
['strictDepBuilds', true],
|
|
350
|
+
]) {
|
|
351
|
+
if (readPnpmConfig(key) !== expected) {
|
|
352
|
+
console.error(`pnpm ${key} must be ${String(expected)}`);
|
|
353
|
+
process.exit(1);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
if (
|
|
357
|
+
JSON.stringify(readPnpmConfig('allowBuilds')) !==
|
|
358
|
+
JSON.stringify({
|
|
359
|
+
'@swc/core': true,
|
|
360
|
+
'core-js': true,
|
|
361
|
+
esbuild: true,
|
|
362
|
+
lefthook: true,
|
|
363
|
+
'msgpackr-extract': true,
|
|
364
|
+
sharp: true,
|
|
365
|
+
workerd: true,
|
|
366
|
+
})
|
|
367
|
+
) {
|
|
368
|
+
console.error('pnpm allowBuilds must approve only the generated app build dependencies');
|
|
369
|
+
process.exit(1);
|
|
370
|
+
}
|
|
371
|
+
if (
|
|
372
|
+
JSON.stringify(readPnpmConfig('onlyBuiltDependencies')) !==
|
|
373
|
+
JSON.stringify(['@swc/core', 'core-js', 'esbuild', 'lefthook', 'msgpackr-extract', 'sharp', 'workerd'])
|
|
374
|
+
) {
|
|
375
|
+
console.error('pnpm onlyBuiltDependencies must approve only the generated app build dependencies');
|
|
376
|
+
process.exit(1);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
if (packageJson.modernjs?.preset !== 'presetUltramodern') {
|
|
380
|
+
console.error('package.json must declare presetUltramodern metadata');
|
|
381
|
+
process.exit(1);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
if (
|
|
385
|
+
packageJson.modernjs?.packageSource?.config !== './.modernjs/ultramodern-package-source.json'
|
|
386
|
+
) {
|
|
387
|
+
console.error('package.json must retain package source metadata location');
|
|
388
|
+
process.exit(1);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
if (packageSource.schemaVersion !== 1) {
|
|
392
|
+
console.error('Package source metadata must use schemaVersion 1');
|
|
393
|
+
process.exit(1);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
if (packageSource.preset !== 'presetUltramodern') {
|
|
397
|
+
console.error('Package source metadata must declare presetUltramodern');
|
|
398
|
+
process.exit(1);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
if (packageSource.strategy !== 'workspace' && packageSource.strategy !== 'install') {
|
|
402
|
+
console.error('Package source strategy must be workspace or install');
|
|
403
|
+
process.exit(1);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
const expectedModernPackages = [
|
|
407
|
+
'@modern-js/runtime',
|
|
408
|
+
'@modern-js/app-tools',
|
|
409
|
+
'@modern-js/tsconfig',
|
|
410
|
+
'@modern-js/plugin-i18n',
|
|
411
|
+
'@modern-js/plugin-tanstack',
|
|
412
|
+
'@modern-js/plugin-bff',
|
|
413
|
+
'@modern-js/adapter-rstest',
|
|
414
|
+
];
|
|
415
|
+
|
|
416
|
+
for (const packageName of expectedModernPackages) {
|
|
417
|
+
if (!packageSource.modernPackages?.packages?.includes(packageName)) {
|
|
418
|
+
console.error(`Package source metadata must include ${packageName}`);
|
|
419
|
+
process.exit(1);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
const expectedModernSpecifier = packageSource.modernPackages?.specifier;
|
|
424
|
+
if (typeof expectedModernSpecifier !== 'string' || expectedModernSpecifier.length === 0) {
|
|
425
|
+
console.error('Package source metadata must provide a Modern package specifier');
|
|
426
|
+
process.exit(1);
|
|
427
|
+
}
|
|
428
|
+
if (
|
|
429
|
+
packageSource.strategy === 'install' &&
|
|
430
|
+
expectedModernSpecifier !== templateManifest.template?.version
|
|
431
|
+
) {
|
|
432
|
+
console.error(
|
|
433
|
+
`Package source Modern specifier ${expectedModernSpecifier} must match template version ${templateManifest.template?.version}`,
|
|
434
|
+
);
|
|
435
|
+
process.exit(1);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
const expectedModernDependency = (packageName) => {
|
|
439
|
+
const alias = packageSource.modernPackages?.aliases?.[packageName];
|
|
440
|
+
return typeof alias === 'string'
|
|
441
|
+
? `npm:${alias}@${expectedModernSpecifier}`
|
|
442
|
+
: expectedModernSpecifier;
|
|
443
|
+
};
|
|
444
|
+
|
|
445
|
+
for (const packageName of [
|
|
446
|
+
'@modern-js/runtime',
|
|
447
|
+
'@modern-js/plugin-i18n',
|
|
448
|
+
'@modern-js/plugin-tanstack',
|
|
449
|
+
]) {
|
|
450
|
+
if (
|
|
451
|
+
packageJson.dependencies?.[packageName] &&
|
|
452
|
+
packageJson.dependencies[packageName] !== expectedModernDependency(packageName)
|
|
453
|
+
) {
|
|
454
|
+
console.error(`Dependency ${packageName} must match package source metadata`);
|
|
455
|
+
process.exit(1);
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
for (const packageName of [
|
|
460
|
+
'@modern-js/app-tools',
|
|
461
|
+
'@modern-js/adapter-rstest',
|
|
462
|
+
'@modern-js/tsconfig',
|
|
463
|
+
'@modern-js/plugin-bff',
|
|
464
|
+
]) {
|
|
465
|
+
if (
|
|
466
|
+
packageJson.devDependencies?.[packageName] &&
|
|
467
|
+
packageJson.devDependencies[packageName] !== expectedModernDependency(packageName)
|
|
468
|
+
) {
|
|
469
|
+
console.error(`Dev dependency ${packageName} must match package source metadata`);
|
|
470
|
+
process.exit(1);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
for (const dependency of ['@modern-js/plugin-i18n', 'i18next', 'react-i18next']) {
|
|
475
|
+
if (!packageJson.dependencies?.[dependency]) {
|
|
476
|
+
console.error(`Missing dependency: ${dependency}`);
|
|
477
|
+
process.exit(1);
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
142
481
|
for (const dependency of [
|
|
143
482
|
'@effect/tsgo',
|
|
483
|
+
'@modern-js/adapter-rstest',
|
|
484
|
+
'@rstest/core',
|
|
144
485
|
'@typescript/native-preview',
|
|
486
|
+
'happy-dom',
|
|
487
|
+
{{#if enableTailwind}}
|
|
488
|
+
'@tailwindcss/postcss',
|
|
489
|
+
'postcss',
|
|
490
|
+
'tailwindcss',
|
|
491
|
+
{{/if}}
|
|
492
|
+
{{#unless isSubproject}}
|
|
493
|
+
'lefthook',
|
|
145
494
|
'oxlint',
|
|
146
495
|
'oxfmt',
|
|
147
496
|
'ultracite',
|
|
497
|
+
{{/unless}}
|
|
148
498
|
]) {
|
|
149
499
|
if (!packageJson.devDependencies?.[dependency]) {
|
|
150
500
|
console.error(`Missing devDependency: ${dependency}`);
|
|
@@ -152,6 +502,27 @@ for (const dependency of [
|
|
|
152
502
|
}
|
|
153
503
|
}
|
|
154
504
|
|
|
505
|
+
{{#if enableTailwind}}
|
|
506
|
+
if (
|
|
507
|
+
packageJson.devDependencies?.tailwindcss !== '^4.3.0' ||
|
|
508
|
+
packageJson.devDependencies?.['@tailwindcss/postcss'] !== '^4.3.0'
|
|
509
|
+
) {
|
|
510
|
+
console.error('Tailwind CSS dependencies must use the UltraModern default baseline');
|
|
511
|
+
process.exit(1);
|
|
512
|
+
}
|
|
513
|
+
{{/if}}
|
|
514
|
+
{{#unless enableTailwind}}
|
|
515
|
+
if (
|
|
516
|
+
packageJson.devDependencies?.tailwindcss ||
|
|
517
|
+
packageJson.devDependencies?.['@tailwindcss/postcss'] ||
|
|
518
|
+
packageJson.devDependencies?.postcss
|
|
519
|
+
) {
|
|
520
|
+
console.error('Tailwind CSS dependencies must be absent when Tailwind is disabled');
|
|
521
|
+
process.exit(1);
|
|
522
|
+
}
|
|
523
|
+
{{/unless}}
|
|
524
|
+
|
|
525
|
+
{{#unless isSubproject}}
|
|
155
526
|
const privateSource = skillsLock.sources?.find(
|
|
156
527
|
(source) => source.repository === 'https://github.com/TechsioCZ/skills',
|
|
157
528
|
);
|
|
@@ -162,5 +533,6 @@ for (const skillName of ['plan-graph', 'dag', 'subagent-graph', 'helm', 'debugge
|
|
|
162
533
|
process.exit(1);
|
|
163
534
|
}
|
|
164
535
|
}
|
|
536
|
+
{{/unless}}
|
|
165
537
|
|
|
166
538
|
console.log('Ultramodern contract check passed.');
|
|
@@ -1,7 +1,23 @@
|
|
|
1
1
|
import { defineRuntimeConfig } from '@modern-js/runtime';
|
|
2
|
+
import { createInstance } from 'i18next';
|
|
3
|
+
|
|
4
|
+
const i18nInstance = createInstance();
|
|
2
5
|
|
|
3
6
|
export default defineRuntimeConfig({
|
|
7
|
+
i18n: {
|
|
8
|
+
i18nInstance,
|
|
9
|
+
initOptions: {
|
|
10
|
+
defaultNS: 'translation',
|
|
11
|
+
fallbackLng: 'en',
|
|
12
|
+
interpolation: {
|
|
13
|
+
escapeValue: false,
|
|
14
|
+
},
|
|
15
|
+
ns: ['translation'],
|
|
16
|
+
supportedLngs: ['en', 'cs'],
|
|
17
|
+
},
|
|
18
|
+
},
|
|
4
19
|
{{#if isTanstackRouter}} router: {
|
|
5
20
|
framework: 'tanstack',
|
|
6
|
-
},
|
|
21
|
+
},
|
|
22
|
+
{{/if~}}
|
|
7
23
|
});
|