@bleedingdev/modern-js-create 3.2.0-ultramodern.29 → 3.2.0-ultramodern.30
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/dist/index.js +820 -33
- package/package.json +4 -4
- package/template/.github/workflows/ultramodern-gates.yml.handlebars +8 -9
- package/template/.mise.toml.handlebars +2 -0
- package/template/AGENTS.md +1 -1
- package/template/README.md +16 -15
- package/template/package.json.handlebars +3 -3
- package/template/scripts/validate-ultramodern.mjs.handlebars +29 -53
- package/template/src/routes/[lang]/page.tsx.handlebars +1 -2
- package/template/src/routes/layout.tsx.handlebars +1 -0
- package/template/tests/ultramodern.contract.test.ts.handlebars +3 -20
- package/template-workspace/.github/workflows/ultramodern-workspace-gates.yml.handlebars +8 -9
- package/template-workspace/.mise.toml.handlebars +2 -0
- package/template-workspace/AGENTS.md +2 -2
- package/template-workspace/README.md.handlebars +6 -4
- package/template-workspace/scripts/validate-ultramodern-workspace.mjs.handlebars +38 -44
- package/template/.mise.toml +0 -2
- package/template-workspace/.mise.toml +0 -2
package/package.json
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"engines": {
|
|
22
22
|
"node": ">=20"
|
|
23
23
|
},
|
|
24
|
-
"version": "3.2.0-ultramodern.
|
|
24
|
+
"version": "3.2.0-ultramodern.30",
|
|
25
25
|
"types": "./dist/types/index.d.ts",
|
|
26
26
|
"main": "./dist/index.js",
|
|
27
27
|
"bin": {
|
|
@@ -39,9 +39,9 @@
|
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@rslib/core": "0.21.5",
|
|
41
41
|
"@types/node": "^25.9.1",
|
|
42
|
-
"@typescript/native-preview": "7.0.0-dev.
|
|
42
|
+
"@typescript/native-preview": "7.0.0-dev.20260527.2",
|
|
43
43
|
"tsx": "^4.22.3",
|
|
44
|
-
"@modern-js/i18n-utils": "npm:@bleedingdev/modern-js-i18n-utils@3.2.0-ultramodern.
|
|
44
|
+
"@modern-js/i18n-utils": "npm:@bleedingdev/modern-js-i18n-utils@3.2.0-ultramodern.30"
|
|
45
45
|
},
|
|
46
46
|
"publishConfig": {
|
|
47
47
|
"registry": "https://registry.npmjs.org/",
|
|
@@ -54,6 +54,6 @@
|
|
|
54
54
|
"start": "node ./dist/index.js"
|
|
55
55
|
},
|
|
56
56
|
"ultramodern": {
|
|
57
|
-
"frameworkVersion": "3.2.0-ultramodern.
|
|
57
|
+
"frameworkVersion": "3.2.0-ultramodern.30"
|
|
58
58
|
}
|
|
59
59
|
}
|
|
@@ -29,27 +29,26 @@ jobs:
|
|
|
29
29
|
egress-policy: audit
|
|
30
30
|
|
|
31
31
|
- name: Checkout
|
|
32
|
-
uses: actions/checkout@
|
|
32
|
+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
33
33
|
with:
|
|
34
34
|
fetch-depth: 1
|
|
35
35
|
persist-credentials: false
|
|
36
36
|
|
|
37
|
-
- name: Setup pnpm
|
|
38
|
-
uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8
|
|
39
|
-
|
|
40
37
|
- name: Setup Node.js
|
|
41
|
-
uses: actions/setup-node@
|
|
38
|
+
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
|
42
39
|
with:
|
|
43
40
|
node-version: 24
|
|
44
|
-
|
|
41
|
+
|
|
42
|
+
- name: Setup mise
|
|
43
|
+
uses: jdx/mise-action@5ac50f778e26fac95da98d50503682459e86d566 # v3.2.0
|
|
45
44
|
|
|
46
45
|
- name: Install Dependencies
|
|
47
|
-
run: pnpm install --frozen-lockfile
|
|
46
|
+
run: mise exec -- pnpm install --frozen-lockfile
|
|
48
47
|
|
|
49
48
|
- name: Validate Ultramodern Contract
|
|
50
|
-
run: pnpm run ultramodern:check
|
|
49
|
+
run: mise exec -- pnpm run ultramodern:check
|
|
51
50
|
|
|
52
51
|
- name: Build
|
|
53
52
|
env:
|
|
54
53
|
MODERN_PUBLIC_SITE_URL: http://localhost:8080
|
|
55
|
-
run: pnpm run build
|
|
54
|
+
run: mise exec -- pnpm run build
|
package/template/AGENTS.md
CHANGED
|
@@ -8,7 +8,7 @@ This project is generated for Codex-first UltraModern.js work.
|
|
|
8
8
|
- `pnpm format` runs oxfmt.
|
|
9
9
|
- `pnpm typecheck` runs effect-tsgo as the TypeScript checker.
|
|
10
10
|
- `pnpm i18n:check` rejects hardcoded user-visible JSX text.
|
|
11
|
-
- `pnpm ultramodern:check` verifies the generated contract.
|
|
11
|
+
- `mise exec -- pnpm ultramodern:check` verifies the generated contract.
|
|
12
12
|
|
|
13
13
|
## Internationalization
|
|
14
14
|
|
package/template/README.md
CHANGED
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
Install the dependencies:
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
|
|
8
|
+
mise install
|
|
9
|
+
mise exec -- pnpm install
|
|
9
10
|
```
|
|
10
11
|
|
|
11
12
|
## Get Started
|
|
@@ -13,19 +14,19 @@ pnpm install
|
|
|
13
14
|
Start the dev server:
|
|
14
15
|
|
|
15
16
|
```bash
|
|
16
|
-
pnpm dev
|
|
17
|
+
mise exec -- pnpm dev
|
|
17
18
|
```
|
|
18
19
|
|
|
19
20
|
Build the app for production:
|
|
20
21
|
|
|
21
22
|
```bash
|
|
22
|
-
pnpm build
|
|
23
|
+
mise exec -- pnpm build
|
|
23
24
|
```
|
|
24
25
|
|
|
25
26
|
Validate the generated Ultramodern preset contract locally:
|
|
26
27
|
|
|
27
28
|
```bash
|
|
28
|
-
pnpm run ultramodern:check
|
|
29
|
+
mise exec -- pnpm run ultramodern:check
|
|
29
30
|
```
|
|
30
31
|
|
|
31
32
|
The generated preset defaults are opt-out. Disable specific contracts via env vars:
|
|
@@ -37,12 +38,12 @@ MODERN_BASELINE_ENABLE_TELEMETRY_EXPORTERS=false
|
|
|
37
38
|
```
|
|
38
39
|
|
|
39
40
|
The generated starter also includes `.github/workflows/ultramodern-gates.yml`
|
|
40
|
-
and `.github/renovate.json`. The workflow runs
|
|
41
|
-
`pnpm run
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
major upgrades.
|
|
41
|
+
and `.github/renovate.json`. The workflow runs
|
|
42
|
+
`mise exec -- pnpm run ultramodern:check` and `mise exec -- pnpm run build` on
|
|
43
|
+
every push and pull request with read-only permissions, commit-pinned actions,
|
|
44
|
+
frozen installs, and StepSecurity audit-mode runner hardening. Renovate is
|
|
45
|
+
configured for dependency dashboard review, one-day release age, grouped
|
|
46
|
+
updates, action digest pinning, and manual approval for major upgrades.
|
|
46
47
|
|
|
47
48
|
## Micro Vertical Workspaces
|
|
48
49
|
|
|
@@ -51,10 +52,10 @@ the UltraModern add flow. It derives paths, package names, ports, Module
|
|
|
51
52
|
Federation names, topology entries, overlays, ownership, and root dev scripts:
|
|
52
53
|
|
|
53
54
|
```bash
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
55
|
+
mise exec -- pnpm dlx @modern-js/create catalog --microvertical remote
|
|
56
|
+
mise exec -- pnpm dlx @modern-js/create design-system --microvertical horizontal-remote
|
|
57
|
+
mise exec -- pnpm dlx @modern-js/create catalog-api --microvertical service
|
|
58
|
+
mise exec -- pnpm dlx @modern-js/create catalog-contracts --microvertical shared
|
|
58
59
|
```
|
|
59
60
|
|
|
60
61
|
The canonical topology is documented in
|
|
@@ -76,7 +77,7 @@ Verticals.
|
|
|
76
77
|
Preview the production build locally:
|
|
77
78
|
|
|
78
79
|
```bash
|
|
79
|
-
pnpm serve
|
|
80
|
+
mise exec -- pnpm serve
|
|
80
81
|
```
|
|
81
82
|
|
|
82
83
|
For more information, see the
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"version": "0.1.0",
|
|
4
4
|
"private": true,
|
|
5
5
|
"type": "module",
|
|
6
|
-
"packageManager": "pnpm@
|
|
6
|
+
"packageManager": "pnpm@{{pnpmVersion}}",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"reset": "npx rimraf node_modules ./**/node_modules",
|
|
9
9
|
"dev": "modern dev",
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"@types/node": "^20",
|
|
49
49
|
"@types/react": "^19.1.8",
|
|
50
50
|
"@types/react-dom": "^19.1.6",
|
|
51
|
-
"@typescript/native-preview": "7.0.0-dev.
|
|
51
|
+
"@typescript/native-preview": "7.0.0-dev.20260527.2",
|
|
52
52
|
"happy-dom": "^20.9.0",
|
|
53
53
|
{{#unless isSubproject}}
|
|
54
54
|
"lint-staged": "~17.0.5",
|
|
@@ -71,6 +71,6 @@
|
|
|
71
71
|
}{{/unless}},
|
|
72
72
|
"engines": {
|
|
73
73
|
"node": ">=20",
|
|
74
|
-
"pnpm": ">=
|
|
74
|
+
"pnpm": ">={{pnpmVersion}} <11.5.0"
|
|
75
75
|
}
|
|
76
76
|
}
|
|
@@ -25,6 +25,19 @@ const readPnpmConfig = (key) => {
|
|
|
25
25
|
};
|
|
26
26
|
const isSubproject = {{isSubproject}};
|
|
27
27
|
const enableTailwind = {{enableTailwind}};
|
|
28
|
+
const expectedPnpmVersion = '{{pnpmVersion}}';
|
|
29
|
+
const activePnpmVersion = execFileSync('pnpm', ['--version'], {
|
|
30
|
+
cwd: process.cwd(),
|
|
31
|
+
encoding: 'utf-8',
|
|
32
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
33
|
+
}).trim();
|
|
34
|
+
|
|
35
|
+
if (activePnpmVersion !== expectedPnpmVersion) {
|
|
36
|
+
console.error(
|
|
37
|
+
`Generated app requires pnpm ${expectedPnpmVersion}; active pnpm is ${activePnpmVersion}. Run mise install, then rerun through mise exec -- pnpm ...`,
|
|
38
|
+
);
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
28
41
|
|
|
29
42
|
if (!fs.existsSync(configPath)) {
|
|
30
43
|
console.error('modern.config.ts not found');
|
|
@@ -98,6 +111,7 @@ const requiredPaths = [
|
|
|
98
111
|
'config/public/locales/cs/translation.json',
|
|
99
112
|
'src/modern-app-env.d.ts',
|
|
100
113
|
'src/routes/index.css',
|
|
114
|
+
'src/routes/layout.tsx',
|
|
101
115
|
'src/routes/[lang]/page.tsx',
|
|
102
116
|
'tests/ultramodern.contract.test.ts',
|
|
103
117
|
];
|
|
@@ -115,27 +129,7 @@ if (fs.existsSync(path.resolve(process.cwd(), 'src/routes/page.tsx'))) {
|
|
|
115
129
|
process.exit(1);
|
|
116
130
|
}
|
|
117
131
|
|
|
118
|
-
|
|
119
|
-
if (enableTailwind) {
|
|
120
|
-
if (!routeCss.includes("@import 'tailwindcss';")) {
|
|
121
|
-
console.error('src/routes/index.css must import Tailwind CSS by default');
|
|
122
|
-
process.exit(1);
|
|
123
|
-
}
|
|
124
|
-
const postcssConfig = fs.readFileSync(path.resolve(process.cwd(), 'postcss.config.mjs'), 'utf-8');
|
|
125
|
-
if (!postcssConfig.includes("'@tailwindcss/postcss'")) {
|
|
126
|
-
console.error('postcss.config.mjs must configure @tailwindcss/postcss');
|
|
127
|
-
process.exit(1);
|
|
128
|
-
}
|
|
129
|
-
const tailwindConfig = fs.readFileSync(path.resolve(process.cwd(), 'tailwind.config.ts'), 'utf-8');
|
|
130
|
-
if (!tailwindConfig.includes("content: ['./src/**/*.{js,ts,jsx,tsx}']")) {
|
|
131
|
-
console.error('tailwind.config.ts must scan generated source files');
|
|
132
|
-
process.exit(1);
|
|
133
|
-
}
|
|
134
|
-
} else {
|
|
135
|
-
if (routeCss.includes("@import 'tailwindcss';")) {
|
|
136
|
-
console.error('src/routes/index.css must omit Tailwind CSS when disabled');
|
|
137
|
-
process.exit(1);
|
|
138
|
-
}
|
|
132
|
+
if (!enableTailwind) {
|
|
139
133
|
if (
|
|
140
134
|
fs.existsSync(path.resolve(process.cwd(), 'postcss.config.mjs')) ||
|
|
141
135
|
fs.existsSync(path.resolve(process.cwd(), 'tailwind.config.ts'))
|
|
@@ -157,7 +151,10 @@ for (const requiredSnippet of [
|
|
|
157
151
|
'permissions:\n contents: read',
|
|
158
152
|
'pull_request:',
|
|
159
153
|
'persist-credentials: false',
|
|
160
|
-
'
|
|
154
|
+
'jdx/mise-action',
|
|
155
|
+
'mise exec -- pnpm install --frozen-lockfile',
|
|
156
|
+
'mise exec -- pnpm run ultramodern:check',
|
|
157
|
+
'mise exec -- pnpm run build',
|
|
161
158
|
'MODERN_PUBLIC_SITE_URL: http://localhost:8080',
|
|
162
159
|
'timeout-minutes:',
|
|
163
160
|
'egress-policy: audit',
|
|
@@ -192,33 +189,6 @@ if (
|
|
|
192
189
|
}
|
|
193
190
|
{{/unless}}
|
|
194
191
|
|
|
195
|
-
const pageContent = fs.readFileSync(
|
|
196
|
-
path.resolve(process.cwd(), 'src/routes/[lang]/page.tsx'),
|
|
197
|
-
'utf-8',
|
|
198
|
-
);
|
|
199
|
-
for (const token of [
|
|
200
|
-
'rel="canonical"',
|
|
201
|
-
'rel="alternate"',
|
|
202
|
-
'hrefLang="x-default"',
|
|
203
|
-
'localizedPath(',
|
|
204
|
-
'<a',
|
|
205
|
-
{{#if isTanstackRouter}}
|
|
206
|
-
"@modern-js/plugin-tanstack/runtime",
|
|
207
|
-
{{/if}}
|
|
208
|
-
]) {
|
|
209
|
-
if (!pageContent.includes(token)) {
|
|
210
|
-
console.error(`Localized route is missing ${token}`);
|
|
211
|
-
process.exit(1);
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
{{#if isTanstackRouter}}
|
|
215
|
-
const deprecatedTanstackRuntime = '@modern-js/runtime/' + 'tanstack-router';
|
|
216
|
-
if (pageContent.includes(deprecatedTanstackRuntime)) {
|
|
217
|
-
console.error('Localized route must import TanStack runtime from @modern-js/plugin-tanstack/runtime');
|
|
218
|
-
process.exit(1);
|
|
219
|
-
}
|
|
220
|
-
{{/if}}
|
|
221
|
-
|
|
222
192
|
if (templateManifest.schemaVersion !== 1) {
|
|
223
193
|
manifestErrors.push('schemaVersion');
|
|
224
194
|
}
|
|
@@ -318,13 +288,19 @@ if (packageJson.private !== true) {
|
|
|
318
288
|
process.exit(1);
|
|
319
289
|
}
|
|
320
290
|
|
|
321
|
-
|
|
322
|
-
|
|
291
|
+
const miseConfig = fs.readFileSync(path.resolve(process.cwd(), '.mise.toml'), 'utf-8');
|
|
292
|
+
if (!miseConfig.includes(`pnpm = "${expectedPnpmVersion}"`)) {
|
|
293
|
+
console.error(`Generated app must pin pnpm ${expectedPnpmVersion} in .mise.toml`);
|
|
294
|
+
process.exit(1);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
if (packageJson.packageManager !== `pnpm@${expectedPnpmVersion}`) {
|
|
298
|
+
console.error(`Generated app package must pin pnpm@${expectedPnpmVersion}`);
|
|
323
299
|
process.exit(1);
|
|
324
300
|
}
|
|
325
301
|
|
|
326
|
-
if (packageJson.engines?.pnpm !==
|
|
327
|
-
console.error(
|
|
302
|
+
if (packageJson.engines?.pnpm !== `>=${expectedPnpmVersion} <11.5.0`) {
|
|
303
|
+
console.error(`Generated app package must require pnpm >=${expectedPnpmVersion} <11.5.0`);
|
|
328
304
|
process.exit(1);
|
|
329
305
|
}
|
|
330
306
|
|
|
@@ -6,7 +6,6 @@ import { Effect } from '@modern-js/plugin-bff/effect-client';
|
|
|
6
6
|
import { useEffect, useState } from 'react';
|
|
7
7
|
{{/if}}
|
|
8
8
|
import { useTranslation } from 'react-i18next';
|
|
9
|
-
import '../index.css';
|
|
10
9
|
|
|
11
10
|
const fallbackLanguage = 'en';
|
|
12
11
|
const supportedLanguages = ['en', 'cs'] as const;
|
|
@@ -128,7 +127,7 @@ const Index = () => {
|
|
|
128
127
|
<code className="code">modern.config.ts</code>
|
|
129
128
|
{/* i18n-ignore technical token */}
|
|
130
129
|
{t('home.description.afterConfig')}
|
|
131
|
-
<code className="code">pnpm run ultramodern:check</code>
|
|
130
|
+
<code className="code">mise exec -- pnpm run ultramodern:check</code>
|
|
132
131
|
{/* i18n-ignore technical token */}
|
|
133
132
|
{t('home.description.end')}
|
|
134
133
|
</p>
|
|
@@ -14,32 +14,15 @@ describe('generated UltraModern contract', () => {
|
|
|
14
14
|
true,
|
|
15
15
|
);
|
|
16
16
|
expect(fs.existsSync(path.join(root, 'src/routes/page.tsx'))).toBe(false);
|
|
17
|
-
|
|
18
|
-
const page = readText('src/routes/[lang]/page.tsx');
|
|
19
|
-
expect(page).toContain('rel="canonical"');
|
|
20
|
-
expect(page).toContain('rel="alternate"');
|
|
21
|
-
expect(page).toContain('hrefLang="x-default"');
|
|
22
|
-
expect(page).toContain('localizedPath(');
|
|
17
|
+
expect(fs.existsSync(path.join(root, 'src/routes/layout.tsx'))).toBe(true);
|
|
23
18
|
{{#if enableTailwind}}
|
|
24
|
-
expect(
|
|
25
|
-
|
|
26
|
-
);
|
|
27
|
-
expect(readText('postcss.config.mjs')).toContain('@tailwindcss/postcss');
|
|
28
|
-
expect(readText('tailwind.config.ts')).toContain(
|
|
29
|
-
"content: ['./src/**/*.{js,ts,jsx,tsx}']",
|
|
30
|
-
);
|
|
19
|
+
expect(fs.existsSync(path.join(root, 'postcss.config.mjs'))).toBe(true);
|
|
20
|
+
expect(fs.existsSync(path.join(root, 'tailwind.config.ts'))).toBe(true);
|
|
31
21
|
{{/if}}
|
|
32
22
|
{{#unless enableTailwind}}
|
|
33
|
-
expect(readText('src/routes/index.css')).not.toContain(
|
|
34
|
-
"@import 'tailwindcss';",
|
|
35
|
-
);
|
|
36
23
|
expect(fs.existsSync(path.join(root, 'postcss.config.mjs'))).toBe(false);
|
|
37
24
|
expect(fs.existsSync(path.join(root, 'tailwind.config.ts'))).toBe(false);
|
|
38
25
|
{{/unless}}
|
|
39
|
-
|
|
40
|
-
const config = readText('rstest.config.mts');
|
|
41
|
-
expect(config).toContain("withModernConfig()");
|
|
42
|
-
expect(config).toContain("testEnvironment: 'happy-dom'");
|
|
43
26
|
});
|
|
44
27
|
|
|
45
28
|
test('retains package-source metadata for generated Modern.js packages', () => {
|
|
@@ -29,27 +29,26 @@ jobs:
|
|
|
29
29
|
egress-policy: audit
|
|
30
30
|
|
|
31
31
|
- name: Checkout
|
|
32
|
-
uses: actions/checkout@
|
|
32
|
+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
33
33
|
with:
|
|
34
34
|
fetch-depth: 1
|
|
35
35
|
persist-credentials: false
|
|
36
36
|
|
|
37
|
-
- name: Setup pnpm
|
|
38
|
-
uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8
|
|
39
|
-
|
|
40
37
|
- name: Setup Node.js
|
|
41
|
-
uses: actions/setup-node@
|
|
38
|
+
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
|
42
39
|
with:
|
|
43
40
|
node-version: 24
|
|
44
|
-
|
|
41
|
+
|
|
42
|
+
- name: Setup mise
|
|
43
|
+
uses: jdx/mise-action@5ac50f778e26fac95da98d50503682459e86d566 # v3.2.0
|
|
45
44
|
|
|
46
45
|
- name: Install Dependencies
|
|
47
|
-
run: pnpm install --frozen-lockfile
|
|
46
|
+
run: mise exec -- pnpm install --frozen-lockfile
|
|
48
47
|
|
|
49
48
|
- name: Validate Ultramodern Workspace Contract
|
|
50
|
-
run: pnpm run ultramodern:check
|
|
49
|
+
run: mise exec -- pnpm run ultramodern:check
|
|
51
50
|
|
|
52
51
|
- name: Build Workspace Apps
|
|
53
52
|
env:
|
|
54
53
|
MODERN_PUBLIC_SITE_URL: http://localhost:8080
|
|
55
|
-
run: pnpm build
|
|
54
|
+
run: mise exec -- pnpm build
|
|
@@ -26,7 +26,7 @@ Use these skills when the task touches the matching subsystem:
|
|
|
26
26
|
- `rstest-best-practices`: Rstest configuration, test writing, mocking, snapshots, coverage, and CI test behavior.
|
|
27
27
|
- `mf`: Module Federation docs, Modern.js integration, DTS/type checks, shared dependency checks, runtime errors, and observability troubleshooting.
|
|
28
28
|
|
|
29
|
-
The public `module-federation/agent-skills` repository is installed during `pnpm install` and `pnpm skills:install`. `pnpm skills:check` fails when the required public `mf` skill is missing.
|
|
29
|
+
The public `module-federation/agent-skills` repository is installed during `mise exec -- pnpm install` and `mise exec -- pnpm skills:install`. `mise exec -- pnpm skills:check` fails when the required public `mf` skill is missing.
|
|
30
30
|
|
|
31
31
|
## Private Skills
|
|
32
32
|
|
|
@@ -40,7 +40,7 @@ The installer copies only the allowlisted private skills from `.agents/skills-lo
|
|
|
40
40
|
|
|
41
41
|
## Agent Reference Repositories
|
|
42
42
|
|
|
43
|
-
The workspace installs read-only source references under `repos/` by default during `pnpm install` using `git subtree add --squash`. These repositories are reference material for coding agents, not application source:
|
|
43
|
+
The workspace installs read-only source references under `repos/` by default during `mise exec -- pnpm install` using `git subtree add --squash`. These repositories are reference material for coding agents, not application source:
|
|
44
44
|
|
|
45
45
|
- `repos/effect` from `Effect-TS/effect`.
|
|
46
46
|
- `repos/ultramodern.js` from `BleedingDev/ultramodern.js`.
|
|
@@ -25,13 +25,15 @@ packages.
|
|
|
25
25
|
Run the scaffold validator before adding business code:
|
|
26
26
|
|
|
27
27
|
```bash
|
|
28
|
-
|
|
28
|
+
mise install
|
|
29
|
+
mise exec -- pnpm ultramodern:check
|
|
29
30
|
```
|
|
30
31
|
|
|
31
|
-
By default, `pnpm install` also prepares read-only agent reference repositories
|
|
32
|
+
By default, `mise exec -- pnpm install` also prepares read-only agent reference repositories
|
|
32
33
|
under `repos/` for Effect and UltraModern.js source lookup using squashed git
|
|
33
|
-
subtrees. Disable this setup with
|
|
34
|
-
|
|
34
|
+
subtrees. Disable this setup with
|
|
35
|
+
`ULTRAMODERN_SKIP_AGENT_REPOS=1 mise exec -- pnpm install`, or rerun it
|
|
36
|
+
explicitly with `mise exec -- pnpm agents:refs:install`.
|
|
35
37
|
|
|
36
38
|
The topology and ownership metadata are generated under `topology/`. The
|
|
37
39
|
workspace also ships `.github/workflows/ultramodern-workspace-gates.yml` and
|
|
@@ -8,6 +8,7 @@ const tanstackVersion = '1.170.8';
|
|
|
8
8
|
const tailwindEnabled = '{{tailwindEnabled}}' === 'true';
|
|
9
9
|
const tailwindVersion = '^4.3.0';
|
|
10
10
|
const tailwindPostcssVersion = '^4.3.0';
|
|
11
|
+
const expectedPnpmVersion = '{{pnpmVersion}}';
|
|
11
12
|
const rstackAgentSkillsCommit = '61c948b42512e223bad44b83af4080eba48b2677';
|
|
12
13
|
const moduleFederationAgentSkillsCommit = '07bb5b6c43ad457609e00c081b72d4c42508ec76';
|
|
13
14
|
const modernPackages = [
|
|
@@ -52,6 +53,11 @@ const designSystemRemotePath = 'apps/remotes/remote-design-system';
|
|
|
52
53
|
|
|
53
54
|
const readText = (relativePath) => fs.readFileSync(path.join(root, relativePath), 'utf-8');
|
|
54
55
|
const readJson = (relativePath) => JSON.parse(readText(relativePath));
|
|
56
|
+
const activePnpmVersion = execFileSync('pnpm', ['--version'], {
|
|
57
|
+
cwd: root,
|
|
58
|
+
encoding: 'utf-8',
|
|
59
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
60
|
+
}).trim();
|
|
55
61
|
const readPnpmConfig = (key) => {
|
|
56
62
|
const env = { ...process.env };
|
|
57
63
|
for (const envKey of Object.keys(env)) {
|
|
@@ -79,6 +85,11 @@ const assertNotExists = (relativePath) => {
|
|
|
79
85
|
assert(!fs.existsSync(path.join(root, relativePath)), `Unexpected ${relativePath}`);
|
|
80
86
|
};
|
|
81
87
|
|
|
88
|
+
assert(
|
|
89
|
+
activePnpmVersion === expectedPnpmVersion,
|
|
90
|
+
`Generated workspace requires pnpm ${expectedPnpmVersion}; active pnpm is ${activePnpmVersion}. Run mise install, then rerun through mise exec -- pnpm ...`,
|
|
91
|
+
);
|
|
92
|
+
|
|
82
93
|
const requiredPaths = [
|
|
83
94
|
'AGENTS.md',
|
|
84
95
|
'.gitignore',
|
|
@@ -119,6 +130,7 @@ const requiredPaths = [
|
|
|
119
130
|
'apps/shell-super-app/locales/en/translation.json',
|
|
120
131
|
'apps/shell-super-app/locales/cs/translation.json',
|
|
121
132
|
'apps/shell-super-app/src/routes/index.css',
|
|
133
|
+
'apps/shell-super-app/src/routes/layout.tsx',
|
|
122
134
|
'apps/shell-super-app/src/routes/[lang]/page.tsx',
|
|
123
135
|
'apps/remotes/remote-commerce/package.json',
|
|
124
136
|
'apps/remotes/remote-commerce/modern.config.ts',
|
|
@@ -131,6 +143,7 @@ const requiredPaths = [
|
|
|
131
143
|
'apps/remotes/remote-commerce/locales/en/translation.json',
|
|
132
144
|
'apps/remotes/remote-commerce/locales/cs/translation.json',
|
|
133
145
|
'apps/remotes/remote-commerce/src/routes/index.css',
|
|
146
|
+
'apps/remotes/remote-commerce/src/routes/layout.tsx',
|
|
134
147
|
'apps/remotes/remote-commerce/src/routes/[lang]/page.tsx',
|
|
135
148
|
'apps/remotes/remote-identity/package.json',
|
|
136
149
|
'apps/remotes/remote-identity/modern.config.ts',
|
|
@@ -143,6 +156,7 @@ const requiredPaths = [
|
|
|
143
156
|
'apps/remotes/remote-identity/locales/en/translation.json',
|
|
144
157
|
'apps/remotes/remote-identity/locales/cs/translation.json',
|
|
145
158
|
'apps/remotes/remote-identity/src/routes/index.css',
|
|
159
|
+
'apps/remotes/remote-identity/src/routes/layout.tsx',
|
|
146
160
|
'apps/remotes/remote-identity/src/routes/[lang]/page.tsx',
|
|
147
161
|
'apps/remotes/remote-design-system/package.json',
|
|
148
162
|
'apps/remotes/remote-design-system/modern.config.ts',
|
|
@@ -152,6 +166,7 @@ const requiredPaths = [
|
|
|
152
166
|
'apps/remotes/remote-design-system/locales/en/translation.json',
|
|
153
167
|
'apps/remotes/remote-design-system/locales/cs/translation.json',
|
|
154
168
|
'apps/remotes/remote-design-system/src/routes/index.css',
|
|
169
|
+
'apps/remotes/remote-design-system/src/routes/layout.tsx',
|
|
155
170
|
'apps/remotes/remote-design-system/src/routes/[lang]/page.tsx',
|
|
156
171
|
'packages/shared-contracts/src/index.ts',
|
|
157
172
|
'packages/shared-design-tokens/src/index.ts',
|
|
@@ -211,10 +226,18 @@ const expectedModernDependency = (packageName) => {
|
|
|
211
226
|
|
|
212
227
|
assert(rootPackage.private === true, 'Root package must be private');
|
|
213
228
|
assert(rootPackage.modernjs?.preset === 'presetUltramodern', 'Root must declare presetUltramodern');
|
|
214
|
-
|
|
229
|
+
const miseConfig = readText('.mise.toml');
|
|
230
|
+
assert(
|
|
231
|
+
miseConfig.includes(`pnpm = "${expectedPnpmVersion}"`),
|
|
232
|
+
`Root must pin pnpm ${expectedPnpmVersion} in .mise.toml`,
|
|
233
|
+
);
|
|
234
|
+
assert(
|
|
235
|
+
rootPackage.packageManager === `pnpm@${expectedPnpmVersion}`,
|
|
236
|
+
`Root must pin pnpm ${expectedPnpmVersion}`,
|
|
237
|
+
);
|
|
215
238
|
assert(
|
|
216
|
-
rootPackage.engines?.pnpm ===
|
|
217
|
-
|
|
239
|
+
rootPackage.engines?.pnpm === `>=${expectedPnpmVersion} <11.5.0`,
|
|
240
|
+
`Root must require pnpm >=${expectedPnpmVersion} <11.5.0`,
|
|
218
241
|
);
|
|
219
242
|
assert(
|
|
220
243
|
JSON.stringify(readPnpmConfig('packages')) ===
|
|
@@ -278,8 +301,10 @@ for (const requiredSnippet of [
|
|
|
278
301
|
'permissions:\n contents: read',
|
|
279
302
|
'pull_request:',
|
|
280
303
|
'persist-credentials: false',
|
|
281
|
-
'
|
|
282
|
-
'pnpm
|
|
304
|
+
'jdx/mise-action',
|
|
305
|
+
'mise exec -- pnpm install --frozen-lockfile',
|
|
306
|
+
'mise exec -- pnpm run ultramodern:check',
|
|
307
|
+
'mise exec -- pnpm build',
|
|
283
308
|
'MODERN_PUBLIC_SITE_URL: http://localhost:8080',
|
|
284
309
|
'timeout-minutes:',
|
|
285
310
|
'egress-policy: audit',
|
|
@@ -554,7 +579,7 @@ for (const packagePath of appPackagePaths) {
|
|
|
554
579
|
`${packagePath} must include TypeScript 6 only as the stable JS TypeScript package for Module Federation DTS generation`,
|
|
555
580
|
);
|
|
556
581
|
assert(
|
|
557
|
-
packageJson.devDependencies?.['@typescript/native-preview'] === '7.0.0-dev.
|
|
582
|
+
packageJson.devDependencies?.['@typescript/native-preview'] === '7.0.0-dev.20260527.2',
|
|
558
583
|
`${packagePath} must include TypeScript 7 native preview for mandatory native typechecking`,
|
|
559
584
|
);
|
|
560
585
|
assert(
|
|
@@ -666,27 +691,11 @@ for (const appDirectory of [
|
|
|
666
691
|
'apps/remotes/remote-identity',
|
|
667
692
|
'apps/remotes/remote-design-system',
|
|
668
693
|
]) {
|
|
669
|
-
const css = readText(`${appDirectory}/src/routes/index.css`);
|
|
670
694
|
if (tailwindEnabled) {
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
const postcssConfig = readText(`${appDirectory}/postcss.config.mjs`);
|
|
676
|
-
assert(
|
|
677
|
-
postcssConfig.includes("'@tailwindcss/postcss'"),
|
|
678
|
-
`${appDirectory} must configure the Tailwind CSS PostCSS plugin`,
|
|
679
|
-
);
|
|
680
|
-
const tailwindConfig = readText(`${appDirectory}/tailwind.config.ts`);
|
|
681
|
-
assert(
|
|
682
|
-
tailwindConfig.includes("content: ['./src/**/*.{js,jsx,ts,tsx}']"),
|
|
683
|
-
`${appDirectory} must scan app source files for Tailwind CSS classes`,
|
|
684
|
-
);
|
|
685
|
-
} else {
|
|
686
|
-
assert(
|
|
687
|
-
!css.includes("@import 'tailwindcss';"),
|
|
688
|
-
`${appDirectory} must omit Tailwind CSS import when Tailwind is disabled`,
|
|
689
|
-
);
|
|
695
|
+
assertExists(`${appDirectory}/postcss.config.mjs`);
|
|
696
|
+
assertExists(`${appDirectory}/tailwind.config.ts`);
|
|
697
|
+
}
|
|
698
|
+
if (!tailwindEnabled) {
|
|
690
699
|
assert(
|
|
691
700
|
!fs.existsSync(path.join(root, appDirectory, 'postcss.config.mjs')),
|
|
692
701
|
`${appDirectory} must not write PostCSS config when Tailwind is disabled`,
|
|
@@ -744,20 +753,6 @@ for (const configPath of [
|
|
|
744
753
|
);
|
|
745
754
|
}
|
|
746
755
|
|
|
747
|
-
for (const routePath of [
|
|
748
|
-
'apps/shell-super-app/src/routes/[lang]/page.tsx',
|
|
749
|
-
'apps/remotes/remote-commerce/src/routes/[lang]/page.tsx',
|
|
750
|
-
'apps/remotes/remote-identity/src/routes/[lang]/page.tsx',
|
|
751
|
-
'apps/remotes/remote-design-system/src/routes/[lang]/page.tsx',
|
|
752
|
-
]) {
|
|
753
|
-
const route = readText(routePath);
|
|
754
|
-
assert(route.includes('rel="canonical"'), `${routePath} must emit canonical metadata`);
|
|
755
|
-
assert(route.includes('rel="alternate"'), `${routePath} must emit alternate locale metadata`);
|
|
756
|
-
assert(route.includes('hrefLang="x-default"'), `${routePath} must emit x-default metadata`);
|
|
757
|
-
assert(route.includes('localizedPath('), `${routePath} must build localized URLs`);
|
|
758
|
-
assert(route.includes("import '../index.css';"), `${routePath} must import route CSS`);
|
|
759
|
-
}
|
|
760
|
-
|
|
761
756
|
const shellMf = readText('apps/shell-super-app/module-federation.config.ts');
|
|
762
757
|
assert(shellMf.includes("name: 'shellSuperApp'"), 'Shell MF config must name the shell');
|
|
763
758
|
assert(
|
|
@@ -797,9 +792,8 @@ assert(
|
|
|
797
792
|
);
|
|
798
793
|
assert(
|
|
799
794
|
generatedContract.packageManager?.manager === 'pnpm' &&
|
|
800
|
-
generatedContract.packageManager?.version ===
|
|
801
|
-
generatedContract.packageManager?.toolchain === 'mise'
|
|
802
|
-
generatedContract.packageManager?.corepack === false,
|
|
795
|
+
generatedContract.packageManager?.version === expectedPnpmVersion &&
|
|
796
|
+
generatedContract.packageManager?.toolchain === 'mise',
|
|
803
797
|
'Generated contract must declare the pnpm 11/mise toolchain policy',
|
|
804
798
|
);
|
|
805
799
|
|
|
@@ -935,7 +929,7 @@ assert(
|
|
|
935
929
|
'Template manifest must list every private agent skill allowlist entry',
|
|
936
930
|
);
|
|
937
931
|
assert(
|
|
938
|
-
manifest.validation?.expectedCommands?.includes('pnpm run ultramodern:check'),
|
|
932
|
+
manifest.validation?.expectedCommands?.includes('mise exec -- pnpm run ultramodern:check'),
|
|
939
933
|
'Template manifest must document the validation command',
|
|
940
934
|
);
|
|
941
935
|
assert(
|
package/template/.mise.toml
DELETED