@madojs/mado 0.10.1 → 0.11.0
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/AGENTS.md +24 -26
- package/CHANGELOG.md +68 -0
- package/README.md +18 -45
- package/TODO.md +52 -48
- package/dist/src/component.d.ts +2 -1
- package/dist/src/component.js +5 -2
- package/dist/src/component.js.map +1 -1
- package/dist/src/each.d.ts +1 -1
- package/dist/src/each.js +1 -1
- package/dist/src/each.js.map +1 -1
- package/dist/src/index.d.ts +11 -6
- package/dist/src/index.js +5 -3
- package/dist/src/index.js.map +1 -1
- package/dist/src/lazy.d.ts +1 -1
- package/dist/src/lazy.js +1 -1
- package/dist/src/lazy.js.map +1 -1
- package/dist/src/page.d.ts +17 -21
- package/dist/src/page.js +7 -12
- package/dist/src/page.js.map +1 -1
- package/dist/src/router/manifest.d.ts +1 -1
- package/dist/src/router/manifest.js +21 -13
- package/dist/src/router/manifest.js.map +1 -1
- package/dist/src/router/match.d.ts +2 -2
- package/dist/src/router/match.js +3 -3
- package/dist/src/router/match.js.map +1 -1
- package/dist/src/router/navigation.js +1 -1
- package/dist/src/router/navigation.js.map +1 -1
- package/dist/src/vite/index.d.ts +10 -0
- package/dist/src/vite/index.js +33 -0
- package/dist/src/vite/index.js.map +1 -0
- package/docs/en/00-the-mado-way.md +25 -12
- package/docs/en/01-routing.md +90 -142
- package/docs/en/02-project-layout.md +59 -53
- package/docs/en/03-static-bake.md +5 -6
- package/docs/en/05-why-mado.md +6 -6
- package/docs/en/06-for-backenders.md +18 -22
- package/docs/en/08-llm-zero-history-test.md +9 -14
- package/docs/en/09-shadow-vs-light-dom.md +28 -36
- package/docs/en/10-app-architecture.md +158 -96
- package/docs/en/11-layouts.md +22 -24
- package/docs/en/12-auth-and-api.md +89 -182
- package/docs/en/13-deployment.md +18 -22
- package/docs/en/14-testing.md +4 -4
- package/docs/en/16-bake-cookbook.md +11 -12
- package/docs/en/18-api-freeze-map.md +6 -4
- package/docs/en/20-v1-stability.md +1 -1
- package/docs/fr/00-the-mado-way.md +55 -90
- package/docs/fr/01-routing.md +70 -152
- package/docs/fr/02-project-layout.md +61 -42
- package/docs/fr/03-static-bake.md +1 -1
- package/docs/fr/05-why-mado.md +6 -6
- package/docs/fr/06-for-backenders.md +7 -7
- package/docs/fr/08-llm-zero-history-test.md +21 -48
- package/docs/fr/09-shadow-vs-light-dom.md +43 -162
- package/docs/fr/10-app-architecture.md +110 -33
- package/docs/fr/11-layouts.md +24 -12
- package/docs/fr/12-auth-and-api.md +63 -22
- package/docs/fr/13-deployment.md +7 -10
- package/docs/fr/14-testing.md +1 -1
- package/docs/fr/16-bake-cookbook.md +2 -2
- package/docs/fr/18-api-freeze-map.md +1 -1
- package/docs/fr/20-v1-stability.md +1 -1
- package/docs/recipes/nginx/README.md +13 -0
- package/docs/ru/00-the-mado-way.md +53 -75
- package/docs/ru/01-routing.md +68 -143
- package/docs/ru/02-project-layout.md +61 -41
- package/docs/ru/03-static-bake.md +2 -2
- package/docs/ru/05-why-mado.md +6 -6
- package/docs/ru/06-for-backenders.md +7 -7
- package/docs/ru/08-llm-zero-history-test.md +9 -14
- package/docs/ru/09-shadow-vs-light-dom.md +43 -178
- package/docs/ru/10-app-architecture.md +115 -63
- package/docs/ru/11-layouts.md +24 -24
- package/docs/ru/12-auth-and-api.md +57 -35
- package/docs/ru/13-deployment.md +7 -11
- package/docs/ru/14-testing.md +1 -1
- package/docs/ru/16-bake-cookbook.md +12 -6
- package/docs/ru/18-api-freeze-map.md +5 -3
- package/docs/ru/20-v1-stability.md +1 -1
- package/docs/uk/00-the-mado-way.md +70 -44
- package/docs/uk/01-routing.md +41 -47
- package/docs/uk/02-project-layout.md +68 -41
- package/docs/uk/03-static-bake.md +1 -2
- package/docs/uk/06-for-backenders.md +3 -3
- package/docs/uk/08-llm-zero-history-test.md +22 -24
- package/docs/uk/09-shadow-vs-light-dom.md +37 -86
- package/docs/uk/10-app-architecture.md +72 -31
- package/docs/uk/11-layouts.md +25 -12
- package/docs/uk/12-auth-and-api.md +58 -22
- package/docs/uk/13-deployment.md +4 -3
- package/docs/uk/14-testing.md +1 -1
- package/docs/uk/18-api-freeze-map.md +1 -1
- package/docs/uk/20-v1-stability.md +1 -1
- package/llms.txt +14 -15
- package/package.json +18 -11
- package/scripts/_config.mjs +15 -161
- package/scripts/bake.mjs +67 -57
- package/scripts/cli/generate.mjs +348 -0
- package/scripts/cli/help.mjs +27 -0
- package/scripts/cli/index.mjs +79 -0
- package/scripts/cli/init.mjs +153 -0
- package/scripts/cli/release.mjs +152 -0
- package/scripts/cli/run.mjs +96 -0
- package/scripts/cli.mjs +2 -621
- package/scripts/package-smoke.mjs +4 -1
- package/scripts/preview.mjs +13 -37
- package/scripts/size-budget.mjs +5 -2
- package/scripts/vite.default.mjs +11 -0
- package/starters/default/.editorconfig +12 -0
- package/starters/default/README.md +74 -0
- package/starters/default/eslint.config.mjs +256 -0
- package/starters/default/index.html +13 -0
- package/starters/default/package.json +30 -0
- package/starters/default/public/favicon.svg +4 -0
- package/starters/default/src/app.routes.ts +39 -0
- package/starters/default/src/layouts/app-shell.layout.ts +35 -0
- package/starters/default/src/layouts/auth-shell.layout.ts +17 -0
- package/starters/default/src/main.ts +16 -0
- package/starters/default/src/modules/auth/_contracts/auth-api.types.ts +17 -0
- package/starters/default/src/modules/auth/auth.connector.ts +45 -0
- package/starters/default/src/modules/auth/auth.guard.ts +22 -0
- package/starters/default/src/modules/auth/auth.public.ts +9 -0
- package/starters/default/src/modules/auth/auth.routes.ts +8 -0
- package/starters/default/src/modules/auth/auth.service.ts +71 -0
- package/starters/default/src/modules/auth/auth.types.ts +15 -0
- package/starters/default/src/modules/auth/login.page.ts +62 -0
- package/starters/default/src/modules/billing/_contracts/stripe.types.ts +17 -0
- package/starters/default/src/modules/billing/api/stripe.connector.ts +71 -0
- package/starters/default/src/modules/billing/billing.public.ts +5 -0
- package/starters/default/src/modules/billing/billing.routes.ts +9 -0
- package/starters/default/src/modules/billing/billing.types.ts +15 -0
- package/starters/default/src/modules/billing/components/invoice-status-badge.component.ts +43 -0
- package/starters/default/src/modules/billing/data/invoices.resource.ts +35 -0
- package/starters/default/src/modules/billing/pages/invoice-detail.page.ts +70 -0
- package/starters/default/src/modules/billing/pages/invoices-list.page.ts +73 -0
- package/starters/default/src/modules/home/home.page.ts +34 -0
- package/starters/default/src/modules/home/not-found.page.ts +11 -0
- package/starters/default/src/shared/http/http-client.ts +86 -0
- package/starters/default/src/shared/http/http-error.ts +37 -0
- package/starters/default/src/shared/http/interceptors.ts +59 -0
- package/starters/default/src/shared/lib/format-date.ts +19 -0
- package/starters/default/src/shared/styles/content.css +70 -0
- package/starters/default/src/shared/styles/reset.css +32 -0
- package/starters/default/src/shared/styles/shell.css +57 -0
- package/starters/default/src/shared/styles/tokens.css +44 -0
- package/starters/default/src/shared/ui/x-button.component.ts +49 -0
- package/starters/default/src/shared/ui/x-spinner.component.ts +22 -0
- package/starters/default/src/styles.d.ts +1 -0
- package/starters/default/src/vite-env.d.ts +1 -0
- package/starters/default/tsconfig.json +24 -0
- package/starters/default/vite.config.ts +9 -0
- package/MADO_V1_PLAN.md +0 -179
- package/ROADMAP.md +0 -178
- package/dist/src/html.d.ts +0 -18
- package/dist/src/html.js +0 -17
- package/dist/src/html.js.map +0 -1
- package/dist/src/router.d.ts +0 -13
- package/dist/src/router.js +0 -13
- package/dist/src/router.js.map +0 -1
- package/scripts/bundle.mjs +0 -212
- package/scripts/llm-zero-history-smoke.mjs +0 -93
- package/scripts/new.mjs +0 -80
- package/scripts/showcase-regression.mjs +0 -392
- package/server/serve.mjs +0 -455
- package/starters/admin/README.md +0 -63
- package/starters/admin/index.html +0 -28
- package/starters/admin/mado.config.json +0 -22
- package/starters/admin/package.json +0 -24
- package/starters/admin/public/favicon.svg +0 -4
- package/starters/admin/src/components/x-button.ts +0 -82
- package/starters/admin/src/components/x-input.ts +0 -105
- package/starters/admin/src/layouts/app.ts +0 -101
- package/starters/admin/src/layouts/auth.ts +0 -41
- package/starters/admin/src/lib/api.ts +0 -184
- package/starters/admin/src/lib/auth.ts +0 -83
- package/starters/admin/src/main.ts +0 -15
- package/starters/admin/src/pages/admin/dashboard.ts +0 -48
- package/starters/admin/src/pages/admin/order-detail.ts +0 -80
- package/starters/admin/src/pages/admin/orders.ts +0 -117
- package/starters/admin/src/pages/home.ts +0 -34
- package/starters/admin/src/pages/login.ts +0 -70
- package/starters/admin/src/pages/not-found.ts +0 -12
- package/starters/admin/src/routes.ts +0 -40
- package/starters/admin/src/styles/global.ts +0 -86
- package/starters/admin/tsconfig.json +0 -15
- package/starters/crud/README.md +0 -33
- package/starters/crud/index.html +0 -28
- package/starters/crud/mado.config.json +0 -20
- package/starters/crud/package.json +0 -24
- package/starters/crud/src/components/app-shell.ts +0 -56
- package/starters/crud/src/components/ticket-detail.ts +0 -33
- package/starters/crud/src/components/ticket-form.ts +0 -69
- package/starters/crud/src/components/ticket-list.ts +0 -66
- package/starters/crud/src/lib/api.ts +0 -76
- package/starters/crud/src/main.ts +0 -9
- package/starters/crud/src/pages/home.ts +0 -34
- package/starters/crud/src/pages/not-found.ts +0 -12
- package/starters/crud/src/pages/ticket-detail.ts +0 -7
- package/starters/crud/src/pages/ticket-new.ts +0 -7
- package/starters/crud/src/pages/tickets.ts +0 -7
- package/starters/crud/src/routes.ts +0 -11
- package/starters/crud/src/styles/global.ts +0 -155
- package/starters/crud/tsconfig.json +0 -15
- package/starters/minimal/README.md +0 -21
- package/starters/minimal/index.html +0 -28
- package/starters/minimal/mado.config.json +0 -20
- package/starters/minimal/package.json +0 -24
- package/starters/minimal/src/components/app-counter.ts +0 -31
- package/starters/minimal/src/main.ts +0 -9
- package/starters/minimal/src/pages/home.ts +0 -35
- package/starters/minimal/src/pages/not-found.ts +0 -14
- package/starters/minimal/src/routes.ts +0 -8
- package/starters/minimal/src/styles/global.ts +0 -60
- package/starters/minimal/tsconfig.json +0 -15
- package/templates/page-detail.ts +0 -63
- package/templates/page-form.ts +0 -94
- package/templates/page-list.ts +0 -79
|
@@ -12,15 +12,15 @@ For the first pass, give the agent only:
|
|
|
12
12
|
- `AGENTS.md`
|
|
13
13
|
- `README.md`
|
|
14
14
|
- `docs/en/07-llm-pitfalls.md`
|
|
15
|
-
- `examples
|
|
16
|
-
|
|
15
|
+
- files from the external `madojs-examples` workspace only when the agent asks
|
|
16
|
+
for a larger app pattern
|
|
17
17
|
|
|
18
18
|
The agent may search targeted APIs in `src/` when blocked, but should not load
|
|
19
19
|
the whole framework into context.
|
|
20
20
|
|
|
21
21
|
## Task
|
|
22
22
|
|
|
23
|
-
Build
|
|
23
|
+
Build a small ticket-admin SPA for a solo/backend developer.
|
|
24
24
|
|
|
25
25
|
Required behavior:
|
|
26
26
|
|
|
@@ -47,15 +47,10 @@ Look for these after implementation:
|
|
|
47
47
|
|
|
48
48
|
## Result notes
|
|
49
49
|
|
|
50
|
-
The
|
|
51
|
-
|
|
50
|
+
The historical tickets implementation lives in the external examples workspace.
|
|
51
|
+
The core repository no longer ships that artifact or a dedicated smoke command;
|
|
52
|
+
use this document as a manual evaluation script when updating LLM guidance.
|
|
52
53
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
patterns, then builds and runs `test/tickets-smoke.test.mjs`.
|
|
57
|
-
|
|
58
|
-
The main documentation pressure point remains lifecycle: older examples can make
|
|
59
|
-
it look acceptable to create `resource()` directly in `page.view()`. The tickets
|
|
60
|
-
example uses page-level wrapper components instead, so resources are registered
|
|
61
|
-
inside component setup and clean up with the component.
|
|
54
|
+
The main documentation pressure point remains lifecycle: examples should not
|
|
55
|
+
make it look acceptable to create long-lived `resource()` instances accidentally
|
|
56
|
+
at module scope or in route code that never cleans up.
|
|
@@ -6,18 +6,19 @@ an application.
|
|
|
6
6
|
|
|
7
7
|
## Rule of Thumb
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
Use Mado route layouts (`page({ view: ({ child }) => ... })`) for app zones:
|
|
10
|
+
auth shells, admin shells, public shells and embedded shells. These live in
|
|
11
|
+
`src/layouts/` and are composed from `src/app.routes.ts`.
|
|
12
12
|
|
|
13
|
-
Use
|
|
13
|
+
Use Web Components registered with `component()` for reusable UI elements and
|
|
14
|
+
widgets. Use plain functions only for small inline template helpers:
|
|
14
15
|
|
|
15
16
|
```ts
|
|
16
17
|
const money = (value: number) => html`<span>${formatMoney(value)}</span>`;
|
|
17
18
|
```
|
|
18
19
|
|
|
19
|
-
Do not
|
|
20
|
-
|
|
20
|
+
Do not hide app shells inside `main.ts` or generic helper functions. The app
|
|
21
|
+
map should show which layout wraps which route group.
|
|
21
22
|
|
|
22
23
|
Use **Shadow DOM** for leaf widgets:
|
|
23
24
|
|
|
@@ -26,23 +27,14 @@ Use **Shadow DOM** for leaf widgets:
|
|
|
26
27
|
- embed widgets that should not inherit app CSS accidentally;
|
|
27
28
|
- components whose styling should be owned by the component itself.
|
|
28
29
|
|
|
29
|
-
Use **Light DOM**
|
|
30
|
-
global CSS utilities:
|
|
30
|
+
Use **Light DOM** for app structure that wants to share global CSS:
|
|
31
31
|
|
|
32
|
-
- route
|
|
32
|
+
- route pages and route layouts;
|
|
33
33
|
- admin screens with dense table/form layouts;
|
|
34
34
|
- data-heavy screens with tables and forms;
|
|
35
|
-
- components that intentionally share global layout, form and table utilities;
|
|
36
35
|
- places where children should simply remain normal document DOM.
|
|
37
36
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
- app shells that render `<slot>`;
|
|
41
|
-
- sidebar/content wrappers;
|
|
42
|
-
- reusable layout frames that own their own grid/header/sidebar CSS.
|
|
43
|
-
|
|
44
|
-
`<slot>` is a Shadow DOM feature. In a `shadow: false` component, `<slot>` is
|
|
45
|
-
just a normal element and does not move children into that position.
|
|
37
|
+
Route layouts receive `child` from Mado, so they do not need `<slot>`.
|
|
46
38
|
|
|
47
39
|
## The Footgun
|
|
48
40
|
|
|
@@ -96,12 +88,15 @@ Now global utilities and local scoped styles both work.
|
|
|
96
88
|
## Recommended App Shape
|
|
97
89
|
|
|
98
90
|
```ts
|
|
99
|
-
//
|
|
100
|
-
|
|
101
|
-
|
|
91
|
+
// app zone: route layout, styled by shared/styles/shell.css
|
|
92
|
+
export default page({
|
|
93
|
+
view: ({ child }) => html`<main class="app-main">${child}</main>`,
|
|
94
|
+
});
|
|
102
95
|
|
|
103
|
-
//
|
|
104
|
-
|
|
96
|
+
// route page: light DOM, styled by shared/styles/content.css
|
|
97
|
+
export default page({
|
|
98
|
+
view: () => html`<section><h1>Users</h1></section>`,
|
|
99
|
+
});
|
|
105
100
|
|
|
106
101
|
// leaf widgets: Shadow DOM default
|
|
107
102
|
component("x-status-badge", setup);
|
|
@@ -109,8 +104,8 @@ component("x-stat-card", setup);
|
|
|
109
104
|
component("x-toast-stack", setup);
|
|
110
105
|
```
|
|
111
106
|
|
|
112
|
-
This gives
|
|
113
|
-
|
|
107
|
+
This gives admin screens predictable CSS while preserving encapsulation for
|
|
108
|
+
reusable leaf widgets.
|
|
114
109
|
|
|
115
110
|
The import model is deliberately browser-native:
|
|
116
111
|
|
|
@@ -124,9 +119,8 @@ The import registers the custom element with `customElements.define()`. The
|
|
|
124
119
|
template creates an `<x-app-layout>` element. The browser connects the two.
|
|
125
120
|
There is no React-style component value being passed around.
|
|
126
121
|
|
|
127
|
-
If
|
|
128
|
-
|
|
129
|
-
`<slot>`, keep Shadow DOM and put the shell styles in that component.
|
|
122
|
+
If you do need a reusable slot-based frame, keep it as a Shadow DOM component
|
|
123
|
+
and put frame styles in that component.
|
|
130
124
|
|
|
131
125
|
## Routing and Links
|
|
132
126
|
|
|
@@ -167,16 +161,14 @@ tiny demos, but it hides ownership and defeats lazy route loading. Prefer:
|
|
|
167
161
|
- feature-owned shared components in the feature entry page;
|
|
168
162
|
- truly global leaf components in `main.ts` only when they are used everywhere.
|
|
169
163
|
|
|
170
|
-
##
|
|
164
|
+
## Starter Lesson
|
|
171
165
|
|
|
172
|
-
|
|
166
|
+
The default starter uses this split deliberately:
|
|
173
167
|
|
|
174
|
-
-
|
|
175
|
-
- `
|
|
176
|
-
|
|
177
|
-
-
|
|
178
|
-
- leaf components such as `x-stat-card`, `x-status-badge`, `x-modal`, and
|
|
179
|
-
`x-toast-stack` keep Shadow DOM.
|
|
168
|
+
- route layouts are `page()` files under `src/layouts/`;
|
|
169
|
+
- `shell.css` owns app-zone chrome;
|
|
170
|
+
- `content.css` owns page-level tables/forms/prose;
|
|
171
|
+
- leaf components such as `x-button`, `x-spinner` and badges keep Shadow DOM.
|
|
180
172
|
|
|
181
173
|
If a page suddenly looks unstyled, check whether it uses global classes inside a
|
|
182
174
|
Shadow DOM component. That is usually the issue.
|
|
@@ -1,141 +1,203 @@
|
|
|
1
1
|
# App architecture
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
The official starter is the canonical production shape for Mado apps. It is
|
|
4
|
+
not a demo architecture and not a framework inside the framework: it is plain
|
|
5
|
+
files, imports, Mado primitives, and ESLint boundaries.
|
|
6
6
|
|
|
7
|
-
## File
|
|
7
|
+
## File Tree
|
|
8
8
|
|
|
9
9
|
```txt
|
|
10
10
|
src/
|
|
11
11
|
├── main.ts
|
|
12
|
-
├── routes.ts
|
|
12
|
+
├── app.routes.ts
|
|
13
13
|
├── layouts/
|
|
14
|
-
│ ├── app.ts
|
|
15
|
-
│ └── auth.ts
|
|
16
|
-
├──
|
|
17
|
-
│ ├──
|
|
18
|
-
│ ├──
|
|
19
|
-
│ ├──
|
|
20
|
-
│ └──
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
│
|
|
24
|
-
├──
|
|
25
|
-
│ ├──
|
|
26
|
-
│
|
|
27
|
-
├──
|
|
28
|
-
│ ├──
|
|
29
|
-
│ └──
|
|
30
|
-
└──
|
|
31
|
-
|
|
14
|
+
│ ├── app-shell.layout.ts
|
|
15
|
+
│ └── auth-shell.layout.ts
|
|
16
|
+
├── shared/
|
|
17
|
+
│ ├── http/
|
|
18
|
+
│ ├── lib/
|
|
19
|
+
│ ├── styles/
|
|
20
|
+
│ └── ui/
|
|
21
|
+
└── modules/
|
|
22
|
+
├── auth/
|
|
23
|
+
│ ├── auth.routes.ts
|
|
24
|
+
│ ├── auth.public.ts
|
|
25
|
+
│ ├── auth.service.ts
|
|
26
|
+
│ ├── auth.connector.ts
|
|
27
|
+
│ ├── auth.guard.ts
|
|
28
|
+
│ ├── login.page.ts
|
|
29
|
+
│ └── _contracts/
|
|
30
|
+
└── billing/
|
|
31
|
+
├── billing.routes.ts
|
|
32
|
+
├── billing.public.ts
|
|
33
|
+
├── billing.types.ts
|
|
34
|
+
├── api/
|
|
35
|
+
├── data/
|
|
36
|
+
├── pages/
|
|
37
|
+
├── components/
|
|
38
|
+
└── _contracts/
|
|
32
39
|
```
|
|
33
40
|
|
|
34
|
-
|
|
35
|
-
`components/`. A page should import the components it renders.
|
|
41
|
+
## The App Map
|
|
36
42
|
|
|
37
|
-
|
|
43
|
+
`src/app.routes.ts` is the whole application map. Modules export plain route
|
|
44
|
+
maps; app routes decide which shell and guard wrap each zone.
|
|
38
45
|
|
|
39
46
|
```ts
|
|
40
|
-
// src/main.ts
|
|
41
|
-
import { html, render } from "@madojs/mado";
|
|
42
|
-
import "./styles/global.js";
|
|
43
|
-
import "./components/x-button.js";
|
|
44
|
-
import routesApi from "./routes.js";
|
|
45
|
-
|
|
46
|
-
render(html`${routesApi.view}`, document.getElementById("app")!);
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
Import global providers and tiny shared components here. Do not bulk-import
|
|
50
|
-
every feature component.
|
|
51
|
-
|
|
52
|
-
## Routes
|
|
53
|
-
|
|
54
|
-
```ts
|
|
55
|
-
// src/routes.ts
|
|
56
47
|
import { layout, routes } from "@madojs/mado";
|
|
57
|
-
import { requireAuth } from "./
|
|
48
|
+
import { requireAuth } from "./modules/auth/auth.public";
|
|
49
|
+
import { authRoutes } from "./modules/auth/auth.routes";
|
|
50
|
+
import { billingRoutes } from "./modules/billing/billing.routes";
|
|
58
51
|
|
|
59
52
|
export const manifest = {
|
|
60
|
-
"/": () => import("./
|
|
53
|
+
"/": () => import("./modules/home/home.page"),
|
|
61
54
|
"/login": layout({
|
|
62
|
-
layout: () => import("./layouts/auth.
|
|
63
|
-
routes:
|
|
55
|
+
layout: () => import("./layouts/auth-shell.layout"),
|
|
56
|
+
routes: authRoutes,
|
|
64
57
|
}),
|
|
65
|
-
"/
|
|
66
|
-
layout: () => import("./layouts/app.
|
|
58
|
+
"/billing": layout({
|
|
59
|
+
layout: () => import("./layouts/app-shell.layout"),
|
|
67
60
|
guard: requireAuth,
|
|
68
|
-
routes:
|
|
69
|
-
"/": () => import("./pages/admin/dashboard.js"),
|
|
70
|
-
"/orders": () => import("./pages/admin/orders.js"),
|
|
71
|
-
"/orders/:id": () => import("./pages/admin/order-detail.js"),
|
|
72
|
-
},
|
|
61
|
+
routes: billingRoutes,
|
|
73
62
|
}),
|
|
74
|
-
"*": () => import("./
|
|
63
|
+
"*": () => import("./modules/home/not-found.page"),
|
|
75
64
|
};
|
|
76
65
|
|
|
77
|
-
export default routes(manifest
|
|
78
|
-
errorPage: (err) => html`<main><h1>Something went wrong</h1><pre>${err.message}</pre></main>`,
|
|
79
|
-
});
|
|
66
|
+
export default routes(manifest);
|
|
80
67
|
```
|
|
81
68
|
|
|
82
|
-
|
|
69
|
+
Rules:
|
|
83
70
|
|
|
84
|
-
|
|
71
|
+
- Export `manifest` so `mado bake` can discover bakeable pages.
|
|
72
|
+
- Modules never call `layout()`.
|
|
73
|
+
- Layouts describe app zones, not domains.
|
|
74
|
+
- Do not hide the router inside a custom element or a second shell in
|
|
75
|
+
`main.ts`.
|
|
85
76
|
|
|
86
|
-
|
|
87
|
-
version with token storage, a single-flight refresh request, and a route guard.
|
|
77
|
+
## File Forms
|
|
88
78
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
79
|
+
The suffix tells you the shape:
|
|
80
|
+
|
|
81
|
+
| Suffix | Role |
|
|
82
|
+
| ----------------- | ------------------------------------- |
|
|
83
|
+
| `*.page.ts` | route page, default `page({...})` |
|
|
84
|
+
| `*.layout.ts` | app-zone wrapper, default `page(...)` |
|
|
85
|
+
| `*.connector.ts` | one external API system |
|
|
86
|
+
| `*.resource.ts` | `resource()` and `mutation()` layer |
|
|
87
|
+
| `*.service.ts` | module singleton state |
|
|
88
|
+
| `*.guard.ts` | route guard |
|
|
89
|
+
| `*.routes.ts` | module-local route map |
|
|
90
|
+
| `*.public.ts` | only public module surface |
|
|
91
|
+
| `*.types.ts` | domain types |
|
|
92
|
+
| `*.component.ts` | Web Component registration |
|
|
93
|
+
|
|
94
|
+
Page-local signals, resources and forms live inside `view()`. Module-wide
|
|
95
|
+
state lives in `*.service.ts`.
|
|
96
|
+
|
|
97
|
+
## Data Flow
|
|
98
|
+
|
|
99
|
+
```txt
|
|
100
|
+
shared/http/http-client.ts
|
|
101
|
+
▲
|
|
102
|
+
modules/<x>/api/*.connector.ts DTO -> domain mapping
|
|
103
|
+
▲
|
|
104
|
+
modules/<x>/data/*.resource.ts cache keys + mutations
|
|
105
|
+
▲
|
|
106
|
+
modules/<x>/pages/*.page.ts UI consumes domain types
|
|
107
107
|
```
|
|
108
108
|
|
|
109
|
-
|
|
109
|
+
Connectors talk to the wire and return domain types. Resources own cache keys
|
|
110
|
+
and invalidation. Pages render and call resources/mutations.
|
|
110
111
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
112
|
+
## Module Boundaries
|
|
113
|
+
|
|
114
|
+
Each module is opaque except for `<module>.public.ts`. Other modules import
|
|
115
|
+
through that file or not at all. DTOs live under `_contracts/` and stay private
|
|
116
|
+
to the connector that understands that external system.
|
|
117
|
+
|
|
118
|
+
The default starter enforces this with ESLint:
|
|
119
|
+
|
|
120
|
+
- no barrels (`index.ts`);
|
|
121
|
+
- no `export *`;
|
|
122
|
+
- no CSS imports outside `src/main.ts`;
|
|
123
|
+
- no cross-module imports except public surfaces;
|
|
124
|
+
- no `_contracts` imports outside connectors.
|
|
125
|
+
|
|
126
|
+
## Styles
|
|
127
|
+
|
|
128
|
+
The starter uses plain CSS plus component-local ``css`...` ``. The split is
|
|
129
|
+
intentional:
|
|
130
|
+
|
|
131
|
+
| File | Role |
|
|
132
|
+
| ---- | ---- |
|
|
133
|
+
| `src/shared/styles/tokens.css` | design tokens as CSS custom properties |
|
|
134
|
+
| `src/shared/styles/reset.css` | document/light DOM reset |
|
|
135
|
+
| `src/shared/styles/shell.css` | app-zone layouts from `src/layouts/` |
|
|
136
|
+
| `src/shared/styles/content.css` | page-level forms, tables, prose and states |
|
|
137
|
+
|
|
138
|
+
Reusable leaf components keep their own styles in ``css`...` `` inside
|
|
139
|
+
`component()` options. They should depend on tokens (`var(--color-text)`)
|
|
140
|
+
rather than global classes.
|
|
141
|
+
|
|
142
|
+
`vite.config.ts` opts into Vite's Lightning CSS transformer. Mado does not own
|
|
143
|
+
prefixing, CSS lowering or minification.
|
|
144
|
+
|
|
145
|
+
## Growth
|
|
146
|
+
|
|
147
|
+
Start flat when a module is small:
|
|
148
|
+
|
|
149
|
+
```txt
|
|
150
|
+
modules/auth/
|
|
151
|
+
auth.types.ts
|
|
152
|
+
auth.routes.ts
|
|
153
|
+
auth.public.ts
|
|
154
|
+
auth.service.ts
|
|
155
|
+
auth.connector.ts
|
|
156
|
+
login.page.ts
|
|
115
157
|
```
|
|
116
158
|
|
|
117
|
-
|
|
159
|
+
When a module grows, group by role without changing suffixes:
|
|
118
160
|
|
|
119
|
-
|
|
161
|
+
```txt
|
|
162
|
+
modules/billing/
|
|
163
|
+
pages/
|
|
164
|
+
data/
|
|
165
|
+
api/
|
|
166
|
+
components/
|
|
167
|
+
forms/
|
|
168
|
+
_contracts/
|
|
169
|
+
```
|
|
120
170
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
171
|
+
If a module becomes too large, split it by subdomain and keep communication
|
|
172
|
+
through public surfaces.
|
|
173
|
+
|
|
174
|
+
## CLI
|
|
175
|
+
|
|
176
|
+
Use `mado new` for boring file scaffolding:
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
mado new module billing
|
|
180
|
+
mado new page billing/pages/invoices-list
|
|
181
|
+
mado new connector billing/api/stripe
|
|
182
|
+
mado new resource billing/data/invoices
|
|
183
|
+
mado new service billing/cart
|
|
184
|
+
mado new form billing/invoice
|
|
185
|
+
mado new component billing/components/invoice-status-badge
|
|
186
|
+
mado new guard billing/billing
|
|
187
|
+
mado new layout app-shell
|
|
127
188
|
```
|
|
128
189
|
|
|
129
|
-
|
|
130
|
-
|
|
190
|
+
The generator only writes new files. It does not edit `app.routes.ts`, does
|
|
191
|
+
not scan the filesystem, and refuses to overwrite existing files.
|
|
131
192
|
|
|
132
193
|
## Release
|
|
133
194
|
|
|
134
|
-
Local development uses `mado dev`. Production uses
|
|
195
|
+
Local development uses Vite through `mado dev`. Production uses one deploy
|
|
196
|
+
artifact:
|
|
135
197
|
|
|
136
198
|
```bash
|
|
137
199
|
mado release
|
|
138
200
|
rsync -avz out/ user@server:/var/www/app/
|
|
139
201
|
```
|
|
140
202
|
|
|
141
|
-
`out/` is the deploy folder. `dist/` is internal
|
|
203
|
+
`out/` is the deploy folder. `dist/` is internal package output.
|
package/docs/en/11-layouts.md
CHANGED
|
@@ -1,34 +1,32 @@
|
|
|
1
1
|
# Layouts
|
|
2
2
|
|
|
3
|
-
> **One blessed path.** Layouts in Mado are
|
|
3
|
+
> **One blessed path.** Layouts in Mado are route groups with a shared
|
|
4
4
|
> shell. There is exactly one canonical place to declare a layout — your
|
|
5
|
-
> `routes.ts` manifest. Putting layout code anywhere else (in `main.ts`, in a
|
|
5
|
+
> `app.routes.ts` manifest. Putting layout code anywhere else (in `main.ts`, in a
|
|
6
6
|
> page view, in a global custom-element wrapper) is a bug pattern: the LLM and
|
|
7
7
|
> the human both produce visually broken UI when they guess differently.
|
|
8
8
|
|
|
9
9
|
## The canonical recipe
|
|
10
10
|
|
|
11
11
|
```ts
|
|
12
|
-
// src/routes.ts
|
|
12
|
+
// src/app.routes.ts
|
|
13
13
|
import { layout, routes } from "@madojs/mado";
|
|
14
|
-
import { requireAuth } from "./
|
|
14
|
+
import { requireAuth } from "./modules/auth/auth.public.js";
|
|
15
|
+
import { authRoutes } from "./modules/auth/auth.routes.js";
|
|
16
|
+
import { billingRoutes } from "./modules/billing/billing.routes.js";
|
|
15
17
|
|
|
16
18
|
export const manifest = {
|
|
17
|
-
"/": () => import("./
|
|
19
|
+
"/": () => import("./modules/home/home.page.js"),
|
|
18
20
|
"/login": layout({
|
|
19
|
-
layout: () => import("./layouts/auth.js"),
|
|
20
|
-
routes:
|
|
21
|
+
layout: () => import("./layouts/auth-shell.layout.js"),
|
|
22
|
+
routes: authRoutes,
|
|
21
23
|
}),
|
|
22
|
-
"/
|
|
23
|
-
layout: () => import("./layouts/app.js"),
|
|
24
|
-
guard: requireAuth,
|
|
25
|
-
routes:
|
|
26
|
-
"/": () => import("./pages/admin/dashboard.js"),
|
|
27
|
-
"/orders": () => import("./pages/admin/orders.js"),
|
|
28
|
-
"/orders/:id": () => import("./pages/admin/order-detail.js"),
|
|
29
|
-
},
|
|
24
|
+
"/billing": layout({
|
|
25
|
+
layout: () => import("./layouts/app-shell.layout.js"),
|
|
26
|
+
guard: requireAuth,
|
|
27
|
+
routes: billingRoutes,
|
|
30
28
|
}),
|
|
31
|
-
"*": () => import("./
|
|
29
|
+
"*": () => import("./modules/home/not-found.page.js"),
|
|
32
30
|
};
|
|
33
31
|
|
|
34
32
|
export default routes(manifest);
|
|
@@ -62,14 +60,14 @@ That is the whole API.
|
|
|
62
60
|
Without this convention, every page accumulates `<x-app-shell>${...}</x-app-shell>`
|
|
63
61
|
boilerplate, the LLM eventually puts the shell wrapper into `main.ts` "to make
|
|
64
62
|
it consistent", and the next refactor produces the classic
|
|
65
|
-
*"navigation appears below the page content"* screenshot. The
|
|
63
|
+
*"navigation appears below the page content"* screenshot. The layout-group
|
|
66
64
|
recipe makes the shell the **outer frame** structurally; there is no way to
|
|
67
65
|
re-order it by accident.
|
|
68
66
|
|
|
69
67
|
## Two acceptable alternatives (with caveats)
|
|
70
68
|
|
|
71
|
-
These exist for completeness. Reach for them only if you cannot use
|
|
72
|
-
|
|
69
|
+
These exist for completeness. Reach for them only if you cannot use layout
|
|
70
|
+
groups.
|
|
73
71
|
|
|
74
72
|
### a) A single shell with the router slot in `main.ts`
|
|
75
73
|
|
|
@@ -104,12 +102,12 @@ place. **Do not start with this.**
|
|
|
104
102
|
|
|
105
103
|
## Where to find more
|
|
106
104
|
|
|
107
|
-
- `src/page.ts` defines `layout()`, `page()`, `Guard` and `
|
|
108
|
-
- `src/router/manifest.ts` flattens the
|
|
105
|
+
- `src/page.ts` defines `layout()`, `page()`, `Guard` and `LayoutRoutes`.
|
|
106
|
+
- `src/router/manifest.ts` flattens the layout manifest and applies guards
|
|
109
107
|
outer → inner before the page renders.
|
|
110
|
-
- The
|
|
111
|
-
|
|
108
|
+
- The default starter (`mado init my-app`) ships with public, auth and
|
|
109
|
+
authenticated app zones and is the reference implementation.
|
|
112
110
|
|
|
113
111
|
If you ever feel tempted to invent a fourth pattern, write it down in your
|
|
114
112
|
project `docs/` first and discuss it with the team. The cost of inconsistency
|
|
115
|
-
in this exact spot is higher than the cost of a slightly awkward layout.
|
|
113
|
+
in this exact spot is higher than the cost of a slightly awkward layout.
|