@madojs/mado 0.11.1 → 0.13.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 +116 -32
- package/CHANGELOG.md +216 -1
- package/README.md +158 -173
- package/dist/src/component.d.ts +6 -3
- package/dist/src/component.d.ts.map +1 -0
- package/dist/src/component.js +163 -36
- package/dist/src/component.js.map +1 -1
- package/dist/src/context.d.ts +13 -32
- package/dist/src/context.d.ts.map +1 -0
- package/dist/src/context.js +73 -48
- package/dist/src/context.js.map +1 -1
- package/dist/src/css.d.ts +1 -0
- package/dist/src/css.d.ts.map +1 -0
- package/dist/src/css.js +34 -5
- package/dist/src/css.js.map +1 -1
- package/dist/src/devtools-hook.d.ts +14 -0
- package/dist/src/devtools-hook.d.ts.map +1 -0
- package/dist/src/devtools-hook.js +27 -0
- package/dist/src/devtools-hook.js.map +1 -0
- package/dist/src/devtools.d.ts +24 -22
- package/dist/src/devtools.d.ts.map +1 -0
- package/dist/src/devtools.js +230 -58
- package/dist/src/devtools.js.map +1 -1
- package/dist/src/diagnostics.d.ts +12 -0
- package/dist/src/diagnostics.d.ts.map +1 -0
- package/dist/src/diagnostics.js +68 -8
- package/dist/src/diagnostics.js.map +1 -1
- package/dist/src/each.d.ts +1 -5
- package/dist/src/each.d.ts.map +1 -0
- package/dist/src/each.js +0 -8
- package/dist/src/each.js.map +1 -1
- package/dist/src/forms.d.ts +27 -96
- package/dist/src/forms.d.ts.map +1 -0
- package/dist/src/forms.js +184 -394
- package/dist/src/forms.js.map +1 -1
- package/dist/src/head.d.ts +7 -6
- package/dist/src/head.d.ts.map +1 -0
- package/dist/src/head.js +8 -7
- package/dist/src/head.js.map +1 -1
- package/dist/src/html/bindings.d.ts +1 -0
- package/dist/src/html/bindings.d.ts.map +1 -0
- package/dist/src/html/bindings.js.map +1 -1
- package/dist/src/html/parser.d.ts +4 -5
- package/dist/src/html/parser.d.ts.map +1 -0
- package/dist/src/html/parser.js +64 -15
- package/dist/src/html/parser.js.map +1 -1
- package/dist/src/html/template-types.d.ts +1 -0
- package/dist/src/html/template-types.d.ts.map +1 -0
- package/dist/src/html/template-types.js.map +1 -1
- package/dist/src/html/template.d.ts +5 -1
- package/dist/src/html/template.d.ts.map +1 -0
- package/dist/src/html/template.js +102 -13
- package/dist/src/html/template.js.map +1 -1
- package/dist/src/index.d.ts +15 -12
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +18 -10
- package/dist/src/index.js.map +1 -1
- package/dist/src/json.d.ts +3 -0
- package/dist/src/json.d.ts.map +1 -0
- package/dist/src/json.js +14 -0
- package/dist/src/json.js.map +1 -0
- package/dist/src/lifecycle.d.ts +1 -0
- package/dist/src/lifecycle.d.ts.map +1 -0
- package/dist/src/lifecycle.js +3 -2
- package/dist/src/lifecycle.js.map +1 -1
- package/dist/src/page.d.ts +63 -28
- package/dist/src/page.d.ts.map +1 -0
- package/dist/src/page.js +5 -0
- package/dist/src/page.js.map +1 -1
- package/dist/src/persisted.d.ts +6 -1
- package/dist/src/persisted.d.ts.map +1 -0
- package/dist/src/persisted.js +17 -11
- package/dist/src/persisted.js.map +1 -1
- package/dist/src/resource.d.ts +2 -1
- package/dist/src/resource.d.ts.map +1 -0
- package/dist/src/resource.js +128 -50
- package/dist/src/resource.js.map +1 -1
- package/dist/src/router/base.d.ts +66 -0
- package/dist/src/router/base.d.ts.map +1 -0
- package/dist/src/router/base.js +168 -0
- package/dist/src/router/base.js.map +1 -0
- package/dist/src/router/manifest.d.ts +1 -0
- package/dist/src/router/manifest.d.ts.map +1 -0
- package/dist/src/router/manifest.js +156 -171
- package/dist/src/router/manifest.js.map +1 -1
- package/dist/src/router/match.d.ts +17 -2
- package/dist/src/router/match.d.ts.map +1 -0
- package/dist/src/router/match.js +60 -6
- package/dist/src/router/match.js.map +1 -1
- package/dist/src/router/navigation.d.ts +1 -0
- package/dist/src/router/navigation.d.ts.map +1 -0
- package/dist/src/router/navigation.js +58 -9
- package/dist/src/router/navigation.js.map +1 -1
- package/dist/src/signal.d.ts +1 -0
- package/dist/src/signal.d.ts.map +1 -0
- package/dist/src/signal.js +46 -17
- package/dist/src/signal.js.map +1 -1
- package/dist/src/static-runtime.d.ts +82 -0
- package/dist/src/static-runtime.d.ts.map +1 -0
- package/dist/src/static-runtime.js +209 -0
- package/dist/src/static-runtime.js.map +1 -0
- package/dist/src/vite/index.d.ts +32 -3
- package/dist/src/vite/index.d.ts.map +1 -0
- package/dist/src/vite/index.js +49 -17
- package/dist/src/vite/index.js.map +1 -1
- package/docs/README.md +5 -8
- package/docs/architecture/adr/0001-browser-static-snapshots.md +132 -0
- package/docs/architecture/v1-roadmap.md +19 -0
- package/docs/en/00-the-mado-way.md +1 -1
- package/docs/en/01-quickstart.md +183 -0
- package/docs/en/10-pages-and-components.md +279 -0
- package/docs/en/11-templates-and-signals.md +211 -0
- package/docs/en/12-routing.md +229 -0
- package/docs/en/13-data.md +227 -0
- package/docs/en/14-forms.md +91 -0
- package/docs/en/15-static-snapshots.md +181 -0
- package/docs/en/{10-app-architecture.md → 16-app-architecture.md} +56 -4
- package/docs/en/{13-deployment.md → 20-deployment.md} +79 -17
- package/docs/en/{15-error-handling.md → 21-error-handling.md} +9 -6
- package/docs/en/{14-testing.md → 22-testing.md} +6 -5
- package/docs/en/23-cookbook.md +292 -0
- package/docs/en/24-devtools-and-diagnostics.md +45 -0
- package/docs/en/{18-api-freeze-map.md → 30-api-freeze-map.md} +17 -9
- package/docs/en/{20-v1-stability.md → 32-v1-stability.md} +5 -5
- package/docs/en/33-migration-0.12-0.13.md +56 -0
- package/docs/en/40-llm-guide.md +774 -0
- package/docs/en/{06-for-backenders.md → 41-for-backenders.md} +28 -25
- package/docs/en/{05-why-mado.md → 42-why-mado.md} +3 -3
- package/docs/en/README.md +99 -27
- package/docs/recipes/nginx/Containerfile +26 -0
- package/docs/recipes/nginx/nginx.conf +42 -0
- package/llms.txt +263 -210
- package/package.json +51 -23
- package/scripts/clean.mjs +8 -0
- package/scripts/cli/generate.mjs +57 -10
- package/scripts/cli/help.mjs +13 -5
- package/scripts/cli/index.mjs +31 -7
- package/scripts/cli/init.mjs +19 -19
- package/scripts/cli/release.mjs +38 -83
- package/scripts/cli/run.mjs +5 -3
- package/scripts/logger.mjs +117 -0
- package/scripts/output-guard.mjs +128 -0
- package/scripts/preview.mjs +105 -24
- package/scripts/static/browser.mjs +695 -0
- package/scripts/static/discover.mjs +284 -0
- package/scripts/static/output.mjs +141 -0
- package/scripts/static/serialize.mjs +212 -0
- package/scripts/static/server.mjs +167 -0
- package/scripts/static.mjs +205 -0
- package/starters/default/README.md +57 -55
- package/starters/default/package.json +6 -12
- package/starters/default/src/app.routes.ts +20 -33
- package/starters/default/src/components/feature-card.component.ts +40 -0
- package/starters/default/src/components/live-counter.component.ts +57 -0
- package/starters/default/src/content/guides.ts +55 -0
- package/starters/default/src/main.ts +9 -13
- package/starters/default/src/pages/app.page.ts +41 -0
- package/starters/default/src/pages/guide.page.ts +40 -0
- package/starters/default/src/pages/home.page.ts +49 -0
- package/starters/default/src/pages/not-found.page.ts +18 -0
- package/starters/default/src/styles/document.css +39 -0
- package/starters/default/src/styles/reset.css +37 -0
- package/starters/default/src/styles/tokens.css +30 -0
- package/starters/default/src/styles.d.ts +3 -1
- package/starters/default/src/vite-env.d.ts +1 -1
- package/starters/default/tsconfig.node.json +13 -0
- package/starters/default/vite.config.ts +14 -4
- package/starters/modular/README.md +142 -0
- package/starters/modular/index.html +13 -0
- package/starters/modular/package.json +30 -0
- package/starters/modular/src/app.routes.ts +39 -0
- package/starters/{default → modular}/src/layouts/app-shell.layout.ts +9 -7
- package/starters/{default → modular}/src/layouts/auth-shell.layout.ts +3 -3
- package/starters/modular/src/main.ts +16 -0
- package/starters/{default → modular}/src/modules/auth/login.page.ts +5 -12
- package/starters/{default → modular}/src/modules/billing/pages/invoice-detail.page.ts +3 -4
- package/starters/{default → modular}/src/modules/billing/pages/invoices-list.page.ts +4 -4
- package/starters/{default → modular}/src/modules/home/home.page.ts +5 -7
- package/starters/modular/src/modules/home/not-found.page.ts +14 -0
- package/starters/{default → modular}/src/shared/styles/reset.css +21 -1
- package/starters/modular/src/styles.d.ts +1 -0
- package/starters/modular/src/vite-env.d.ts +1 -0
- package/starters/modular/tsconfig.json +33 -0
- package/starters/modular/tsconfig.node.json +13 -0
- package/starters/modular/vite.config.ts +132 -0
- package/TODO.md +0 -79
- package/dist/src/lazy.d.ts +0 -38
- package/dist/src/lazy.js +0 -73
- package/dist/src/lazy.js.map +0 -1
- package/docs/en/01-routing.md +0 -152
- package/docs/en/02-project-layout.md +0 -124
- package/docs/en/03-static-bake.md +0 -249
- package/docs/en/04-ide-setup.md +0 -162
- package/docs/en/07-llm-pitfalls.md +0 -724
- package/docs/en/08-llm-zero-history-test.md +0 -56
- package/docs/en/09-shadow-vs-light-dom.md +0 -174
- package/docs/en/11-layouts.md +0 -113
- package/docs/en/12-auth-and-api.md +0 -124
- package/docs/en/16-bake-cookbook.md +0 -100
- package/docs/en/17-shadow-dom-forms.md +0 -192
- package/docs/fr/00-the-mado-way.md +0 -85
- package/docs/fr/01-routing.md +0 -120
- package/docs/fr/02-project-layout.md +0 -84
- package/docs/fr/03-static-bake.md +0 -289
- package/docs/fr/04-ide-setup.md +0 -162
- package/docs/fr/05-why-mado.md +0 -193
- package/docs/fr/06-for-backenders.md +0 -438
- package/docs/fr/07-llm-pitfalls.md +0 -625
- package/docs/fr/08-llm-zero-history-test.md +0 -38
- package/docs/fr/09-shadow-vs-light-dom.md +0 -65
- package/docs/fr/10-app-architecture.md +0 -138
- package/docs/fr/11-layouts.md +0 -47
- package/docs/fr/12-auth-and-api.md +0 -76
- package/docs/fr/13-deployment.md +0 -57
- package/docs/fr/14-testing.md +0 -41
- package/docs/fr/15-error-handling.md +0 -50
- package/docs/fr/16-bake-cookbook.md +0 -88
- package/docs/fr/17-shadow-dom-forms.md +0 -196
- package/docs/fr/18-api-freeze-map.md +0 -63
- package/docs/fr/19-reactivity-ordering.md +0 -97
- package/docs/fr/20-v1-stability.md +0 -88
- package/docs/fr/README.md +0 -27
- package/docs/ru/00-the-mado-way.md +0 -84
- package/docs/ru/01-routing.md +0 -119
- package/docs/ru/02-project-layout.md +0 -84
- package/docs/ru/03-static-bake.md +0 -250
- package/docs/ru/04-ide-setup.md +0 -144
- package/docs/ru/05-why-mado.md +0 -193
- package/docs/ru/06-for-backenders.md +0 -428
- package/docs/ru/07-llm-pitfalls.md +0 -624
- package/docs/ru/08-llm-zero-history-test.md +0 -57
- package/docs/ru/09-shadow-vs-light-dom.md +0 -63
- package/docs/ru/10-app-architecture.md +0 -152
- package/docs/ru/11-layouts.md +0 -47
- package/docs/ru/12-auth-and-api.md +0 -75
- package/docs/ru/13-deployment.md +0 -66
- package/docs/ru/14-testing.md +0 -50
- package/docs/ru/15-error-handling.md +0 -56
- package/docs/ru/16-bake-cookbook.md +0 -95
- package/docs/ru/17-shadow-dom-forms.md +0 -193
- package/docs/ru/18-api-freeze-map.md +0 -64
- package/docs/ru/19-reactivity-ordering.md +0 -95
- package/docs/ru/20-v1-stability.md +0 -82
- package/docs/ru/README.md +0 -25
- package/docs/uk/00-the-mado-way.md +0 -82
- package/docs/uk/01-routing.md +0 -76
- package/docs/uk/02-project-layout.md +0 -73
- package/docs/uk/03-static-bake.md +0 -48
- package/docs/uk/04-ide-setup.md +0 -26
- package/docs/uk/05-why-mado.md +0 -34
- package/docs/uk/06-for-backenders.md +0 -55
- package/docs/uk/07-llm-pitfalls.md +0 -145
- package/docs/uk/08-llm-zero-history-test.md +0 -34
- package/docs/uk/09-shadow-vs-light-dom.md +0 -58
- package/docs/uk/10-app-architecture.md +0 -97
- package/docs/uk/11-layouts.md +0 -47
- package/docs/uk/12-auth-and-api.md +0 -70
- package/docs/uk/13-deployment.md +0 -40
- package/docs/uk/14-testing.md +0 -34
- package/docs/uk/15-error-handling.md +0 -32
- package/docs/uk/16-bake-cookbook.md +0 -36
- package/docs/uk/17-shadow-dom-forms.md +0 -193
- package/docs/uk/18-api-freeze-map.md +0 -61
- package/docs/uk/19-reactivity-ordering.md +0 -95
- package/docs/uk/20-v1-stability.md +0 -83
- package/docs/uk/README.md +0 -27
- package/scripts/bake.mjs +0 -570
- package/scripts/package-smoke.mjs +0 -77
- package/scripts/release-notes.mjs +0 -66
- package/scripts/size-budget.mjs +0 -91
- package/starters/default/src/modules/home/not-found.page.ts +0 -11
- package/starters/default/src/shared/ui/x-button.component.ts +0 -49
- /package/docs/en/{19-reactivity-ordering.md → 31-reactivity-ordering.md} +0 -0
- /package/starters/{default → modular}/.editorconfig +0 -0
- /package/starters/{default → modular}/eslint.config.mjs +0 -0
- /package/starters/{default → modular}/public/favicon.svg +0 -0
- /package/starters/{default → modular}/src/modules/auth/_contracts/auth-api.types.ts +0 -0
- /package/starters/{default → modular}/src/modules/auth/auth.connector.ts +0 -0
- /package/starters/{default → modular}/src/modules/auth/auth.guard.ts +0 -0
- /package/starters/{default → modular}/src/modules/auth/auth.public.ts +0 -0
- /package/starters/{default → modular}/src/modules/auth/auth.routes.ts +0 -0
- /package/starters/{default → modular}/src/modules/auth/auth.service.ts +0 -0
- /package/starters/{default → modular}/src/modules/auth/auth.types.ts +0 -0
- /package/starters/{default → modular}/src/modules/billing/_contracts/stripe.types.ts +0 -0
- /package/starters/{default → modular}/src/modules/billing/api/stripe.connector.ts +0 -0
- /package/starters/{default → modular}/src/modules/billing/billing.public.ts +0 -0
- /package/starters/{default → modular}/src/modules/billing/billing.routes.ts +0 -0
- /package/starters/{default → modular}/src/modules/billing/billing.types.ts +0 -0
- /package/starters/{default → modular}/src/modules/billing/components/invoice-status-badge.component.ts +0 -0
- /package/starters/{default → modular}/src/modules/billing/data/invoices.resource.ts +0 -0
- /package/starters/{default → modular}/src/shared/http/http-client.ts +0 -0
- /package/starters/{default → modular}/src/shared/http/http-error.ts +0 -0
- /package/starters/{default → modular}/src/shared/http/interceptors.ts +0 -0
- /package/starters/{default → modular}/src/shared/lib/format-date.ts +0 -0
- /package/starters/{default → modular}/src/shared/styles/content.css +0 -0
- /package/starters/{default → modular}/src/shared/styles/shell.css +0 -0
- /package/starters/{default → modular}/src/shared/styles/tokens.css +0 -0
- /package/starters/{default → modular}/src/shared/ui/x-spinner.component.ts +0 -0
package/llms.txt
CHANGED
|
@@ -1,281 +1,334 @@
|
|
|
1
1
|
# Mado
|
|
2
2
|
|
|
3
|
-
> A calm
|
|
4
|
-
>
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
- **
|
|
20
|
-
|
|
21
|
-
|
|
3
|
+
> A calm native-first web framework for both static sites and live
|
|
4
|
+
> SPAs. One Web Component model, one page model, one release command.
|
|
5
|
+
> Browser-native source, Vite as the canonical development and delivery
|
|
6
|
+
> transport, zero runtime dependencies.
|
|
7
|
+
|
|
8
|
+
Current stable release: **0.12.0**.
|
|
9
|
+
|
|
10
|
+
Mado deliberately rejects compilers (no JSX, no special template
|
|
11
|
+
language), VDOM diffing, and SSR/hydration. Static documents are
|
|
12
|
+
captured by a real headless Chromium during `mado release`, including
|
|
13
|
+
the Shadow DOM through Declarative Shadow DOM (DSD), and the live SPA
|
|
14
|
+
then atomically replaces the static tree with no hydration protocol or
|
|
15
|
+
node reconciliation.
|
|
16
|
+
|
|
17
|
+
## What an AI assistant must never invent
|
|
18
|
+
|
|
19
|
+
- **This is NOT React, NOT Next, NOT Lit, NOT Solid.** Do not write
|
|
20
|
+
JSX, `useState`, `useEffect`, decorators, classes-with-render or
|
|
21
|
+
`requestUpdate`. Those patterns will not compile.
|
|
22
|
+
- **Vite is the canonical transport.** Every starter, the CLI and the
|
|
23
|
+
static snapshot pipeline run through `@madojs/mado/vite`. The
|
|
24
|
+
framework runtime reads `import.meta.env.BASE_URL` once and exposes
|
|
25
|
+
it through `appBase` / `routeUrl()`.
|
|
26
|
+
<!-- docs-lint:allow-legacy-mention -->
|
|
27
|
+
- **There is no `bake` API.** The old `bake.paths`, `bake.data`,
|
|
28
|
+
`bake.revalidate`, `npm run bake`, "Smart Static" and "no Chromium"
|
|
29
|
+
language refer to a removed system. A page becomes static by
|
|
30
|
+
declaring `static: true` (or `static: { paths, initialData }`); the
|
|
31
|
+
CLI command is `mado static`, normally invoked as part of
|
|
32
|
+
`mado release`.
|
|
33
|
+
<!-- /docs-lint:allow-legacy-mention -->
|
|
34
|
+
- **`page()` is a semantic document.** It renders in the light DOM and
|
|
35
|
+
owns route + load + head + view + optional static declaration.
|
|
36
|
+
`component()` is the unit of Web Component encapsulation; by default
|
|
37
|
+
it attaches an open Shadow DOM. `{ shadow: false }` exists as an
|
|
38
|
+
advanced escape hatch when a custom element MUST live in the light
|
|
39
|
+
DOM (e.g. to participate in a parent form). Pages are never built
|
|
40
|
+
with `component()` and components never participate in routing.
|
|
41
|
+
<!-- docs-lint:allow-legacy-mention -->
|
|
42
|
+
- **A compatible Chromium is required at release time.** `mado static`
|
|
43
|
+
spawns Playwright (`playwright-core`) and captures real DOM. There is
|
|
44
|
+
no `linkedom`-based renderer, no `npm run bake`, no "meta-shell".
|
|
45
|
+
<!-- /docs-lint:allow-legacy-mention -->
|
|
22
46
|
|
|
23
47
|
## Critical template rules
|
|
24
48
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
```ts
|
|
58
|
-
// ❌ throws: RAW_TEXT elements cannot host child slots
|
|
59
|
-
html`<textarea>${draft}</textarea>`;
|
|
60
|
-
|
|
61
|
-
// ✅ bind the DOM property instead
|
|
62
|
-
html`<textarea .value=${draft}></textarea>`;
|
|
63
|
-
|
|
64
|
-
// ❌ throws: nested SVG template loses namespace context
|
|
65
|
-
html`<svg>${html`<circle r="5"></circle>`}</svg>`;
|
|
66
|
-
|
|
67
|
-
// ✅ keep SVG internals in one template
|
|
68
|
-
html`<svg viewBox="0 0 10 10"><circle r="5"></circle></svg>`;
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
## Canonical imports
|
|
49
|
+
`html\`...\`` is a tagged template parser, not JSX. Only these bindings
|
|
50
|
+
are recognised:
|
|
51
|
+
|
|
52
|
+
- `${value}` — child content (text, nodes, arrays, nested templates, `each`)
|
|
53
|
+
- `attr=${v}` — HTML attribute (string/number)
|
|
54
|
+
- `.prop=${v}` — DOM property (objects, arrays, numbers without serialisation)
|
|
55
|
+
- `?attr=${flag}` — boolean attribute (toggle)
|
|
56
|
+
- `@event=${fn}` — event listener
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
// ❌ not reactive — `count()` runs once at render
|
|
60
|
+
html`<div>${count() * 2}</div>`
|
|
61
|
+
|
|
62
|
+
// ✅ subscription — the function is re-run on signal change
|
|
63
|
+
html`<div>${() => count() * 2}</div>`
|
|
64
|
+
|
|
65
|
+
// ✅ the signal itself works because it is already a function
|
|
66
|
+
html`<div>${count}</div>`
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Lists must use `each(items, keyFn, renderFn)` for stable identity:
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
import { each } from "@madojs/mado";
|
|
73
|
+
html`<ul>${() => each(users(), (u) => u.id, (u) => html`<li>${u.name}</li>`)}</ul>`
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
The parser refuses dynamic `${...}` child slots inside `<script>`,
|
|
77
|
+
`<style>`, `<textarea>`, `<title>`, and refuses nested SVG-only `html`
|
|
78
|
+
templates inside an outer `<svg>` (namespace context is lost).
|
|
79
|
+
|
|
80
|
+
## Public API surface
|
|
72
81
|
|
|
73
82
|
```ts
|
|
74
|
-
// Everything you normally need — from one place:
|
|
75
83
|
import {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
84
|
+
// reactivity
|
|
85
|
+
signal, computed, effect, untracked, batch, flushSync,
|
|
86
|
+
// rendering + templates
|
|
87
|
+
html, render, unmount, each, unsafeHTML, ref, classMap, styleMap,
|
|
88
|
+
// components
|
|
89
|
+
component, css, cssVars,
|
|
90
|
+
// routing
|
|
81
91
|
routes, router, navigate, queryParam, prefetchPath,
|
|
82
|
-
|
|
83
|
-
|
|
92
|
+
routeUrl, appBase,
|
|
93
|
+
// pages
|
|
94
|
+
page, layout, applyHead,
|
|
95
|
+
// data
|
|
96
|
+
resource, mutation, invalidate, jsonFetcher, HttpError,
|
|
97
|
+
// forms / context / persistence
|
|
84
98
|
useForm,
|
|
85
99
|
createContext, provide, inject,
|
|
86
100
|
persisted,
|
|
87
|
-
lazy,
|
|
88
101
|
} from "@madojs/mado";
|
|
102
|
+
|
|
103
|
+
// Development overlay/controller:
|
|
104
|
+
import { devtools } from "@madojs/mado/devtools.js";
|
|
105
|
+
|
|
106
|
+
// Vite plugin:
|
|
107
|
+
import { mado } from "@madojs/mado/vite";
|
|
89
108
|
```
|
|
90
109
|
|
|
91
|
-
|
|
110
|
+
Everything else under the package is internal and subject to change
|
|
111
|
+
without notice (see `docs/en/30-api-freeze-map.md`).
|
|
112
|
+
|
|
113
|
+
`page.load` is synchronous: return a plain value or `Resource`, never a
|
|
114
|
+
Promise. Async work belongs in `resource()` so navigation can commit or roll
|
|
115
|
+
back lifecycle ownership transactionally.
|
|
116
|
+
|
|
117
|
+
## Pages and the static snapshot pipeline
|
|
118
|
+
|
|
119
|
+
A page declares its participation in static capture through the
|
|
120
|
+
`static` field on `page({ ... })`:
|
|
92
121
|
|
|
93
122
|
```ts
|
|
94
|
-
|
|
95
|
-
const variant = attr("variant", "default"); // Signal<string>, auto-updates
|
|
96
|
-
const size = attr("size", "md");
|
|
123
|
+
import { html, page } from "@madojs/mado";
|
|
97
124
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
125
|
+
export default page({
|
|
126
|
+
static: true, // capture as HTML at release
|
|
127
|
+
title: "Mado Keyboard",
|
|
128
|
+
head: () => ({ description: "Canonical product page." }),
|
|
129
|
+
view: () => html`<h1>Welcome</h1>`,
|
|
101
130
|
});
|
|
102
131
|
```
|
|
103
132
|
|
|
104
|
-
|
|
105
|
-
plain attribute changes and the component re-renders reactively.
|
|
106
|
-
|
|
107
|
-
## Shadow DOM + Forms
|
|
133
|
+
Dynamic static routes provide `paths()` and `initialData()`:
|
|
108
134
|
|
|
109
|
-
|
|
135
|
+
```ts
|
|
136
|
+
export default page({
|
|
137
|
+
static: {
|
|
138
|
+
paths: async () => [{ slug: "alpha" }, { slug: "beta" }],
|
|
139
|
+
initialData: async ({ slug }) => loadGuide(slug),
|
|
140
|
+
},
|
|
141
|
+
title: ({ slug }) => `Guide: ${slug}`,
|
|
142
|
+
view: (ctx) => html`<article>${ctx.data}</article>`,
|
|
143
|
+
});
|
|
144
|
+
```
|
|
110
145
|
|
|
111
|
-
|
|
112
|
-
```ts
|
|
113
|
-
Object.defineProperty(host, "name", { get: () => host.getAttribute("name") ?? "" });
|
|
114
|
-
Object.defineProperty(host, "value", { get: () => host.shadowRoot?.querySelector("input")?.value ?? "" });
|
|
115
|
-
```
|
|
146
|
+
`mado release` does the full pipeline:
|
|
116
147
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
148
|
+
1. Vite builds the SPA.
|
|
149
|
+
2. `mado static` discovers every `static: true | { ... }` page through
|
|
150
|
+
the routes manifest.
|
|
151
|
+
3. A real headless Chromium navigates each page through an internal
|
|
152
|
+
capture server.
|
|
153
|
+
4. The captured HTML — including DSD-serialised Shadow DOM, canonical /
|
|
154
|
+
`og:url` fallback and the seed `<script type="application/json">` —
|
|
155
|
+
is written into `out/<route>/index.html`.
|
|
156
|
+
5. SPA-only routes keep the `_mado/spa.html` fallback.
|
|
157
|
+
6. The internal `_mado/build.json` bridge is dropped from the deployed
|
|
158
|
+
artefact.
|
|
121
159
|
|
|
122
|
-
|
|
160
|
+
On first paint the live SPA takes over atomically: Mado replaces the
|
|
161
|
+
static tree with the live tree, no hydration protocol or node
|
|
162
|
+
reconciliation. The seed JSON is consumed once so `page.load()` returns
|
|
163
|
+
the same data the server already rendered.
|
|
123
164
|
|
|
124
|
-
##
|
|
165
|
+
## Internal links and base path
|
|
125
166
|
|
|
126
|
-
|
|
167
|
+
Vite's `base` flows automatically into `appBase` and `routeUrl()`. App
|
|
168
|
+
code must use `routeUrl()` for every internal anchor so the URL stays
|
|
169
|
+
correct under `/`, `/mado/` or any sub-path deployment:
|
|
127
170
|
|
|
128
171
|
```ts
|
|
129
|
-
import {
|
|
130
|
-
import { apiFetcher } from "../lib/api.js";
|
|
172
|
+
import { routeUrl } from "@madojs/mado";
|
|
131
173
|
|
|
132
|
-
|
|
174
|
+
html`<a data-link href=${routeUrl("/users/42")}>User</a>`;
|
|
175
|
+
html`<a data-link href=${routeUrl("/")}>Home</a>`; // → "/mado/"
|
|
133
176
|
```
|
|
134
177
|
|
|
135
|
-
|
|
136
|
-
|
|
178
|
+
The router only intercepts anchors that opt in with `data-link`. Bare
|
|
179
|
+
`<a href>` performs a full document load (intentional, for foreign URLs
|
|
180
|
+
and downloads).
|
|
181
|
+
|
|
182
|
+
## Components are Shadow DOM by default
|
|
137
183
|
|
|
138
|
-
|
|
184
|
+
```ts
|
|
185
|
+
import { component, css, html, signal } from "@madojs/mado";
|
|
139
186
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
187
|
+
component(
|
|
188
|
+
"x-counter",
|
|
189
|
+
() => {
|
|
190
|
+
const n = signal(0);
|
|
191
|
+
return () => html`<button @click=${() => n.set(n() + 1)}>${n}</button>`;
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
styles: css`
|
|
195
|
+
:host { display: inline-block; }
|
|
196
|
+
button { padding: .5rem 1rem; }
|
|
197
|
+
`,
|
|
198
|
+
},
|
|
199
|
+
);
|
|
200
|
+
```
|
|
147
201
|
|
|
148
|
-
|
|
202
|
+
- Custom element names MUST include a hyphen.
|
|
203
|
+
- Setup runs once per host; the returned function is the reactive view.
|
|
204
|
+
- `ctx.attr("name", "default")` returns a `Signal<string>` that
|
|
205
|
+
auto-updates on attribute changes.
|
|
206
|
+
- `ctx.onDispose(fn)` registers cleanup for raw APIs (`setInterval`,
|
|
207
|
+
`WebSocket`); `resource()` / `effect()` clean themselves up.
|
|
208
|
+
- `{ shadow: false }` exists for components that MUST live in the light
|
|
209
|
+
DOM (form participation, host-level styling). It is an escape hatch,
|
|
210
|
+
not the default.
|
|
149
211
|
|
|
150
|
-
|
|
151
|
-
route maps; app routes decide which layout and guard wrap each zone. A layout
|
|
152
|
-
view should be a pure wrapper around `${child}` and shared chrome:
|
|
212
|
+
## Resources and mutations
|
|
153
213
|
|
|
154
214
|
```ts
|
|
155
|
-
|
|
156
|
-
|
|
215
|
+
const users = resource(
|
|
216
|
+
() => `/api/users?page=${page()}`,
|
|
217
|
+
jsonFetcher<User[]>(),
|
|
218
|
+
{ staleTime: 30_000 },
|
|
219
|
+
);
|
|
220
|
+
|
|
221
|
+
const create = mutation((u) => api.create(u), {
|
|
222
|
+
invalidates: ["/api/users*"],
|
|
157
223
|
});
|
|
158
224
|
```
|
|
159
225
|
|
|
160
|
-
|
|
161
|
-
|
|
226
|
+
Cache identity is the key plus fetcher. `staleTime: 0` only de-dupes an
|
|
227
|
+
in-flight request; positive values expire automatically. `mutation().run()` is concurrent by default;
|
|
228
|
+
`{ abortPrevious: true }` keeps only the latest in flight.
|
|
229
|
+
`invalidates` runs after a successful mutation and is best-effort.
|
|
162
230
|
|
|
163
|
-
|
|
164
|
-
does not hydrate server-rendered code. Baked views must be deterministic from
|
|
165
|
-
`params`, `bake.data`, and plain values. Avoid browser-only effects, timers,
|
|
166
|
-
relative `fetch`, and runtime directives like keyed `each()` during bake.
|
|
231
|
+
## Forms
|
|
167
232
|
|
|
168
|
-
|
|
233
|
+
```ts
|
|
234
|
+
const form = useForm({ initial: { email: "" } });
|
|
235
|
+
html`<form @submit=${form.onSubmit(save)}>
|
|
236
|
+
<input name="email" type="email" required @input=${form.onInput} />
|
|
237
|
+
<button type="submit">Save</button>
|
|
238
|
+
</form>`;
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
Native constraints and `ValidityState` are authoritative. Custom async
|
|
242
|
+
validation is `validate(values, { signal, form })`; reset aborts it. Use native
|
|
243
|
+
controls for checkbox/radio groups, multiple select, files and keyboard submit.
|
|
244
|
+
|
|
245
|
+
## Static snapshot constraints
|
|
246
|
+
|
|
247
|
+
- `paths()` and `initialData()` run during discovery AND ship in the
|
|
248
|
+
client bundle. Keep them browser-safe; never read secrets.
|
|
249
|
+
- Pages marked `static` cannot use guards (route- or layout-level) —
|
|
250
|
+
they are public by definition.
|
|
251
|
+
- `*` and other wildcard routes cannot be static.
|
|
252
|
+
- Discovery uses Vite SSR as a control plane only; nothing is rendered
|
|
253
|
+
in Node. The actual capture happens in headless Chromium.
|
|
254
|
+
|
|
255
|
+
## Canonical "hello world"
|
|
169
256
|
|
|
170
257
|
```ts
|
|
171
258
|
// src/main.ts
|
|
172
259
|
import { html, render } from "@madojs/mado";
|
|
173
|
-
import
|
|
174
|
-
render(html`${
|
|
260
|
+
import appRoutes from "./app.routes";
|
|
261
|
+
render(html`${appRoutes.view}`, document.getElementById("app")!);
|
|
175
262
|
|
|
176
263
|
// src/app.routes.ts
|
|
177
264
|
import { routes } from "@madojs/mado";
|
|
265
|
+
import home from "./pages/home.page";
|
|
178
266
|
export const manifest = {
|
|
179
|
-
"/":
|
|
180
|
-
"*":
|
|
267
|
+
"/": home,
|
|
268
|
+
"*": () => import("./pages/not-found.page"),
|
|
181
269
|
};
|
|
182
270
|
export default routes(manifest);
|
|
183
271
|
|
|
184
|
-
// src/
|
|
185
|
-
import {
|
|
186
|
-
|
|
187
|
-
component("x-counter", () => {
|
|
188
|
-
const count = signal(0);
|
|
189
|
-
return () => html`
|
|
190
|
-
<button @click=${() => count.update(n => n + 1)}>${count}</button>
|
|
191
|
-
`;
|
|
192
|
-
}, { styles: css`button { padding: .5rem 1rem; }` });
|
|
193
|
-
|
|
272
|
+
// src/pages/home.page.ts
|
|
273
|
+
import { html, page, routeUrl } from "@madojs/mado";
|
|
194
274
|
export default page({
|
|
275
|
+
static: true,
|
|
195
276
|
title: "Home",
|
|
196
|
-
view: () => html
|
|
277
|
+
view: () => html`
|
|
278
|
+
<main>
|
|
279
|
+
<h1>Mado App</h1>
|
|
280
|
+
<a data-link href=${routeUrl("/about")}>About</a>
|
|
281
|
+
</main>
|
|
282
|
+
`,
|
|
197
283
|
});
|
|
198
284
|
```
|
|
199
285
|
|
|
200
|
-
##
|
|
286
|
+
## Starters
|
|
201
287
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
288
|
+
- `mado init my-app` — universal starter (default). One Shadow Component
|
|
289
|
+
shared between a static landing page and a live SPA route. Folders:
|
|
290
|
+
`src/pages`, `src/components`, `src/content`.
|
|
291
|
+
- `mado init my-app --starter modular` — long-lived reference
|
|
292
|
+
architecture: auth shell, guarded zones, billing module, HTTP client,
|
|
293
|
+
module boundaries. Folders: `src/modules/<name>/`, `src/layouts/`,
|
|
294
|
+
`src/shared/`. A dev-only mock API is mounted from
|
|
295
|
+
`vite.config.ts`; disable with `MADO_MOCK_API=0`.
|
|
209
296
|
|
|
210
|
-
|
|
211
|
-
registration near the route/page that owns the tag. This preserves lazy loading
|
|
212
|
-
and makes the code readable to humans and LLMs.
|
|
213
|
-
|
|
214
|
-
## Canonical CRUD pattern (for backend developers)
|
|
215
|
-
|
|
216
|
-
```ts
|
|
217
|
-
import { page, html, resource, mutation, each, useForm, navigate } from "@madojs/mado";
|
|
218
|
-
import { api } from "../lib/api.js";
|
|
297
|
+
## Documentation
|
|
219
298
|
|
|
220
|
-
|
|
221
|
-
const create = mutation((u) => api.create(u), { invalidates: ["/api/users*"] });
|
|
299
|
+
Canonical docs (English only): https://github.com/madojs/mado/tree/main/docs/en
|
|
222
300
|
|
|
223
|
-
|
|
224
|
-
view: () => {
|
|
225
|
-
const f = useForm({ name: { required: true } });
|
|
226
|
-
return html`
|
|
227
|
-
<form @submit=${f.onSubmit(async v => { await create.run(v); navigate("/"); })}>
|
|
228
|
-
<input name="name" @input=${f.onInput}>
|
|
229
|
-
<button ?disabled=${() => !f.isValid()}>Create</button>
|
|
230
|
-
</form>
|
|
231
|
-
${() => each(users.data() ?? [], u => u.id, u => html`<li>${u.name}</li>`)}
|
|
232
|
-
`;
|
|
233
|
-
},
|
|
234
|
-
});
|
|
235
|
-
```
|
|
301
|
+
Highlights:
|
|
236
302
|
|
|
237
|
-
|
|
303
|
+
- `docs/en/00-the-mado-way.md` — philosophy and boundaries
|
|
304
|
+
- `docs/en/01-quickstart.md` — scaffold + dev loop + IDE setup
|
|
305
|
+
- `docs/en/10-pages-and-components.md` — the one rule (page vs component)
|
|
306
|
+
- `docs/en/11-templates-and-signals.md` — bindings, signals, lifecycle
|
|
307
|
+
- `docs/en/12-routing.md` — router, layouts, guards, `routeUrl()`
|
|
308
|
+
- `docs/en/13-data.md` — `resource()` / `mutation()` / auth
|
|
309
|
+
- `docs/en/14-forms.md` — `useForm()` + Shadow DOM inputs
|
|
310
|
+
- `docs/en/15-static-snapshots.md` — static snapshots guide
|
|
311
|
+
- `docs/en/16-app-architecture.md` — canonical app layout
|
|
312
|
+
- `docs/en/20-deployment.md` — deployment recipes (including base)
|
|
313
|
+
- `docs/en/23-cookbook.md` — static snapshots cookbook
|
|
314
|
+
- `docs/en/30-api-freeze-map.md` — stable public API vs internal
|
|
315
|
+
- `docs/en/40-llm-guide.md` — LLM pitfalls and zero-history test
|
|
238
316
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
- docs/en/01-routing.md — detailed router documentation
|
|
242
|
-
- docs/en/02-project-layout.md — recommended project structure
|
|
243
|
-
- docs/en/03-static-bake.md — SEO without SSR (static build / edge prerender)
|
|
244
|
-
- docs/en/04-ide-setup.md — VS Code / WebStorm configuration
|
|
245
|
-
- docs/en/05-why-mado.md — honest comparison with Lit / Solid / Svelte / htmx / Alpine / React
|
|
246
|
-
- docs/en/06-for-backenders.md — mental model in 10 minutes for Go/Rust/.NET/Java developers
|
|
247
|
-
- docs/en/07-llm-pitfalls.md — common mistakes when generating Mado code
|
|
248
|
-
- docs/en/10-app-architecture.md — canonical app file layout, routes, auth/API and release shape
|
|
249
|
-
- docs/en/11-layouts.md — blessed layout recipe
|
|
250
|
-
- docs/en/12-auth-and-api.md — blessed auth/API client recipe
|
|
251
|
-
- docs/en/13-deployment.md — deployment recipes and cache rules
|
|
252
|
-
- docs/en/14-testing.md — testing strategy and commands
|
|
253
|
-
- docs/en/15-error-handling.md — route/data/action error boundaries
|
|
254
|
-
- docs/en/16-bake-cookbook.md — static bake recipes and failure modes
|
|
255
|
-
- docs/en/17-shadow-dom-forms.md — Shadow DOM + useForm() patterns (proxy properties, form submit bridge)
|
|
256
|
-
- docs/en/18-api-freeze-map.md — stable public API vs internal implementation details
|
|
257
|
-
- docs/en/19-reactivity-ordering.md — signal ordering, batching and teardown guarantees
|
|
258
|
-
- docs/en/20-v1-stability.md — v1 SemVer contract and what remains internal
|
|
259
|
-
- starters/default/ — canonical generated app shape
|
|
317
|
+
The repository is English-only. Older `docs/fr`, `docs/uk`, `docs/ru`
|
|
318
|
+
trees were removed at v0.12 and survive only in git history.
|
|
260
319
|
|
|
261
320
|
## What Mado does NOT do (intentionally)
|
|
262
321
|
|
|
263
|
-
- ❌ JSX
|
|
264
|
-
- ❌ Virtual DOM
|
|
265
|
-
- ❌ SSR with hydration
|
|
266
|
-
- ❌
|
|
267
|
-
- ❌
|
|
268
|
-
- ❌
|
|
269
|
-
- ❌ React-
|
|
270
|
-
- ❌
|
|
271
|
-
- ❌
|
|
272
|
-
- ❌
|
|
273
|
-
|
|
274
|
-
## Version
|
|
275
|
-
|
|
276
|
-
`0.10.0` — pre-1.0 API-lock release. Phase B closed the public surface,
|
|
277
|
-
package exports, docs and CI gates. SemVer is not guaranteed on minor versions
|
|
278
|
-
before 1.0.
|
|
322
|
+
- ❌ JSX → tagged template `html\`...\``
|
|
323
|
+
- ❌ Virtual DOM → fine-grained signal updates
|
|
324
|
+
- ❌ SSR with hydration → browser-rendered static snapshots + atomic takeover
|
|
325
|
+
- ❌ React-style hooks → signals
|
|
326
|
+
- ❌ Framework-specific compiler → none; the source is plain TS / Web Components
|
|
327
|
+
- ❌ Runtime framework dependencies → zero (Vite is dev/build tooling)
|
|
328
|
+
- ❌ React-Router / TanStack → built-in `routes()`
|
|
329
|
+
- ❌ React-Query / SWR → built-in `resource()` / `mutation()`
|
|
330
|
+
- ❌ Formik / RHF → built-in `useForm()`
|
|
331
|
+
- ❌ Redux / Zustand → `signal()` + `createContext`
|
|
279
332
|
|
|
280
333
|
## License
|
|
281
334
|
|