@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.
- package/AGENTS.md +102 -25
- package/CHANGELOG.md +158 -1
- package/README.md +135 -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/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 -569
- 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,625 +0,0 @@
|
|
|
1
|
-
# Mado · Pièges LLM
|
|
2
|
-
|
|
3
|
-
> Erreurs typiques que les assistants IA (Copilot, Claude, ChatGPT, Cursor)
|
|
4
|
-
> commettent lors de la génération de code Mado. Et comment les corriger.
|
|
5
|
-
|
|
6
|
-
Ce document s'adresse à **deux publics** :
|
|
7
|
-
|
|
8
|
-
1. **Les agents IA dans l'IDE** qui lisent `AGENTS.md` / `.cursorrules` / `.github/copilot-instructions.md`. Plus de détails sur les pièges typiques sont fournis ici.
|
|
9
|
-
2. **Les humains** qui ont reçu du code d'une IA avec ces erreurs et ne comprennent pas ce qui ne va pas.
|
|
10
|
-
|
|
11
|
-
---
|
|
12
|
-
|
|
13
|
-
## Piège #1 : `${signal()}` au lieu de `${() => signal()}`
|
|
14
|
-
|
|
15
|
-
**Symptôme :** la valeur dans le template s'affiche mais ne se met pas à jour quand le signal change.
|
|
16
|
-
|
|
17
|
-
```ts
|
|
18
|
-
const count = signal(0);
|
|
19
|
-
|
|
20
|
-
// ❌ L'IA génère souvent ceci
|
|
21
|
-
html`<div>Compte : ${count() * 2}</div>`;
|
|
22
|
-
// → Affichera "Compte : 0" et ne se mettra plus jamais à jour.
|
|
23
|
-
// count() est lu une seule fois quand le TemplateResult est créé.
|
|
24
|
-
|
|
25
|
-
// ✅ Correct — fonction getter
|
|
26
|
-
html`<div>Compte : ${() => count() * 2}</div>`;
|
|
27
|
-
// → Mado créera un effect() pour cette fonction et re-rendra quand count change.
|
|
28
|
-
|
|
29
|
-
// ✅ Aussi correct — le signal lui-même est une fonction
|
|
30
|
-
html`<div>Compte : ${count}</div>`;
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
**Règle :**
|
|
34
|
-
|
|
35
|
-
- Si `${...}` contient une **expression** (quelque chose est fait avec le signal) — enveloppez dans `() => ...`.
|
|
36
|
-
- Si `${...}` contient **le signal lui-même** — il peut être utilisé tel quel.
|
|
37
|
-
|
|
38
|
-
Ceci s'applique aux **bindings enfants** (texte à l'intérieur des tags) et aux **attributs de valeur** (`@click`, `.prop`, `?attr`, attributs ordinaires).
|
|
39
|
-
|
|
40
|
-
---
|
|
41
|
-
|
|
42
|
-
## Piège #2 : `<button disabled=${loading}>` au lieu de `?disabled`
|
|
43
|
-
|
|
44
|
-
**Symptôme :** le bouton n'est pas désactivé, ou est toujours désactivé.
|
|
45
|
-
|
|
46
|
-
```ts
|
|
47
|
-
const loading = signal(false);
|
|
48
|
-
|
|
49
|
-
// ❌ C'est setAttribute("disabled", "false") — le DOM traite ça comme disabled
|
|
50
|
-
html`<button disabled=${loading()}>Enregistrer</button>`;
|
|
51
|
-
|
|
52
|
-
// ✅ Correct — binding booléen (basculer l'attribut)
|
|
53
|
-
html`<button ?disabled=${loading}>Enregistrer</button>`;
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
**Règles pour les attributs :**
|
|
57
|
-
| Préfixe | Ce qu'il fait | Quand utiliser |
|
|
58
|
-
|---|---|---|
|
|
59
|
-
| `attr=` | `setAttribute("attr", value)` | strings, nombres, URLs |
|
|
60
|
-
| `.attr=` | `el.attr = value` (propriété DOM) | objets, tableaux, `.value` d'input |
|
|
61
|
-
| `?attr=` | basculer l'attribut (par vérité) | `disabled`, `hidden`, `checked`, etc. |
|
|
62
|
-
| `@evt=` | `addEventListener("evt", fn)` | gestionnaires d'événements |
|
|
63
|
-
|
|
64
|
-
---
|
|
65
|
-
|
|
66
|
-
## Piège #3 : style useState / useEffect
|
|
67
|
-
|
|
68
|
-
**Symptôme :** l'IA génère du code de style React qui ne fonctionne pas dans Mado.
|
|
69
|
-
|
|
70
|
-
```ts
|
|
71
|
-
// ❌ L'IA écrit souvent ceci
|
|
72
|
-
function Counter() {
|
|
73
|
-
const [count, setCount] = useState(0);
|
|
74
|
-
useEffect(() => { console.log(count); }, [count]);
|
|
75
|
-
return <button onClick={() => setCount(c => c + 1)}>{count}</button>;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// ✅ Correct dans Mado
|
|
79
|
-
import { component, signal, effect, html } from "@madojs/mado";
|
|
80
|
-
|
|
81
|
-
component("x-counter", () => {
|
|
82
|
-
const count = signal(0);
|
|
83
|
-
effect(() => console.log(count())); // auto-abonnement, disposé automatiquement
|
|
84
|
-
return () => html`
|
|
85
|
-
<button @click=${() => count.update(c => c + 1)}>${count}</button>
|
|
86
|
-
`;
|
|
87
|
-
});
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
**Différences clés :**
|
|
91
|
-
|
|
92
|
-
- Pas de hooks, pas de règles de hooks.
|
|
93
|
-
- `signal()` peut être créé n'importe où — dans le setup, dans un effect, dans un handler.
|
|
94
|
-
- `effect()` voit ce qu'il a lu de lui-même — pas besoin de tableau de dépendances.
|
|
95
|
-
- Un composant est `component("x-name", setup)`, pas une fonction JSX.
|
|
96
|
-
|
|
97
|
-
---
|
|
98
|
-
|
|
99
|
-
## Piège #4 : `useEffect(() => { ... return cleanup })`
|
|
100
|
-
|
|
101
|
-
**Symptôme :** l'IA écrit `return cleanup` à l'intérieur d'un effect, attendant que ça fonctionne comme dans React.
|
|
102
|
-
|
|
103
|
-
```ts
|
|
104
|
-
// ❌ L'IA essaie d'écrire ceci
|
|
105
|
-
component("x-timer", () => {
|
|
106
|
-
effect(() => {
|
|
107
|
-
const id = setInterval(..., 1000);
|
|
108
|
-
return () => clearInterval(id); // NE fonctionnera PAS, utilisez ctx.onDispose à la place
|
|
109
|
-
});
|
|
110
|
-
return () => html`...`;
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
// ✅ Correct : nettoyage via ctx.onDispose
|
|
114
|
-
component("x-timer", (ctx) => {
|
|
115
|
-
const id = setInterval(..., 1000);
|
|
116
|
-
ctx.onDispose(() => clearInterval(id));
|
|
117
|
-
return () => html`...`;
|
|
118
|
-
});
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
**Note :** `effect()` supporte bien `return cleanup`, mais c'est un **nettoyage par exécution** (s'exécute avant la prochaine exécution de l'effect), pas un nettoyage au démontage. Pour le nettoyage au démontage, utilisez `ctx.onDispose`.
|
|
122
|
-
|
|
123
|
-
---
|
|
124
|
-
|
|
125
|
-
## Piège #5 : Composant comme classe ou avec un décorateur
|
|
126
|
-
|
|
127
|
-
**Symptôme :** l'IA génère une classe de style Lit ou vanilla WebComponent.
|
|
128
|
-
|
|
129
|
-
```ts
|
|
130
|
-
// ❌ IA : "faisons comme Lit"
|
|
131
|
-
import { LitElement, html } from "lit";
|
|
132
|
-
import { customElement, property } from "lit/decorators.js";
|
|
133
|
-
|
|
134
|
-
@customElement('x-counter')
|
|
135
|
-
class XCounter extends LitElement { ... }
|
|
136
|
-
|
|
137
|
-
// ❌ IA : "faisons en style vanilla"
|
|
138
|
-
class XCounter extends HTMLElement {
|
|
139
|
-
connectedCallback() { ... }
|
|
140
|
-
}
|
|
141
|
-
customElements.define("x-counter", XCounter);
|
|
142
|
-
|
|
143
|
-
// ✅ Correct : component() fonctionnel
|
|
144
|
-
import { component, html, signal } from "@madojs/mado";
|
|
145
|
-
|
|
146
|
-
component("x-counter", () => {
|
|
147
|
-
const count = signal(0);
|
|
148
|
-
return () => html`<button @click=${() => count.update(n => n + 1)}>${count}</button>`;
|
|
149
|
-
});
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
---
|
|
153
|
-
|
|
154
|
-
## Piège #6 : imports sans l'extension `.js`
|
|
155
|
-
|
|
156
|
-
**Symptôme :** TypeScript compile, mais le navigateur obtient une 404.
|
|
157
|
-
|
|
158
|
-
```ts
|
|
159
|
-
// ❌ L'IA omet souvent l'extension
|
|
160
|
-
import { foo } from "./bar";
|
|
161
|
-
import { Home } from "./pages/home";
|
|
162
|
-
|
|
163
|
-
// ✅ Correct : les modules ES dans le navigateur nécessitent l'extension
|
|
164
|
-
import { foo } from "./bar.js";
|
|
165
|
-
import { Home } from "./pages/home.js";
|
|
166
|
-
```
|
|
167
|
-
|
|
168
|
-
**Pourquoi `.js` et pas `.ts` :** le navigateur reçoit du JS déjà compilé. TypeScript est assez intelligent pour comprendre `./bar.js` comme une référence à `./bar.ts` à la compilation.
|
|
169
|
-
|
|
170
|
-
---
|
|
171
|
-
|
|
172
|
-
## Piège #7 : listes via `.map()` sans clés
|
|
173
|
-
|
|
174
|
-
**Symptôme :** lors du réordonnancement des éléments, le focus d'input est perdu / les animations CSS se cassent / les performances souffrent sur les grandes listes.
|
|
175
|
-
|
|
176
|
-
```ts
|
|
177
|
-
// ❌ Fonctionne, mais sans clé : recrée le DOM à chaque changement
|
|
178
|
-
html`<ul>
|
|
179
|
-
${() => items().map((t) => html`<li>${t.name}</li>`)}
|
|
180
|
-
</ul>`;
|
|
181
|
-
|
|
182
|
-
// ✅ Correct : each() avec une fonction de clé
|
|
183
|
-
import { each } from "@madojs/mado";
|
|
184
|
-
html`<ul>
|
|
185
|
-
${() =>
|
|
186
|
-
each(
|
|
187
|
-
items(),
|
|
188
|
-
(t) => t.id,
|
|
189
|
-
(t) => html`<li>${t.name}</li>`,
|
|
190
|
-
)}
|
|
191
|
-
</ul>`;
|
|
192
|
-
```
|
|
193
|
-
|
|
194
|
-
**Règle :** utilisez toujours `each()` pour les listes de tableaux avec des IDs stables. Réservez `.map()` uniquement pour les listes statiques.
|
|
195
|
-
|
|
196
|
-
---
|
|
197
|
-
|
|
198
|
-
## Piège #8 : `signal.value` ou `count.get()`
|
|
199
|
-
|
|
200
|
-
**Symptôme :** l'IA écrit une API de style Vue ou Solid pre-v1.
|
|
201
|
-
|
|
202
|
-
```ts
|
|
203
|
-
const count = signal(0);
|
|
204
|
-
|
|
205
|
-
// ❌ Pas une telle API
|
|
206
|
-
count.value;
|
|
207
|
-
count.value = 5;
|
|
208
|
-
count.get();
|
|
209
|
-
|
|
210
|
-
// ✅ Correct
|
|
211
|
-
count(); // lecture
|
|
212
|
-
count.set(5); // écriture
|
|
213
|
-
count.update((n) => n + 1);
|
|
214
|
-
count.peek(); // lecture sans abonnement
|
|
215
|
-
```
|
|
216
|
-
|
|
217
|
-
---
|
|
218
|
-
|
|
219
|
-
## Piège #9 : `provide(ApiCtx, value)` sans host
|
|
220
|
-
|
|
221
|
-
**Symptôme :** TypeError lors de la tentative de fournir le context.
|
|
222
|
-
|
|
223
|
-
```ts
|
|
224
|
-
// ❌ L'IA oublie host
|
|
225
|
-
provide(ApiCtx, myApi);
|
|
226
|
-
inject(ApiCtx);
|
|
227
|
-
|
|
228
|
-
// ✅ Correct : le premier argument est host (le composant courant)
|
|
229
|
-
component("x-app", ({ host }) => {
|
|
230
|
-
provide(host, ApiCtx, myApi);
|
|
231
|
-
return () => html`...`;
|
|
232
|
-
});
|
|
233
|
-
|
|
234
|
-
component("x-child", ({ host }) => {
|
|
235
|
-
const api = inject(host, ApiCtx); // signal<valeur>
|
|
236
|
-
return () => html`...`;
|
|
237
|
-
});
|
|
238
|
-
```
|
|
239
|
-
|
|
240
|
-
---
|
|
241
|
-
|
|
242
|
-
## Piège #10 : attendre du SSR
|
|
243
|
-
|
|
244
|
-
**Symptôme :** l'IA écrit du code en supposant que la page est pré-rendue côté serveur.
|
|
245
|
-
|
|
246
|
-
```ts
|
|
247
|
-
// ❌ Ceci ne fonctionne que dans le navigateur
|
|
248
|
-
const userId = location.pathname.split("/")[2];
|
|
249
|
-
|
|
250
|
-
// ❌ Ceci aussi ne fonctionne que dans le navigateur
|
|
251
|
-
if (typeof window !== "undefined") { ... } // dans Mado, window est TOUJOURS disponible
|
|
252
|
-
```
|
|
253
|
-
|
|
254
|
-
Mado **ne fait pas de SSR avec hydratation**. Le code ne s'exécute pas sur le serveur — il y a uniquement `bake` (prérendu statique au moment du build) et edge-prerender. Les deux remplacent le code utilisateur par un environnement linkedom, mais c'est **uniquement** pour générer du HTML avec des meta tags, pas pour exécuter la logique de page.
|
|
255
|
-
|
|
256
|
-
Cela signifie :
|
|
257
|
-
|
|
258
|
-
- ✅ `window`, `document`, `location`, `fetch` — disponibles sans vérifications.
|
|
259
|
-
- ❌ N'écrivez pas de code qui essaie de "fonctionner universellement sur serveur et client".
|
|
260
|
-
- ❌ N'utilisez pas les patterns Next.js (`getServerSideProps`, `headers()`).
|
|
261
|
-
|
|
262
|
-
---
|
|
263
|
-
|
|
264
|
-
## Piège #11 : `useForm()` avec un résolveur zod/yup
|
|
265
|
-
|
|
266
|
-
**Symptôme :** l'IA veut brancher un validateur.
|
|
267
|
-
|
|
268
|
-
```ts
|
|
269
|
-
// ❌ Pas une telle API
|
|
270
|
-
const f = useForm({ resolver: zodResolver(schema) });
|
|
271
|
-
|
|
272
|
-
// ✅ Correct : validation proche du HTML via le schéma useForm
|
|
273
|
-
const f = useForm({
|
|
274
|
-
email: { required: true, type: "email" },
|
|
275
|
-
age: { required: true, type: "number", min: 18 },
|
|
276
|
-
});
|
|
277
|
-
|
|
278
|
-
// ✅ Ou une fonction personnalisée si HTML5 ne suffit pas
|
|
279
|
-
const f = useForm(
|
|
280
|
-
{ name: { required: true } },
|
|
281
|
-
{
|
|
282
|
-
validate: (values) => {
|
|
283
|
-
const errors: Record<string, string> = {};
|
|
284
|
-
if (values.name && /\d/.test(values.name as string)) {
|
|
285
|
-
errors.name = "Le nom ne doit pas contenir de chiffres";
|
|
286
|
-
}
|
|
287
|
-
return Object.keys(errors).length ? errors : null;
|
|
288
|
-
},
|
|
289
|
-
},
|
|
290
|
-
);
|
|
291
|
-
```
|
|
292
|
-
|
|
293
|
-
---
|
|
294
|
-
|
|
295
|
-
## Piège #12 : Tailwind / styled-components / CSS Modules
|
|
296
|
-
|
|
297
|
-
**Symptôme :** l'IA suggère des solutions CSS React standard.
|
|
298
|
-
|
|
299
|
-
Mado utilise **Shadow DOM + `css\`\`` + variables CSS**. Les frameworks UI globaux (Tailwind,
|
|
300
|
-
Bootstrap-via-classes) **ne fonctionnent qu'en light DOM** (`shadow: false`) :
|
|
301
|
-
|
|
302
|
-
```ts
|
|
303
|
-
// Composant page/écran Light-DOM, les classes Tailwind fonctionnent
|
|
304
|
-
component(
|
|
305
|
-
"x-admin-page",
|
|
306
|
-
() => () => html`
|
|
307
|
-
<section class="bg-white shadow-lg rounded-lg p-4">...</section>
|
|
308
|
-
`,
|
|
309
|
-
{ shadow: false },
|
|
310
|
-
);
|
|
311
|
-
|
|
312
|
-
// Composant Shadow-DOM (par défaut) — Tailwind ne fonctionne PAS.
|
|
313
|
-
// Utilisez css`` ou ::part() pour le stylage externe.
|
|
314
|
-
component("x-button", () => () => html`<button><slot></slot></button>`, {
|
|
315
|
-
styles: css`
|
|
316
|
-
button {
|
|
317
|
-
background: var(--button-bg, #2563eb);
|
|
318
|
-
color: white;
|
|
319
|
-
padding: 0.5rem 1rem;
|
|
320
|
-
border-radius: 6px;
|
|
321
|
-
}
|
|
322
|
-
`,
|
|
323
|
-
});
|
|
324
|
-
```
|
|
325
|
-
|
|
326
|
-
**Thèmes et personnalisation — via des variables CSS**, pas des classes.
|
|
327
|
-
|
|
328
|
-
---
|
|
329
|
-
|
|
330
|
-
## Piège #13 : `import * as Mado from "@madojs/mado"`
|
|
331
|
-
|
|
332
|
-
**Symptôme :** l'IA veut un import de namespace.
|
|
333
|
-
|
|
334
|
-
Cela fonctionne, mais duplique les noms et se tree-shake mal. Les imports nommés sont préférés :
|
|
335
|
-
|
|
336
|
-
```ts
|
|
337
|
-
// ✅ Canonique
|
|
338
|
-
import { signal, html, component, css, page } from "@madojs/mado";
|
|
339
|
-
|
|
340
|
-
// ⚠️ Fonctionne, mais excessif
|
|
341
|
-
import * as Mado from "@madojs/mado";
|
|
342
|
-
Mado.signal(0);
|
|
343
|
-
```
|
|
344
|
-
|
|
345
|
-
---
|
|
346
|
-
|
|
347
|
-
## Piège #14 : tentative d'ajouter une dépendance runtime
|
|
348
|
-
|
|
349
|
-
**Symptôme :** l'IA suggère `npm install lodash` / `npm install date-fns` / etc.
|
|
350
|
-
|
|
351
|
-
Mado est **zéro dépendances runtime** par conception. Si l'IA veut ajouter :
|
|
352
|
-
|
|
353
|
-
- **lodash** → utilisez du JS natif (`Object.entries`, `Array.prototype`, `structuredClone`) ;
|
|
354
|
-
- **date-fns** → utilisez `Intl.DateTimeFormat` et `Intl.RelativeTimeFormat` ;
|
|
355
|
-
- **uuid** → `crypto.randomUUID()` ;
|
|
356
|
-
- **axios** → `fetch` natif + `jsonFetcher()` de Mado ;
|
|
357
|
-
- **classnames** → template literal natif ou une map d'objet.
|
|
358
|
-
|
|
359
|
-
Toute dépendance runtime est une **violation des principes du framework**. Si vous ne pouvez vraiment pas l'éviter — ajoutez-la au projet utilisateur, pas au cœur de Mado.
|
|
360
|
-
|
|
361
|
-
---
|
|
362
|
-
|
|
363
|
-
## Piège #15 : `<style>` inline dans les templates de page
|
|
364
|
-
|
|
365
|
-
**Symptôme :** l'IA met un grand `<style>` directement dans une page `html\`\``.
|
|
366
|
-
|
|
367
|
-
```ts
|
|
368
|
-
// ❌ Fonctionne, mais se met à l'échelle difficilement et complique le nettoyage
|
|
369
|
-
page({
|
|
370
|
-
view: () => html`
|
|
371
|
-
<style>
|
|
372
|
-
.panel {
|
|
373
|
-
padding: 1rem;
|
|
374
|
-
}
|
|
375
|
-
</style>
|
|
376
|
-
<section class="panel">...</section>
|
|
377
|
-
`,
|
|
378
|
-
});
|
|
379
|
-
|
|
380
|
-
// ✅ Correct : styles de composant via css``
|
|
381
|
-
component(
|
|
382
|
-
"x-admin-panel",
|
|
383
|
-
() => () => html` <section class="panel">...</section> `,
|
|
384
|
-
{
|
|
385
|
-
styles: css`
|
|
386
|
-
.panel {
|
|
387
|
-
padding: 1rem;
|
|
388
|
-
}
|
|
389
|
-
`,
|
|
390
|
-
},
|
|
391
|
-
);
|
|
392
|
-
```
|
|
393
|
-
|
|
394
|
-
Pour les écrans route/page d'admin backend, il est souvent approprié d'utiliser `shadow: false`,
|
|
395
|
-
afin que les utilitaires globaux de layout/form/table fonctionnent comme un panneau d'admin
|
|
396
|
-
ordinaire. Mais si le layout utilise `<slot>` pour projeter la page dans le shell, gardez le
|
|
397
|
-
layout en Shadow DOM et gardez les styles du shell dans `styles: css\`\``.
|
|
398
|
-
|
|
399
|
-
---
|
|
400
|
-
|
|
401
|
-
## Piège #16 : liens Shadow DOM sans `data-link`
|
|
402
|
-
|
|
403
|
-
**Symptôme :** un lien à l'intérieur d'un Web Component provoque un rechargement complet de
|
|
404
|
-
la page ou n'est pas préchargé.
|
|
405
|
-
|
|
406
|
-
```ts
|
|
407
|
-
// ❌ Lien ordinaire : le navigateur effectuera un rechargement complet
|
|
408
|
-
html`<a href="/tickets/42">Ouvrir</a>`;
|
|
409
|
-
|
|
410
|
-
// ✅ Navigation SPA : router() interceptera le clic même à travers Shadow DOM
|
|
411
|
-
html`<a href="/tickets/42" data-link>Ouvrir</a>`;
|
|
412
|
-
```
|
|
413
|
-
|
|
414
|
-
Mado trouve le lien via `event.composedPath()`, donc `data-link` fonctionne aussi à l'intérieur
|
|
415
|
-
de Shadow DOM. Le hover-prefetch utilise le même chemin ; `data-no-prefetch` désactive le
|
|
416
|
-
prefetch pour un lien spécifique.
|
|
417
|
-
|
|
418
|
-
---
|
|
419
|
-
|
|
420
|
-
## Piège #17 : `resource()` en dehors du setup du composant
|
|
421
|
-
|
|
422
|
-
**Symptôme :** l'IA crée une resource dans le scope du module pour "réutiliser" des données
|
|
423
|
-
entre les pages.
|
|
424
|
-
|
|
425
|
-
```ts
|
|
426
|
-
// ❌ Pas de nettoyage du lifecycle, générera un avertissement dev
|
|
427
|
-
const tickets = resource(
|
|
428
|
-
() => "tickets",
|
|
429
|
-
() => api.listTickets(),
|
|
430
|
-
);
|
|
431
|
-
|
|
432
|
-
component("x-tickets", () => {
|
|
433
|
-
return () => html`${() => tickets.data()?.length ?? 0}`;
|
|
434
|
-
});
|
|
435
|
-
|
|
436
|
-
// ✅ Créer la resource à l'intérieur du setup du composant
|
|
437
|
-
component("x-tickets", () => {
|
|
438
|
-
const tickets = resource(
|
|
439
|
-
() => "tickets",
|
|
440
|
-
() => api.listTickets(),
|
|
441
|
-
);
|
|
442
|
-
return () => html`${() => tickets.data()?.length ?? 0}`;
|
|
443
|
-
});
|
|
444
|
-
```
|
|
445
|
-
|
|
446
|
-
Ainsi, les abonnements d'invalidation, les abort controllers et les effects seront
|
|
447
|
-
nettoyés quand le composant se déconnecte.
|
|
448
|
-
|
|
449
|
-
---
|
|
450
|
-
|
|
451
|
-
## Piège #18 : supposer que les templates imbriqués ne nécessitent pas de nettoyage
|
|
452
|
-
|
|
453
|
-
**Symptôme :** l'IA assemble un outlet de route ou une UI conditionnelle à partir de
|
|
454
|
-
`TemplateResult`s imbriqués, et les anciens éléments continuent de vivre sous la nouvelle page.
|
|
455
|
-
|
|
456
|
-
```ts
|
|
457
|
-
const view = signal(html`<x-home></x-home>`);
|
|
458
|
-
|
|
459
|
-
// ✅ Pattern normal : un TemplateResult imbriqué peut être retourné depuis un binding enfant
|
|
460
|
-
html`${view}`;
|
|
461
|
-
```
|
|
462
|
-
|
|
463
|
-
À partir de v0.3, ceci est garanti par des tests de régression : quand un binding enfant est
|
|
464
|
-
remplacé, Mado dispose récursivement les instances/effects de template imbriqués. Si vous
|
|
465
|
-
voyez des pages s'accumuler dans `#app`, c'est un bug core, pas quelque chose que vous devez
|
|
466
|
-
nettoyer manuellement.
|
|
467
|
-
|
|
468
|
-
---
|
|
469
|
-
|
|
470
|
-
## Piège #19 : utilitaires CSS globaux dans Shadow DOM
|
|
471
|
-
|
|
472
|
-
**Symptôme :** la page a l'air "sans style" : `.page-head`, `.btn`, `.form-grid`,
|
|
473
|
-
`.metric-grid` ne sont pas appliqués.
|
|
474
|
-
|
|
475
|
-
```ts
|
|
476
|
-
// ❌ .page-head est déclaré globalement, mais x-dashboard utilise Shadow DOM par défaut
|
|
477
|
-
component(
|
|
478
|
-
"x-dashboard",
|
|
479
|
-
() => () => html`
|
|
480
|
-
<header class="page-head">...</header>
|
|
481
|
-
<div class="metric-grid">...</div>
|
|
482
|
-
`,
|
|
483
|
-
);
|
|
484
|
-
|
|
485
|
-
// ✅ Les composants page/layout/admin-shell doivent souvent être Light DOM
|
|
486
|
-
component(
|
|
487
|
-
"x-dashboard",
|
|
488
|
-
() => () => html`
|
|
489
|
-
<header class="page-head">...</header>
|
|
490
|
-
<div class="metric-grid">...</div>
|
|
491
|
-
`,
|
|
492
|
-
{ shadow: false },
|
|
493
|
-
);
|
|
494
|
-
```
|
|
495
|
-
|
|
496
|
-
Règle : Shadow DOM — pour les widgets feuilles et les layouts basés sur slot, Light DOM — pour
|
|
497
|
-
les composants route/page/admin-screen qui utilisent intentionnellement des utilitaires partagés
|
|
498
|
-
de layout/form/table. Rappel : `<slot>` ne projette les enfants qu'en Shadow DOM ; avec
|
|
499
|
-
`shadow: false` c'est un élément ordinaire.
|
|
500
|
-
Plus de détails : [`09-shadow-vs-light-dom.md`](./09-shadow-vs-light-dom.md).
|
|
501
|
-
|
|
502
|
-
---
|
|
503
|
-
|
|
504
|
-
## Piège #20 : `host.getAttribute()` dans render = pas réactif
|
|
505
|
-
|
|
506
|
-
**Symptôme :** l'apparence du composant ne se met pas à jour quand le parent change un attribut.
|
|
507
|
-
|
|
508
|
-
```ts
|
|
509
|
-
// ❌ host.getAttribute() dans la fonction render est lu une seule fois.
|
|
510
|
-
// Le render ne se relance que quand ses propres signaux changent.
|
|
511
|
-
component("x-badge", ({ host }) => () => {
|
|
512
|
-
const variant = host.getAttribute("variant") ?? "default";
|
|
513
|
-
return html`<span class=${variant}>...</span>`;
|
|
514
|
-
});
|
|
515
|
-
|
|
516
|
-
// ✅ Correct : ctx.attr() — retourne un Signal<string> réactif
|
|
517
|
-
component("x-badge", ({ attr }) => {
|
|
518
|
-
const variant = attr("variant", "default");
|
|
519
|
-
return () => html`<span class=${() => `badge-${variant()}`}>...</span>`;
|
|
520
|
-
});
|
|
521
|
-
```
|
|
522
|
-
|
|
523
|
-
**Règle :** n'utilisez jamais `host.getAttribute()` ou `host.hasAttribute()` dans la
|
|
524
|
-
fonction render pour des valeurs qui peuvent changer de l'extérieur. Utilisez `ctx.attr()` —
|
|
525
|
-
il retourne un Signal qui se met à jour via `attributeChangedCallback`.
|
|
526
|
-
|
|
527
|
-
---
|
|
528
|
-
|
|
529
|
-
## Piège #21 : `<button>` Shadow DOM ne soumet pas les formulaires
|
|
530
|
-
|
|
531
|
-
**Symptôme :** cliquer sur `<x-button type="submit">` dans un `<form>` ne fait rien.
|
|
532
|
-
|
|
533
|
-
Un `<button>` dans le Shadow DOM ne participe pas à l'algorithme form-owner pour
|
|
534
|
-
`<form>` dans le Light DOM — c'est une limitation de la spécification.
|
|
535
|
-
|
|
536
|
-
```ts
|
|
537
|
-
// ❌ Le <button type="submit"> interne ne peut pas déclencher le <form> parent
|
|
538
|
-
component("x-button", ({ host }) => {
|
|
539
|
-
return () => html`<button type="submit"><slot></slot></button>`;
|
|
540
|
-
});
|
|
541
|
-
|
|
542
|
-
// ✅ Pont via requestSubmit()
|
|
543
|
-
component("x-button", ({ host, attr }) => {
|
|
544
|
-
const disabled = attr("disabled");
|
|
545
|
-
|
|
546
|
-
const handleClick = () => {
|
|
547
|
-
const typeAttr = host.getAttribute("type");
|
|
548
|
-
if (typeAttr === "button" || typeAttr === "reset") return;
|
|
549
|
-
const form = host.closest("form");
|
|
550
|
-
if (form && !host.hasAttribute("disabled")) form.requestSubmit();
|
|
551
|
-
};
|
|
552
|
-
|
|
553
|
-
return () => html`
|
|
554
|
-
<button ?disabled=${() => disabled() !== ""} @click=${handleClick}>
|
|
555
|
-
<slot></slot>
|
|
556
|
-
</button>
|
|
557
|
-
`;
|
|
558
|
-
});
|
|
559
|
-
```
|
|
560
|
-
|
|
561
|
-
Plus de détails : [`17-shadow-dom-forms.md`](./17-shadow-dom-forms.md).
|
|
562
|
-
|
|
563
|
-
---
|
|
564
|
-
|
|
565
|
-
## Piège #22 : `useForm()` avec des inputs Shadow DOM personnalisés
|
|
566
|
-
|
|
567
|
-
**Symptôme :** `form.onInput` reçoit `undefined` pour name/value de `<x-input>`.
|
|
568
|
-
|
|
569
|
-
Quand un input Shadow DOM dispatche un événement `input`, le navigateur retarget
|
|
570
|
-
`e.target` du `<input>` interne vers le host `<x-input>`. Mais `<x-input>`
|
|
571
|
-
(HTMLElement) n'a pas `.name` ni `.value` — donc `useForm` ne reçoit rien.
|
|
572
|
-
|
|
573
|
-
```ts
|
|
574
|
-
// ❌ Pas de propriétés proxy — useForm ignore silencieusement les événements
|
|
575
|
-
component("x-input", ({ host, attr }) => {
|
|
576
|
-
const name = attr("name", "");
|
|
577
|
-
return () => html`<input name=${name} />`;
|
|
578
|
-
});
|
|
579
|
-
|
|
580
|
-
// ✅ Ajouter des propriétés proxy pour la compatibilité useForm
|
|
581
|
-
component("x-input", ({ host, attr }) => {
|
|
582
|
-
const name = attr("name", "");
|
|
583
|
-
|
|
584
|
-
Object.defineProperty(host, "name", {
|
|
585
|
-
get: () => host.getAttribute("name") ?? "",
|
|
586
|
-
configurable: true,
|
|
587
|
-
});
|
|
588
|
-
Object.defineProperty(host, "value", {
|
|
589
|
-
get: () => host.shadowRoot?.querySelector("input")?.value ?? "",
|
|
590
|
-
configurable: true,
|
|
591
|
-
});
|
|
592
|
-
|
|
593
|
-
return () => html`<input name=${name} />`;
|
|
594
|
-
});
|
|
595
|
-
```
|
|
596
|
-
|
|
597
|
-
Plus de détails : [`17-shadow-dom-forms.md`](./17-shadow-dom-forms.md).
|
|
598
|
-
|
|
599
|
-
---
|
|
600
|
-
|
|
601
|
-
## Aide-mémoire pour l'IA
|
|
602
|
-
|
|
603
|
-
| Si vous voulez faire… | Correct dans Mado |
|
|
604
|
-
| ------------------------------------- | ------------------------------------------- |
|
|
605
|
-
| `useState(0)` | `signal(0)` |
|
|
606
|
-
| `useEffect(() => {...}, [a, b])` | `effect(() => {...})` (auto-dépendances) |
|
|
607
|
-
| `useEffect(() => return cleanup, [])` | `ctx.onDispose(cleanup)` |
|
|
608
|
-
| `useMemo(() => x, [a])` | `computed(() => x)` |
|
|
609
|
-
| `useCallback(fn, [])` | fonction ordinaire |
|
|
610
|
-
| `useContext(Ctx)` | `inject(host, Ctx)` |
|
|
611
|
-
| `useQuery(['key'], fn)` | `resource(() => 'key', fn)` |
|
|
612
|
-
| `useMutation(fn)` | `mutation(fn, { invalidates: [...] })` |
|
|
613
|
-
| `useRouter().push('/')` | `navigate('/')` |
|
|
614
|
-
| `useRouter().query.q` | `queryParam('q')` |
|
|
615
|
-
| `<input value={v} onChange={...}>` | `<input .value=${v} @input=${...}>` |
|
|
616
|
-
| `{items.map(x => ...)}` | `${() => each(items, x => x.id, x => ...)}` |
|
|
617
|
-
| `useForm({ resolver: zodResolver })` | `useForm({...}, { validate: (v) => ... })` |
|
|
618
|
-
| `class extends HTMLElement` | `component('x-name', setup)` |
|
|
619
|
-
| `@customElement('x')` | `component('x-name', setup)` |
|
|
620
|
-
| `host.getAttribute('x')` dans render | `ctx.attr('x', default)` (réactif) |
|
|
621
|
-
| `jsonFetcher()` avec auth | `apiFetcher()` (attache le Bearer token) |
|
|
622
|
-
| `setInterval` dans page view | `onDispose(() => clearInterval(id))` |
|
|
623
|
-
| lecture signal dans async init view() | `untracked(() => cursor())` |
|
|
624
|
-
|
|
625
|
-
Si quelque chose ne rentre pas dans cette liste — ouvrez `src/` et **lisez 500 lignes**. Sérieusement. Mado est intentionnellement petit pour être lisible.
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
# Test LLM sans historique
|
|
2
|
-
|
|
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.
|
|
6
|
-
|
|
7
|
-
## Contexte autorisé
|
|
8
|
-
|
|
9
|
-
- `AGENTS.md`
|
|
10
|
-
- `README.md`
|
|
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
|
|
14
|
-
|
|
15
|
-
## Tâche
|
|
16
|
-
|
|
17
|
-
Construire une petite SPA ticket-admin :
|
|
18
|
-
|
|
19
|
-
- routes : `/`, `/tickets`, `/tickets/new`, `/tickets/:id`, `*` ;
|
|
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()`.
|
|
24
|
-
|
|
25
|
-
## Checklist d'échec
|
|
26
|
-
|
|
27
|
-
- JSX, `useState`, `useEffect`, `ref`, `$state`, classes custom elements ;
|
|
28
|
-
- `${signal()}` là où un child thunk réactif est nécessaire ;
|
|
29
|
-
- `disabled=${...}` au lieu de `?disabled=${...}` ;
|
|
30
|
-
- `.map()` non-keyed pour des listes dynamiques ;
|
|
31
|
-
- `resource()` créé hors contexte lifecycle-aware ;
|
|
32
|
-
- nouvelles dépendances runtime ou nouvelles API publiques.
|
|
33
|
-
|
|
34
|
-
## Notes
|
|
35
|
-
|
|
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.
|