@bleedingdev/modern-js-create 3.2.0-ultramodern.59 → 3.2.0-ultramodern.60
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 +857 -1169
- package/dist/types/locale/en.d.ts +1 -0
- package/dist/types/locale/zh.d.ts +1 -0
- package/package.json +3 -3
- package/template/README.md +53 -125
- package/template/config/public/locales/cs/translation.json +10 -10
- package/template/config/public/locales/en/translation.json +10 -10
- package/template/src/routes/[lang]/page.tsx.handlebars +4 -2
- package/template-workspace/AGENTS.md +17 -4
- package/template-workspace/README.md.handlebars +22 -19
- package/template-workspace/scripts/assert-mf-types.mjs +0 -44
- package/template-workspace/scripts/validate-ultramodern-workspace.mjs.handlebars +0 -935
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.60",
|
|
25
25
|
"types": "./dist/types/index.d.ts",
|
|
26
26
|
"main": "./dist/index.js",
|
|
27
27
|
"bin": {
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"@types/node": "^25.9.1",
|
|
42
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.59"
|
|
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.59"
|
|
58
58
|
}
|
|
59
59
|
}
|
package/template/README.md
CHANGED
|
@@ -1,18 +1,9 @@
|
|
|
1
1
|
# UltraModern.js 3.0 Starter
|
|
2
2
|
|
|
3
|
-
This generated
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
| --- | --- |
|
|
8
|
-
| `apps/shell-super-app` | Module Federation host, route assembly, topology selection, shell fallback policy |
|
|
9
|
-
| `verticals/explore` | Explore routes, MF exposes, locale JSON, CSS layer, and `/explore-api/effect/explore/*` |
|
|
10
|
-
| `verticals/decide` | Decide routes, MF exposes, locale JSON, CSS layer, and `/decide-api/effect/decide/*` |
|
|
11
|
-
| `verticals/checkout` | Checkout routes, MF exposes, locale JSON, CSS layer, and `/checkout-api/effect/checkout/*` |
|
|
12
|
-
|
|
13
|
-
Each vertical is a one-package ownership boundary. Its UI, Effect BFF contract,
|
|
14
|
-
generated client, `localisedUrls`, dynamic locale JSON, CSS, MF manifest, and
|
|
15
|
-
Cloudflare Worker output must move together for version-switching proof.
|
|
3
|
+
This generated app is a simple UltraModern.js starting point. It gives one app
|
|
4
|
+
with localized routes, production URL metadata, optional BFF support, Rstest,
|
|
5
|
+
Oxlint, oxfmt, and a local contract check. You can build a useful product here
|
|
6
|
+
without deleting fake product areas, shell packages, or deployment topology.
|
|
16
7
|
|
|
17
8
|
## Setup
|
|
18
9
|
|
|
@@ -34,143 +25,80 @@ pnpm dev
|
|
|
34
25
|
Build the app for production:
|
|
35
26
|
|
|
36
27
|
```bash
|
|
37
|
-
pnpm build
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
Validate the generated Ultramodern preset contract locally:
|
|
41
|
-
|
|
42
|
-
```bash
|
|
43
|
-
pnpm run ultramodern:check
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
Run the normal local gates before treating the scaffold as adoption-ready:
|
|
47
|
-
|
|
48
|
-
```bash
|
|
49
|
-
pnpm run check
|
|
50
|
-
pnpm run build
|
|
51
|
-
pnpm run cloudflare:build
|
|
28
|
+
MODERN_PUBLIC_SITE_URL=https://example.com pnpm build
|
|
52
29
|
```
|
|
53
30
|
|
|
54
|
-
|
|
31
|
+
Preview the production build locally:
|
|
55
32
|
|
|
56
33
|
```bash
|
|
57
|
-
|
|
58
|
-
MODERN_BASELINE_ENABLE_BFF_REQUEST_ID=false
|
|
59
|
-
MODERN_BASELINE_ENABLE_TELEMETRY_EXPORTERS=false
|
|
34
|
+
pnpm serve
|
|
60
35
|
```
|
|
61
36
|
|
|
62
|
-
|
|
63
|
-
and `.github/renovate.json`. The workflow runs
|
|
64
|
-
`pnpm run ultramodern:check` and `pnpm run build` on
|
|
65
|
-
every push and pull request with read-only permissions, commit-pinned actions,
|
|
66
|
-
frozen installs, and StepSecurity audit-mode runner hardening. Renovate is
|
|
67
|
-
configured for dependency dashboard review, one-day release age, grouped
|
|
68
|
-
updates, action digest pinning, and manual approval for major upgrades.
|
|
69
|
-
|
|
70
|
-
## Vertical Workspaces
|
|
71
|
-
|
|
72
|
-
Inside an UltraModern workspace, add a full-stack vertical from the workspace
|
|
73
|
-
root. The add flow derives paths, package names, ports, Module Federation names,
|
|
74
|
-
topology entries, overlays, ownership, Effect BFF contracts, and root dev
|
|
75
|
-
scripts:
|
|
37
|
+
Run the local gates before treating the scaffold as ready:
|
|
76
38
|
|
|
77
39
|
```bash
|
|
78
|
-
pnpm
|
|
40
|
+
pnpm run ultramodern:check
|
|
41
|
+
MODERN_PUBLIC_SITE_URL=https://example.com pnpm run build
|
|
79
42
|
```
|
|
80
43
|
|
|
81
|
-
|
|
82
|
-
`docs/super-app-rfc-adr/WORKSPACE-0001-micro-vertical-workspace-scaffolding.md`.
|
|
83
|
-
Shell packages own route assembly and topology selection. Verticals own route
|
|
84
|
-
subtrees, degraded UI, Effect BFF contracts, generated clients, and deployment
|
|
85
|
-
evidence. Ordinary workspace packages are limited to tokens, primitives,
|
|
86
|
-
generated clients, or domain-neutral utilities.
|
|
44
|
+
## What You Get
|
|
87
45
|
|
|
88
|
-
|
|
89
|
-
independent rollout, rollback, Cloudflare/Zephyr evidence, or incident routing.
|
|
90
|
-
Keep code inside an existing vertical when it shares the same product owner,
|
|
91
|
-
fallback behavior, release train, and Effect contract. Do not use a shared
|
|
92
|
-
package for feature composites or workflow state.
|
|
46
|
+
The default app is intentionally monolith-friendly:
|
|
93
47
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
## Module Federation And Effect Ownership
|
|
103
|
-
|
|
104
|
-
The shell consumes vertical UI through MF manifests and consumes vertical data
|
|
105
|
-
through generated Effect clients exported by the vertical packages:
|
|
106
|
-
|
|
107
|
-
- Explore exposes header, footer, recommendations, store picker, and route UI.
|
|
108
|
-
- Decide exposes product page and route UI, and may consume Explore or Checkout
|
|
109
|
-
verticals through declared `verticalRefs`.
|
|
110
|
-
- Checkout exposes cart, checkout, thanks, mini-cart, add-to-cart, and route UI.
|
|
111
|
-
|
|
112
|
-
The shell owns orchestration and fallback policy. A vertical owns its route-local
|
|
113
|
-
loader/action behavior, degraded UI, `api/effect/index.ts`, shared Effect API
|
|
114
|
-
contract, generated client, and readiness endpoint. Hono remains a compatibility
|
|
115
|
-
lane only when explicitly scaffolded outside the default Tractor workspace.
|
|
116
|
-
|
|
117
|
-
## I18n And CSS Ownership
|
|
118
|
-
|
|
119
|
-
Route localization is owned by the package that owns the route. Each app emits
|
|
120
|
-
`src/routes/ultramodern-route-metadata` and passes
|
|
121
|
-
`ultramodernLocalisedUrls` to `@modern-js/plugin-i18n`. Dynamic JSON resources
|
|
122
|
-
are served from `/locales/{{lng}}/{{ns}}.json`; shell rewrites must not replace
|
|
123
|
-
vertical-owned localized URL maps.
|
|
124
|
-
|
|
125
|
-
CSS federation is recorded in `.modernjs/ultramodern-generated-contract.json`:
|
|
48
|
+
| Area | Starting Point |
|
|
49
|
+
| --- | --- |
|
|
50
|
+
| App routes | Locale-prefixed pages under `src/routes/[lang]` |
|
|
51
|
+
| Copy | English and Czech resources in `config/public/locales` |
|
|
52
|
+
| Styling | App-local CSS, with Tailwind files only when selected |
|
|
53
|
+
| Server logic | Optional BFF entrypoints under `api/` |
|
|
54
|
+
| Tests | Rstest smoke coverage in `tests/` |
|
|
55
|
+
| Agent workflow | Generated `AGENTS.md`, hooks, and local quality gates |
|
|
126
56
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
- Tailwind CSS v4 is app-local through `@tailwindcss/postcss`.
|
|
132
|
-
- Duplicate base styles are forbidden. SSR first paint requires shared tokens
|
|
133
|
-
and Modern/Rspack-emitted app CSS.
|
|
57
|
+
Keep feature code in the app while one team owns the workflow, release train,
|
|
58
|
+
and operational behavior. Add ordinary workspace packages for shared tokens,
|
|
59
|
+
small UI primitives, generated clients, or domain-neutral utilities when that
|
|
60
|
+
keeps the app easier to understand.
|
|
134
61
|
|
|
135
|
-
##
|
|
62
|
+
## Customize The App
|
|
136
63
|
|
|
137
|
-
|
|
64
|
+
Start with the generated page and replace the placeholder cards with your first
|
|
65
|
+
real routes, actions, and API calls. Put user-visible text in
|
|
66
|
+
`config/public/locales/<lang>/translation.json`, then render it through
|
|
67
|
+
`react-i18next` or `@modern-js/plugin-i18n/runtime`.
|
|
138
68
|
|
|
139
|
-
|
|
140
|
-
MODERN_PUBLIC_SITE_URL
|
|
141
|
-
|
|
69
|
+
Tune the preset in `modern.config.ts`. Production builds require
|
|
70
|
+
`MODERN_PUBLIC_SITE_URL` so canonical and `hreflang` URLs use your deployed
|
|
71
|
+
origin. The local fallback is `http://localhost:8080`.
|
|
142
72
|
|
|
143
|
-
|
|
73
|
+
The generated preset defaults are opt-out. Disable specific contracts via env
|
|
74
|
+
vars when your app needs a softer lane:
|
|
144
75
|
|
|
145
76
|
```bash
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
--expect-en "Checkout Vertical" \
|
|
150
|
-
--match-build-marker \
|
|
151
|
-
--out .codex/reports/cloudflare-ssr/checkout-local.json
|
|
77
|
+
MODERN_BASELINE_ENABLE_MF_SSR=false
|
|
78
|
+
MODERN_BASELINE_ENABLE_BFF_REQUEST_ID=false
|
|
79
|
+
MODERN_BASELINE_ENABLE_TELEMETRY_EXPORTERS=false
|
|
152
80
|
```
|
|
153
81
|
|
|
154
|
-
|
|
82
|
+
## Grow When Needed
|
|
155
83
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
ULTRAMODERN_PUBLIC_URL_CHECKOUT=https://checkout.example.workers.dev \
|
|
161
|
-
pnpm run cloudflare:proof -- --require-public-urls
|
|
162
|
-
```
|
|
84
|
+
Stay in the single app until a boundary has a real owner and operational reason
|
|
85
|
+
to split. A separate package is usually enough when you only need reusable code.
|
|
86
|
+
Consider a larger workspace boundary later when a feature needs independent
|
|
87
|
+
ownership, rollout, rollback, incident routing, or deployment evidence.
|
|
163
88
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
credentials. Without those values, only dry-run Zephyr evidence can be claimed.
|
|
89
|
+
The public opinionated entrypoint is `presetUltramodern(...)`. It keeps Effect,
|
|
90
|
+
TanStack, SSR, BFF, i18n, and quality gates available without requiring a
|
|
91
|
+
distributed app layout on day one.
|
|
168
92
|
|
|
169
|
-
|
|
93
|
+
## Generated Automation
|
|
170
94
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
95
|
+
The generated starter includes `.github/workflows/ultramodern-gates.yml` and
|
|
96
|
+
`.github/renovate.json` for full app projects. The workflow runs
|
|
97
|
+
`pnpm run ultramodern:check` and `pnpm run build` on every push and pull request
|
|
98
|
+
with read-only permissions, commit-pinned actions, frozen installs, and
|
|
99
|
+
StepSecurity audit-mode runner hardening. Renovate is configured for dependency
|
|
100
|
+
dashboard review, one-day release age, grouped updates, action digest pinning,
|
|
101
|
+
and manual approval for major upgrades.
|
|
174
102
|
|
|
175
103
|
For more information, see the
|
|
176
104
|
[UltraModern.js guide](https://bleedingdev.github.io/ultramodern.js/guides/get-started/ultramodern.html)
|
|
@@ -5,27 +5,27 @@
|
|
|
5
5
|
},
|
|
6
6
|
"cards": {
|
|
7
7
|
"bff": {
|
|
8
|
-
"body": "
|
|
8
|
+
"body": "Pridej serverovou logiku, kdyz route potrebuje typovana data nebo mutace.",
|
|
9
9
|
"title": "BFF + Effect"
|
|
10
10
|
},
|
|
11
11
|
"config": {
|
|
12
|
-
"body": "Upravuj
|
|
12
|
+
"body": "Upravuj vychozi hodnoty v modern.config.ts podle rustu aplikace.",
|
|
13
13
|
"title": "Konfigurace presetUltramodern"
|
|
14
14
|
},
|
|
15
15
|
"gates": {
|
|
16
|
-
"body": "
|
|
16
|
+
"body": "Pred releasem spust ultramodern:check, testy, typecheck, lint a build.",
|
|
17
17
|
"title": "Ultramodern kontroly"
|
|
18
18
|
},
|
|
19
19
|
"guide": {
|
|
20
|
-
"body": "
|
|
20
|
+
"body": "Zacni jednou aplikaci a pridej routy, API nebo balicky az kdyz pomuzou.",
|
|
21
21
|
"title": "UltraModern.js pruvodce"
|
|
22
22
|
}
|
|
23
23
|
},
|
|
24
24
|
"description": {
|
|
25
|
-
"afterConfig": "
|
|
26
|
-
"afterPreset": "
|
|
27
|
-
"end": "zelene
|
|
28
|
-
"intro": "
|
|
25
|
+
"afterConfig": "podle rustu aplikace a udrzuj",
|
|
26
|
+
"afterPreset": "profilem. Lad",
|
|
27
|
+
"end": "zelene pri pridavani rout, API a workspace balicku.",
|
|
28
|
+
"intro": "Zacni s verejnym"
|
|
29
29
|
},
|
|
30
30
|
"language": {
|
|
31
31
|
"cs": "Cestina",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"switcher": "Jazyk"
|
|
34
34
|
},
|
|
35
35
|
"logoAlt": "Logo UltraModern.js",
|
|
36
|
-
"name": "
|
|
37
|
-
"title": "UltraModern.js
|
|
36
|
+
"name": "Jednoduchy starter aplikace",
|
|
37
|
+
"title": "UltraModern.js Starter"
|
|
38
38
|
}
|
|
39
39
|
}
|
|
@@ -5,27 +5,27 @@
|
|
|
5
5
|
},
|
|
6
6
|
"cards": {
|
|
7
7
|
"bff": {
|
|
8
|
-
"body": "
|
|
8
|
+
"body": "Add server logic when a route needs typed data or mutation handling.",
|
|
9
9
|
"title": "BFF + Effect"
|
|
10
10
|
},
|
|
11
11
|
"config": {
|
|
12
|
-
"body": "Tune
|
|
12
|
+
"body": "Tune generated defaults in modern.config.ts as the app grows.",
|
|
13
13
|
"title": "Configure presetUltramodern"
|
|
14
14
|
},
|
|
15
15
|
"gates": {
|
|
16
|
-
"body": "
|
|
16
|
+
"body": "Run ultramodern:check, tests, typecheck, lint, and build before release.",
|
|
17
17
|
"title": "Ultramodern Gates"
|
|
18
18
|
},
|
|
19
19
|
"guide": {
|
|
20
|
-
"body": "
|
|
20
|
+
"body": "Start with one app, then add routes, APIs, and packages when they help.",
|
|
21
21
|
"title": "UltraModern.js Guide"
|
|
22
22
|
}
|
|
23
23
|
},
|
|
24
24
|
"description": {
|
|
25
|
-
"afterConfig": ", keep",
|
|
26
|
-
"afterPreset": "profile.
|
|
27
|
-
"end": "green
|
|
28
|
-
"intro": "
|
|
25
|
+
"afterConfig": "as the app grows, and keep",
|
|
26
|
+
"afterPreset": "profile. Tune",
|
|
27
|
+
"end": "green while you add routes, APIs, and workspace packages.",
|
|
28
|
+
"intro": "Start from the public"
|
|
29
29
|
},
|
|
30
30
|
"language": {
|
|
31
31
|
"cs": "Czech",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"switcher": "Language"
|
|
34
34
|
},
|
|
35
35
|
"logoAlt": "UltraModern.js Logo",
|
|
36
|
-
"name": "
|
|
37
|
-
"title": "UltraModern.js
|
|
36
|
+
"name": "Simple app starter",
|
|
37
|
+
"title": "UltraModern.js Starter"
|
|
38
38
|
}
|
|
39
39
|
}
|
|
@@ -123,12 +123,14 @@ const Index = () => {
|
|
|
123
123
|
<p className="description{{#if enableTailwind}} text-emerald-700 font-semibold{{/if}}">
|
|
124
124
|
{t('home.description.intro')} <code className="code">presetUltramodern(...)</code>{' '}
|
|
125
125
|
{/* i18n-ignore technical token */}
|
|
126
|
-
{t('home.description.afterPreset')}
|
|
126
|
+
{t('home.description.afterPreset')}{' '}
|
|
127
127
|
<code className="code">modern.config.ts</code>
|
|
128
128
|
{/* i18n-ignore technical token */}
|
|
129
|
-
{
|
|
129
|
+
{' '}
|
|
130
|
+
{t('home.description.afterConfig')}{' '}
|
|
130
131
|
<code className="code">pnpm run ultramodern:check</code>
|
|
131
132
|
{/* i18n-ignore technical token */}
|
|
133
|
+
{' '}
|
|
132
134
|
{t('home.description.end')}
|
|
133
135
|
</p>
|
|
134
136
|
{{#if useEffectBff}}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# UltraModern Agent Contract
|
|
2
2
|
|
|
3
|
-
This workspace is generated as an agent-ready UltraModern.js SuperApp
|
|
3
|
+
This workspace is generated as an agent-ready UltraModern.js SuperApp shell.
|
|
4
|
+
Agents should treat the files under `.agents/skills` as local project
|
|
5
|
+
instructions, not optional reading.
|
|
4
6
|
|
|
5
7
|
## Quality Gates
|
|
6
8
|
|
|
@@ -13,7 +15,14 @@ This workspace is generated as an agent-ready UltraModern.js SuperApp. Agents sh
|
|
|
13
15
|
|
|
14
16
|
## Localized Routes
|
|
15
17
|
|
|
16
|
-
Generated apps keep locale-prefixed entry routes under `src/routes/[lang]`,
|
|
18
|
+
Generated apps keep locale-prefixed entry routes under `src/routes/[lang]`,
|
|
19
|
+
static language links, and canonical plus `hreflang` metadata. A new workspace
|
|
20
|
+
starts shell-only; `create <domain> --vertical` adds route-owned metadata,
|
|
21
|
+
localized resources, and Effect BFF surfaces for that domain. Runtime i18n is
|
|
22
|
+
not enabled in the starter because the current React 19 + Module Federation
|
|
23
|
+
streaming SSR stack must render predictably first. Production builds fail unless
|
|
24
|
+
`MODERN_PUBLIC_SITE_URL` is set per deployed app, so canonical URLs always use
|
|
25
|
+
the production origin.
|
|
17
26
|
|
|
18
27
|
## Required Skill Baseline
|
|
19
28
|
|
|
@@ -52,9 +61,13 @@ Agents may read files under `repos/` to understand upstream patterns, APIs, and
|
|
|
52
61
|
## Project Priorities
|
|
53
62
|
|
|
54
63
|
- Keep `presetUltramodern` as the single preset.
|
|
55
|
-
-
|
|
64
|
+
- Keep the initial workspace shell-only unless a user explicitly asks for a
|
|
65
|
+
starter vertical.
|
|
66
|
+
- Use `create <domain> --vertical` as the growth path for real business
|
|
67
|
+
MicroVerticals.
|
|
68
|
+
- Prefer Effect for BFF code.
|
|
56
69
|
- Prefer TanStack Router for app routing.
|
|
57
|
-
- Keep design-system code as
|
|
70
|
+
- Keep UI-kit or design-system code as ordinary vertical or shared package code, not a special core path.
|
|
58
71
|
- Keep generated packages explicit and publishable: stable `exports`, correct declarations, small public APIs, and clear ownership metadata.
|
|
59
72
|
- Do not add migration tooling or codemods unless the project owner explicitly asks for migration work.
|
|
60
73
|
|
|
@@ -3,25 +3,28 @@
|
|
|
3
3
|
Generated UltraModern SuperApp workspace.
|
|
4
4
|
|
|
5
5
|
This workspace keeps `presetUltramodern(...)` as the single public
|
|
6
|
-
UltraModern.js 3.0 SuperApp surface and
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
- `
|
|
11
|
-
|
|
12
|
-
- `verticals
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
6
|
+
UltraModern.js 3.0 SuperApp surface and starts with an explicit shell:
|
|
7
|
+
|
|
8
|
+
- `apps/shell-super-app` owns shell route assembly, Module Federation host
|
|
9
|
+
wiring, shared SSR/i18n runtime setup, and the boundary debugger.
|
|
10
|
+
- `packages/shared-*` provide placeholders for cross-workspace contracts,
|
|
11
|
+
design tokens, and Effect API sharing.
|
|
12
|
+
- `verticals/*` is intentionally empty until a real business domain is added.
|
|
13
|
+
|
|
14
|
+
Add a full-stack MicroVertical when the product needs one:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pnpm create modern@latest transportation --vertical
|
|
18
|
+
pnpm create modern@latest payments --vertical
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Each added vertical owns its UI/routes, browser-safe Module Federation exposes,
|
|
22
|
+
localized route metadata, CSS prefix, Effect BFF handlers, local API contract,
|
|
23
|
+
and typed client surface. Server handlers and Effect client/contract modules
|
|
24
|
+
stay out of browser exposes.
|
|
25
|
+
|
|
26
|
+
Run the scaffold validator before adding business code and after every
|
|
27
|
+
`--vertical` mutation:
|
|
25
28
|
|
|
26
29
|
```bash
|
|
27
30
|
mise install
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
|
|
4
|
-
const root = process.cwd();
|
|
5
|
-
const defaultAppDirs = [
|
|
6
|
-
'verticals/commerce',
|
|
7
|
-
'verticals/identity',
|
|
8
|
-
'verticals/design-system',
|
|
9
|
-
];
|
|
10
|
-
|
|
11
|
-
const candidateDirs = process.argv.slice(2);
|
|
12
|
-
const appDirs = candidateDirs.length
|
|
13
|
-
? candidateDirs
|
|
14
|
-
: fs.existsSync(path.join(root, 'module-federation.config.ts'))
|
|
15
|
-
? ['.']
|
|
16
|
-
: defaultAppDirs;
|
|
17
|
-
|
|
18
|
-
for (const appDir of appDirs) {
|
|
19
|
-
const configPath = path.join(root, appDir, 'module-federation.config.ts');
|
|
20
|
-
if (!fs.existsSync(configPath)) {
|
|
21
|
-
throw new Error(
|
|
22
|
-
`Missing Module Federation config: ${path.relative(root, configPath)}`,
|
|
23
|
-
);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const config = fs.readFileSync(configPath, 'utf-8');
|
|
27
|
-
if (!config.includes('exposes:') || config.includes('dts: false')) {
|
|
28
|
-
continue;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const typesArchivePath = path.join(root, appDir, 'dist/@mf-types.zip');
|
|
32
|
-
if (!fs.existsSync(typesArchivePath)) {
|
|
33
|
-
throw new Error(
|
|
34
|
-
`Missing Module Federation DTS archive: ${path.relative(root, typesArchivePath)}`,
|
|
35
|
-
);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const stats = fs.statSync(typesArchivePath);
|
|
39
|
-
if (stats.size === 0) {
|
|
40
|
-
throw new Error(
|
|
41
|
-
`Empty Module Federation DTS archive: ${path.relative(root, typesArchivePath)}`,
|
|
42
|
-
);
|
|
43
|
-
}
|
|
44
|
-
}
|