@madojs/mado 0.10.1 → 0.11.1
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 +95 -0
- package/README.md +22 -47
- 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/html/bindings.js +3 -3
- package/dist/src/html/bindings.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 +74 -63
- 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
package/docs/en/05-why-mado.md
CHANGED
|
@@ -36,11 +36,11 @@ If your case does not fall into the last point — Mado is most likely not the b
|
|
|
36
36
|
| Size | ~6 KB | ~16 KB |
|
|
37
37
|
| Age / support | ~10 years, Google | 6 months, single author |
|
|
38
38
|
| Reactivity | `@property` decorators + manual `requestUpdate` | signals (`signal`/`computed`/`effect`) out of the box |
|
|
39
|
-
| Router | none, you need to find one (`@lit-labs/router`, etc) | included: `routes()` +
|
|
39
|
+
| Router | none, you need to find one (`@lit-labs/router`, etc) | included: `routes()` + layout groups + prefetch |
|
|
40
40
|
| Data fetching | none, you need to assemble it | `resource()` + `mutation()` + glob invalidation |
|
|
41
41
|
| Forms | none | `useForm()` with HTML-like constraints |
|
|
42
42
|
| SEO / static | complex (`@lit-labs/ssr`) | `bake` (linkedom) + edge-prerender |
|
|
43
|
-
| Build | needs
|
|
43
|
+
| Build | needs framework-specific build plugins | Vite transport + native runtime |
|
|
44
44
|
| Code style | classes + decorators | functions + tagged templates |
|
|
45
45
|
| Ecosystem | real (Shoelace, Material Web, etc.) | none |
|
|
46
46
|
| When to choose | writing a design system / Web Components library for embedding | writing a full application, want everything in one box |
|
|
@@ -60,14 +60,14 @@ If your case does not fall into the last point — Mado is most likely not the b
|
|
|
60
60
|
| Reactivity | signals (same class of ideas) | signals |
|
|
61
61
|
| Templates | JSX (compiled to reactive expressions) | tagged template `html\`\`` |
|
|
62
62
|
| Component model | functions, Solid virtual nodes | Web Components |
|
|
63
|
-
| Build | Vite + babel-plugin-solid required |
|
|
63
|
+
| Build | Vite + babel-plugin-solid required | Vite for dev/build, no runtime dependencies |
|
|
64
64
|
| Router | `@solidjs/router` | included |
|
|
65
65
|
| Data | `createResource` | `resource()` |
|
|
66
66
|
| SSR | seriously supported (SolidStart) | intentionally none |
|
|
67
67
|
| Ecosystem | growing, ~50 packages | none |
|
|
68
|
-
| When to choose | need top performance + JSX + willing to configure the build | want
|
|
68
|
+
| When to choose | need top performance + JSX + willing to configure the build | want browser-native runtime with simple tooling |
|
|
69
69
|
|
|
70
|
-
**Honest pitch:** _"Solid is technically faster and more mature.
|
|
70
|
+
**Honest pitch:** _"Solid is technically faster and more mature. Mado is smaller in concept: browser-native runtime, Web Components, tagged templates, and Vite doing the boring dev/build work. If you want JSX and a larger ecosystem, go with Solid."_
|
|
71
71
|
|
|
72
72
|
---
|
|
73
73
|
|
|
@@ -162,7 +162,7 @@ I won't dwell on this for long, because React is in a **different weight class**
|
|
|
162
162
|
|
|
163
163
|
Not size, not performance, not signals — everything has better competitors.
|
|
164
164
|
|
|
165
|
-
> **"Open the source and read it in an evening. ~3500 lines,
|
|
165
|
+
> **"Open the source and read it in an evening. ~3500 lines, small modules. If something breaks — you don't go to an issue with 3000 comments. You go to `src/router/` and read the code."**
|
|
166
166
|
|
|
167
167
|
This is called **ownership** — you own the code, rather than depending on someone else's.
|
|
168
168
|
|
|
@@ -14,7 +14,7 @@ Mado is structured **like an HTTP server**. Seriously:
|
|
|
14
14
|
| ----------------------------------- | ---------------------------------------------- |
|
|
15
15
|
| HTTP router (chi, axum, mux) | `routes()` — path manifest |
|
|
16
16
|
| Handler `func(req, resp)` | `page({ view: (ctx) => html\`...\` })` |
|
|
17
|
-
| Middleware | `layout
|
|
17
|
+
| Middleware | `layout()` route group (wraps the handler) |
|
|
18
18
|
| Template engine (Jinja, Handlebars) | `html\`\`` tagged template |
|
|
19
19
|
| HTTP client with cache | `resource()` — fetch + cache + invalidation |
|
|
20
20
|
| Reactive variable / atom | `signal()` — reactive getter |
|
|
@@ -61,7 +61,7 @@ http.ListenAndServe(":8080", r)
|
|
|
61
61
|
### Mado — the same thing
|
|
62
62
|
|
|
63
63
|
```ts
|
|
64
|
-
// src/routes.ts
|
|
64
|
+
// src/app.routes.ts
|
|
65
65
|
import { routes } from "@madojs/mado";
|
|
66
66
|
|
|
67
67
|
export default routes({
|
|
@@ -71,7 +71,7 @@ export default routes({
|
|
|
71
71
|
```
|
|
72
72
|
|
|
73
73
|
```ts
|
|
74
|
-
// src/pages/home.ts
|
|
74
|
+
// src/modules/<module>/pages/home.ts
|
|
75
75
|
import { page, html } from "@madojs/mado";
|
|
76
76
|
export default page({
|
|
77
77
|
view: () => html`<h1>Hello</h1>`,
|
|
@@ -79,7 +79,7 @@ export default page({
|
|
|
79
79
|
```
|
|
80
80
|
|
|
81
81
|
```ts
|
|
82
|
-
// src/pages/user.ts
|
|
82
|
+
// src/modules/<module>/pages/user.ts
|
|
83
83
|
import { page, html } from "@madojs/mado";
|
|
84
84
|
export default page<{ id: string }>({
|
|
85
85
|
view: ({ params }) => html`<h1>User ${params.id}</h1>`,
|
|
@@ -266,7 +266,7 @@ This is like `context.WithValue` / `ctx.Value` in Go, but reactive.
|
|
|
266
266
|
If you're used to server-side rendering for SEO, in Mado this is solved differently: **prerender at build time**.
|
|
267
267
|
|
|
268
268
|
```ts
|
|
269
|
-
// src/pages/product.ts
|
|
269
|
+
// src/modules/<module>/pages/product.ts
|
|
270
270
|
export default page({
|
|
271
271
|
bake: {
|
|
272
272
|
paths: () => api.allProductSlugs(), // build-time fetch
|
|
@@ -369,37 +369,33 @@ export default page({
|
|
|
369
369
|
```
|
|
370
370
|
|
|
371
371
|
```ts
|
|
372
|
-
// src/routes.ts
|
|
373
|
-
import {
|
|
372
|
+
// src/app.routes.ts
|
|
373
|
+
import { layout, routes } from "@madojs/mado";
|
|
374
374
|
|
|
375
375
|
export default routes({
|
|
376
376
|
"/login": () => import("./pages/login.js"),
|
|
377
377
|
|
|
378
|
-
"/app
|
|
378
|
+
"/app": layout({
|
|
379
379
|
layout: () => import("./layouts/auth-layout.js"),
|
|
380
380
|
routes: {
|
|
381
|
-
dashboard: () => import("./pages/dashboard.js"),
|
|
382
|
-
users: () => import("./pages/users.js"),
|
|
381
|
+
"/dashboard": () => import("./pages/dashboard.js"),
|
|
382
|
+
"/users": () => import("./pages/users.js"),
|
|
383
383
|
},
|
|
384
384
|
}),
|
|
385
385
|
});
|
|
386
386
|
```
|
|
387
387
|
|
|
388
|
-
###
|
|
388
|
+
### Shared HTTP client (like a small transport package in Go)
|
|
389
389
|
|
|
390
390
|
```ts
|
|
391
|
-
// src/
|
|
392
|
-
export
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
}
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
export const api = new ApiClient("/api");
|
|
391
|
+
// src/shared/http/http-client.ts
|
|
392
|
+
export const httpClient = {
|
|
393
|
+
get: <T>(path: string): Promise<T> =>
|
|
394
|
+
fetch(path).then((r) => r.json() as Promise<T>),
|
|
395
|
+
};
|
|
400
396
|
```
|
|
401
397
|
|
|
402
|
-
|
|
398
|
+
Module connectors build on this transport and map DTOs to domain types.
|
|
403
399
|
|
|
404
400
|
---
|
|
405
401
|
|
|
@@ -442,6 +438,6 @@ Everything else — standard browser + TypeScript.
|
|
|
442
438
|
- **[`01-routing.md`](./01-routing.md)** — the router in detail.
|
|
443
439
|
- **[`02-project-layout.md`](./02-project-layout.md)** — project structure.
|
|
444
440
|
- **[`03-static-bake.md`](./03-static-bake.md)** — SEO without SSR.
|
|
445
|
-
- **
|
|
441
|
+
- **External `madojs-examples` workspace** — full demos (landing + admin).
|
|
446
442
|
|
|
447
443
|
If something is unclear — open an issue, or just open the source. It really is readable in an evening.
|
|
@@ -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.
|