@madojs/mado 0.11.0 → 0.12.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 +102 -25
- package/CHANGELOG.md +185 -1
- package/README.md +137 -172
- package/dist/src/component.d.ts +3 -2
- package/dist/src/component.js +98 -6
- package/dist/src/component.js.map +1 -1
- package/dist/src/css.js +34 -5
- package/dist/src/css.js.map +1 -1
- package/dist/src/head.d.ts +6 -6
- package/dist/src/head.js +6 -6
- package/dist/src/html/bindings.js +3 -3
- package/dist/src/html/bindings.js.map +1 -1
- package/dist/src/html/parser.d.ts +1 -1
- package/dist/src/html/parser.js +1 -1
- package/dist/src/html/template.js +24 -10
- package/dist/src/html/template.js.map +1 -1
- package/dist/src/index.d.ts +8 -4
- package/dist/src/index.js +13 -3
- package/dist/src/index.js.map +1 -1
- package/dist/src/page.d.ts +62 -28
- package/dist/src/page.js +5 -0
- package/dist/src/page.js.map +1 -1
- package/dist/src/resource.js +2 -1
- package/dist/src/resource.js.map +1 -1
- package/dist/src/router/base.d.ts +65 -0
- package/dist/src/router/base.js +168 -0
- package/dist/src/router/base.js.map +1 -0
- package/dist/src/router/manifest.js +75 -31
- package/dist/src/router/manifest.js.map +1 -1
- package/dist/src/router/match.d.ts +16 -2
- package/dist/src/router/match.js +41 -1
- package/dist/src/router/match.js.map +1 -1
- package/dist/src/router/navigation.js +58 -9
- package/dist/src/router/navigation.js.map +1 -1
- package/dist/src/static-runtime.d.ts +81 -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 +31 -3
- package/dist/src/vite/index.js +49 -18
- 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/en/00-the-mado-way.md +1 -1
- package/docs/en/01-quickstart.md +183 -0
- package/docs/en/10-pages-and-components.md +271 -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 +225 -0
- package/docs/en/14-forms.md +244 -0
- package/docs/en/15-static-snapshots.md +181 -0
- package/docs/en/{10-app-architecture.md → 16-app-architecture.md} +53 -2
- package/docs/en/{13-deployment.md → 20-deployment.md} +59 -10
- package/docs/en/{14-testing.md → 22-testing.md} +6 -5
- package/docs/en/23-cookbook.md +292 -0
- package/docs/en/{18-api-freeze-map.md → 30-api-freeze-map.md} +12 -3
- package/docs/en/{20-v1-stability.md → 32-v1-stability.md} +3 -3
- package/docs/en/40-llm-guide.md +776 -0
- package/docs/en/{06-for-backenders.md → 41-for-backenders.md} +15 -16
- package/docs/en/{05-why-mado.md → 42-why-mado.md} +1 -1
- package/docs/en/README.md +97 -27
- package/docs/recipes/nginx/Containerfile +26 -0
- package/docs/recipes/nginx/nginx.conf +42 -0
- package/llms.txt +246 -211
- package/package.json +17 -10
- package/scripts/bake.mjs +4 -568
- package/scripts/cli/generate.mjs +55 -5
- package/scripts/cli/help.mjs +11 -5
- package/scripts/cli/index.mjs +20 -4
- package/scripts/cli/init.mjs +7 -2
- package/scripts/cli/release.mjs +20 -68
- package/scripts/docs-lint.mjs +170 -0
- package/scripts/package-smoke.mjs +67 -9
- package/scripts/preview.mjs +95 -6
- package/scripts/size-budget.mjs +5 -1
- package/scripts/static/browser.mjs +654 -0
- package/scripts/static/discover.mjs +281 -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 +191 -0
- package/starters/default/README.md +57 -55
- package/starters/default/package.json +3 -9
- 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/vite.config.ts +14 -1
- 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 +8 -5
- 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/billing/pages/invoices-list.page.ts +3 -3
- 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/modular/src/styles.d.ts +1 -0
- package/starters/modular/src/vite-env.d.ts +1 -0
- package/starters/modular/tsconfig.json +24 -0
- package/starters/modular/vite.config.ts +131 -0
- package/TODO.md +0 -79
- 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/starters/default/src/modules/home/not-found.page.ts +0 -11
- /package/docs/en/{15-error-handling.md → 21-error-handling.md} +0 -0
- /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/auth/login.page.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/modules/billing/pages/invoice-detail.page.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/reset.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-button.component.ts +0 -0
- /package/starters/{default → modular}/src/shared/ui/x-spinner.component.ts +0 -0
|
@@ -1,192 +0,0 @@
|
|
|
1
|
-
# Shadow DOM + Forms
|
|
2
|
-
|
|
3
|
-
Using `useForm()` with custom input components that have Shadow DOM requires
|
|
4
|
-
awareness of two browser-level behaviours:
|
|
5
|
-
|
|
6
|
-
1. **Event retargeting** — events that bubble from Shadow DOM have their
|
|
7
|
-
`e.target` retargeted to the host element. `useForm().onInput` reads
|
|
8
|
-
`e.target.name` and `e.target.value`, but an `<x-input>` host element
|
|
9
|
-
doesn't natively have these properties.
|
|
10
|
-
|
|
11
|
-
2. **Form association** — a `<button type="submit">` inside a Shadow Root is
|
|
12
|
-
NOT part of the form-owner algorithm for `<form>` in Light DOM. Clicking it
|
|
13
|
-
does not trigger form submit.
|
|
14
|
-
|
|
15
|
-
Both are spec-level limitations, not Mado bugs. But the framework provides
|
|
16
|
-
patterns that make them painless.
|
|
17
|
-
|
|
18
|
-
## Pattern: Proxy Properties on Input Components
|
|
19
|
-
|
|
20
|
-
When wrapping `<input>` in a Shadow DOM component, expose `name` and `value`
|
|
21
|
-
as DOM properties on the host so that `useForm().onInput` works after event
|
|
22
|
-
retargeting:
|
|
23
|
-
|
|
24
|
-
```ts
|
|
25
|
-
import { component, css, html } from "@madojs/mado";
|
|
26
|
-
|
|
27
|
-
component("x-input", ({ host, attr }) => {
|
|
28
|
-
const name = attr("name", "");
|
|
29
|
-
const type = attr("type", "text");
|
|
30
|
-
const value = attr("value", "");
|
|
31
|
-
|
|
32
|
-
// Proxy properties for useForm() compatibility.
|
|
33
|
-
// After Shadow DOM retargets e.target from <input> to <x-input>,
|
|
34
|
-
// useForm reads e.target.name / e.target.value — these getters bridge the gap.
|
|
35
|
-
Object.defineProperty(host, "name", {
|
|
36
|
-
get: () => host.getAttribute("name") ?? "",
|
|
37
|
-
configurable: true,
|
|
38
|
-
});
|
|
39
|
-
Object.defineProperty(host, "value", {
|
|
40
|
-
get: () => host.shadowRoot?.querySelector("input")?.value ?? "",
|
|
41
|
-
set: (v: string) => {
|
|
42
|
-
const input = host.shadowRoot?.querySelector("input");
|
|
43
|
-
if (input) input.value = v;
|
|
44
|
-
},
|
|
45
|
-
configurable: true,
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
return () => html` <input name=${name} type=${type} .value=${value} /> `;
|
|
49
|
-
});
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
The `input` event from the inner `<input>` has `composed: true` by default, so
|
|
53
|
-
it will bubble through the shadow boundary. After retargeting, `e.target` is
|
|
54
|
-
`<x-input>`, but now it has `.name` and `.value` getters → `useForm` works.
|
|
55
|
-
|
|
56
|
-
## Pattern: Form Submit from Shadow DOM Buttons
|
|
57
|
-
|
|
58
|
-
A `<button type="submit">` inside Shadow DOM cannot trigger `<form>` submit in
|
|
59
|
-
Light DOM. Bridge it with `requestSubmit()`:
|
|
60
|
-
|
|
61
|
-
```ts
|
|
62
|
-
import { component, css, html } from "@madojs/mado";
|
|
63
|
-
|
|
64
|
-
component("x-button", ({ host, attr }) => {
|
|
65
|
-
const disabled = attr("disabled");
|
|
66
|
-
|
|
67
|
-
const handleClick = () => {
|
|
68
|
-
const typeAttr = host.getAttribute("type");
|
|
69
|
-
if (typeAttr === "button" || typeAttr === "reset") return;
|
|
70
|
-
const form = host.closest("form");
|
|
71
|
-
if (form && !host.hasAttribute("disabled")) form.requestSubmit();
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
return () => html`
|
|
75
|
-
<button ?disabled=${() => disabled() !== ""} @click=${handleClick}>
|
|
76
|
-
<slot></slot>
|
|
77
|
-
</button>
|
|
78
|
-
`;
|
|
79
|
-
});
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
`host.closest("form")` works because the host element itself lives in Light DOM
|
|
83
|
-
(only its internals are shadowed). `requestSubmit()` triggers validation and the
|
|
84
|
-
`submit` event exactly as if the user had clicked a native submit button inside
|
|
85
|
-
the form.
|
|
86
|
-
|
|
87
|
-
## Pattern: Reactive Attributes with ctx.attr()
|
|
88
|
-
|
|
89
|
-
Since v0.7, `ctx.attr(name, defaultValue?)` returns a `Signal<string>` that
|
|
90
|
-
updates automatically when the attribute changes on the host. No more
|
|
91
|
-
`MutationObserver` boilerplate:
|
|
92
|
-
|
|
93
|
-
```ts
|
|
94
|
-
component("x-badge", ({ attr }) => {
|
|
95
|
-
const variant = attr("variant", "default"); // Signal<string>
|
|
96
|
-
|
|
97
|
-
return () =>
|
|
98
|
-
html`<span class=${() => `badge badge-${variant()}`}>
|
|
99
|
-
<slot></slot>
|
|
100
|
-
</span>`;
|
|
101
|
-
});
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
The parent can use `?disabled=${() => !form.isValid()}` (boolean attribute) or
|
|
105
|
-
`.variant=${"danger"}` — the component re-renders reactively either way.
|
|
106
|
-
|
|
107
|
-
## Complete Form Example
|
|
108
|
-
|
|
109
|
-
```ts
|
|
110
|
-
import { page, html, useForm, navigate } from "@madojs/mado";
|
|
111
|
-
import "../components/x-input.js";
|
|
112
|
-
import "../components/x-button.js";
|
|
113
|
-
|
|
114
|
-
export default page({
|
|
115
|
-
title: "Login",
|
|
116
|
-
view: () => {
|
|
117
|
-
const form = useForm({
|
|
118
|
-
email: { required: true, type: "email" },
|
|
119
|
-
password: { required: true, minLength: 6 },
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
const handleLogin = async (values) => {
|
|
123
|
-
await api("/auth/login", { method: "POST", json: values });
|
|
124
|
-
navigate("/admin");
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
return html`
|
|
128
|
-
<form @submit=${form.onSubmit(handleLogin)}>
|
|
129
|
-
<x-input
|
|
130
|
-
name="email"
|
|
131
|
-
type="email"
|
|
132
|
-
label="Email"
|
|
133
|
-
required
|
|
134
|
-
@input=${form.onInput}
|
|
135
|
-
@blur=${form.onBlur}
|
|
136
|
-
></x-input>
|
|
137
|
-
${() =>
|
|
138
|
-
form.errors().email
|
|
139
|
-
? html`<small class="err">${form.errors().email}</small>`
|
|
140
|
-
: null}
|
|
141
|
-
|
|
142
|
-
<x-input
|
|
143
|
-
name="password"
|
|
144
|
-
type="password"
|
|
145
|
-
label="Password"
|
|
146
|
-
required
|
|
147
|
-
@input=${form.onInput}
|
|
148
|
-
@blur=${form.onBlur}
|
|
149
|
-
></x-input>
|
|
150
|
-
|
|
151
|
-
<x-button type="submit" ?disabled=${() => !form.isValid()}>
|
|
152
|
-
Sign in
|
|
153
|
-
</x-button>
|
|
154
|
-
</form>
|
|
155
|
-
`;
|
|
156
|
-
},
|
|
157
|
-
});
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
## When to Use Light DOM Instead
|
|
161
|
-
|
|
162
|
-
If your input component is just a styled wrapper without encapsulation needs,
|
|
163
|
-
`shadow: false` avoids both retargeting and form-association issues entirely:
|
|
164
|
-
|
|
165
|
-
```ts
|
|
166
|
-
component(
|
|
167
|
-
"x-field",
|
|
168
|
-
({ attr }) => {
|
|
169
|
-
const label = attr("label", "");
|
|
170
|
-
return () => html`
|
|
171
|
-
<label>
|
|
172
|
-
<span>${label}</span>
|
|
173
|
-
<slot></slot>
|
|
174
|
-
</label>
|
|
175
|
-
`;
|
|
176
|
-
},
|
|
177
|
-
{ shadow: false },
|
|
178
|
-
);
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
With Light DOM, the native `<input>` is part of the document tree, events
|
|
182
|
-
are not retargeted, and form submission works natively. The tradeoff: styles
|
|
183
|
-
are not encapsulated (you must scope them yourself).
|
|
184
|
-
|
|
185
|
-
## Summary
|
|
186
|
-
|
|
187
|
-
| Concern | Shadow DOM Solution | Light DOM Alternative |
|
|
188
|
-
| ------------------------ | --------------------------------------- | ----------------------------- |
|
|
189
|
-
| `useForm` + custom input | Proxy `name`/`value` on host | Use native `<input>` in slot |
|
|
190
|
-
| Form submit | `form.requestSubmit()` in click handler | Native button works |
|
|
191
|
-
| Reactive attributes | `ctx.attr()` → auto-signal | `ctx.attr()` works everywhere |
|
|
192
|
-
| Style encapsulation | Yes (automatic) | Manual `@scope` or BEM |
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
# La voie Mado
|
|
2
|
-
|
|
3
|
-
> Une seule bonne façon. Des contrats stricts. Pas de magie.
|
|
4
|
-
|
|
5
|
-
Mado est un framework pour les équipes qui construisent des panneaux d'admin,
|
|
6
|
-
des outils internes et des SPA métier. Ces apps doivent être simples à créer
|
|
7
|
-
et ennuyeuses à maintenir. Mado préfère donc des conventions claires à cinq
|
|
8
|
-
styles équivalents.
|
|
9
|
-
|
|
10
|
-
## Principes
|
|
11
|
-
|
|
12
|
-
1. **Une seule voie.** Si vous écrivez quelque chose d'inhabituel, vérifiez
|
|
13
|
-
d'abord s'il existe déjà un helper/API canonique.
|
|
14
|
-
2. **L'explicite plutôt que la magie.** Pas de file-system scanners, globals
|
|
15
|
-
implicites ou side effects cachés.
|
|
16
|
-
3. **La plateforme d'abord.** Web Components, History API, `<form>`, `fetch` et
|
|
17
|
-
Shadow DOM restent visibles.
|
|
18
|
-
4. **Types stricts.** `tsc --strict --noUncheckedIndexedAccess` toujours.
|
|
19
|
-
5. **Pas de dépendances runtime.** Le tooling dev/build est acceptable ; le
|
|
20
|
-
runtime Mado reste natif.
|
|
21
|
-
|
|
22
|
-
## Structure du projet
|
|
23
|
-
|
|
24
|
-
```txt
|
|
25
|
-
src/
|
|
26
|
-
├── main.ts ← boot: global CSS/providers + render router
|
|
27
|
-
├── app.routes.ts ← readable app map, exports `manifest` + default routes()
|
|
28
|
-
├── layouts/ ← app-zone wrappers (`page({ view: ({ child }) => ... })`)
|
|
29
|
-
├── shared/ ← UI bricks, http client, pure lib, global CSS
|
|
30
|
-
└── modules/ ← bounded contexts
|
|
31
|
-
└── billing/
|
|
32
|
-
├── billing.routes.ts
|
|
33
|
-
├── billing.public.ts
|
|
34
|
-
├── billing.types.ts
|
|
35
|
-
├── pages/
|
|
36
|
-
├── data/
|
|
37
|
-
├── api/
|
|
38
|
-
└── _contracts/
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
Le starter par défaut est la version canonique de cette forme. Si d'anciens
|
|
42
|
-
exemples divergent, le starter et `docs/10-app-architecture.md` gagnent.
|
|
43
|
-
|
|
44
|
-
## Un composant = un fichier
|
|
45
|
-
|
|
46
|
-
```ts
|
|
47
|
-
import { component, css, html } from "@madojs/mado";
|
|
48
|
-
|
|
49
|
-
component("x-user-card", () => () => html`<div class="card"><slot></slot></div>`, {
|
|
50
|
-
styles: css`
|
|
51
|
-
.card { padding: 1rem; }
|
|
52
|
-
`,
|
|
53
|
-
});
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
Importer le fichier du composant enregistre l'élément. Importez-le là où le tag
|
|
57
|
-
est utilisé.
|
|
58
|
-
|
|
59
|
-
## Une seule façon de décrire une page
|
|
60
|
-
|
|
61
|
-
```ts
|
|
62
|
-
import { html, page, resource, jsonFetcher } from "@madojs/mado";
|
|
63
|
-
|
|
64
|
-
export default page({
|
|
65
|
-
title: ({ id }) => `User #${id}`,
|
|
66
|
-
view: ({ params }) => {
|
|
67
|
-
const user = resource(() => `/api/users/${params.id}`, jsonFetcher());
|
|
68
|
-
return html`...`;
|
|
69
|
-
},
|
|
70
|
-
});
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
Les signals, resources et forms locaux à une page vivent dans `view()`. L'état
|
|
74
|
-
partagé par un module vit dans `*.service.ts`.
|
|
75
|
-
|
|
76
|
-
## Ce que nous ne faisons pas
|
|
77
|
-
|
|
78
|
-
- Pas de JSX/Vue/Svelte syntax.
|
|
79
|
-
- Pas de custom elements sans trait d'union.
|
|
80
|
-
- Pas de lecture de signal via `.value`; un signal est une fonction.
|
|
81
|
-
- Pas de `innerHTML` direct.
|
|
82
|
-
- Pas de runtime packages sans discussion.
|
|
83
|
-
|
|
84
|
-
En cas de doute, documentez une recette claire plutôt que d'ajouter un nouveau
|
|
85
|
-
primitive au core.
|
package/docs/fr/01-routing.md
DELETED
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
# Routage
|
|
2
|
-
|
|
3
|
-
> Une seule app map. Pas de folder scanners. Pas de syntaxe magique.
|
|
4
|
-
|
|
5
|
-
Mado ne déduit pas les routes depuis les fichiers. La composition doit rester
|
|
6
|
-
lisible dans un seul endroit.
|
|
7
|
-
|
|
8
|
-
## App Manifest
|
|
9
|
-
|
|
10
|
-
Utilisez `src/app.routes.ts` comme carte de l'application :
|
|
11
|
-
|
|
12
|
-
```ts
|
|
13
|
-
import { layout, routes } from "@madojs/mado";
|
|
14
|
-
import { requireAuth } from "./modules/auth/auth.public";
|
|
15
|
-
import { authRoutes } from "./modules/auth/auth.routes";
|
|
16
|
-
import { billingRoutes } from "./modules/billing/billing.routes";
|
|
17
|
-
|
|
18
|
-
export const manifest = {
|
|
19
|
-
"/": () => import("./modules/home/home.page.js"),
|
|
20
|
-
"/login": layout({
|
|
21
|
-
layout: () => import("./layouts/auth-shell.layout.js"),
|
|
22
|
-
routes: authRoutes,
|
|
23
|
-
}),
|
|
24
|
-
"/billing": layout({
|
|
25
|
-
layout: () => import("./layouts/app-shell.layout.js"),
|
|
26
|
-
guard: requireAuth,
|
|
27
|
-
routes: billingRoutes,
|
|
28
|
-
}),
|
|
29
|
-
"*": () => import("./modules/home/not-found.page.js"),
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
export default routes(manifest);
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
Ouvrez `app.routes.ts` pour voir les zones de l'app : pages publiques, auth,
|
|
36
|
-
zones protégées, guards et shells.
|
|
37
|
-
|
|
38
|
-
Exportez `manifest` pour `mado bake`.
|
|
39
|
-
|
|
40
|
-
## Module Routes
|
|
41
|
-
|
|
42
|
-
Les modules exportent de simples route maps. Ils n'appellent pas `layout()` et
|
|
43
|
-
ne décident pas quel shell les enveloppe.
|
|
44
|
-
|
|
45
|
-
```ts
|
|
46
|
-
export const billingRoutes = {
|
|
47
|
-
"/invoices": () => import("./pages/invoices-list.page.js"),
|
|
48
|
-
"/invoices/:id": () => import("./pages/invoice-detail.page.js"),
|
|
49
|
-
};
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
Le prefix est appliqué par `src/app.routes.ts` quand le module est monté sous
|
|
53
|
-
`"/billing"`.
|
|
54
|
-
|
|
55
|
-
## Layout Group
|
|
56
|
-
|
|
57
|
-
```ts
|
|
58
|
-
"/admin": layout({
|
|
59
|
-
layout: () => import("./layouts/app-shell.layout.js"),
|
|
60
|
-
guard: requireAuth,
|
|
61
|
-
routes: adminRoutes,
|
|
62
|
-
}),
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
Un layout est un fichier `page({...})` ordinaire :
|
|
66
|
-
|
|
67
|
-
```ts
|
|
68
|
-
import { html, page } from "@madojs/mado";
|
|
69
|
-
|
|
70
|
-
export default page({
|
|
71
|
-
view: ({ child }) => html`
|
|
72
|
-
<div class="layout layout--app">
|
|
73
|
-
<main class="app-main">${child}</main>
|
|
74
|
-
</div>
|
|
75
|
-
`,
|
|
76
|
-
});
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
Gardez les layout views stateless. Les signals, resources et forms spécifiques
|
|
80
|
-
à une page vivent dans les pages/components/resources.
|
|
81
|
-
|
|
82
|
-
## Page Contract
|
|
83
|
-
|
|
84
|
-
```ts
|
|
85
|
-
import { html, page } from "@madojs/mado";
|
|
86
|
-
|
|
87
|
-
export default page<{ id: string }>({
|
|
88
|
-
title: ({ id }) => `User ${id}`,
|
|
89
|
-
view: ({ params }) => html`<h1>${params.id}</h1>`,
|
|
90
|
-
});
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
## Navigation
|
|
94
|
-
|
|
95
|
-
```ts
|
|
96
|
-
import appRoutes from "./app.routes.js";
|
|
97
|
-
|
|
98
|
-
appRoutes.navigate("/billing/invoices");
|
|
99
|
-
appRoutes.navigate("/billing/invoices?page=2");
|
|
100
|
-
appRoutes.navigate("/login", { replace: true });
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
## Query Parameters
|
|
104
|
-
|
|
105
|
-
```ts
|
|
106
|
-
import { queryParam } from "@madojs/mado";
|
|
107
|
-
|
|
108
|
-
const page = queryParam("page", "1");
|
|
109
|
-
page();
|
|
110
|
-
page.set("2");
|
|
111
|
-
page.set(null);
|
|
112
|
-
page.set("3", { push: true });
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
## Ce qui est absent volontairement
|
|
116
|
-
|
|
117
|
-
- Pas d'auto-scan des dossiers de pages.
|
|
118
|
-
- Pas de syntaxe filesystem comme `[id]`, `(group)`, `_layout`.
|
|
119
|
-
- Pas de server routes dans le manifest client.
|
|
120
|
-
- Pas de découverte cachée des layouts.
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
# Structure du projet
|
|
2
|
-
|
|
3
|
-
Chaque application Mado utilise la même forme canonique. Cette convention
|
|
4
|
-
permet aux humains et aux assistants IA de savoir où placer le code.
|
|
5
|
-
|
|
6
|
-
```txt
|
|
7
|
-
my-app/
|
|
8
|
-
├── package.json # runtime dep: @madojs/mado
|
|
9
|
-
├── tsconfig.json # strict TS, ES2022, Bundler resolution
|
|
10
|
-
├── vite.config.ts # mado() from @madojs/mado/vite
|
|
11
|
-
├── index.html # entrée Vite + shell SPA
|
|
12
|
-
├── public/ # assets statiques: favicon, images, robots.txt
|
|
13
|
-
└── src/
|
|
14
|
-
├── main.ts # importe le CSS et monte le router dans #app
|
|
15
|
-
├── app.routes.ts # app map: manifest + default routes(...)
|
|
16
|
-
├── layouts/ # layouts de zones d'application
|
|
17
|
-
├── shared/ # ui, http, lib, styles
|
|
18
|
-
└── modules/ # bounded contexts
|
|
19
|
-
└── billing/
|
|
20
|
-
├── billing.routes.ts
|
|
21
|
-
├── billing.public.ts
|
|
22
|
-
├── billing.types.ts
|
|
23
|
-
├── pages/
|
|
24
|
-
├── data/
|
|
25
|
-
├── api/
|
|
26
|
-
└── _contracts/
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
## États Des Artefacts
|
|
30
|
-
|
|
31
|
-
| Dossier | Ce que c'est | Écrit par | Déployer ? |
|
|
32
|
-
| --- | --- | --- | --- |
|
|
33
|
-
| `src/` | sources TypeScript | vous | non |
|
|
34
|
-
| `public/` | assets statiques copiés tels quels | vous | via `out/` |
|
|
35
|
-
| `out/` | artefact déployable: shell SPA + assets + HTML baked | `mado release` | oui |
|
|
36
|
-
|
|
37
|
-
`mado release` = `typecheck` + build Vite (`out/index.html`, `out/assets/`,
|
|
38
|
-
`public/*`) + `bake` directement dans les chemins de routes + `sitemap.xml` +
|
|
39
|
-
precompression.
|
|
40
|
-
|
|
41
|
-
`index.html` reste à la racine car Vite le traite comme entry template. Mettez
|
|
42
|
-
dans `public/` uniquement les fichiers à copier tels quels.
|
|
43
|
-
|
|
44
|
-
## Où mettre un nouveau fichier ?
|
|
45
|
-
|
|
46
|
-
| Quoi | Où |
|
|
47
|
-
| --- | --- |
|
|
48
|
-
| Page pour une nouvelle URL | `src/modules/<module>/pages/<name>.page.ts` + module routes |
|
|
49
|
-
| Route map de module | `src/modules/<module>/<module>.routes.ts` |
|
|
50
|
-
| App shell/layout | `src/layouts/<zone>.layout.ts` |
|
|
51
|
-
| Widget UI partagé | `src/shared/ui/<x-name>.component.ts` |
|
|
52
|
-
| Widget UI propre à un module | `src/modules/<module>/components/<name>.component.ts` |
|
|
53
|
-
| API connector | `src/modules/<module>/api/<provider>.connector.ts` |
|
|
54
|
-
| Data resource/mutation | `src/modules/<module>/data/<name>.resource.ts` |
|
|
55
|
-
| Auth/session | `src/modules/auth/` |
|
|
56
|
-
| Surface publique de module | `src/modules/<module>/<module>.public.ts` |
|
|
57
|
-
| Fonction pure sans UI | `src/shared/lib/<name>.ts` |
|
|
58
|
-
| Image statique / favicon | `public/<file>` |
|
|
59
|
-
| CSS de shell | `src/shared/styles/shell.css` |
|
|
60
|
-
| CSS de contenu page | `src/shared/styles/content.css` |
|
|
61
|
-
|
|
62
|
-
## Vite Config
|
|
63
|
-
|
|
64
|
-
```ts
|
|
65
|
-
import { defineConfig } from "vite";
|
|
66
|
-
import { mado } from "@madojs/mado/vite";
|
|
67
|
-
|
|
68
|
-
export default defineConfig({
|
|
69
|
-
plugins: [mado()],
|
|
70
|
-
css: {
|
|
71
|
-
transformer: "lightningcss",
|
|
72
|
-
},
|
|
73
|
-
});
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
Le starter active le transformer Lightning CSS de Vite. Mado ne possède pas le
|
|
77
|
-
prefixing, le lowering CSS ou la minification.
|
|
78
|
-
|
|
79
|
-
## Ce qui ne va PAS dans `src/`
|
|
80
|
-
|
|
81
|
-
- Configs de build-tool supplémentaires — utilisez `vite.config.ts`.
|
|
82
|
-
- Fichiers `.env` — lisez l'env dans `src/shared/lib/config.ts` puis importez
|
|
83
|
-
ce module.
|
|
84
|
-
- Tests mélangés au code — gardez-les dans `test/`.
|