@madojs/mado 0.11.1 → 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.
Files changed (219) hide show
  1. package/AGENTS.md +102 -25
  2. package/CHANGELOG.md +158 -1
  3. package/README.md +135 -172
  4. package/dist/src/component.d.ts +3 -2
  5. package/dist/src/component.js +98 -6
  6. package/dist/src/component.js.map +1 -1
  7. package/dist/src/css.js +34 -5
  8. package/dist/src/css.js.map +1 -1
  9. package/dist/src/head.d.ts +6 -6
  10. package/dist/src/head.js +6 -6
  11. package/dist/src/html/parser.d.ts +1 -1
  12. package/dist/src/html/parser.js +1 -1
  13. package/dist/src/html/template.js +24 -10
  14. package/dist/src/html/template.js.map +1 -1
  15. package/dist/src/index.d.ts +8 -4
  16. package/dist/src/index.js +13 -3
  17. package/dist/src/index.js.map +1 -1
  18. package/dist/src/page.d.ts +62 -28
  19. package/dist/src/page.js +5 -0
  20. package/dist/src/page.js.map +1 -1
  21. package/dist/src/resource.js +2 -1
  22. package/dist/src/resource.js.map +1 -1
  23. package/dist/src/router/base.d.ts +65 -0
  24. package/dist/src/router/base.js +168 -0
  25. package/dist/src/router/base.js.map +1 -0
  26. package/dist/src/router/manifest.js +75 -31
  27. package/dist/src/router/manifest.js.map +1 -1
  28. package/dist/src/router/match.d.ts +16 -2
  29. package/dist/src/router/match.js +41 -1
  30. package/dist/src/router/match.js.map +1 -1
  31. package/dist/src/router/navigation.js +58 -9
  32. package/dist/src/router/navigation.js.map +1 -1
  33. package/dist/src/static-runtime.d.ts +81 -0
  34. package/dist/src/static-runtime.js +209 -0
  35. package/dist/src/static-runtime.js.map +1 -0
  36. package/dist/src/vite/index.d.ts +31 -3
  37. package/dist/src/vite/index.js +49 -18
  38. package/dist/src/vite/index.js.map +1 -1
  39. package/docs/README.md +5 -8
  40. package/docs/architecture/adr/0001-browser-static-snapshots.md +132 -0
  41. package/docs/en/00-the-mado-way.md +1 -1
  42. package/docs/en/01-quickstart.md +183 -0
  43. package/docs/en/10-pages-and-components.md +271 -0
  44. package/docs/en/11-templates-and-signals.md +211 -0
  45. package/docs/en/12-routing.md +229 -0
  46. package/docs/en/13-data.md +225 -0
  47. package/docs/en/14-forms.md +244 -0
  48. package/docs/en/15-static-snapshots.md +181 -0
  49. package/docs/en/{10-app-architecture.md → 16-app-architecture.md} +53 -2
  50. package/docs/en/{13-deployment.md → 20-deployment.md} +59 -10
  51. package/docs/en/{14-testing.md → 22-testing.md} +6 -5
  52. package/docs/en/23-cookbook.md +292 -0
  53. package/docs/en/{18-api-freeze-map.md → 30-api-freeze-map.md} +12 -3
  54. package/docs/en/{20-v1-stability.md → 32-v1-stability.md} +3 -3
  55. package/docs/en/40-llm-guide.md +776 -0
  56. package/docs/en/{06-for-backenders.md → 41-for-backenders.md} +15 -16
  57. package/docs/en/{05-why-mado.md → 42-why-mado.md} +1 -1
  58. package/docs/en/README.md +97 -27
  59. package/docs/recipes/nginx/Containerfile +26 -0
  60. package/docs/recipes/nginx/nginx.conf +42 -0
  61. package/llms.txt +246 -211
  62. package/package.json +17 -10
  63. package/scripts/bake.mjs +4 -569
  64. package/scripts/cli/generate.mjs +55 -5
  65. package/scripts/cli/help.mjs +11 -5
  66. package/scripts/cli/index.mjs +20 -4
  67. package/scripts/cli/init.mjs +7 -2
  68. package/scripts/cli/release.mjs +20 -68
  69. package/scripts/docs-lint.mjs +170 -0
  70. package/scripts/package-smoke.mjs +67 -9
  71. package/scripts/preview.mjs +95 -6
  72. package/scripts/size-budget.mjs +5 -1
  73. package/scripts/static/browser.mjs +654 -0
  74. package/scripts/static/discover.mjs +281 -0
  75. package/scripts/static/output.mjs +141 -0
  76. package/scripts/static/serialize.mjs +212 -0
  77. package/scripts/static/server.mjs +167 -0
  78. package/scripts/static.mjs +191 -0
  79. package/starters/default/README.md +57 -55
  80. package/starters/default/package.json +3 -9
  81. package/starters/default/src/app.routes.ts +20 -33
  82. package/starters/default/src/components/feature-card.component.ts +40 -0
  83. package/starters/default/src/components/live-counter.component.ts +57 -0
  84. package/starters/default/src/content/guides.ts +55 -0
  85. package/starters/default/src/main.ts +9 -13
  86. package/starters/default/src/pages/app.page.ts +41 -0
  87. package/starters/default/src/pages/guide.page.ts +40 -0
  88. package/starters/default/src/pages/home.page.ts +49 -0
  89. package/starters/default/src/pages/not-found.page.ts +18 -0
  90. package/starters/default/src/styles/document.css +39 -0
  91. package/starters/default/src/styles/reset.css +37 -0
  92. package/starters/default/src/styles/tokens.css +30 -0
  93. package/starters/default/src/styles.d.ts +3 -1
  94. package/starters/default/src/vite-env.d.ts +1 -1
  95. package/starters/default/vite.config.ts +14 -1
  96. package/starters/modular/README.md +142 -0
  97. package/starters/modular/index.html +13 -0
  98. package/starters/modular/package.json +30 -0
  99. package/starters/modular/src/app.routes.ts +39 -0
  100. package/starters/{default → modular}/src/layouts/app-shell.layout.ts +8 -5
  101. package/starters/{default → modular}/src/layouts/auth-shell.layout.ts +3 -3
  102. package/starters/modular/src/main.ts +16 -0
  103. package/starters/{default → modular}/src/modules/billing/pages/invoices-list.page.ts +3 -3
  104. package/starters/{default → modular}/src/modules/home/home.page.ts +5 -7
  105. package/starters/modular/src/modules/home/not-found.page.ts +14 -0
  106. package/starters/modular/src/styles.d.ts +1 -0
  107. package/starters/modular/src/vite-env.d.ts +1 -0
  108. package/starters/modular/tsconfig.json +24 -0
  109. package/starters/modular/vite.config.ts +131 -0
  110. package/TODO.md +0 -79
  111. package/docs/en/01-routing.md +0 -152
  112. package/docs/en/02-project-layout.md +0 -124
  113. package/docs/en/03-static-bake.md +0 -249
  114. package/docs/en/04-ide-setup.md +0 -162
  115. package/docs/en/07-llm-pitfalls.md +0 -724
  116. package/docs/en/08-llm-zero-history-test.md +0 -56
  117. package/docs/en/09-shadow-vs-light-dom.md +0 -174
  118. package/docs/en/11-layouts.md +0 -113
  119. package/docs/en/12-auth-and-api.md +0 -124
  120. package/docs/en/16-bake-cookbook.md +0 -100
  121. package/docs/en/17-shadow-dom-forms.md +0 -192
  122. package/docs/fr/00-the-mado-way.md +0 -85
  123. package/docs/fr/01-routing.md +0 -120
  124. package/docs/fr/02-project-layout.md +0 -84
  125. package/docs/fr/03-static-bake.md +0 -289
  126. package/docs/fr/04-ide-setup.md +0 -162
  127. package/docs/fr/05-why-mado.md +0 -193
  128. package/docs/fr/06-for-backenders.md +0 -438
  129. package/docs/fr/07-llm-pitfalls.md +0 -625
  130. package/docs/fr/08-llm-zero-history-test.md +0 -38
  131. package/docs/fr/09-shadow-vs-light-dom.md +0 -65
  132. package/docs/fr/10-app-architecture.md +0 -138
  133. package/docs/fr/11-layouts.md +0 -47
  134. package/docs/fr/12-auth-and-api.md +0 -76
  135. package/docs/fr/13-deployment.md +0 -57
  136. package/docs/fr/14-testing.md +0 -41
  137. package/docs/fr/15-error-handling.md +0 -50
  138. package/docs/fr/16-bake-cookbook.md +0 -88
  139. package/docs/fr/17-shadow-dom-forms.md +0 -196
  140. package/docs/fr/18-api-freeze-map.md +0 -63
  141. package/docs/fr/19-reactivity-ordering.md +0 -97
  142. package/docs/fr/20-v1-stability.md +0 -88
  143. package/docs/fr/README.md +0 -27
  144. package/docs/ru/00-the-mado-way.md +0 -84
  145. package/docs/ru/01-routing.md +0 -119
  146. package/docs/ru/02-project-layout.md +0 -84
  147. package/docs/ru/03-static-bake.md +0 -250
  148. package/docs/ru/04-ide-setup.md +0 -144
  149. package/docs/ru/05-why-mado.md +0 -193
  150. package/docs/ru/06-for-backenders.md +0 -428
  151. package/docs/ru/07-llm-pitfalls.md +0 -624
  152. package/docs/ru/08-llm-zero-history-test.md +0 -57
  153. package/docs/ru/09-shadow-vs-light-dom.md +0 -63
  154. package/docs/ru/10-app-architecture.md +0 -152
  155. package/docs/ru/11-layouts.md +0 -47
  156. package/docs/ru/12-auth-and-api.md +0 -75
  157. package/docs/ru/13-deployment.md +0 -66
  158. package/docs/ru/14-testing.md +0 -50
  159. package/docs/ru/15-error-handling.md +0 -56
  160. package/docs/ru/16-bake-cookbook.md +0 -95
  161. package/docs/ru/17-shadow-dom-forms.md +0 -193
  162. package/docs/ru/18-api-freeze-map.md +0 -64
  163. package/docs/ru/19-reactivity-ordering.md +0 -95
  164. package/docs/ru/20-v1-stability.md +0 -82
  165. package/docs/ru/README.md +0 -25
  166. package/docs/uk/00-the-mado-way.md +0 -82
  167. package/docs/uk/01-routing.md +0 -76
  168. package/docs/uk/02-project-layout.md +0 -73
  169. package/docs/uk/03-static-bake.md +0 -48
  170. package/docs/uk/04-ide-setup.md +0 -26
  171. package/docs/uk/05-why-mado.md +0 -34
  172. package/docs/uk/06-for-backenders.md +0 -55
  173. package/docs/uk/07-llm-pitfalls.md +0 -145
  174. package/docs/uk/08-llm-zero-history-test.md +0 -34
  175. package/docs/uk/09-shadow-vs-light-dom.md +0 -58
  176. package/docs/uk/10-app-architecture.md +0 -97
  177. package/docs/uk/11-layouts.md +0 -47
  178. package/docs/uk/12-auth-and-api.md +0 -70
  179. package/docs/uk/13-deployment.md +0 -40
  180. package/docs/uk/14-testing.md +0 -34
  181. package/docs/uk/15-error-handling.md +0 -32
  182. package/docs/uk/16-bake-cookbook.md +0 -36
  183. package/docs/uk/17-shadow-dom-forms.md +0 -193
  184. package/docs/uk/18-api-freeze-map.md +0 -61
  185. package/docs/uk/19-reactivity-ordering.md +0 -95
  186. package/docs/uk/20-v1-stability.md +0 -83
  187. package/docs/uk/README.md +0 -27
  188. package/starters/default/src/modules/home/not-found.page.ts +0 -11
  189. /package/docs/en/{15-error-handling.md → 21-error-handling.md} +0 -0
  190. /package/docs/en/{19-reactivity-ordering.md → 31-reactivity-ordering.md} +0 -0
  191. /package/starters/{default → modular}/.editorconfig +0 -0
  192. /package/starters/{default → modular}/eslint.config.mjs +0 -0
  193. /package/starters/{default → modular}/public/favicon.svg +0 -0
  194. /package/starters/{default → modular}/src/modules/auth/_contracts/auth-api.types.ts +0 -0
  195. /package/starters/{default → modular}/src/modules/auth/auth.connector.ts +0 -0
  196. /package/starters/{default → modular}/src/modules/auth/auth.guard.ts +0 -0
  197. /package/starters/{default → modular}/src/modules/auth/auth.public.ts +0 -0
  198. /package/starters/{default → modular}/src/modules/auth/auth.routes.ts +0 -0
  199. /package/starters/{default → modular}/src/modules/auth/auth.service.ts +0 -0
  200. /package/starters/{default → modular}/src/modules/auth/auth.types.ts +0 -0
  201. /package/starters/{default → modular}/src/modules/auth/login.page.ts +0 -0
  202. /package/starters/{default → modular}/src/modules/billing/_contracts/stripe.types.ts +0 -0
  203. /package/starters/{default → modular}/src/modules/billing/api/stripe.connector.ts +0 -0
  204. /package/starters/{default → modular}/src/modules/billing/billing.public.ts +0 -0
  205. /package/starters/{default → modular}/src/modules/billing/billing.routes.ts +0 -0
  206. /package/starters/{default → modular}/src/modules/billing/billing.types.ts +0 -0
  207. /package/starters/{default → modular}/src/modules/billing/components/invoice-status-badge.component.ts +0 -0
  208. /package/starters/{default → modular}/src/modules/billing/data/invoices.resource.ts +0 -0
  209. /package/starters/{default → modular}/src/modules/billing/pages/invoice-detail.page.ts +0 -0
  210. /package/starters/{default → modular}/src/shared/http/http-client.ts +0 -0
  211. /package/starters/{default → modular}/src/shared/http/http-error.ts +0 -0
  212. /package/starters/{default → modular}/src/shared/http/interceptors.ts +0 -0
  213. /package/starters/{default → modular}/src/shared/lib/format-date.ts +0 -0
  214. /package/starters/{default → modular}/src/shared/styles/content.css +0 -0
  215. /package/starters/{default → modular}/src/shared/styles/reset.css +0 -0
  216. /package/starters/{default → modular}/src/shared/styles/shell.css +0 -0
  217. /package/starters/{default → modular}/src/shared/styles/tokens.css +0 -0
  218. /package/starters/{default → modular}/src/shared/ui/x-button.component.ts +0 -0
  219. /package/starters/{default → modular}/src/shared/ui/x-spinner.component.ts +0 -0
