@madojs/mado 0.10.1 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (217) hide show
  1. package/AGENTS.md +24 -26
  2. package/CHANGELOG.md +68 -0
  3. package/README.md +18 -45
  4. package/TODO.md +52 -48
  5. package/dist/src/component.d.ts +2 -1
  6. package/dist/src/component.js +5 -2
  7. package/dist/src/component.js.map +1 -1
  8. package/dist/src/each.d.ts +1 -1
  9. package/dist/src/each.js +1 -1
  10. package/dist/src/each.js.map +1 -1
  11. package/dist/src/index.d.ts +11 -6
  12. package/dist/src/index.js +5 -3
  13. package/dist/src/index.js.map +1 -1
  14. package/dist/src/lazy.d.ts +1 -1
  15. package/dist/src/lazy.js +1 -1
  16. package/dist/src/lazy.js.map +1 -1
  17. package/dist/src/page.d.ts +17 -21
  18. package/dist/src/page.js +7 -12
  19. package/dist/src/page.js.map +1 -1
  20. package/dist/src/router/manifest.d.ts +1 -1
  21. package/dist/src/router/manifest.js +21 -13
  22. package/dist/src/router/manifest.js.map +1 -1
  23. package/dist/src/router/match.d.ts +2 -2
  24. package/dist/src/router/match.js +3 -3
  25. package/dist/src/router/match.js.map +1 -1
  26. package/dist/src/router/navigation.js +1 -1
  27. package/dist/src/router/navigation.js.map +1 -1
  28. package/dist/src/vite/index.d.ts +10 -0
  29. package/dist/src/vite/index.js +33 -0
  30. package/dist/src/vite/index.js.map +1 -0
  31. package/docs/en/00-the-mado-way.md +25 -12
  32. package/docs/en/01-routing.md +90 -142
  33. package/docs/en/02-project-layout.md +59 -53
  34. package/docs/en/03-static-bake.md +5 -6
  35. package/docs/en/05-why-mado.md +6 -6
  36. package/docs/en/06-for-backenders.md +18 -22
  37. package/docs/en/08-llm-zero-history-test.md +9 -14
  38. package/docs/en/09-shadow-vs-light-dom.md +28 -36
  39. package/docs/en/10-app-architecture.md +158 -96
  40. package/docs/en/11-layouts.md +22 -24
  41. package/docs/en/12-auth-and-api.md +89 -182
  42. package/docs/en/13-deployment.md +18 -22
  43. package/docs/en/14-testing.md +4 -4
  44. package/docs/en/16-bake-cookbook.md +11 -12
  45. package/docs/en/18-api-freeze-map.md +6 -4
  46. package/docs/en/20-v1-stability.md +1 -1
  47. package/docs/fr/00-the-mado-way.md +55 -90
  48. package/docs/fr/01-routing.md +70 -152
  49. package/docs/fr/02-project-layout.md +61 -42
  50. package/docs/fr/03-static-bake.md +1 -1
  51. package/docs/fr/05-why-mado.md +6 -6
  52. package/docs/fr/06-for-backenders.md +7 -7
  53. package/docs/fr/08-llm-zero-history-test.md +21 -48
  54. package/docs/fr/09-shadow-vs-light-dom.md +43 -162
  55. package/docs/fr/10-app-architecture.md +110 -33
  56. package/docs/fr/11-layouts.md +24 -12
  57. package/docs/fr/12-auth-and-api.md +63 -22
  58. package/docs/fr/13-deployment.md +7 -10
  59. package/docs/fr/14-testing.md +1 -1
  60. package/docs/fr/16-bake-cookbook.md +2 -2
  61. package/docs/fr/18-api-freeze-map.md +1 -1
  62. package/docs/fr/20-v1-stability.md +1 -1
  63. package/docs/recipes/nginx/README.md +13 -0
  64. package/docs/ru/00-the-mado-way.md +53 -75
  65. package/docs/ru/01-routing.md +68 -143
  66. package/docs/ru/02-project-layout.md +61 -41
  67. package/docs/ru/03-static-bake.md +2 -2
  68. package/docs/ru/05-why-mado.md +6 -6
  69. package/docs/ru/06-for-backenders.md +7 -7
  70. package/docs/ru/08-llm-zero-history-test.md +9 -14
  71. package/docs/ru/09-shadow-vs-light-dom.md +43 -178
  72. package/docs/ru/10-app-architecture.md +115 -63
  73. package/docs/ru/11-layouts.md +24 -24
  74. package/docs/ru/12-auth-and-api.md +57 -35
  75. package/docs/ru/13-deployment.md +7 -11
  76. package/docs/ru/14-testing.md +1 -1
  77. package/docs/ru/16-bake-cookbook.md +12 -6
  78. package/docs/ru/18-api-freeze-map.md +5 -3
  79. package/docs/ru/20-v1-stability.md +1 -1
  80. package/docs/uk/00-the-mado-way.md +70 -44
  81. package/docs/uk/01-routing.md +41 -47
  82. package/docs/uk/02-project-layout.md +68 -41
  83. package/docs/uk/03-static-bake.md +1 -2
  84. package/docs/uk/06-for-backenders.md +3 -3
  85. package/docs/uk/08-llm-zero-history-test.md +22 -24
  86. package/docs/uk/09-shadow-vs-light-dom.md +37 -86
  87. package/docs/uk/10-app-architecture.md +72 -31
  88. package/docs/uk/11-layouts.md +25 -12
  89. package/docs/uk/12-auth-and-api.md +58 -22
  90. package/docs/uk/13-deployment.md +4 -3
  91. package/docs/uk/14-testing.md +1 -1
  92. package/docs/uk/18-api-freeze-map.md +1 -1
  93. package/docs/uk/20-v1-stability.md +1 -1
  94. package/llms.txt +14 -15
  95. package/package.json +18 -11
  96. package/scripts/_config.mjs +15 -161
  97. package/scripts/bake.mjs +67 -57
  98. package/scripts/cli/generate.mjs +348 -0
  99. package/scripts/cli/help.mjs +27 -0
  100. package/scripts/cli/index.mjs +79 -0
  101. package/scripts/cli/init.mjs +153 -0
  102. package/scripts/cli/release.mjs +152 -0
  103. package/scripts/cli/run.mjs +96 -0
  104. package/scripts/cli.mjs +2 -621
  105. package/scripts/package-smoke.mjs +4 -1
  106. package/scripts/preview.mjs +13 -37
  107. package/scripts/size-budget.mjs +5 -2
  108. package/scripts/vite.default.mjs +11 -0
  109. package/starters/default/.editorconfig +12 -0
  110. package/starters/default/README.md +74 -0
  111. package/starters/default/eslint.config.mjs +256 -0
  112. package/starters/default/index.html +13 -0
  113. package/starters/default/package.json +30 -0
  114. package/starters/default/public/favicon.svg +4 -0
  115. package/starters/default/src/app.routes.ts +39 -0
  116. package/starters/default/src/layouts/app-shell.layout.ts +35 -0
  117. package/starters/default/src/layouts/auth-shell.layout.ts +17 -0
  118. package/starters/default/src/main.ts +16 -0
  119. package/starters/default/src/modules/auth/_contracts/auth-api.types.ts +17 -0
  120. package/starters/default/src/modules/auth/auth.connector.ts +45 -0
  121. package/starters/default/src/modules/auth/auth.guard.ts +22 -0
  122. package/starters/default/src/modules/auth/auth.public.ts +9 -0
  123. package/starters/default/src/modules/auth/auth.routes.ts +8 -0
  124. package/starters/default/src/modules/auth/auth.service.ts +71 -0
  125. package/starters/default/src/modules/auth/auth.types.ts +15 -0
  126. package/starters/default/src/modules/auth/login.page.ts +62 -0
  127. package/starters/default/src/modules/billing/_contracts/stripe.types.ts +17 -0
  128. package/starters/default/src/modules/billing/api/stripe.connector.ts +71 -0
  129. package/starters/default/src/modules/billing/billing.public.ts +5 -0
  130. package/starters/default/src/modules/billing/billing.routes.ts +9 -0
  131. package/starters/default/src/modules/billing/billing.types.ts +15 -0
  132. package/starters/default/src/modules/billing/components/invoice-status-badge.component.ts +43 -0
  133. package/starters/default/src/modules/billing/data/invoices.resource.ts +35 -0
  134. package/starters/default/src/modules/billing/pages/invoice-detail.page.ts +70 -0
  135. package/starters/default/src/modules/billing/pages/invoices-list.page.ts +73 -0
  136. package/starters/default/src/modules/home/home.page.ts +34 -0
  137. package/starters/default/src/modules/home/not-found.page.ts +11 -0
  138. package/starters/default/src/shared/http/http-client.ts +86 -0
  139. package/starters/default/src/shared/http/http-error.ts +37 -0
  140. package/starters/default/src/shared/http/interceptors.ts +59 -0
  141. package/starters/default/src/shared/lib/format-date.ts +19 -0
  142. package/starters/default/src/shared/styles/content.css +70 -0
  143. package/starters/default/src/shared/styles/reset.css +32 -0
  144. package/starters/default/src/shared/styles/shell.css +57 -0
  145. package/starters/default/src/shared/styles/tokens.css +44 -0
  146. package/starters/default/src/shared/ui/x-button.component.ts +49 -0
  147. package/starters/default/src/shared/ui/x-spinner.component.ts +22 -0
  148. package/starters/default/src/styles.d.ts +1 -0
  149. package/starters/default/src/vite-env.d.ts +1 -0
  150. package/starters/default/tsconfig.json +24 -0
  151. package/starters/default/vite.config.ts +9 -0
  152. package/MADO_V1_PLAN.md +0 -179
  153. package/ROADMAP.md +0 -178
  154. package/dist/src/html.d.ts +0 -18
  155. package/dist/src/html.js +0 -17
  156. package/dist/src/html.js.map +0 -1
  157. package/dist/src/router.d.ts +0 -13
  158. package/dist/src/router.js +0 -13
  159. package/dist/src/router.js.map +0 -1
  160. package/scripts/bundle.mjs +0 -212
  161. package/scripts/llm-zero-history-smoke.mjs +0 -93
  162. package/scripts/new.mjs +0 -80
  163. package/scripts/showcase-regression.mjs +0 -392
  164. package/server/serve.mjs +0 -455
  165. package/starters/admin/README.md +0 -63
  166. package/starters/admin/index.html +0 -28
  167. package/starters/admin/mado.config.json +0 -22
  168. package/starters/admin/package.json +0 -24
  169. package/starters/admin/public/favicon.svg +0 -4
  170. package/starters/admin/src/components/x-button.ts +0 -82
  171. package/starters/admin/src/components/x-input.ts +0 -105
  172. package/starters/admin/src/layouts/app.ts +0 -101
  173. package/starters/admin/src/layouts/auth.ts +0 -41
  174. package/starters/admin/src/lib/api.ts +0 -184
  175. package/starters/admin/src/lib/auth.ts +0 -83
  176. package/starters/admin/src/main.ts +0 -15
  177. package/starters/admin/src/pages/admin/dashboard.ts +0 -48
  178. package/starters/admin/src/pages/admin/order-detail.ts +0 -80
  179. package/starters/admin/src/pages/admin/orders.ts +0 -117
  180. package/starters/admin/src/pages/home.ts +0 -34
  181. package/starters/admin/src/pages/login.ts +0 -70
  182. package/starters/admin/src/pages/not-found.ts +0 -12
  183. package/starters/admin/src/routes.ts +0 -40
  184. package/starters/admin/src/styles/global.ts +0 -86
  185. package/starters/admin/tsconfig.json +0 -15
  186. package/starters/crud/README.md +0 -33
  187. package/starters/crud/index.html +0 -28
  188. package/starters/crud/mado.config.json +0 -20
  189. package/starters/crud/package.json +0 -24
  190. package/starters/crud/src/components/app-shell.ts +0 -56
  191. package/starters/crud/src/components/ticket-detail.ts +0 -33
  192. package/starters/crud/src/components/ticket-form.ts +0 -69
  193. package/starters/crud/src/components/ticket-list.ts +0 -66
  194. package/starters/crud/src/lib/api.ts +0 -76
  195. package/starters/crud/src/main.ts +0 -9
  196. package/starters/crud/src/pages/home.ts +0 -34
  197. package/starters/crud/src/pages/not-found.ts +0 -12
  198. package/starters/crud/src/pages/ticket-detail.ts +0 -7
  199. package/starters/crud/src/pages/ticket-new.ts +0 -7
  200. package/starters/crud/src/pages/tickets.ts +0 -7
  201. package/starters/crud/src/routes.ts +0 -11
  202. package/starters/crud/src/styles/global.ts +0 -155
  203. package/starters/crud/tsconfig.json +0 -15
  204. package/starters/minimal/README.md +0 -21
  205. package/starters/minimal/index.html +0 -28
  206. package/starters/minimal/mado.config.json +0 -20
  207. package/starters/minimal/package.json +0 -24
  208. package/starters/minimal/src/components/app-counter.ts +0 -31
  209. package/starters/minimal/src/main.ts +0 -9
  210. package/starters/minimal/src/pages/home.ts +0 -35
  211. package/starters/minimal/src/pages/not-found.ts +0 -14
  212. package/starters/minimal/src/routes.ts +0 -8
  213. package/starters/minimal/src/styles/global.ts +0 -60
  214. package/starters/minimal/tsconfig.json +0 -15
  215. package/templates/page-detail.ts +0 -63
  216. package/templates/page-form.ts +0 -94
  217. package/templates/page-list.ts +0 -79
