@madojs/mado 0.11.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +102 -25
- package/CHANGELOG.md +185 -1
- package/README.md +137 -172
- package/dist/src/component.d.ts +3 -2
- package/dist/src/component.js +98 -6
- package/dist/src/component.js.map +1 -1
- package/dist/src/css.js +34 -5
- package/dist/src/css.js.map +1 -1
- package/dist/src/head.d.ts +6 -6
- package/dist/src/head.js +6 -6
- package/dist/src/html/bindings.js +3 -3
- package/dist/src/html/bindings.js.map +1 -1
- package/dist/src/html/parser.d.ts +1 -1
- package/dist/src/html/parser.js +1 -1
- package/dist/src/html/template.js +24 -10
- package/dist/src/html/template.js.map +1 -1
- package/dist/src/index.d.ts +8 -4
- package/dist/src/index.js +13 -3
- package/dist/src/index.js.map +1 -1
- package/dist/src/page.d.ts +62 -28
- package/dist/src/page.js +5 -0
- package/dist/src/page.js.map +1 -1
- package/dist/src/resource.js +2 -1
- package/dist/src/resource.js.map +1 -1
- package/dist/src/router/base.d.ts +65 -0
- package/dist/src/router/base.js +168 -0
- package/dist/src/router/base.js.map +1 -0
- package/dist/src/router/manifest.js +75 -31
- package/dist/src/router/manifest.js.map +1 -1
- package/dist/src/router/match.d.ts +16 -2
- package/dist/src/router/match.js +41 -1
- package/dist/src/router/match.js.map +1 -1
- package/dist/src/router/navigation.js +58 -9
- package/dist/src/router/navigation.js.map +1 -1
- package/dist/src/static-runtime.d.ts +81 -0
- package/dist/src/static-runtime.js +209 -0
- package/dist/src/static-runtime.js.map +1 -0
- package/dist/src/vite/index.d.ts +31 -3
- package/dist/src/vite/index.js +49 -18
- package/dist/src/vite/index.js.map +1 -1
- package/docs/README.md +5 -8
- package/docs/architecture/adr/0001-browser-static-snapshots.md +132 -0
- package/docs/en/00-the-mado-way.md +1 -1
- package/docs/en/01-quickstart.md +183 -0
- package/docs/en/10-pages-and-components.md +271 -0
- package/docs/en/11-templates-and-signals.md +211 -0
- package/docs/en/12-routing.md +229 -0
- package/docs/en/13-data.md +225 -0
- package/docs/en/14-forms.md +244 -0
- package/docs/en/15-static-snapshots.md +181 -0
- package/docs/en/{10-app-architecture.md → 16-app-architecture.md} +53 -2
- package/docs/en/{13-deployment.md → 20-deployment.md} +59 -10
- package/docs/en/{14-testing.md → 22-testing.md} +6 -5
- package/docs/en/23-cookbook.md +292 -0
- package/docs/en/{18-api-freeze-map.md → 30-api-freeze-map.md} +12 -3
- package/docs/en/{20-v1-stability.md → 32-v1-stability.md} +3 -3
- package/docs/en/40-llm-guide.md +776 -0
- package/docs/en/{06-for-backenders.md → 41-for-backenders.md} +15 -16
- package/docs/en/{05-why-mado.md → 42-why-mado.md} +1 -1
- package/docs/en/README.md +97 -27
- package/docs/recipes/nginx/Containerfile +26 -0
- package/docs/recipes/nginx/nginx.conf +42 -0
- package/llms.txt +246 -211
- package/package.json +17 -10
- package/scripts/bake.mjs +4 -568
- package/scripts/cli/generate.mjs +55 -5
- package/scripts/cli/help.mjs +11 -5
- package/scripts/cli/index.mjs +20 -4
- package/scripts/cli/init.mjs +7 -2
- package/scripts/cli/release.mjs +20 -68
- package/scripts/docs-lint.mjs +170 -0
- package/scripts/package-smoke.mjs +67 -9
- package/scripts/preview.mjs +95 -6
- package/scripts/size-budget.mjs +5 -1
- package/scripts/static/browser.mjs +654 -0
- package/scripts/static/discover.mjs +281 -0
- package/scripts/static/output.mjs +141 -0
- package/scripts/static/serialize.mjs +212 -0
- package/scripts/static/server.mjs +167 -0
- package/scripts/static.mjs +191 -0
- package/starters/default/README.md +57 -55
- package/starters/default/package.json +3 -9
- package/starters/default/src/app.routes.ts +20 -33
- package/starters/default/src/components/feature-card.component.ts +40 -0
- package/starters/default/src/components/live-counter.component.ts +57 -0
- package/starters/default/src/content/guides.ts +55 -0
- package/starters/default/src/main.ts +9 -13
- package/starters/default/src/pages/app.page.ts +41 -0
- package/starters/default/src/pages/guide.page.ts +40 -0
- package/starters/default/src/pages/home.page.ts +49 -0
- package/starters/default/src/pages/not-found.page.ts +18 -0
- package/starters/default/src/styles/document.css +39 -0
- package/starters/default/src/styles/reset.css +37 -0
- package/starters/default/src/styles/tokens.css +30 -0
- package/starters/default/src/styles.d.ts +3 -1
- package/starters/default/src/vite-env.d.ts +1 -1
- package/starters/default/vite.config.ts +14 -1
- package/starters/modular/README.md +142 -0
- package/starters/modular/index.html +13 -0
- package/starters/modular/package.json +30 -0
- package/starters/modular/src/app.routes.ts +39 -0
- package/starters/{default → modular}/src/layouts/app-shell.layout.ts +8 -5
- package/starters/{default → modular}/src/layouts/auth-shell.layout.ts +3 -3
- package/starters/modular/src/main.ts +16 -0
- package/starters/{default → modular}/src/modules/billing/pages/invoices-list.page.ts +3 -3
- package/starters/{default → modular}/src/modules/home/home.page.ts +5 -7
- package/starters/modular/src/modules/home/not-found.page.ts +14 -0
- package/starters/modular/src/styles.d.ts +1 -0
- package/starters/modular/src/vite-env.d.ts +1 -0
- package/starters/modular/tsconfig.json +24 -0
- package/starters/modular/vite.config.ts +131 -0
- package/TODO.md +0 -79
- package/docs/en/01-routing.md +0 -152
- package/docs/en/02-project-layout.md +0 -124
- package/docs/en/03-static-bake.md +0 -249
- package/docs/en/04-ide-setup.md +0 -162
- package/docs/en/07-llm-pitfalls.md +0 -724
- package/docs/en/08-llm-zero-history-test.md +0 -56
- package/docs/en/09-shadow-vs-light-dom.md +0 -174
- package/docs/en/11-layouts.md +0 -113
- package/docs/en/12-auth-and-api.md +0 -124
- package/docs/en/16-bake-cookbook.md +0 -100
- package/docs/en/17-shadow-dom-forms.md +0 -192
- package/docs/fr/00-the-mado-way.md +0 -85
- package/docs/fr/01-routing.md +0 -120
- package/docs/fr/02-project-layout.md +0 -84
- package/docs/fr/03-static-bake.md +0 -289
- package/docs/fr/04-ide-setup.md +0 -162
- package/docs/fr/05-why-mado.md +0 -193
- package/docs/fr/06-for-backenders.md +0 -438
- package/docs/fr/07-llm-pitfalls.md +0 -625
- package/docs/fr/08-llm-zero-history-test.md +0 -38
- package/docs/fr/09-shadow-vs-light-dom.md +0 -65
- package/docs/fr/10-app-architecture.md +0 -138
- package/docs/fr/11-layouts.md +0 -47
- package/docs/fr/12-auth-and-api.md +0 -76
- package/docs/fr/13-deployment.md +0 -57
- package/docs/fr/14-testing.md +0 -41
- package/docs/fr/15-error-handling.md +0 -50
- package/docs/fr/16-bake-cookbook.md +0 -88
- package/docs/fr/17-shadow-dom-forms.md +0 -196
- package/docs/fr/18-api-freeze-map.md +0 -63
- package/docs/fr/19-reactivity-ordering.md +0 -97
- package/docs/fr/20-v1-stability.md +0 -88
- package/docs/fr/README.md +0 -27
- package/docs/ru/00-the-mado-way.md +0 -84
- package/docs/ru/01-routing.md +0 -119
- package/docs/ru/02-project-layout.md +0 -84
- package/docs/ru/03-static-bake.md +0 -250
- package/docs/ru/04-ide-setup.md +0 -144
- package/docs/ru/05-why-mado.md +0 -193
- package/docs/ru/06-for-backenders.md +0 -428
- package/docs/ru/07-llm-pitfalls.md +0 -624
- package/docs/ru/08-llm-zero-history-test.md +0 -57
- package/docs/ru/09-shadow-vs-light-dom.md +0 -63
- package/docs/ru/10-app-architecture.md +0 -152
- package/docs/ru/11-layouts.md +0 -47
- package/docs/ru/12-auth-and-api.md +0 -75
- package/docs/ru/13-deployment.md +0 -66
- package/docs/ru/14-testing.md +0 -50
- package/docs/ru/15-error-handling.md +0 -56
- package/docs/ru/16-bake-cookbook.md +0 -95
- package/docs/ru/17-shadow-dom-forms.md +0 -193
- package/docs/ru/18-api-freeze-map.md +0 -64
- package/docs/ru/19-reactivity-ordering.md +0 -95
- package/docs/ru/20-v1-stability.md +0 -82
- package/docs/ru/README.md +0 -25
- package/docs/uk/00-the-mado-way.md +0 -82
- package/docs/uk/01-routing.md +0 -76
- package/docs/uk/02-project-layout.md +0 -73
- package/docs/uk/03-static-bake.md +0 -48
- package/docs/uk/04-ide-setup.md +0 -26
- package/docs/uk/05-why-mado.md +0 -34
- package/docs/uk/06-for-backenders.md +0 -55
- package/docs/uk/07-llm-pitfalls.md +0 -145
- package/docs/uk/08-llm-zero-history-test.md +0 -34
- package/docs/uk/09-shadow-vs-light-dom.md +0 -58
- package/docs/uk/10-app-architecture.md +0 -97
- package/docs/uk/11-layouts.md +0 -47
- package/docs/uk/12-auth-and-api.md +0 -70
- package/docs/uk/13-deployment.md +0 -40
- package/docs/uk/14-testing.md +0 -34
- package/docs/uk/15-error-handling.md +0 -32
- package/docs/uk/16-bake-cookbook.md +0 -36
- package/docs/uk/17-shadow-dom-forms.md +0 -193
- package/docs/uk/18-api-freeze-map.md +0 -61
- package/docs/uk/19-reactivity-ordering.md +0 -95
- package/docs/uk/20-v1-stability.md +0 -83
- package/docs/uk/README.md +0 -27
- package/starters/default/src/modules/home/not-found.page.ts +0 -11
- /package/docs/en/{15-error-handling.md → 21-error-handling.md} +0 -0
- /package/docs/en/{19-reactivity-ordering.md → 31-reactivity-ordering.md} +0 -0
- /package/starters/{default → modular}/.editorconfig +0 -0
- /package/starters/{default → modular}/eslint.config.mjs +0 -0
- /package/starters/{default → modular}/public/favicon.svg +0 -0
- /package/starters/{default → modular}/src/modules/auth/_contracts/auth-api.types.ts +0 -0
- /package/starters/{default → modular}/src/modules/auth/auth.connector.ts +0 -0
- /package/starters/{default → modular}/src/modules/auth/auth.guard.ts +0 -0
- /package/starters/{default → modular}/src/modules/auth/auth.public.ts +0 -0
- /package/starters/{default → modular}/src/modules/auth/auth.routes.ts +0 -0
- /package/starters/{default → modular}/src/modules/auth/auth.service.ts +0 -0
- /package/starters/{default → modular}/src/modules/auth/auth.types.ts +0 -0
- /package/starters/{default → modular}/src/modules/auth/login.page.ts +0 -0
- /package/starters/{default → modular}/src/modules/billing/_contracts/stripe.types.ts +0 -0
- /package/starters/{default → modular}/src/modules/billing/api/stripe.connector.ts +0 -0
- /package/starters/{default → modular}/src/modules/billing/billing.public.ts +0 -0
- /package/starters/{default → modular}/src/modules/billing/billing.routes.ts +0 -0
- /package/starters/{default → modular}/src/modules/billing/billing.types.ts +0 -0
- /package/starters/{default → modular}/src/modules/billing/components/invoice-status-badge.component.ts +0 -0
- /package/starters/{default → modular}/src/modules/billing/data/invoices.resource.ts +0 -0
- /package/starters/{default → modular}/src/modules/billing/pages/invoice-detail.page.ts +0 -0
- /package/starters/{default → modular}/src/shared/http/http-client.ts +0 -0
- /package/starters/{default → modular}/src/shared/http/http-error.ts +0 -0
- /package/starters/{default → modular}/src/shared/http/interceptors.ts +0 -0
- /package/starters/{default → modular}/src/shared/lib/format-date.ts +0 -0
- /package/starters/{default → modular}/src/shared/styles/content.css +0 -0
- /package/starters/{default → modular}/src/shared/styles/reset.css +0 -0
- /package/starters/{default → modular}/src/shared/styles/shell.css +0 -0
- /package/starters/{default → modular}/src/shared/styles/tokens.css +0 -0
- /package/starters/{default → modular}/src/shared/ui/x-button.component.ts +0 -0
- /package/starters/{default → modular}/src/shared/ui/x-spinner.component.ts +0 -0
|
@@ -1,289 +0,0 @@
|
|
|
1
|
-
# Static intelligent (`bake`)
|
|
2
|
-
|
|
3
|
-
> Générer du HTML pour le SEO sans SSR runtime. **Idée : données et vue dans un seul fichier, sortie statique.**
|
|
4
|
-
|
|
5
|
-
`bake` est un **prérendu au moment du build**, pas du SSR. La sortie est des fichiers `*.html`
|
|
6
|
-
statiques que n'importe quel nginx/Cloudflare sert comme du contenu statique ordinaire. Sur le
|
|
7
|
-
client, les Web Components s'animent et la navigation SPA continue de fonctionner.
|
|
8
|
-
|
|
9
|
-
---
|
|
10
|
-
|
|
11
|
-
## Quand `bake` est adapté
|
|
12
|
-
|
|
13
|
-
- **Pages marketing** : landing pages, sous-landings pour des campagnes publicitaires, pages produits.
|
|
14
|
-
- **Catalogue avec un ensemble de pages relativement stable** : blog, documentation, portfolio,
|
|
15
|
-
e-commerce jusqu'à **~10k SKUs** avec des mises à jour moins d'une fois par heure.
|
|
16
|
-
- **Contenu identique pour tous les utilisateurs** : la page entière est identique pour un
|
|
17
|
-
visiteur et un utilisateur authentifié (ou l'authentification est rendue côté client via `effect()`).
|
|
18
|
-
- Vous avez besoin d'un **bon SEO** (rich snippets, OG, JSON-LD) et d'un **premier affichage
|
|
19
|
-
rapide** sans faire tourner des serveurs node en production.
|
|
20
|
-
|
|
21
|
-
## Quand `bake` n'est **pas** adapté
|
|
22
|
-
|
|
23
|
-
- **Des centaines de milliers de pages avec des changements fréquents**. `bake` parcourt tous
|
|
24
|
-
les `paths` de façon synchrone en une seule exécution. Pour 100k+ pages, cela signifie des
|
|
25
|
-
minutes de rebuild, et l'invalidation d'une page nécessite soit un rebake complet, soit une
|
|
26
|
-
logique CI séparée (voir ci-dessous sur la revalidation ciblée).
|
|
27
|
-
- **Contenu personnalisé dans le HTML**. Si la page doit afficher "Bonjour, Ivan" dans le
|
|
28
|
-
`<title>` ou dans les meta — ce n'est pas pour `bake`. Les tableaux de bord authentifiés,
|
|
29
|
-
les fils personnalisés, un panier avec des prix réels pour l'utilisateur — gardez-les en SPA.
|
|
30
|
-
- **Des API uniquement serveur sont nécessaires lors du rendu** : cookies, headers, vraies
|
|
31
|
-
requêtes réseau vers des API privées. Côté bake, seul `linkedom` est disponible, pas
|
|
32
|
-
d'environnement Node pour les composants.
|
|
33
|
-
- **Tests A/B et flags qui modifient le markup au premier affichage**. `bake` verrouillera
|
|
34
|
-
une variante. Gérez le comportement dynamique côté client via `effect()`.
|
|
35
|
-
- **Données en temps réel / qui changent fréquemment** (cours boursiers, stock entrepôt à la
|
|
36
|
-
minute). `bake.revalidate` est des métadonnées, pas du runtime : le framework ne re-bake
|
|
37
|
-
rien lui-même.
|
|
38
|
-
- **Contenu derrière une authentification** (admin, outils internes). Inutile ; utilisez le
|
|
39
|
-
mode SPA.
|
|
40
|
-
|
|
41
|
-
---
|
|
42
|
-
|
|
43
|
-
## Concept
|
|
44
|
-
|
|
45
|
-
`page({...})` a quatre emplacements optionnels liés à `bake` :
|
|
46
|
-
|
|
47
|
-
- `head` — meta, OG, JSON-LD.
|
|
48
|
-
- `bake.paths` — liste des paramètres URL pour la génération (build-time, peut être `async`).
|
|
49
|
-
- `bake.data` — données pour une URL spécifique (build-time, peut être `async`).
|
|
50
|
-
- `bake.revalidate` — après combien de secondes le cache est périmé (écrit dans `<meta>`, la
|
|
51
|
-
vraie invalidation est gérée par votre CI/CDN).
|
|
52
|
-
|
|
53
|
-
La commande `npm run bake` parcourt toutes les entrées `page` avec `bake`, génère le HTML via
|
|
54
|
-
`linkedom`, et le place dans `out/<chemin>/index.html`. **Pas de Chromium nécessaire** —
|
|
55
|
-
`linkedom` fait ~50 Ko.
|
|
56
|
-
|
|
57
|
-
---
|
|
58
|
-
|
|
59
|
-
## Exemple
|
|
60
|
-
|
|
61
|
-
```ts
|
|
62
|
-
// src/pages/product.ts
|
|
63
|
-
import { page, component, html } from "@madojs/mado";
|
|
64
|
-
import { findProduct, products, type Product } from "../lib/products.js";
|
|
65
|
-
|
|
66
|
-
component("x-product-page", ({ host }) => {
|
|
67
|
-
return () => {
|
|
68
|
-
const p = findProduct(host.dataset.slug);
|
|
69
|
-
return p
|
|
70
|
-
? html`<h1>${p.name}</h1><p>${p.description}</p>`
|
|
71
|
-
: html`<p>Introuvable.</p>`;
|
|
72
|
-
};
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
export default page<{ slug: string }, Product | undefined>({
|
|
76
|
-
title: ({ slug }) => `${findProduct(slug)?.name} — MaBoutique`,
|
|
77
|
-
|
|
78
|
-
head: ({ slug }, baked) => {
|
|
79
|
-
const p = baked ?? findProduct(slug);
|
|
80
|
-
if (!p) return {};
|
|
81
|
-
return {
|
|
82
|
-
description: p.description,
|
|
83
|
-
canonical: `/product/${p.slug}`,
|
|
84
|
-
og: { title: p.name, image: p.image, type: "product" },
|
|
85
|
-
jsonLd: {
|
|
86
|
-
"@context": "https://schema.org",
|
|
87
|
-
"@type": "Product",
|
|
88
|
-
name: p.name,
|
|
89
|
-
offers: { "@type": "Offer", price: p.price, priceCurrency: p.currency },
|
|
90
|
-
},
|
|
91
|
-
};
|
|
92
|
-
},
|
|
93
|
-
|
|
94
|
-
bake: {
|
|
95
|
-
paths: () => products.map((p) => ({ slug: p.slug })),
|
|
96
|
-
data: ({ slug }) => findProduct(slug),
|
|
97
|
-
revalidate: 3600,
|
|
98
|
-
},
|
|
99
|
-
|
|
100
|
-
view: ({ params }) =>
|
|
101
|
-
html`<x-product-page data-slug=${params.slug}></x-product-page>`,
|
|
102
|
-
});
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
```ts
|
|
106
|
-
// src/routes.ts
|
|
107
|
-
import { routes, type RoutesMap } from "@madojs/mado";
|
|
108
|
-
|
|
109
|
-
// Exporter À LA FOIS default (RouterApi pour le runtime) ET manifest (pour le script bake).
|
|
110
|
-
export const manifest: RoutesMap = {
|
|
111
|
-
"/": () => import("./pages/home.js"),
|
|
112
|
-
"/product/:slug": () => import("./pages/product.js"),
|
|
113
|
-
"*": () => import("./pages/not-found.js"),
|
|
114
|
-
};
|
|
115
|
-
|
|
116
|
-
export default routes(manifest, { titleSuffix: " · MaBoutique" });
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
## Exécution
|
|
120
|
-
|
|
121
|
-
```bash
|
|
122
|
-
npm install -D linkedom esbuild
|
|
123
|
-
npm run build
|
|
124
|
-
npm run bake
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
Vous obtenez :
|
|
128
|
-
|
|
129
|
-
```
|
|
130
|
-
out/
|
|
131
|
-
├── product/
|
|
132
|
-
│ ├── mado-mug/index.html ← HTML avec meta + JSON-LD
|
|
133
|
-
│ ├── raw-bundler/index.html
|
|
134
|
-
│ └── shadow-dom/index.html
|
|
135
|
-
└── sitemap.xml
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
## Contenu du HTML généré
|
|
139
|
-
|
|
140
|
-
```html
|
|
141
|
-
<head>
|
|
142
|
-
<title>Mado Mug — MaBoutique</title>
|
|
143
|
-
<meta name="description" content="..." data-mado-head="baked">
|
|
144
|
-
<link rel="canonical" href="/product/mado-mug" data-mado-head="baked">
|
|
145
|
-
<meta property="og:title" content="Mado Mug" data-mado-head="baked">
|
|
146
|
-
<meta property="og:image" content="..." data-mado-head="baked">
|
|
147
|
-
<script type="application/ld+json" data-mado-head="baked">
|
|
148
|
-
{"@context":"https://schema.org","@type":"Product","..."}
|
|
149
|
-
</script>
|
|
150
|
-
<meta name="bake-revalidate" content="3600" data-mado-head="baked">
|
|
151
|
-
</head>
|
|
152
|
-
<body>
|
|
153
|
-
<div id="app">
|
|
154
|
-
<x-product-page data-slug="mado-mug">
|
|
155
|
-
<h1>Mado Mug</h1>
|
|
156
|
-
<p>Un mug avec l'inscription "zéro dépendances".</p>
|
|
157
|
-
<strong>12 EUR</strong>
|
|
158
|
-
</x-product-page>
|
|
159
|
-
</div>
|
|
160
|
-
|
|
161
|
-
<!-- données préchargées pour l'hydratation -->
|
|
162
|
-
<script id="bake" type="application/json">
|
|
163
|
-
{"slug":"mado-mug","name":"Mado Mug","price":12,"..."}
|
|
164
|
-
</script>
|
|
165
|
-
|
|
166
|
-
<script type="module" src="/assets/main-abc123.js"></script>
|
|
167
|
-
</body>
|
|
168
|
-
```
|
|
169
|
-
|
|
170
|
-
Après le chargement du JS :
|
|
171
|
-
|
|
172
|
-
1. Les Web Components `<x-product-page>` s'animent (le navigateur connaît déjà les custom elements).
|
|
173
|
-
2. `page.load()` reçoit `baked` comme initialData — pas d'appels `fetch` inutiles.
|
|
174
|
-
3. La navigation SPA continue de fonctionner normalement.
|
|
175
|
-
|
|
176
|
-
---
|
|
177
|
-
|
|
178
|
-
## Cookbook : Scénarios typiques
|
|
179
|
-
|
|
180
|
-
### Blog (Markdown → bake)
|
|
181
|
-
|
|
182
|
-
```ts
|
|
183
|
-
// src/lib/posts.ts
|
|
184
|
-
import { readdirSync, readFileSync } from "node:fs";
|
|
185
|
-
// (ce module est importé uniquement depuis le script bake,
|
|
186
|
-
// il ne doit pas se retrouver dans le graph navigateur — mettez-le dans lib/server/ ou excluez-le)
|
|
187
|
-
|
|
188
|
-
export const allPosts = () =>
|
|
189
|
-
readdirSync("content/blog").map((file) => ({
|
|
190
|
-
slug: file.replace(/\.md$/, ""),
|
|
191
|
-
...parseFrontmatter(readFileSync(`content/blog/${file}`, "utf-8")),
|
|
192
|
-
}));
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
```ts
|
|
196
|
-
// src/pages/blog-post.ts
|
|
197
|
-
import { page, html } from "@madojs/mado";
|
|
198
|
-
import { allPosts } from "../lib/posts.js";
|
|
199
|
-
|
|
200
|
-
export default page<{ slug: string }>({
|
|
201
|
-
title: ({ slug }) => allPosts().find((p) => p.slug === slug)?.title ?? slug,
|
|
202
|
-
head: ({ slug }, post) => ({
|
|
203
|
-
description: post?.excerpt,
|
|
204
|
-
canonical: `/blog/${slug}`,
|
|
205
|
-
og: { title: post?.title, type: "article" },
|
|
206
|
-
}),
|
|
207
|
-
bake: {
|
|
208
|
-
paths: () => allPosts().map(({ slug }) => ({ slug })),
|
|
209
|
-
data: ({ slug }) => allPosts().find((p) => p.slug === slug),
|
|
210
|
-
},
|
|
211
|
-
view: ({ params }) =>
|
|
212
|
-
html`<x-blog-post data-slug=${params.slug}></x-blog-post>`,
|
|
213
|
-
});
|
|
214
|
-
```
|
|
215
|
-
|
|
216
|
-
### Catalogue produits (e-commerce, ≤ 10k SKUs)
|
|
217
|
-
|
|
218
|
-
- `bake.paths` → SELECT slug FROM products
|
|
219
|
-
- `bake.data` → SELECT * FROM products WHERE slug=?
|
|
220
|
-
- Invalidation complète toutes les N heures via cron + `npm run bake`
|
|
221
|
-
- Invalidation ciblée d'un seul produit : webhook → rebuild uniquement cette entrée `paths`
|
|
222
|
-
|
|
223
|
-
### Documentation
|
|
224
|
-
|
|
225
|
-
- `bake.paths` — parcours du système de fichiers `docs/**/*.md`
|
|
226
|
-
- `bake.data` — parsing Markdown
|
|
227
|
-
- `head.jsonLd` — `TechArticle`
|
|
228
|
-
|
|
229
|
-
---
|
|
230
|
-
|
|
231
|
-
## Revalidate / CDN
|
|
232
|
-
|
|
233
|
-
`bake.revalidate: 3600` écrit `<meta name="bake-revalidate" content="3600">`
|
|
234
|
-
dans le HTML. C'est des **métadonnées** — le framework ne re-bake rien lui-même. Stratégies :
|
|
235
|
-
|
|
236
|
-
1. **Option la plus simple** : cron dans CI — `npm run bake && rsync out/ origin:/var/www/`.
|
|
237
|
-
2. **Via CDN** (Cloudflare/Fastly) : servir le HTML avec `Cache-Control: max-age=3600`. Le CDN
|
|
238
|
-
s'invalide lui-même.
|
|
239
|
-
3. **Déclencheur webhook** : API boutique → POST `/_revalidate?path=/product/mado-mug` → CI
|
|
240
|
-
re-bake uniquement cette page (vous pouvez implémenter `bake.paths` pour retourner une liste
|
|
241
|
-
ciblée basée sur un paramètre d'environnement).
|
|
242
|
-
|
|
243
|
-
---
|
|
244
|
-
|
|
245
|
-
## Comparaison avec les alternatives
|
|
246
|
-
|
|
247
|
-
| | Next.js SSG/ISR | playwright-prerender | **mado bake** |
|
|
248
|
-
|--------------------------|--------------------|----------------------|-----------------|
|
|
249
|
-
| Chrome requis en CI | non | **oui** (~300 Mo) | **non** |
|
|
250
|
-
| Node requis en production | pour ISR | non | **non** |
|
|
251
|
-
| Temps pour 1000 pages | minutes | minutes | **secondes** |
|
|
252
|
-
| Niveau de magie | élevé | faible | **zéro** |
|
|
253
|
-
| Parser HTML | moteur React | navigateur | linkedom (~50 Ko) |
|
|
254
|
-
| Configuration | next.config.js + … | script unique | script unique |
|
|
255
|
-
| Source de vérité vue+données | séparées | page | **une seule page** |
|
|
256
|
-
|
|
257
|
-
---
|
|
258
|
-
|
|
259
|
-
## Limitations et pièges
|
|
260
|
-
|
|
261
|
-
- **Il n'y a pas de navigateur côté bake.** Ne fonctionnent pas : `setTimeout` (techniquement
|
|
262
|
-
ça marche, mais bake se termine avant qu'il ne se déclenche), `fetch` vers des URLs relatives,
|
|
263
|
-
tous les effets de bord `effect()`/`signal()`, le vrai `requestAnimationFrame`. La fonction
|
|
264
|
-
de rendu doit être déterministe selon `params` / `data`.
|
|
265
|
-
- **`linkedom` ≠ navigateur.** Toutes les API DOM ne sont pas supportées (par exemple,
|
|
266
|
-
`HTMLElement.click()` se comporte plus simplement). La logique complexe dans les Web Components
|
|
267
|
-
ne s'exécutera dans le navigateur qu'après `connectedCallback` ; seul ce qui a été rendu
|
|
268
|
-
de façon synchrone lors du premier passage se retrouvera dans le HTML baked.
|
|
269
|
-
- **Rendre le contenu dynamique côté client.** L'heure actuelle, les tests A/B, les
|
|
270
|
-
banners géo, le panier de l'utilisateur — ceux-ci ne doivent pas être dans le HTML baked.
|
|
271
|
-
Utilisez `effect()` pour le rendu côté client.
|
|
272
|
-
- **Les imports côté serveur ne doivent pas se retrouver dans le graph client.** Si
|
|
273
|
-
`lib/posts.ts` importe `node:fs`, il ne peut pas être importé depuis `view`. Gardez ces
|
|
274
|
-
modules dans un dossier séparé (`lib/build/`) et utilisez-les uniquement depuis
|
|
275
|
-
`bake.paths`/`bake.data`.
|
|
276
|
-
- **`paths` et `data` sont exécutés à chaque exécution de bake.** S'ils impliquent une
|
|
277
|
-
requête de base de données lourde — mettez en cache au niveau du script.
|
|
278
|
-
|
|
279
|
-
---
|
|
280
|
-
|
|
281
|
-
## TL;DR
|
|
282
|
-
|
|
283
|
-
Si une page est **identique pour tous les utilisateurs**, a **un ensemble d'URLs relativement
|
|
284
|
-
stable**, et que **le SEO + le premier affichage** comptent — ajoutez `bake: { paths, data }`
|
|
285
|
-
et obtenez du HTML statique avec meta/JSON-LD/sitemap en millisecondes. Pas de serveur node,
|
|
286
|
-
pas de Chrome, pas de magie.
|
|
287
|
-
|
|
288
|
-
Si la page est personnalisée, ou qu'il y a des millions d'URLs, ou que le contenu change en
|
|
289
|
-
temps réel — `bake` n'est pas votre outil. Gardez-la en SPA ou intégrez un framework SSR séparé.
|
package/docs/fr/04-ide-setup.md
DELETED
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
# Support IDE pour `html\`\`` et `css\`\``
|
|
2
|
-
|
|
3
|
-
Par défaut, `html\`...\`` et `css\`...\`` ne sont que des tagged-template strings.
|
|
4
|
-
TypeScript ne les connaît pas, l'IDE ne les colore pas. C'est un **compromis délibéré**
|
|
5
|
-
en faveur de zéro dépendances runtime et pas de plugins de build.
|
|
6
|
-
|
|
7
|
-
La bonne nouvelle : Mado utilise les mêmes conventions que [lit](https://lit.dev), donc les
|
|
8
|
-
outils IDE **prêts à l'emploi** de l'écosystème lit fonctionnent directement.
|
|
9
|
-
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
## VS Code (configuration recommandée)
|
|
13
|
-
|
|
14
|
-
### 1. Installer [lit-plugin](https://marketplace.visualstudio.com/items?itemName=runem.lit-plugin)
|
|
15
|
-
|
|
16
|
-
VS Code → Extensions → chercher **"lit-plugin"** (par runem) → Installer.
|
|
17
|
-
|
|
18
|
-
Ce que vous obtenez :
|
|
19
|
-
|
|
20
|
-
- Coloration HTML à l'intérieur de `html\`\``.
|
|
21
|
-
- Coloration CSS à l'intérieur de `css\`\``.
|
|
22
|
-
- Auto-complétion pour les tags HTML, attributs et événements.
|
|
23
|
-
- Vérification des fautes de frappe dans les attributs.
|
|
24
|
-
- Aller à la définition pour les custom elements (si décrits via `customElements.json` ou JSDoc).
|
|
25
|
-
- Diagnostics sur les bindings invalides.
|
|
26
|
-
|
|
27
|
-
### 2. Spécifier les noms de tags
|
|
28
|
-
|
|
29
|
-
`lit-plugin` cherche les identifiants `html` et `css` dans les imports. Si vous ne les
|
|
30
|
-
renommez pas à l'import — la configuration est nulle, tout fonctionne :
|
|
31
|
-
|
|
32
|
-
```ts
|
|
33
|
-
import { html, css } from "@madojs/mado";
|
|
34
|
-
|
|
35
|
-
const tpl = html`<button @click=${fn}>${label}</button>`;
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
### 3. (Optionnel) Votre propre `customElements.json`
|
|
39
|
-
|
|
40
|
-
Si vous voulez l'auto-complétion pour vos propres composants `<x-*>`, décrivez-les via
|
|
41
|
-
[Custom Elements Manifest](https://github.com/webcomponents/custom-elements-manifest) :
|
|
42
|
-
|
|
43
|
-
```bash
|
|
44
|
-
npm install --save-dev @custom-elements-manifest/analyzer
|
|
45
|
-
npx cem analyze --globs "src/components/**/*.ts"
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
Cela crée `custom-elements.json`, que `lit-plugin` récupère automatiquement.
|
|
49
|
-
|
|
50
|
-
---
|
|
51
|
-
|
|
52
|
-
## WebStorm / IntelliJ
|
|
53
|
-
|
|
54
|
-
WebStorm comprend `html\`\`` et `css\`\`` **nativement** — support natif des template literals
|
|
55
|
-
de style lit depuis 2021. Pas de plugins nécessaires.
|
|
56
|
-
|
|
57
|
-
Si la coloration n'apparaît pas :
|
|
58
|
-
|
|
59
|
-
- Settings → Languages & Frameworks → JavaScript → vérifiez que "Use types from server" est activé
|
|
60
|
-
- Redémarrer le serveur TS : ⌘+⇧+P → "Restart TypeScript Server"
|
|
61
|
-
|
|
62
|
-
---
|
|
63
|
-
|
|
64
|
-
## Neovim / Helix
|
|
65
|
-
|
|
66
|
-
Utilisez [`lit-html-server`](https://github.com/runem/lit-analyzer/tree/master/packages/lit-html-server)
|
|
67
|
-
(serveur LSP du même auteur que lit-plugin) :
|
|
68
|
-
|
|
69
|
-
```bash
|
|
70
|
-
npm install -g lit-html-server
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
`init.lua` (pour `lspconfig`) :
|
|
74
|
-
|
|
75
|
-
```lua
|
|
76
|
-
require('lspconfig').lit_html.setup{
|
|
77
|
-
cmd = { 'lit-html-server', '--stdio' },
|
|
78
|
-
filetypes = { 'typescript', 'javascript' },
|
|
79
|
-
}
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
---
|
|
83
|
-
|
|
84
|
-
## Ce qui ne fonctionne PAS (limitations connues)
|
|
85
|
-
|
|
86
|
-
- **Vérification de type des bindings de signal.** `html\`<input .value=${count}>\`` — `lit-plugin`
|
|
87
|
-
attend une string, mais `count` est un `Signal<number>`. Supprimez avec `// @ts-expect-error`
|
|
88
|
-
ou `<!-- @ts-ignore -->`. Sera amélioré en Phase 3+.
|
|
89
|
-
- **Les directives personnalisées (`each`)** sont reconnues comme des fonctions ordinaires — sans
|
|
90
|
-
sémantique spéciale dans le plugin.
|
|
91
|
-
- **Les attributs avec préfixes `@`, `.`, `?`** sont parfois signalés comme des erreurs si
|
|
92
|
-
`lit-plugin` a `"no-unknown-attribute": false` désactivé. Dans `.vscode/settings.json` :
|
|
93
|
-
|
|
94
|
-
```json
|
|
95
|
-
{
|
|
96
|
-
"lit-plugin.rules": {
|
|
97
|
-
"no-unknown-attribute": "off",
|
|
98
|
-
"no-incompatible-type-binding": "off"
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
---
|
|
104
|
-
|
|
105
|
-
## Typage JSDoc pour les composants
|
|
106
|
-
|
|
107
|
-
Pour que l'IDE récupère les custom elements à l'intérieur de `html\`\``, annotez la
|
|
108
|
-
définition `component()` via JSDoc :
|
|
109
|
-
|
|
110
|
-
```ts
|
|
111
|
-
/**
|
|
112
|
-
* @element x-counter
|
|
113
|
-
* @attr {number} initial - valeur de départ
|
|
114
|
-
* @fires {CustomEvent<number>} change - à chaque changement
|
|
115
|
-
*/
|
|
116
|
-
component("x-counter", () => {
|
|
117
|
-
/* ... */
|
|
118
|
-
});
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
`lit-plugin` le reconnaît et suggère des attributs quand vous tapez `<x-counter ...>`.
|
|
122
|
-
|
|
123
|
-
---
|
|
124
|
-
|
|
125
|
-
## Prettier / formatage
|
|
126
|
-
|
|
127
|
-
Prettier 3.0+ formate `html\`\`` via [`@prettier/plugin-xml`](https://github.com/prettier/plugin-xml)
|
|
128
|
-
ou le mode intégré. `.prettierrc` minimal :
|
|
129
|
-
|
|
130
|
-
```json
|
|
131
|
-
{
|
|
132
|
-
"semi": true,
|
|
133
|
-
"singleQuote": false,
|
|
134
|
-
"embeddedLanguageFormatting": "auto"
|
|
135
|
-
}
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
`embeddedLanguageFormatting: "auto"` (par défaut) formate le contenu des tagged-template
|
|
139
|
-
literals avec des tags connus (`html`, `css`).
|
|
140
|
-
|
|
141
|
-
---
|
|
142
|
-
|
|
143
|
-
## ESLint
|
|
144
|
-
|
|
145
|
-
Si vous utilisez ESLint, le plugin [`eslint-plugin-lit`](https://github.com/43081j/eslint-plugin-lit)
|
|
146
|
-
fournit des règles spécifiques au HTML en tagged-template, et
|
|
147
|
-
[`eslint-plugin-wc`](https://github.com/43081j/eslint-plugin-wc) couvre les Web Components
|
|
148
|
-
en général. La configuration est à votre discrétion, elle n'est pas obligatoire.
|
|
149
|
-
|
|
150
|
-
---
|
|
151
|
-
|
|
152
|
-
## TL;DR
|
|
153
|
-
|
|
154
|
-
| Éditeur | Configuration | Niveau DX |
|
|
155
|
-
|---|---|---|
|
|
156
|
-
| **VS Code** | installer `lit-plugin` | ★★★★ |
|
|
157
|
-
| **WebStorm** | rien | ★★★★ |
|
|
158
|
-
| **Neovim/Helix** | `lit-html-server` via LSP | ★★★ |
|
|
159
|
-
| **Vim sans LSP** | manuel | ★ |
|
|
160
|
-
|
|
161
|
-
Mado fonctionne aussi sans plugin IDE : `html\`\`` reste du code TS valide, tout se
|
|
162
|
-
compile et s'exécute. La string à l'intérieur est juste colorée comme une string.
|
package/docs/fr/05-why-mado.md
DELETED
|
@@ -1,193 +0,0 @@
|
|
|
1
|
-
# Pourquoi Mado (et pourquoi pas Lit / Solid / Alpine / htmx)
|
|
2
|
-
|
|
3
|
-
> Si vous choisissez un stack frontend pour un nouveau projet, cette page est pour vous.
|
|
4
|
-
> Si vous avez déjà quelque chose qui fonctionne — **ne migrez pas pour le plaisir de migrer**, ça coûte toujours plus cher qu'il n'y paraît.
|
|
5
|
-
|
|
6
|
-
Mado n'est pas un "tueur" de React/Vue/Svelte. C'est un outil spécialisé. J'explique ici honnêtement **dans quels cas Mado est vraiment meilleur que les alternatives**, et dans lesquels il ne l'est pas.
|
|
7
|
-
|
|
8
|
-
---
|
|
9
|
-
|
|
10
|
-
## TL;DR — un seul tableau
|
|
11
|
-
|
|
12
|
-
| Si vous vous souciez de… | Choisissez |
|
|
13
|
-
|---|---|
|
|
14
|
-
| Meilleure infrastructure d'apprentissage / grand écosystème | **React** ou **Vue** |
|
|
15
|
-
| Design system de composants pour l'intégration dans n'importe quel framework | **Lit** |
|
|
16
|
-
| Top performance sur les grandes listes, "proche du vanilla" avec JSX | **Solid** ou **Svelte 5** |
|
|
17
|
-
| Amélioration progressive d'apps classiques rendues côté serveur | **htmx** + votre backend |
|
|
18
|
-
| "Saupoudrer" de la réactivité sur un site statique | **Alpine.js** |
|
|
19
|
-
| Outillage minimal, maximum de plateforme, tout dans une boîte (router + data + forms + SEO), lisible en une soirée | **Mado** ✓ |
|
|
20
|
-
|
|
21
|
-
Si votre cas ne tombe pas dans le dernier point — Mado n'est probablement pas le meilleur choix. C'est normal.
|
|
22
|
-
|
|
23
|
-
---
|
|
24
|
-
|
|
25
|
-
## Mado vs Lit
|
|
26
|
-
|
|
27
|
-
**Lit** est l'alternative la plus proche dans l'esprit. Même approche : Web Components + tagged templates + magie minimale.
|
|
28
|
-
|
|
29
|
-
| | Lit | Mado |
|
|
30
|
-
|---|---|---|
|
|
31
|
-
| Taille | ~6 Ko | ~16 Ko |
|
|
32
|
-
| Âge / support | ~10 ans, Google | 6 mois, auteur unique |
|
|
33
|
-
| Réactivité | décorateurs `@property` + `requestUpdate` manuel | signals (`signal`/`computed`/`effect`) intégrés |
|
|
34
|
-
| Router | aucun, vous devez en trouver un (`@lit-labs/router`, etc.) | inclus : `routes()` + layout groups + prefetch |
|
|
35
|
-
| Chargement de données | aucun, vous devez l'assembler | `resource()` + `mutation()` + invalidation glob |
|
|
36
|
-
| Forms | aucun | `useForm()` avec contraintes proches du HTML |
|
|
37
|
-
| SEO / statique | complexe (`@lit-labs/ssr`) | `bake` (linkedom) + edge-prerender |
|
|
38
|
-
| Build | nécessite des plugins spécifiques au framework | Vite transport + runtime natif |
|
|
39
|
-
| Style de code | classes + décorateurs | fonctions + tagged templates |
|
|
40
|
-
| Écosystème | réel (Shoelace, Material Web, etc.) | aucun |
|
|
41
|
-
| Quand choisir | écrire un design system / bibliothèque Web Components pour l'intégration | écrire une application complète, vouloir tout dans une boîte |
|
|
42
|
-
|
|
43
|
-
**Pitch honnête :** *"Lit est meilleur si vous écrivez un design system de composants. Mado est meilleur si vous écrivez une application et voulez des batteries incluses sans assembler 8 packages."*
|
|
44
|
-
|
|
45
|
-
---
|
|
46
|
-
|
|
47
|
-
## Mado vs Solid
|
|
48
|
-
|
|
49
|
-
**Solid** est une bibliothèque réactive de premier plan construite sur les signals. Techniquement très impressionnant.
|
|
50
|
-
|
|
51
|
-
| | Solid | Mado |
|
|
52
|
-
|---|---|---|
|
|
53
|
-
| Taille | ~7 Ko | ~16 Ko |
|
|
54
|
-
| Performance | top-3 sur js-framework-benchmark | bonne, mais pas au top |
|
|
55
|
-
| Réactivité | signals (même classe d'idées) | signals |
|
|
56
|
-
| Templates | JSX (compilé en expressions réactives) | tagged template `html\`\`` |
|
|
57
|
-
| Modèle de composant | fonctions, nœuds virtuels Solid | Web Components |
|
|
58
|
-
| Build | Vite + babel-plugin-solid requis | Vite pour dev/build, aucune dépendance runtime |
|
|
59
|
-
| Router | `@solidjs/router` | inclus |
|
|
60
|
-
| Données | `createResource` | `resource()` |
|
|
61
|
-
| SSR | sérieusement supporté (SolidStart) | intentionnellement absent |
|
|
62
|
-
| Écosystème | en croissance, ~50 packages | aucun |
|
|
63
|
-
| Quand choisir | besoin de top performance + JSX + prêt à configurer le build | vouloir un runtime browser-native avec tooling simple |
|
|
64
|
-
|
|
65
|
-
**Pitch honnête :** *"Solid est techniquement plus rapide et plus mature. Mado est plus simple dans son idée : runtime browser-native, Web Components, tagged templates, et Vite fait le travail dev/build ennuyeux. Si vous voulez JSX et un écosystème plus large, allez avec Solid."*
|
|
66
|
-
|
|
67
|
-
---
|
|
68
|
-
|
|
69
|
-
## Mado vs Svelte 5
|
|
70
|
-
|
|
71
|
-
**Svelte 5** avec les runes — aussi un modèle signal, aussi minimaliste.
|
|
72
|
-
|
|
73
|
-
| | Svelte 5 | Mado |
|
|
74
|
-
|---|---|---|
|
|
75
|
-
| Taille runtime | ~3 Ko | ~16 Ko |
|
|
76
|
-
| Compilateur | requis (.svelte → JS) | aucun |
|
|
77
|
-
| Syntaxe | format .svelte personnalisé | TS + tagged templates |
|
|
78
|
-
| Réactivité | `$state`/`$derived` (runes) | `signal`/`computed` |
|
|
79
|
-
| SSR / SvelteKit | complet, mature | intentionnellement absent |
|
|
80
|
-
| Écosystème | large, excellents dev-tools | aucun |
|
|
81
|
-
| Quand choisir | nouveau projet de production avec une équipe | outil privé/interne, besoin de simplicité |
|
|
82
|
-
|
|
83
|
-
**Pitch honnête :** *"Svelte est un choix produit. Mado est un choix ingénierie. Si vous avez une équipe et une app de production — Svelte. Si vous êtes seul et voulez le contrôle — Mado."*
|
|
84
|
-
|
|
85
|
-
---
|
|
86
|
-
|
|
87
|
-
## Mado vs htmx
|
|
88
|
-
|
|
89
|
-
**htmx** est une autre école : des fragments HTML sur le fil.
|
|
90
|
-
|
|
91
|
-
| | htmx | Mado |
|
|
92
|
-
|---|---|---|
|
|
93
|
-
| Architecture | HTML du serveur, mis à jour via des fragments | SPA : JS charge les données, se rend lui-même |
|
|
94
|
-
| Dépendance backend | forte (le backend doit pouvoir servir du HTML) | faible (le backend est une API JSON) |
|
|
95
|
-
| État client | minimal (cookies, localStorage) | complet (signal, persisted) |
|
|
96
|
-
| Mises à jour optimistes | difficiles | faciles (mutation + invalidates) |
|
|
97
|
-
| Offline / PWA | faible | correct |
|
|
98
|
-
| Taille | ~14 Ko | ~16 Ko |
|
|
99
|
-
| Quand choisir | app classique rendue côté serveur (Rails, Django, Phoenix), besoin de "revitaliser" | expérience SPA requise, backend est REST/GraphQL |
|
|
100
|
-
|
|
101
|
-
**Pitch honnête :** *"htmx — si le backend est solide et peut servir du HTML. Mado — si le backend sert du JSON et que vous avez besoin d'une expérience SPA complète."*
|
|
102
|
-
|
|
103
|
-
---
|
|
104
|
-
|
|
105
|
-
## Mado vs Alpine.js
|
|
106
|
-
|
|
107
|
-
**Alpine** — attributs réactifs directement dans le HTML.
|
|
108
|
-
|
|
109
|
-
| | Alpine | Mado |
|
|
110
|
-
|---|---|---|
|
|
111
|
-
| Objectif | améliorer du HTML statique | SPA complet |
|
|
112
|
-
| Taille | ~7 Ko | ~16 Ko |
|
|
113
|
-
| Gestion d'état | `x-data` localement | signals + context + persisted |
|
|
114
|
-
| Routage | aucun | inclus |
|
|
115
|
-
| TypeScript | faible | première classe |
|
|
116
|
-
| Quand choisir | sites statiques, landing pages, besoin de 5 boutons interactifs | app complète : pages, navigation, forms, données |
|
|
117
|
-
|
|
118
|
-
**Pitch honnête :** *"Alpine — pour l'interactivité sur les sites statiques. Mado — pour une application complète."*
|
|
119
|
-
|
|
120
|
-
---
|
|
121
|
-
|
|
122
|
-
## Mado vs React + écosystème
|
|
123
|
-
|
|
124
|
-
Je ne vais pas m'étendre là-dessus longtemps, car React est dans une **catégorie différente** en termes d'écosystème et de maturité. Mais si vous comparez sérieusement :
|
|
125
|
-
|
|
126
|
-
**React gagne :**
|
|
127
|
-
- écosystème massif : milliers de kits UI, milliers d'articles, tutoriels infinis ;
|
|
128
|
-
- les assistants IA (ChatGPT, Copilot) connaissent React mieux que tout ;
|
|
129
|
-
- meilleur marché de l'emploi ;
|
|
130
|
-
- meilleur support SSR (Next.js).
|
|
131
|
-
|
|
132
|
-
**Mado gagne :**
|
|
133
|
-
- taille de bundle des dizaines de fois plus petite ;
|
|
134
|
-
- zéro infrastructure (pas de Vite, pas de Babel, pas de 200 packages) ;
|
|
135
|
-
- lisible en une soirée — si quelque chose casse, ouvrez `src/` ;
|
|
136
|
-
- signals au lieu de hooks (pas de règles "ne peut pas être utilisé dans un if", pas de pièges de stale-closure) ;
|
|
137
|
-
- pas besoin de migrer entre les versions majeures.
|
|
138
|
-
|
|
139
|
-
**Quand choisir Mado plutôt que React :**
|
|
140
|
-
- projet de 1–3 personnes, pour des années à venir ;
|
|
141
|
-
- la taille du bundle est critique ;
|
|
142
|
-
- vous êtes fatigué du React fatigue et prêt à sacrifier l'écosystème pour la simplicité.
|
|
143
|
-
|
|
144
|
-
**Quand choisir React :**
|
|
145
|
-
- équipe de 5 personnes ou plus ;
|
|
146
|
-
- vous avez besoin de kits UI, vous avez besoin de l'écosystème ;
|
|
147
|
-
- un projet qui recrutera de nouvelles personnes sur le marché ;
|
|
148
|
-
- vous avez besoin de SSR avec hydratation (Next.js).
|
|
149
|
-
|
|
150
|
-
---
|
|
151
|
-
|
|
152
|
-
## L'argument le plus fort de Mado
|
|
153
|
-
|
|
154
|
-
Pas la taille, pas la performance, pas les signals — tout a de meilleurs concurrents.
|
|
155
|
-
|
|
156
|
-
> **"Ouvrez le source et lisez-le en une soirée. ~3500 lignes, petits modules. Si quelque chose casse — vous n'allez pas sur une issue avec 3000 commentaires. Vous allez dans `src/router/` et lisez le code."**
|
|
157
|
-
|
|
158
|
-
C'est ce qu'on appelle la **propriété** — vous possédez le code, plutôt que de dépendre de celui de quelqu'un d'autre.
|
|
159
|
-
|
|
160
|
-
Pour les développeurs backend habitués aux petites bibliothèques compréhensibles (chi en Go, axum en Rust, FastAPI en Python), c'est un **sentiment familier**. Pour ceux à qui ça n'importe pas — prenez ce qui est plus grand et plus mature.
|
|
161
|
-
|
|
162
|
-
---
|
|
163
|
-
|
|
164
|
-
## Qu'en est-il de la performance ?
|
|
165
|
-
|
|
166
|
-
Honnêtement : **Mado n'est pas le plus rapide**. Le top-3 sur js-framework-benchmark sont Solid, Inferno et Svelte. Mado est plus proche de Lit / Preact en termes de caractéristiques.
|
|
167
|
-
|
|
168
|
-
Ce que Mado fait pour la performance par défaut :
|
|
169
|
-
- **`computed` lazy** (dirty-flag, pas eager) ;
|
|
170
|
-
- **planificateur de microtâche par batch** pour `signal.set` ;
|
|
171
|
-
- **réconciliation par clé** dans `each()` avec réutilisation réelle du DOM ;
|
|
172
|
-
- **rendu synchrone** pour les pages en cache dans le router ;
|
|
173
|
-
- **hover-prefetch** pour les chunks lazy ;
|
|
174
|
-
- **View Transitions API** pour des transitions fluides ;
|
|
175
|
-
- **`adoptedStyleSheets` partagés** pour le CSS ;
|
|
176
|
-
- **hints `modulepreload`** sur le serveur de développement.
|
|
177
|
-
|
|
178
|
-
C'est suffisant pour la plupart des applications. Si vous construisez Excel dans le navigateur ou une visualisation WebGL à 60fps — ce n'est pas ici (c'est Solid ou du JS natif).
|
|
179
|
-
|
|
180
|
-
---
|
|
181
|
-
|
|
182
|
-
## Résumé
|
|
183
|
-
|
|
184
|
-
Mado est un outil **ciblé** avec un positionnement honnête. Il est le plus fort quand :
|
|
185
|
-
|
|
186
|
-
1. Vous voulez **posséder** le code et le lire en entier.
|
|
187
|
-
2. La **simplicité d'infrastructure** est critique (pas de Vite/Webpack/Babel).
|
|
188
|
-
3. Vous avez besoin de **batteries dans une boîte** (router + données + forms + SEO).
|
|
189
|
-
4. Vous n'êtes pas junior et n'avez pas peur des Web Components.
|
|
190
|
-
|
|
191
|
-
Si même un seul point ne s'applique pas à vous — prenez une alternative du tableau ci-dessus. Ne vous battez pas avec un outil qui ne convient pas.
|
|
192
|
-
|
|
193
|
-
— L'auteur de Mado, ex-développeur React passé au backend qui maintenant colle des frontends dans ses temps libres.
|