@@ -1,438 +0,0 @@
1
- # Mado pour les développeurs backend
2
-
3
- > Vous écrivez en Go / Rust / .NET / Java / Python et vous devez construire une UI web.
4
- > Cette page est le modèle mental de Mado en 10 minutes, dans votre langage.
5
-
6
- ---
7
-
8
- ## L'analogie principale
9
-
10
- Mado est structuré **comme un serveur HTTP**. Sérieusement :
11
-
12
- | Monde serveur | Mado |
13
- |---|---|
14
- | Routeur HTTP (chi, axum, mux) | `routes()` — manifeste de chemins |
15
- | Handler `func(req, resp)` | `page({ view: (ctx) => html\`...\` })` |
16
- | Middleware | route group via `layout()` (enveloppe le handler) |
17
- | Moteur de template (Jinja, Handlebars) | tagged template `html\`\`` |
18
- | Client HTTP avec cache | `resource()` — fetch + cache + invalidation |
19
- | Variable réactive / atom | `signal()` — getter réactif |
20
- | Goroutine de fond / tâche | `effect()` — se ré-exécute automatiquement quand un signal change |
21
- | `defer cleanup()` | `ctx.onDispose(fn)` dans le setup du composant |
22
- | Variables ENV | `createContext()` + `provide()`/`inject()` |
23
-
24
- Si vous comprenez un serveur HTTP, vous comprenez Mado.
25
-
26
- ---
27
-
28
- ## Structure de fichiers — comme une application ordinaire
29
-
30
- ```
31
- src/
32
- ├── routes.ts ← manifeste de chemins (comme router.go dans chi)
33
- ├── main.ts ← point d'entrée (comme main.go : setup + run)
34
- ├── pages/ ← un fichier par page (comme handler.go)
35
- ├── components/ ← UI réutilisable (comme helpers/)
36
- ├── layouts/ ← wrappers pour des groupes de pages (comme middleware/)
37
- └── lib/ ← logique métier, client API (comme service/, repo/)
38
- ```
39
-
40
- Un fichier = une page. Pas de routage magique basé sur les fichiers — tout est déclaré à la main dans `routes.ts`.
41
-
42
- ---
43
-
44
- ## Hello World — analogie serveur
45
-
46
- ### Go (chi) — pour comparaison
47
-
48
- ```go
49
- r := chi.NewRouter()
50
- r.Get("/", func(w http.ResponseWriter, r *http.Request) {
51
- w.Write([]byte("<h1>Bonjour</h1>"))
52
- })
53
- r.Get("/users/{id}", func(w http.ResponseWriter, r *http.Request) {
54
- id := chi.URLParam(r, "id")
55
- fmt.Fprintf(w, "<h1>Utilisateur %s</h1>", id)
56
- })
57
- http.ListenAndServe(":8080", r)
58
- ```
59
-
60
- ### Mado — la même chose
61
-
62
- ```ts
63
- // src/routes.ts
64
- import { routes } from "@madojs/mado";
65
-
66
- export default routes({
67
- "/": () => import("./pages/home.js"),
68
- "/users/:id": () => import("./pages/user.js"),
69
- });
70
- ```
71
-
72
- ```ts
73
- // src/pages/home.ts
74
- import { page, html } from "@madojs/mado";
75
- export default page({
76
- view: () => html`<h1>Bonjour</h1>`,
77
- });
78
- ```
79
-
80
- ```ts
81
- // src/pages/user.ts
82
- import { page, html } from "@madojs/mado";
83
- export default page<{ id: string }>({
84
- view: ({ params }) => html`<h1>Utilisateur ${params.id}</h1>`,
85
- });
86
- ```
87
-
88
- Les paramètres de chemin sont disponibles dans `params` — exactement comme `chi.URLParam`.
89
-
90
- ---
91
-
92
- ## Signals — une variable réactive
93
-
94
- Si vous avez écrit Erlang/Elixir avec `Agent`, ou Rust avec `Arc<Mutex<T>>`, ou simplement
95
- stocké de l'état dans une struct et l'avez mis à jour — `signal` est la même chose, plus
96
- le **re-rendu automatique** des composants qui lisent cet état.
97
-
98
- ```ts
99
- import { signal, effect } from "@madojs/mado";
100
-
101
- // "variable" avec abonnement
102
- const count = signal(0);
103
-
104
- // lecture
105
- console.log(count()); // 0
106
-
107
- // écriture
108
- count.set(5);
109
-
110
- // "goroutine" qui s'exécute à chaque changement
111
- effect(() => {
112
- console.log("count vaut maintenant", count());
113
- });
114
- // → affichera "count vaut maintenant 5"
115
-
116
- count.set(10);
117
- // → affichera "count vaut maintenant 10"
118
- ```
119
-
120
- Pas de règles comme "ne peut pas être utilisé dans une condition". Un signal est juste une
121
- fonction getter. Là où il est lu — c'est là que l'abonnement est créé.
122
-
123
- ---
124
-
125
- ## `resource()` — client HTTP avec cache (comme `cache.GetOrSet`)
126
-
127
- C'est l'**abstraction la plus utile pour un développeur backend**. C'est comme Redis avec
128
- invalidation automatique, mais dans le navigateur.
129
-
130
- ```ts
131
- import { resource, mutation, jsonFetcher, invalidate } from "@madojs/mado";
132
-
133
- // "référentiel d'utilisateurs"
134
- const userId = signal(1);
135
-
136
- const user = resource(
137
- () => `/api/users/${userId()}`, // clé de cache (réactive !)
138
- jsonFetcher<User>(), // comment charger
139
- { staleTime: 60_000 }, // cache de 60 secondes
140
- );
141
-
142
- // dans le composant :
143
- user.data(); // User | undefined
144
- user.error(); // Error | null
145
- user.loading(); // boolean
146
-
147
- // mutation (comme POST/PUT)
148
- const save = mutation<User, User>(
149
- (u) => fetch("/api/users", { method: "POST", body: JSON.stringify(u) }).then(r => r.json()),
150
- { invalidates: ["/api/users*"] }, // invalidation glob — comme `cache.Drop("users:*")`
151
- );
152
-
153
- await save.run(newUser);
154
- // automatiquement : user.data() se mettra à jour si le glob correspond
155
- ```
156
-
157
- Les clés de `resource()` sont l'identité du cache. Incluez l'endpoint, les query
158
- params et la forme des données dans la clé : deux `resource()` vivants avec la
159
- même clé partagent le cache et la requête in-flight. Si la même clé est utilisée
160
- avec un fetcher différent, Mado avertit, car cela signifie généralement que la
161
- clé de cache est trop large.
162
-
163
- Si une telle abstraction existait dans le monde Go pour les caches côté serveur — on
164
- pleurerait tous de joie.
165
-
166
- ---
167
-
168
- ## Composants = handler avec sa propre mémoire
169
-
170
- Un composant est un **handler** qui rend son bout d'UI. Il possède :
171
-
172
- - des paramètres (attributs/propriétés) ;
173
- - un état interne (`signal`s) ;
174
- - un lifecycle : `connectedCallback` (comme Init), `disconnectedCallback` (comme Close).
175
-
176
- ```ts
177
- import { component, html, signal } from "@madojs/mado";
178
-
179
- component("x-counter", () => {
180
- const count = signal(0);
181
-
182
- return () => html`
183
- <button @click=${() => count.update(n => n + 1)}>
184
- Clics : ${count}
185
- </button>
186
- `;
187
- });
188
- ```
189
-
190
- Utilisation :
191
-
192
- ```ts
193
- html`<x-counter></x-counter>`
194
- ```
195
-
196
- Nous enregistrons le tag `<x-counter>` dans le navigateur — il devient une "fonction" qui
197
- peut être insérée dans le HTML. C'est un mécanisme **natif** du navigateur (Web Components),
198
- Mado ne fait que le coller avec les signals.
199
-
200
- ---
201
-
202
- ## Forms — comme `form.Validate()` côté backend
203
-
204
- Mado utilise une **validation par schéma proche des contraintes HTML natives**, plus le suivi d'état.
205
-
206
- ```ts
207
- import { useForm } from "@madojs/mado";
208
-
209
- const f = useForm({
210
- email: { required: true, type: "email" },
211
- age: { required: true, type: "number", min: 18 },
212
- });
213
-
214
- // dans le template :
215
- html`
216
- <form @submit=${f.onSubmit(async (v) => {
217
- await api.save(v);
218
- f.reset();
219
- })}>
220
- <input name="email" .value=${() => f.values().email ?? ""}
221
- @input=${f.onInput} @blur=${f.onBlur} />
222
-
223
- ${() => f.errors().email && f.touched().email
224
- ? html`<small>${f.errors().email}</small>`
225
- : null}
226
-
227
- <button ?disabled=${() => !f.isValid() || f.submitting()}>Enregistrer</button>
228
- </form>
229
- `;
230
- ```
231
-
232
- Validation personnalisée — `validate: (values) => errors | null`. Pas de schémas Yup ni de dépendances.
233
-
234
- ---
235
-
236
- ## Context = DI / injection de dépendances
237
-
238
- Tout comme vous passez `context.Context` à travers la pile d'appels en Go — dans Mado le
239
- context est propagé à travers l'arbre DOM.
240
-
241
- ```ts
242
- import { createContext, provide, inject } from "@madojs/mado";
243
-
244
- // déclarer le "type" de la dépendance
245
- const ApiCtx = createContext<ApiClient>(defaultApiClient);
246
-
247
- // dans le composant racine — fournir
248
- component("x-app", ({ host }) => {
249
- provide(host, ApiCtx, new ApiClient("https://api.example.com"));
250
- return () => html`<x-page/>`;
251
- });
252
-
253
- // dans n'importe quel enfant — consommer
254
- component("x-page", ({ host }) => {
255
- const api = inject(host, ApiCtx); // signal<ApiClient>
256
- return () => html`<div>Version API : ${() => api().version}</div>`;
257
- });
258
- ```
259
-
260
- C'est comme `context.WithValue` / `ctx.Value` en Go, mais réactif.
261
-
262
- ---
263
-
264
- ## SEO — pas du SSR, mais `bake` (comme `templ generate` en Go)
265
-
266
- Si vous êtes habitué au rendu côté serveur pour le SEO, dans Mado c'est résolu différemment :
267
- **prérendu au moment du build**.
268
-
269
- ```ts
270
- // src/pages/product.ts
271
- export default page({
272
- bake: {
273
- paths: () => api.allProductSlugs(), // fetch au build-time
274
- data: ({ slug }) => api.getProduct(slug),
275
- revalidate: 3600,
276
- },
277
- head: ({ slug }, data) => ({
278
- description: data.description,
279
- canonical: `/product/${slug}`,
280
- og: { title: data.name, image: data.image },
281
- }),
282
- view: ({ params }) => html`<x-product data-slug=${params.slug}/>`,
283
- });
284
- ```
285
-
286
- ```bash
287
- npm run bake # → out/product/iphone-15/index.html (+ sitemap)
288
- ```
289
-
290
- Le crawler voit du HTML prêt avec des meta tags. L'utilisateur voit la même chose + interactivité
291
- après le chargement du JS.
292
-
293
- Plus de détails : [`03-static-bake.md`](./03-static-bake.md).
294
-
295
- ---
296
-
297
- ## Tâches typiques d'un développeur backend — recettes
298
-
299
- ### Page CRUD avec une liste
300
-
301
- ```ts
302
- import { page, html, resource, each, signal } from "@madojs/mado";
303
-
304
- export default page({
305
- view: () => {
306
- const users = resource(() => "/api/users", jsonFetcher<User[]>());
307
-
308
- return html`
309
- ${() => users.loading() ? html`<p>Chargement…</p>` : null}
310
- ${() => users.error() ? html`<p>Erreur : ${users.error()!.message}</p>` : null}
311
- <ul>
312
- ${() => each(users.data() ?? [], u => u.id, u => html`
313
- <li><a href="/users/${u.id}" data-link>${u.name}</a></li>
314
- `)}
315
- </ul>
316
- `;
317
- },
318
- });
319
- ```
320
-
321
- ### Formulaire avec POST
322
-
323
- ```ts
324
- import { useForm, mutation } from "@madojs/mado";
325
-
326
- const createUser = mutation<NewUser, User>(
327
- (u) => fetch("/api/users", { method: "POST", body: JSON.stringify(u) }).then(r => r.json()),
328
- { invalidates: ["/api/users*"] },
329
- );
330
-
331
- // dans page.view :
332
- const f = useForm({ name: { required: true } });
333
-
334
- html`
335
- <form @submit=${f.onSubmit(async (v) => {
336
- await createUser.run(v);
337
- navigate("/users");
338
- })}>
339
- <input name="name" @input=${f.onInput}>
340
- <button>Créer</button>
341
- </form>
342
- `;
343
- ```
344
-
345
- ### Zone protégée (middleware auth)
346
-
347
- ```ts
348
- // src/layouts/auth-layout.ts
349
- import { page, html, effect } from "@madojs/mado";
350
- import { isAuthed, navigate } from "../lib/auth.js";
351
-
352
- export default page({
353
- view: ({ child }) => {
354
- effect(() => {
355
- if (!isAuthed()) navigate("/login");
356
- });
357
- return html`<div class="app-shell">${child}</div>`;
358
- },
359
- });
360
- ```
361
-
362
- ```ts
363
- // src/app.routes.ts
364
- import { layout, routes } from "@madojs/mado";
365
-
366
- export default routes({
367
- "/login": () => import("./pages/login.js"),
368
-
369
- "/app": layout({
370
- layout: () => import("./layouts/auth-layout.js"),
371
- routes: {
372
- "/dashboard": () => import("./pages/dashboard.js"),
373
- "/users": () => import("./pages/users.js"),
374
- },
375
- }),
376
- });
377
- ```
378
-
379
- ### Client API global (comme un singleton en Go)
380
-
381
- ```ts
382
- // src/lib/api.ts
383
- export class ApiClient {
384
- constructor(private base: string) {}
385
- get<T>(path: string): Promise<T> {
386
- return fetch(this.base + path).then(r => r.json());
387
- }
388
- }
389
-
390
- export const api = new ApiClient("/api");
391
- ```
392
-
393
- Utilisé directement via `import { api } from '...'` ou via `createContext` pour la testabilité.
394
-
395
- ---
396
-
397
- ## Ce que vous n'avez **pas** besoin d'apprendre (bonne nouvelle)
398
-
399
- - **Hooks et règles des hooks.** Pas dans Mado. Les signals sont des fonctions ordinaires.
400
- - **VDOM et réconciliation.** Aucun. Les signals mettent à jour le DOM directement, chirurgicalement.
401
- - **Configurations Webpack/Vite.** Pas de build. `tsc → navigateur`.
402
- - **Tableaux de dépendances `useEffect`.** `effect()` voit ce que vous lisez de lui-même.
403
- - **Bibliothèques de gestion d'état** (Redux/Zustand). Signals + context.
404
- - **Transformations CSS-in-JS.** Shadow DOM + `css\`\`` + cssVars.
405
- - **Guide de migration routing v6 → v7.** `routes()` fait 500 lignes, lisible en 20 minutes.
406
-
407
- ---
408
-
409
- ## Ce que vous **devrez** apprendre (honnêtement)
410
-
411
- Ce sont de nouveaux concepts. Pas effrayants, mais ce sont des additions à votre base React/Vue :
412
-
413
- 1. **Custom Elements / Shadow DOM.** `<x-foo>` n'est pas une div, c'est un élément à part entière avec son propre DOM. Slots, CSS scopé. Une soirée de lecture MDN.
414
- 2. **`attribute` vs `property`.** L'attribut est une string en HTML (`data-id="5"`), la property est une propriété JS (`el.id = 5`). `?attr=${flag}` et `.prop=${value}` dans les templates se réfèrent à des choses différentes. Règle principale : **nombres/objets/tableaux — via `.prop`, drapeaux — via `?attr`, strings — via `attr`**.
415
- 3. **Signals.** Si c'est votre première fois — vous bloquerez 10 minutes, puis c'est plus facile que les hooks.
416
- 4. **templates `html\`\``.** C'est juste une fonction JS avec coloration via [lit-plugin](./04-ide-setup.md). Pas de magie.
417
-
418
- Tout le reste — navigateur standard + TypeScript.
419
-
420
- ---
421
-
422
- ## Ce qui manque (honnêtement)
423
-
424
- - Pas de hot reload, seulement un rechargement complet via SSE. Suffisant pour la plupart des cas, mais pas comme Vite.
425
- - Pas d'extension navigateur dev-tools. Utilisez `localStorage.madoDebug = '1'` + console.
426
- - Pas de starters StackBlitz (encore).
427
- - Pas d'assistant IA qui connaît Mado aussi bien que React. En cas de doute — lisez `src/`, c'est pas effrayant.
428
-
429
- ---
430
-
431
- ## Lecture complémentaire
432
-
433
- - **[`01-routing.md`](./01-routing.md)** — le router en détail.
434
- - **[`02-project-layout.md`](./02-project-layout.md)** — structure du projet.
435
- - **[`03-static-bake.md`](./03-static-bake.md)** — SEO sans SSR.
436
- - **[`10-app-architecture.md`](./10-app-architecture.md)** — forme canonique du starter.
437
-
438
- Si quelque chose n'est pas clair — ouvrez une issue, ou ouvrez simplement le source. Il est vraiment lisible en une soirée.