@@ -13,7 +13,7 @@ Mado est structuré **comme un serveur HTTP**. Sérieusement :
13
13
  |---|---|
14
14
  | Routeur HTTP (chi, axum, mux) | `routes()` — manifeste de chemins |
15
15
  | Handler `func(req, resp)` | `page({ view: (ctx) => html\`...\` })` |
16
- | Middleware | `layout` dans `nested()` (enveloppe le handler) |
16
+ | Middleware | route group via `layout()` (enveloppe le handler) |
17
17
  | Moteur de template (Jinja, Handlebars) | tagged template `html\`\`` |
18
18
  | Client HTTP avec cache | `resource()` — fetch + cache + invalidation |
19
19
  | Variable réactive / atom | `signal()` — getter réactif |
@@ -360,17 +360,17 @@ export default page({
360
360
  ```
361
361
 
362
362
  ```ts
363
- // src/routes.ts
364
- import { routes, nested } from "@madojs/mado";
363
+ // src/app.routes.ts
364
+ import { layout, routes } from "@madojs/mado";
365
365
 
366
366
  export default routes({
367
367
  "/login": () => import("./pages/login.js"),
368
368
 
369
- "/app/*": nested({
369
+ "/app": layout({
370
370
  layout: () => import("./layouts/auth-layout.js"),
371
371
  routes: {
372
- "dashboard": () => import("./pages/dashboard.js"),
373
- "users": () => import("./pages/users.js"),
372
+ "/dashboard": () => import("./pages/dashboard.js"),
373
+ "/users": () => import("./pages/users.js"),
374
374
  },
375
375
  }),
376
376
  });
@@ -433,6 +433,6 @@ Tout le reste — navigateur standard + TypeScript.
433
433
  - **[`01-routing.md`](./01-routing.md)** — le router en détail.
434
434
  - **[`02-project-layout.md`](./02-project-layout.md)** — structure du projet.
435
435
  - **[`03-static-bake.md`](./03-static-bake.md)** — SEO sans SSR.
436
- - **[`examples/showcase/`](../../examples/showcase/)** — exemple complet (landing + admin).
436
+ - **[`10-app-architecture.md`](./10-app-architecture.md)** — forme canonique du starter.
437
437
 
438
438
  Si quelque chose n'est pas clair — ouvrez une issue, ou ouvrez simplement le source. Il est vraiment lisible en une soirée.
@@ -1,65 +1,38 @@
1
1
  # Test LLM sans historique
2
2
 
3
- Ce document définit un test de validation pratique pour Mado.
4
-
5
- La question n'est pas "un LLM peut-il générer du code frontend ?" Il le peut. La question est :
6
- un LLM fraîchement initialisé peut-il écrire du Mado idiomatique sans retomber dans du code
7
- de forme React ?
3
+ Ce document définit un test manuel pour vérifier qu'un LLM fraîchement
4
+ initialisé écrit du Mado idiomatique au lieu de reproduire React dans des
5
+ tagged templates.
8
6
 
9
7
  ## Contexte autorisé
10
8
 
11
- Pour le premier passage, donnez à l'agent uniquement :
12
-
13
9
  - `AGENTS.md`
14
10
  - `README.md`
15
- - `docs/ru/07-llm-pitfalls.md`
16
- - `examples/basic/README.md` si un tour minimal de l'API est nécessaire
17
- - des fichiers `examples/showcase/**` spécifiques uniquement quand l'agent demande un pattern
18
- d'application plus grande
19
-
20
- L'agent peut rechercher des API ciblées dans `src/` quand il est bloqué, mais ne doit pas
21
- charger tout le framework dans le context.
11
+ - `docs/fr/07-llm-pitfalls.md` ou la version anglaise
12
+ - fichiers de l'espace externe `madojs-examples` seulement si l'agent demande
13
+ un pattern d'application plus large
22
14
 
23
15
  ## Tâche
24
16
 
25
- Construire `examples/tickets` : une petite SPA d'administration de tickets pour un développeur
26
- solo/backend.
27
-
28
- Comportement requis :
17
+ Construire une petite SPA ticket-admin :
29
18
 
30
19
  - routes : `/`, `/tickets`, `/tickets/new`, `/tickets/:id`, `*` ;
31
- - API mock en mémoire avec des délais async réalistes ;
32
- - page de liste avec `resource()`, `queryParam()` filtres de recherche/statut, `computed()`,
33
- et des lignes `each()` avec clés ;
34
- - flux de création et d'édition avec `useForm()` + `mutation()` + `invalidates` ;
35
- - état UI local avec `signal()` ;
36
- - composants shell, metric et badge avec slot pour une UI admin plus réaliste ;
37
- - test de smoke important le build de l'exemple.
20
+ - mock API en mémoire avec délais async réalistes ;
21
+ - liste avec `resource()`, `queryParam()`, `computed()` et `each()` keyed ;
22
+ - create/edit avec `useForm()` + `mutation()` + `invalidates` ;
23
+ - état UI local avec `signal()`.
38
24
 
39
- ## Liste de contrôle des échecs
25
+ ## Checklist d'échec
40
26
 
41
- Cherchez ces éléments après l'implémentation :
42
-
43
- - JSX, `useState`, `useEffect`, `ref`, `$state`, ou composants de style classe ;
44
- - `${signal()}` ou `${signal() + 1}` là où un thunk enfant réactif est requis ;
27
+ - JSX, `useState`, `useEffect`, `ref`, `$state`, classes custom elements ;
28
+ - `${signal()}` là où un child thunk réactif est nécessaire ;
45
29
  - `disabled=${...}` au lieu de `?disabled=${...}` ;
46
- - listes dynamiques rendues avec un mapping de tableau sans clé au lieu de `each()` ;
47
- - imports ESM navigateur sans `.js` ;
48
- - `resource()` créé en dehors du setup du composant ;
49
- - nouvelles dépendances runtime ou nouvelles API publiques du framework.
50
-
51
- ## Notes de résultats
52
-
53
- L'implémentation actuelle de `examples/tickets` n'a pas nécessité de nouvelles API publiques ni
54
- de dépendances runtime.
30
+ - `.map()` non-keyed pour des listes dynamiques ;
31
+ - `resource()` créé hors contexte lifecycle-aware ;
32
+ - nouvelles dépendances runtime ou nouvelles API publiques.
55
33
 
56
- CI exécute `npm run llm:smoke` comme proxy déterministe pour cette tâche :
57
- il vérifie que `llms.txt` contient toujours les règles clés, compare l'artefact
58
- commité `examples/tickets` à la surface API Mado requise et aux failure
59
- patterns, puis build le projet et lance `test/tickets-smoke.test.mjs`.
34
+ ## Notes
60
35
 
61
- Le principal point de pression dans la documentation reste le lifecycle : les anciens exemples
62
- peuvent donner l'impression qu'il est acceptable de créer `resource()` directement dans
63
- `page.view()`. L'exemple tickets utilise plutôt des composants wrapper au niveau page, de sorte
64
- que les resources sont enregistrées à l'intérieur du setup du composant et se nettoient avec
65
- le composant.
36
+ L'implémentation historique tickets vit dans l'espace externe d'exemples. Le
37
+ core repository ne livre plus cet artefact ; utilisez cette page comme script
38
+ d'évaluation manuel quand vous mettez à jour les règles LLM.
@@ -1,184 +1,65 @@
1
1
  # Shadow DOM vs Light DOM
2
2
 
3
- Les composants Mado utilisent Shadow DOM par défaut. C'est un bon défaut pour les widgets
4
- autonomes, mais ce n'est pas le bon défaut pour chaque composant dans une application.
3
+ Les composants Mado utilisent Shadow DOM par défaut. C'est un bon défaut pour
4
+ les widgets autonomes, mais les zones d'application et les pages restent
5
+ généralement de simples templates light DOM.
5
6
 
6
- ## Règle générale
7
+ ## Règle
7
8
 
8
- Dans Mado, un layout est aussi un composant. Si un fichier décrit une partie
9
- visible et réutilisable de l'arbre UI — app shell, sidebar, modal, table,
10
- section de page — préférez un Web Component déclaré avec `component()`.
11
-
12
- Gardez les fonctions simples pour de petits helpers inline :
9
+ Utilisez les route layouts pour les zones d'application :
13
10
 
14
11
  ```ts
15
- const money = (value: number) => html`<span>${formatMoney(value)}</span>`;
12
+ export default page({
13
+ view: ({ child }) => html`<main class="app-main">${child}</main>`,
14
+ });
16
15
  ```
17
16
 
18
- Ne faites pas d'app shell sous forme de fonction dans les exemples publics. Cela
19
- fonctionne, mais cela cache le modèle du navigateur au lieu de l'enseigner.
20
-
21
- Utilisez **Shadow DOM** pour les widgets feuilles :
22
-
23
- - boutons, badges, cartes, métriques ;
24
- - modals, toasts, petits composants visuels ;
25
- - widgets d'intégration qui ne devraient pas hériter du CSS de l'app accidentellement ;
26
- - composants dont le stylage doit appartenir au composant lui-même.
27
-
28
- Utilisez **Light DOM** (`{ shadow: false }`) pour la structure de l'app qui veut partager
29
- les utilitaires CSS globaux :
30
-
31
- - composants route/page ;
32
- - écrans admin avec des layouts denses de tableau/formulaire ;
33
- - écrans riches en données avec des tableaux et des formulaires ;
34
- - composants qui partagent intentionnellement les utilitaires globaux de layout, formulaire et
35
- tableau ;
36
- - endroits où les enfants doivent simplement rester dans le DOM normal du document.
17
+ Ces fichiers vivent dans `src/layouts/` et sont composés depuis
18
+ `src/app.routes.ts` avec `layout()`. Ils sont stylés par
19
+ `src/shared/styles/shell.css`.
37
20
 
38
- Utilisez **Shadow DOM** pour les layouts basés sur des slots :
39
-
40
- - app shells qui rendent `<slot>` ;
41
- - wrappers sidebar/contenu ;
42
- - frames de layout réutilisables qui possèdent leur propre CSS grid/header/sidebar.
43
-
44
- `<slot>` est une fonctionnalité Shadow DOM. Dans un composant `shadow: false`,
45
- `<slot>` est juste un élément DOM normal et ne déplace pas les enfants à cet
46
- endroit du layout.
47
-
48
- ## Le piège
49
-
50
- Le CSS global ne franchit pas une frontière Shadow DOM.
21
+ Utilisez les page files pour les écrans :
51
22
 
52
23
  ```ts
53
- // global.ts
54
- export const globalStyles = css`
55
- .page-head { display: flex; justify-content: space-between; }
56
- .metric-grid { display: grid; grid-template-columns: repeat(4, 1fr); }
57
- `;
58
-
59
- // ❌ .page-head et .metric-grid ne s'appliqueront pas à l'intérieur du shadowRoot de x-dashboard
60
- component("x-dashboard", () => () => html`
61
- <header class="page-head">...</header>
62
- <div class="metric-grid">...</div>
63
- `);
24
+ export default page({
25
+ view: () => html`<section><h1>Users</h1></section>`,
26
+ });
64
27
  ```
65
28
 
66
- Corrigez en rendant le composant route/page Light DOM :
29
+ Les tables, forms, prose et états simples de page sont stylés par
30
+ `src/shared/styles/content.css`.
31
+
32
+ Utilisez Shadow DOM components pour les widgets feuilles :
33
+
34
+ - buttons, badges, cards, metrics ;
35
+ - spinners, modals, toasts ;
36
+ - widgets qui doivent posséder leur CSS.
67
37
 
68
38
  ```ts
69
- component("x-dashboard", () => () => html`
70
- <header class="page-head">...</header>
71
- <div class="metric-grid">...</div>
72
- `, {
73
- shadow: false,
39
+ component("x-status-badge", ({ attr }) => {
40
+ const status = attr("status", "draft");
41
+ return () => html`<span>${status}</span>`;
42
+ }, {
74
43
  styles: css`
75
- x-dashboard { display: block; }
76
- x-dashboard .panel { padding: 1rem; }
44
+ :host { display: inline-block; }
45
+ span { color: var(--color-text-muted); }
77
46
  `,
78
47
  });
79
48
  ```
80
49
 
81
- Maintenant les utilitaires globaux et les styles locaux scopés fonctionnent tous les deux.
82
-
83
50
  ## Comment les styles se comportent
84
51
 
85
- - `styles: css\`\`` en Shadow DOM est adopté dans le shadowRoot du composant.
86
- - `styles: css\`\`` avec `shadow: false` est scopé au nom du tag et adopté globalement.
87
- - Les propriétés CSS personnalisées (`--accent`, `--bg`, etc.) traversent les frontières Shadow DOM.
88
- - Les sélecteurs de classe comme `.btn`, `.form-grid`, `.page-head` ne traversent **pas** les
89
- frontières Shadow DOM.
90
- - Les enfants slottés conservent leurs propres styles de document ; le composant shadow ne peut
91
- les cibler qu'à travers `::slotted(...)`.
92
- - `<slot>` projette les enfants uniquement en Shadow DOM. Dans un composant `shadow: false`,
93
- c'est juste un élément `<slot>` normal et ne déplacera pas les enfants à cet endroit dans
94
- votre layout.
95
-
96
- ## Forme recommandée de l'application
97
-
98
- ```ts
99
- // racine et pages : Light DOM
100
- component("x-app", setup, { shadow: false });
101
- component("x-users-page", setup, { shadow: false });
102
-
103
- // layout basé sur slot : Shadow DOM par défaut, car il possède la grille du shell
104
- component("x-app-layout", setup);
105
-
106
- // widgets feuilles : Shadow DOM par défaut
107
- component("x-status-badge", setup);
108
- component("x-stat-card", setup);
109
- component("x-toast-stack", setup);
110
- ```
111
-
112
- Cela donne aux écrans d'admin backend un CSS prévisible tout en préservant l'encapsulation
113
- pour les widgets réutilisables et les shells basés sur slot.
114
-
115
- Le modèle d'import est volontairement natif au navigateur :
116
-
117
- ```ts
118
- import "./components/app-layout.js";
119
-
120
- render(html`<x-app-layout>${router.view}</x-app-layout>`, app);
121
- ```
122
-
123
- L'import enregistre le custom element avec `customElements.define()`. Le template
124
- crée un élément `<x-app-layout>`. Le navigateur relie les deux. Il n'y a pas de
125
- valeur de composant à la React que l'on passe comme fonction.
126
-
127
- Si un layout n'a pas besoin de projection slot et doit être entièrement stylé par du CSS
128
- global, `shadow: false` peut rester un bon choix. S'il contient `<slot>`, gardez Shadow DOM
129
- et mettez les styles du shell dans `styles: css\`\``.
130
-
131
- ## Routage et liens
132
-
133
- `data-link` fonctionne à l'intérieur de Shadow DOM. Le router utilise `event.composedPath()`,
134
- donc l'interception de clic et le hover-prefetch peuvent voir les liens depuis les shadow roots
135
- ouverts.
136
-
137
- ```ts
138
- component("x-card-link", () => () => html`
139
- <a href="/app/accounts" data-link>Comptes</a>
140
- `);
141
- ```
142
-
143
- Le lien peut être en Shadow DOM ; la navigation reste SPA.
144
-
145
- ## Où importer les composants
146
-
147
- Les custom elements sont globaux après leur enregistrement, mais cet
148
- enregistrement reste un import JavaScript explicite.
149
-
150
- ```ts
151
- // main.ts : frame global de l'app
152
- import "./components/app-shell.js";
153
-
154
- // pages/tickets.ts : composant possédé par cette page
155
- import "../components/ticket-list.js";
156
- ```
157
-
158
- Le navigateur ne télécharge **pas** `ticket-list.js` simplement parce qu'il voit
159
- `<ticket-list>`. Le fichier doit d'abord être importé quelque part. Une fois
160
- importé, il appelle `customElements.define(...)`, et le tag devient connu dans
161
- le document courant.
162
-
163
- N'importez pas tous les composants en masse dans `main.ts` "au cas où". Cela
164
- fonctionne pour de petites démos, mais cache l'ownership et casse le chargement
165
- paresseux des routes. Préférez :
166
-
167
- - app shell/providers globaux dans `main.ts` ;
168
- - composants utilisés par une seule page dans ce fichier page ;
169
- - composants partagés d'une feature dans la page d'entrée de cette feature ;
170
- - petits leaf components vraiment globaux dans `main.ts` seulement s'ils sont
171
- utilisés partout.
172
-
173
- ## Leçon du Showcase
174
-
175
- `examples/showcase` utilise cette séparation délibérément :
176
-
177
- - `x-app` et les pages de route CRM sont Light DOM ;
178
- - `x-app-layout` garde Shadow DOM car il possède un shell sidebar/contenu basé sur slot ;
179
- - les utilitaires de tableau/formulaire/page vivent dans `styles/global.ts` ;
180
- - les composants feuilles comme `x-stat-card`, `x-status-badge`, `x-modal` et `x-toast-stack`
181
- gardent Shadow DOM.
182
-
183
- Si une page a soudainement l'air sans style, vérifiez si elle utilise des classes globales
184
- à l'intérieur d'un composant Shadow DOM. C'est généralement le problème.
52
+ - `tokens.css` définit des CSS custom properties ; `var(...)` traverse Shadow
53
+ DOM.
54
+ - `reset.css`, `shell.css`, `content.css` s'appliquent seulement au document /
55
+ light DOM.
56
+ - Les sélecteurs de classe comme `.data`, `.app-main`, `.error` ne traversent
57
+ pas Shadow DOM.
58
+ - Les styles locaux des composants vivent dans ``css`...` `` dans les options de
59
+ `component()`.
60
+ - Si un Shadow component accepte des enfants, utilisez `<slot>` et stylisez le
61
+ frame dans les styles du composant.
62
+
63
+ Si une page semble sans style, vous avez probablement utilisé des classes
64
+ globales dans un Shadow DOM component. Déplacez le markup dans une page/layout
65
+ ou déplacez le CSS dans les styles du composant.
@@ -1,61 +1,138 @@
1
1
  # Architecture d'application
2
2
 
3
- La forme recommandée d'une app Mado en production est volontairement simple :
4
- un manifeste de routes, un shell, un client API, un module auth, et des pages
5
- qui importent leurs propres composants.
3
+ Le starter officiel est la forme canonique d'une application Mado en
4
+ production. Ce n'est pas un framework dans le framework : seulement des
5
+ fichiers, des imports, les primitives Mado et des limites ESLint.
6
6
 
7
7
  ## Structure
8
8
 
9
9
  ```txt
10
10
  src/
11
11
  ├── main.ts
12
- ├── routes.ts
12
+ ├── app.routes.ts
13
13
  ├── layouts/
14
- ├── pages/
15
- ├── components/
16
- ├── lib/
17
- └── styles/
14
+ ├── app-shell.layout.ts
15
+ │ └── auth-shell.layout.ts
16
+ ├── shared/
17
+ │ ├── http/
18
+ │ ├── lib/
19
+ │ ├── styles/
20
+ │ └── ui/
21
+ └── modules/
22
+ ├── auth/
23
+ │ ├── auth.routes.ts
24
+ │ ├── auth.public.ts
25
+ │ ├── auth.service.ts
26
+ │ ├── auth.connector.ts
27
+ │ ├── auth.guard.ts
28
+ │ ├── login.page.ts
29
+ │ └── _contracts/
30
+ └── billing/
31
+ ├── billing.routes.ts
32
+ ├── billing.public.ts
33
+ ├── billing.types.ts
34
+ ├── api/
35
+ ├── data/
36
+ ├── pages/
37
+ ├── components/
38
+ └── _contracts/
18
39
  ```
19
40
 
20
- `lib/` contient la logique métier, `layouts/` enveloppe les groupes de routes,
21
- `components/` contient les tags réutilisables, et `pages/` contient un fichier
22
- par page.
41
+ ## App Map
23
42
 
24
- ```ts
25
- import { html, render } from "@madojs/mado";
26
- import "./styles/global.js";
27
- import routesApi from "./routes.js";
28
-
29
- render(html`${routesApi.view}`, document.getElementById("app")!);
30
- ```
31
-
32
- N'importe pas tous les composants dans `main.ts`. Une page importe les
33
- composants qu'elle rend.
43
+ `src/app.routes.ts` est la carte de toute l'application. Les modules exportent
44
+ des route maps simples ; les app routes décident quel shell et quel guard
45
+ enveloppent chaque zone.
34
46
 
35
47
  ```ts
36
48
  import { layout, routes } from "@madojs/mado";
37
- import { requireAuth } from "./lib/auth.js";
49
+ import { requireAuth } from "./modules/auth/auth.public";
50
+ import { authRoutes } from "./modules/auth/auth.routes";
51
+ import { billingRoutes } from "./modules/billing/billing.routes";
38
52
 
39
53
  export const manifest = {
40
- "/": () => import("./pages/home.js"),
41
- "/admin": layout({
42
- layout: () => import("./layouts/app.js"),
54
+ "/": () => import("./modules/home/home.page"),
55
+ "/login": layout({
56
+ layout: () => import("./layouts/auth-shell.layout"),
57
+ routes: authRoutes,
58
+ }),
59
+ "/billing": layout({
60
+ layout: () => import("./layouts/app-shell.layout"),
43
61
  guard: requireAuth,
44
- routes: { "/": () => import("./pages/admin/dashboard.js") },
62
+ routes: billingRoutes,
45
63
  }),
46
- "*": () => import("./pages/not-found.js"),
64
+ "*": () => import("./modules/home/not-found.page"),
47
65
  };
48
66
 
49
67
  export default routes(manifest);
50
68
  ```
51
69
 
52
- Exporte `manifest` pour `mado bake`. Utilise `resource()` pour les lectures,
53
- `mutation(..., { invalidates })` pour les écritures, et `useForm()` pour les
54
- workflows utilisateur.
70
+ Rules:
71
+
72
+ - Export `manifest` pour `mado bake`.
73
+ - Les modules n'appellent jamais `layout()`.
74
+ - Les layouts décrivent des zones d'application, pas des domaines.
75
+ - Ne cachez pas le router dans un custom element ou un second shell dans
76
+ `main.ts`.
77
+
78
+ ## File Forms
79
+
80
+ | Suffix | Role |
81
+ | --- | --- |
82
+ | `*.page.ts` | route page, default `page({...})` |
83
+ | `*.layout.ts` | app-zone wrapper, default `page(...)` |
84
+ | `*.connector.ts` | one external API system |
85
+ | `*.resource.ts` | `resource()` and `mutation()` layer |
86
+ | `*.service.ts` | module singleton state |
87
+ | `*.guard.ts` | route guard |
88
+ | `*.routes.ts` | module-local route map |
89
+ | `*.public.ts` | only public module surface |
90
+ | `*.types.ts` | domain types |
91
+ | `*.component.ts` | Web Component registration |
92
+
93
+ Les signals, resources et forms locaux à une page vivent dans `view()`. L'état
94
+ partagé par un module vit dans `*.service.ts`.
95
+
96
+ ## Data Flow
97
+
98
+ ```txt
99
+ shared/http/http-client.ts
100
+
101
+ modules/<x>/api/*.connector.ts DTO -> domain mapping
102
+
103
+ modules/<x>/data/*.resource.ts cache keys + mutations
104
+
105
+ modules/<x>/pages/*.page.ts UI consumes domain types
106
+ ```
107
+
108
+ ## Styles
109
+
110
+ | File | Role |
111
+ | --- | --- |
112
+ | `src/shared/styles/tokens.css` | design tokens as CSS custom properties |
113
+ | `src/shared/styles/reset.css` | document/light DOM reset |
114
+ | `src/shared/styles/shell.css` | app-zone layouts from `src/layouts/` |
115
+ | `src/shared/styles/content.css` | page-level forms, tables, prose and states |
116
+
117
+ Les composants feuilles gardent leurs styles dans ``css`...` `` dans les
118
+ options de `component()` et dépendent des tokens, pas de classes globales.
119
+
120
+ `vite.config.ts` active le transformer Lightning CSS de Vite. Mado ne possède
121
+ pas le prefixing, le lowering CSS ou la minification.
122
+
123
+ ## CLI
55
124
 
56
125
  ```bash
57
- mado dev
58
- mado release
126
+ mado new module billing
127
+ mado new page billing/pages/invoices-list
128
+ mado new connector billing/api/stripe
129
+ mado new resource billing/data/invoices
130
+ mado new service billing/cart
131
+ mado new form billing/invoice
132
+ mado new component billing/components/invoice-status-badge
133
+ mado new guard billing/billing
134
+ mado new layout app-shell
59
135
  ```
60
136
 
61
- Le dossier déployable est `out/`. `dist/` est interne.
137
+ Le générateur écrit seulement de nouveaux fichiers. Il ne modifie pas
138
+ `app.routes.ts`.
@@ -1,23 +1,27 @@
1
1
  # Layouts
2
2
 
3
- Le chemin recommandé pour les layouts Mado est un groupe de routes imbriqué
4
- dans `routes.ts`.
3
+ Le chemin recommandé pour les layouts Mado est un route group dans
4
+ `src/app.routes.ts`. Ne mettez pas un shell global dans `main.ts` si l'app a
5
+ plusieurs zones : public, auth, app, embed.
5
6
 
6
7
  ```ts
7
8
  import { layout, routes } from "@madojs/mado";
8
- import { requireAuth } from "./lib/auth.js";
9
+ import { requireAuth } from "./modules/auth/auth.public";
10
+ import { authRoutes } from "./modules/auth/auth.routes";
11
+ import { billingRoutes } from "./modules/billing/billing.routes";
9
12
 
10
13
  export const manifest = {
11
- "/": () => import("./pages/home.js"),
14
+ "/": () => import("./modules/home/home.page.js"),
12
15
  "/login": layout({
13
- layout: () => import("./layouts/auth.js"),
14
- routes: { "/": () => import("./pages/login.js") },
16
+ layout: () => import("./layouts/auth-shell.layout.js"),
17
+ routes: authRoutes,
15
18
  }),
16
- "/admin": layout({
17
- layout: () => import("./layouts/app.js"),
19
+ "/billing": layout({
20
+ layout: () => import("./layouts/app-shell.layout.js"),
18
21
  guard: requireAuth,
19
- routes: { "/": () => import("./pages/admin/dashboard.js") },
22
+ routes: billingRoutes,
20
23
  }),
24
+ "*": () => import("./modules/home/not-found.page.js"),
21
25
  };
22
26
 
23
27
  export default routes(manifest);
@@ -27,9 +31,17 @@ Un layout est une `page({ view })` qui rend `child` :
27
31
 
28
32
  ```ts
29
33
  export default page({
30
- view: ({ child }) => html`<x-app-shell>${child}</x-app-shell>`,
34
+ view: ({ child }) => html`
35
+ <div class="layout layout--app">
36
+ <main class="app-main">${child}</main>
37
+ </div>
38
+ `,
31
39
  });
32
40
  ```
33
41
 
34
- Règles : un shell par groupe, pas par page ; les layouts externes enveloppent
35
- les layouts internes ; un guard sur le groupe protège tout le sous-arbre.
42
+ Rules:
43
+
44
+ - one shell per route group, not per page;
45
+ - modules export plain route maps and do not call `layout()`;
46
+ - a guard on the group protects the whole subtree;
47
+ - layout view stays stateless; page-local state lives in pages/components/resources.