@madojs/mado 0.10.0 → 0.10.1
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/CHANGELOG.md +30 -0
- package/README.md +1 -1
- package/dist/src/html/template.js +10 -0
- package/dist/src/html/template.js.map +1 -1
- package/docs/en/02-project-layout.md +3 -2
- package/docs/en/13-deployment.md +10 -7
- package/docs/en/16-bake-cookbook.md +10 -2
- package/docs/fr/02-project-layout.md +20 -13
- package/docs/fr/13-deployment.md +33 -12
- package/docs/fr/16-bake-cookbook.md +57 -4
- package/docs/ru/02-project-layout.md +22 -15
- package/docs/ru/13-deployment.md +23 -13
- package/docs/ru/16-bake-cookbook.md +42 -8
- package/package.json +1 -1
- package/scripts/bake.mjs +4 -1
- package/scripts/cli.mjs +66 -5
- package/scripts/preview.mjs +7 -27
- package/starters/admin/README.md +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,36 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.10.1 - 2026-06-12
|
|
6
|
+
|
|
7
|
+
Patch release for the production bake/preview path.
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **Baked pages now use the production bundle during `mado release`.** Release
|
|
12
|
+
now bakes from the already bundled `out/index.html`, so baked HTML references
|
|
13
|
+
`/assets/main-*.js` instead of the dev-only `/dist/main.js`.
|
|
14
|
+
|
|
15
|
+
- **Client boot no longer appends pages below baked HTML.** `mado bake` marks
|
|
16
|
+
the app container with `data-mado-baked`, and runtime `render()` replaces
|
|
17
|
+
that marked static DOM with live bindings on startup. This keeps bake as a
|
|
18
|
+
static first-paint/SEO shell, not SSR hydration, while avoiding duplicate
|
|
19
|
+
landing + route content after navigation.
|
|
20
|
+
|
|
21
|
+
- **`mado preview` now serves the final `out/` artifact exactly.** Release
|
|
22
|
+
promotes baked HTML into real route paths inside `out/`, and preview no
|
|
23
|
+
longer applies a preview-only virtual mapping from `out/baked/`.
|
|
24
|
+
|
|
25
|
+
- **`sitemap.xml` is promoted to the site root during release.** Standalone
|
|
26
|
+
`mado bake` still writes `out/baked/sitemap.xml`; `mado release` also copies
|
|
27
|
+
it to `out/sitemap.xml`, where static hosts and crawlers expect it.
|
|
28
|
+
|
|
29
|
+
### Docs
|
|
30
|
+
|
|
31
|
+
- Updated EN/RU/FR bake, deployment and project-layout docs to describe the
|
|
32
|
+
production release artifact, promoted baked routes, root sitemap and
|
|
33
|
+
`data-mado-baked` client boot behavior.
|
|
34
|
+
|
|
5
35
|
## 0.10.0 - 2026-06-12
|
|
6
36
|
|
|
7
37
|
Surface-cleanup and API-lock release from the v1 tracker Phase B: legacy public
|
package/README.md
CHANGED
|
@@ -272,7 +272,7 @@ PoC in [`examples/cloudflare`](./examples/cloudflare/).
|
|
|
272
272
|
## Production
|
|
273
273
|
|
|
274
274
|
```bash
|
|
275
|
-
mado release # typecheck + build + bundle + bake + copy public -> out/
|
|
275
|
+
mado release # typecheck + build + bundle + bake + promote baked HTML + copy public -> out/
|
|
276
276
|
mado preview # serve out/ like a static host
|
|
277
277
|
```
|
|
278
278
|
|
|
@@ -109,6 +109,16 @@ export function render(result, container) {
|
|
|
109
109
|
}
|
|
110
110
|
existing.dispose();
|
|
111
111
|
}
|
|
112
|
+
// `mado bake` writes static first-paint markup into #app and marks the
|
|
113
|
+
// container. That markup is not hydrated: once the client app starts, Mado
|
|
114
|
+
// owns the container again and replaces the baked DOM with live bindings.
|
|
115
|
+
const isBakedContainer = !existing &&
|
|
116
|
+
"hasAttribute" in container &&
|
|
117
|
+
container.hasAttribute("data-mado-baked");
|
|
118
|
+
if (isBakedContainer) {
|
|
119
|
+
container.replaceChildren();
|
|
120
|
+
container.removeAttribute("data-mado-baked");
|
|
121
|
+
}
|
|
112
122
|
if (!existing && container.childNodes.length > 0) {
|
|
113
123
|
warnOnce("render-unmanaged-dom", "render() called on a container with existing DOM that was not created by Mado. It will remain alongside the new render output.");
|
|
114
124
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"template.js","sourceRoot":"","sources":["../../../src/html/template.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"template.js","sourceRoot":"","sources":["../../../src/html/template.ts"],"names":[],"mappings":"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"}
|
|
@@ -36,7 +36,8 @@ One-liner to remember:
|
|
|
36
36
|
> Develop with `mado dev`. To ship: run `mado release`, then upload `out/`.
|
|
37
37
|
|
|
38
38
|
`mado release` = `typecheck` + `build` (tsc → `dist/`) + `bundle` (esbuild
|
|
39
|
-
→ `out/assets/`) + `bake` (HTML → `out/baked/`) +
|
|
39
|
+
→ `out/assets/`) + `bake` (HTML → `out/baked/`) + promote baked HTML and
|
|
40
|
+
`sitemap.xml` into deployable `out/` paths + copy `public/*` → `out/`.
|
|
40
41
|
|
|
41
42
|
You almost never need to look inside `dist/`. It exists so the dev browser can
|
|
42
43
|
load native ESM modules without a bundler during development. In production
|
|
@@ -114,4 +115,4 @@ Precedence: built-in defaults < `mado.config.json` < CLI flags
|
|
|
114
115
|
`process.env` and import that one module everywhere.
|
|
115
116
|
- ❌ Tests mixed with code — put them in `test/`.
|
|
116
117
|
- ❌ `examples/` folder — the framework repository has examples, your app
|
|
117
|
-
does not need one.
|
|
118
|
+
does not need one.
|
package/docs/en/13-deployment.md
CHANGED
|
@@ -12,9 +12,11 @@ out/
|
|
|
12
12
|
├── assets/ ← hashed bundles (main-ABC.js, chunk-XYZ.js, …)
|
|
13
13
|
│ ├── *.gz ← precompressed gzip (gzip_static / Accept-Encoding)
|
|
14
14
|
│ └── *.br ← precompressed brotli (brotli_static / Accept-Encoding)
|
|
15
|
-
├── baked/ ←
|
|
15
|
+
├── baked/ ← bake output copy for inspection/debugging
|
|
16
16
|
│ ├── <route>/index.html
|
|
17
17
|
│ └── sitemap.xml
|
|
18
|
+
├── <route>/index.html ← prerendered SEO HTML promoted for static hosts
|
|
19
|
+
├── sitemap.xml ← sitemap promoted to the site root
|
|
18
20
|
├── favicon.svg ← your public/ assets copied verbatim
|
|
19
21
|
├── _redirects ← Cloudflare Pages / Netlify SPA fallback
|
|
20
22
|
└── _headers ← Cloudflare Pages / Netlify cache rules
|
|
@@ -31,9 +33,9 @@ mado release
|
|
|
31
33
|
mado preview # http://localhost:4173 — serves out/ exactly as a static host would
|
|
32
34
|
```
|
|
33
35
|
|
|
34
|
-
`mado preview`
|
|
35
|
-
`.gz` over raw,
|
|
36
|
-
`index.html` for unknown paths.
|
|
36
|
+
`mado preview` serves the final `out/` directory like a static host: it picks
|
|
37
|
+
`.br` over `.gz` over raw, serves promoted baked HTML when a route has an
|
|
38
|
+
`index.html`, and falls back to `index.html` for unknown SPA paths.
|
|
37
39
|
|
|
38
40
|
---
|
|
39
41
|
|
|
@@ -79,8 +81,9 @@ npx wrangler pages deploy out --project-name=myapp
|
|
|
79
81
|
- The generated `_redirects` (`/* /index.html 200`) gives you SPA fallback.
|
|
80
82
|
- The generated `_headers` (immutable cache for `/assets/*`, `no-cache` for
|
|
81
83
|
HTML) is honored by CF Pages.
|
|
82
|
-
- Baked routes (`out
|
|
83
|
-
fallback because CF Pages matches static
|
|
84
|
+
- Baked routes are promoted to real route files (`out/<route>/index.html`),
|
|
85
|
+
so they take priority over the SPA fallback because CF Pages matches static
|
|
86
|
+
files first.
|
|
84
87
|
|
|
85
88
|
For preview branches, set the same build command in the CF Pages project:
|
|
86
89
|
|
|
@@ -189,4 +192,4 @@ jobs:
|
|
|
189
192
|
|
|
190
193
|
See also: [`02-project-layout.md`](./02-project-layout.md) for the
|
|
191
194
|
`src/`/`dist/`/`public/`/`out/` model and [`03-static-bake.md`](./03-static-bake.md)
|
|
192
|
-
for the SEO bake mechanics.
|
|
195
|
+
for the SEO bake mechanics.
|
|
@@ -55,8 +55,10 @@ export default routes(manifest);
|
|
|
55
55
|
|
|
56
56
|
## Output
|
|
57
57
|
|
|
58
|
-
By default
|
|
59
|
-
|
|
58
|
+
By default standalone `mado bake` writes baked pages under `out/baked/`.
|
|
59
|
+
`mado release` uses the bundled production shell, keeps that `out/baked/` copy
|
|
60
|
+
for inspection, promotes baked HTML into real route paths inside `out/`, and
|
|
61
|
+
copies the generated sitemap to `out/sitemap.xml`:
|
|
60
62
|
|
|
61
63
|
```bash
|
|
62
64
|
mado release
|
|
@@ -65,6 +67,12 @@ tree out
|
|
|
65
67
|
|
|
66
68
|
The deployable folder is `out/`, not `dist/`.
|
|
67
69
|
|
|
70
|
+
## Client boot
|
|
71
|
+
|
|
72
|
+
Baked HTML marks `#app` with `data-mado-baked`. This is not hydration: when the
|
|
73
|
+
client app starts, `render()` replaces the baked DOM with live bindings. The
|
|
74
|
+
first response contains SEO/first-paint HTML, then the SPA takes over normally.
|
|
75
|
+
|
|
68
76
|
## Unsupported values
|
|
69
77
|
|
|
70
78
|
Bake intentionally fails loudly instead of writing `[object Object]`. If a baked
|
|
@@ -4,20 +4,14 @@ Chaque nouveau projet Mado a la même structure. C'est une convention **obligato
|
|
|
4
4
|
|
|
5
5
|
```
|
|
6
6
|
my-app/
|
|
7
|
-
├── package.json #
|
|
8
|
-
├── tsconfig.json #
|
|
9
|
-
├──
|
|
10
|
-
├── .
|
|
11
|
-
├──
|
|
12
|
-
├── scripts/
|
|
13
|
-
│ ├── bundle.mjs # bundle de production esbuild
|
|
14
|
-
│ └── new.mjs # générateur de pages
|
|
15
|
-
├── templates/ # templates pour new.mjs
|
|
16
|
-
├── docs/ # documentation du projet (vous pouvez copier nos guides)
|
|
17
|
-
├── public/ # assets statiques (favicon, manifests)
|
|
7
|
+
├── package.json # runtime dep: @madojs/mado
|
|
8
|
+
├── tsconfig.json # strict TS, ES2022, Bundler resolution
|
|
9
|
+
├── mado.config.json # configuration dev/build/bake/bundle
|
|
10
|
+
├── index.html # shell SPA et template pour bake
|
|
11
|
+
├── public/ # assets statiques (favicon, images, robots.txt)
|
|
18
12
|
└── src/
|
|
19
|
-
├── main.ts # entrée :
|
|
20
|
-
├── routes.ts #
|
|
13
|
+
├── main.ts # entrée : mount router dans #app
|
|
14
|
+
├── routes.ts # route manifest (default + named manifest)
|
|
21
15
|
├── pages/ # une page = un fichier = `export default page({...})`
|
|
22
16
|
├── components/ # composants réutilisables (x-*)
|
|
23
17
|
├── layouts/ # pages de layout (pour nested)
|
|
@@ -28,6 +22,19 @@ my-app/
|
|
|
28
22
|
└── ... # utilitaires, types, règles métier
|
|
29
23
|
```
|
|
30
24
|
|
|
25
|
+
## États Des Artefacts
|
|
26
|
+
|
|
27
|
+
| Dossier | Ce que c'est | Écrit par | Déployer ? |
|
|
28
|
+
|---|---|---|---|
|
|
29
|
+
| `src/` | sources TypeScript | vous | non |
|
|
30
|
+
| `dist/` | output `tsc`, native ESM pour dev | `mado build` | non |
|
|
31
|
+
| `public/` | assets statiques écrits par vous | vous | via `out/` |
|
|
32
|
+
| `out/` | artefact déployable : shell SPA + bundles + HTML baked promu | `mado release` | oui |
|
|
33
|
+
|
|
34
|
+
`mado release` = `typecheck` + `build` (`dist/`) + `bundle`
|
|
35
|
+
(`out/assets/`) + `bake` (`out/baked/`) + promotion du HTML baked et de
|
|
36
|
+
`sitemap.xml` dans les chemins déployables de `out/` + copie de `public/*`.
|
|
37
|
+
|
|
31
38
|
## Où mettre un nouveau fichier ?
|
|
32
39
|
|
|
33
40
|
| Quoi | Où |
|
package/docs/fr/13-deployment.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Déploiement
|
|
2
2
|
|
|
3
|
-
Une commande, un artefact :
|
|
3
|
+
Une commande, un artefact déployable :
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
6
|
mado release
|
|
@@ -10,30 +10,51 @@ Résultat :
|
|
|
10
10
|
|
|
11
11
|
```txt
|
|
12
12
|
out/
|
|
13
|
-
├── index.html
|
|
14
|
-
├── assets/
|
|
15
|
-
├──
|
|
16
|
-
|
|
17
|
-
|
|
13
|
+
├── index.html ← shell SPA ou HTML baked promu pour /
|
|
14
|
+
├── assets/ ← bundles hashés (main-ABC.js, chunk-XYZ.js, ...)
|
|
15
|
+
│ ├── *.gz ← gzip précompressé
|
|
16
|
+
│ └── *.br ← brotli précompressé
|
|
17
|
+
├── baked/ ← copie de bake pour inspection/debugging
|
|
18
|
+
│ ├── <route>/index.html
|
|
19
|
+
│ └── sitemap.xml
|
|
20
|
+
├── <route>/index.html ← HTML baked promu pour les hébergeurs statiques
|
|
21
|
+
├── sitemap.xml ← sitemap à la racine du site
|
|
22
|
+
├── _redirects ← fallback SPA Cloudflare Pages / Netlify
|
|
23
|
+
└── _headers ← règles de cache
|
|
18
24
|
```
|
|
19
25
|
|
|
20
26
|
Déploie `out/` sur nginx, Cloudflare Pages, Netlify, S3/CloudFront ou GitHub
|
|
21
|
-
Pages.
|
|
27
|
+
Pages. Ne déploie pas `dist/` : c'est un output interne.
|
|
28
|
+
|
|
29
|
+
## Preview
|
|
22
30
|
|
|
23
31
|
```bash
|
|
24
32
|
mado release
|
|
25
33
|
mado preview
|
|
26
34
|
```
|
|
27
35
|
|
|
28
|
-
`mado preview` sert `out/` comme un hébergeur statique :
|
|
29
|
-
fallback SPA ensuite.
|
|
36
|
+
`mado preview` sert le `out/` final comme un hébergeur statique : fichiers réels
|
|
37
|
+
d'abord (`/<route>/index.html` si la route est baked), fallback SPA ensuite.
|
|
38
|
+
Preview ne fait plus de mapping virtuel depuis `out/baked/`, donc il vérifie
|
|
39
|
+
exactement ce qui sera déployé.
|
|
30
40
|
|
|
31
|
-
|
|
41
|
+
## VPS + nginx
|
|
32
42
|
|
|
33
43
|
```bash
|
|
34
44
|
mado release
|
|
35
45
|
rsync -avz --delete out/ user@server:/var/www/myapp/
|
|
36
46
|
```
|
|
37
47
|
|
|
38
|
-
Le `nginx.conf` fourni gère cache immutable pour les bundles hashés, no-cache
|
|
39
|
-
pour HTML, et fallback SPA pour les deep links.
|
|
48
|
+
Le `nginx.conf` fourni gère le cache immutable pour les bundles hashés, no-cache
|
|
49
|
+
pour HTML, et le fallback SPA pour les deep links.
|
|
50
|
+
|
|
51
|
+
## Cloudflare / Netlify
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
mado release
|
|
55
|
+
npx wrangler pages deploy out --project-name=myapp
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
`_redirects` et `_headers` sont générés automatiquement si tu n'en fournis pas.
|
|
59
|
+
Les routes baked sont promues en vrais fichiers (`out/<route>/index.html`), donc
|
|
60
|
+
l'hébergeur statique les sert avant le fallback SPA.
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
# Bake
|
|
1
|
+
# Bake Cookbook
|
|
2
2
|
|
|
3
3
|
`mado bake` rend certaines routes en HTML statique. C'est pour le SEO et un
|
|
4
4
|
premier rendu rapide, pas pour SSR + hydration.
|
|
5
5
|
|
|
6
|
+
## Page Minimale
|
|
7
|
+
|
|
6
8
|
```ts
|
|
7
9
|
export default page({
|
|
8
10
|
head: () => ({ title: "Products", description: "Catalog" }),
|
|
@@ -21,15 +23,66 @@ export default page({
|
|
|
21
23
|
```
|
|
22
24
|
|
|
23
25
|
Dans les vues baked, préfère les tableaux simples (`items.map(...)`). Les
|
|
24
|
-
directives runtime comme `each()` sont pour le navigateur.
|
|
26
|
+
directives runtime comme keyed `each()` sont pour le navigateur.
|
|
27
|
+
|
|
28
|
+
## Routes Dynamiques
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
export default page<{ slug: string }>({
|
|
32
|
+
head: ({ slug }, data) => ({ title: data.title, canonical: `/blog/${slug}` }),
|
|
33
|
+
view: ({ data }) => html`<article>${unsafeHTML(data.html)}</article>`,
|
|
34
|
+
bake: {
|
|
35
|
+
paths: async () => (await api.posts()).map((p) => ({ slug: p.slug })),
|
|
36
|
+
data: ({ slug }) => api.post(slug),
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Utilise `unsafeHTML()` seulement pour du HTML fiable ou déjà nettoyé.
|
|
42
|
+
|
|
43
|
+
## Route Manifest
|
|
44
|
+
|
|
45
|
+
`mado bake` a besoin du manifest source :
|
|
25
46
|
|
|
26
47
|
```ts
|
|
27
48
|
export const manifest = {
|
|
28
49
|
"/": () => import("./pages/home.js"),
|
|
29
50
|
"/blog/:slug": () => import("./pages/blog-post.js"),
|
|
30
51
|
};
|
|
52
|
+
|
|
31
53
|
export default routes(manifest);
|
|
32
54
|
```
|
|
33
55
|
|
|
34
|
-
|
|
35
|
-
|
|
56
|
+
## Output
|
|
57
|
+
|
|
58
|
+
Standalone `mado bake` écrit les pages baked dans `out/baked/` par défaut.
|
|
59
|
+
`mado release` utilise le shell de production bundlé, garde cette copie
|
|
60
|
+
`out/baked/` pour inspection, promeut le HTML dans les vrais chemins de route
|
|
61
|
+
dans `out/`, et copie le sitemap vers `out/sitemap.xml`.
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
mado release
|
|
65
|
+
tree out
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Le dossier déployable est `out/`, pas `dist/`.
|
|
69
|
+
|
|
70
|
+
## Client Boot
|
|
71
|
+
|
|
72
|
+
Le HTML baked marque `#app` avec `data-mado-baked`. Ce n'est pas de
|
|
73
|
+
l'hydration : au démarrage, `render()` remplace le DOM baked par les bindings
|
|
74
|
+
vivants de l'application.
|
|
75
|
+
|
|
76
|
+
## Valeurs Non Supportées
|
|
77
|
+
|
|
78
|
+
Bake échoue volontairement au lieu d'écrire `[object Object]`. Si une vue baked
|
|
79
|
+
signale une directive non supportée :
|
|
80
|
+
|
|
81
|
+
- remplace `each()` par `items.map(...)` dans le markup baked ;
|
|
82
|
+
- garde les widgets interactifs dans des routes client-only ;
|
|
83
|
+
- assure-toi que chaque valeur peut être sérialisée en HTML statique.
|
|
84
|
+
|
|
85
|
+
## Canonical Links
|
|
86
|
+
|
|
87
|
+
Passe `--base-url` ou configure `bake.baseUrl` dans `mado.config.json` pour que
|
|
88
|
+
les liens canonical et le sitemap pointent vers la production.
|
|
@@ -1,23 +1,17 @@
|
|
|
1
|
-
# Project
|
|
1
|
+
# Project Layout
|
|
2
2
|
|
|
3
3
|
Каждый new-проект на Mado имеет одинаковую структуру. Это **обязательное** соглашение.
|
|
4
4
|
|
|
5
5
|
```
|
|
6
6
|
my-app/
|
|
7
|
-
├── package.json #
|
|
8
|
-
├── tsconfig.json #
|
|
9
|
-
├──
|
|
10
|
-
├── .
|
|
11
|
-
├──
|
|
12
|
-
├── scripts/
|
|
13
|
-
│ ├── bundle.mjs # esbuild прод-бандл
|
|
14
|
-
│ └── new.mjs # скаффолд страницы
|
|
15
|
-
├── templates/ # шаблоны для new.mjs
|
|
16
|
-
├── docs/ # проектные доки (можно копировать наши гайды)
|
|
17
|
-
├── public/ # статика (favicon, манифесты)
|
|
7
|
+
├── package.json # runtime dep: @madojs/mado
|
|
8
|
+
├── tsconfig.json # strict TS, ES2022, Bundler resolution
|
|
9
|
+
├── mado.config.json # dev/build/bake/bundle config
|
|
10
|
+
├── index.html # SPA shell и template для bake
|
|
11
|
+
├── public/ # статика (favicon, images, robots.txt)
|
|
18
12
|
└── src/
|
|
19
|
-
├── main.ts # точка входа:
|
|
20
|
-
├── routes.ts #
|
|
13
|
+
├── main.ts # точка входа: mount router в #app
|
|
14
|
+
├── routes.ts # route manifest (default + named manifest)
|
|
21
15
|
├── pages/ # одна страница = один файл = `export default page({...})`
|
|
22
16
|
├── components/ # переиспользуемые компоненты (x-*)
|
|
23
17
|
├── layouts/ # layout-страницы (для nested)
|
|
@@ -28,6 +22,19 @@ my-app/
|
|
|
28
22
|
└── ... # утилиты, типы, бизнес-правила
|
|
29
23
|
```
|
|
30
24
|
|
|
25
|
+
## Artifact States
|
|
26
|
+
|
|
27
|
+
| Folder | Что это | Кто пишет | Deploy? |
|
|
28
|
+
|---|---|---|---|
|
|
29
|
+
| `src/` | исходники TypeScript | ты | no |
|
|
30
|
+
| `dist/` | output `tsc`, native ESM для dev | `mado build` | no |
|
|
31
|
+
| `public/` | авторская статика | ты | через `out/` |
|
|
32
|
+
| `out/` | deploy artifact: SPA shell + bundles + promoted baked HTML | `mado release` | yes |
|
|
33
|
+
|
|
34
|
+
`mado release` = `typecheck` + `build` (`dist/`) + `bundle`
|
|
35
|
+
(`out/assets/`) + `bake` (`out/baked/`) + promote baked HTML и
|
|
36
|
+
`sitemap.xml` в deployable `out/` paths + copy `public/*`.
|
|
37
|
+
|
|
31
38
|
## Куда положить новый файл?
|
|
32
39
|
|
|
33
40
|
| Что | Куда |
|
|
@@ -54,4 +61,4 @@ my-app/
|
|
|
54
61
|
|
|
55
62
|
- ❌ Конфиги билдеров (webpack, rollup, vite) — у нас их нет.
|
|
56
63
|
- ❌ `.env`-файлы — env читается из `process.env`/`import.meta.env` в `lib/config.ts`.
|
|
57
|
-
- ❌ Тесты вперемешку с кодом — все в `test/`.
|
|
64
|
+
- ❌ Тесты вперемешку с кодом — все в `test/`.
|
package/docs/ru/13-deployment.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Deployment
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Одна команда, один deploy artifact:
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
6
|
mado release
|
|
@@ -10,15 +10,21 @@ mado release
|
|
|
10
10
|
|
|
11
11
|
```txt
|
|
12
12
|
out/
|
|
13
|
-
├── index.html
|
|
14
|
-
├── assets/
|
|
15
|
-
├──
|
|
16
|
-
|
|
17
|
-
|
|
13
|
+
├── index.html ← SPA shell или promoted baked HTML для /
|
|
14
|
+
├── assets/ ← hashed bundles (main-ABC.js, chunk-XYZ.js, ...)
|
|
15
|
+
│ ├── *.gz ← precompressed gzip
|
|
16
|
+
│ └── *.br ← precompressed brotli
|
|
17
|
+
├── baked/ ← копия результата bake для inspection/debugging
|
|
18
|
+
│ ├── <route>/index.html
|
|
19
|
+
│ └── sitemap.xml
|
|
20
|
+
├── <route>/index.html ← promoted baked HTML для static hosts
|
|
21
|
+
├── sitemap.xml ← sitemap в root сайта
|
|
22
|
+
├── _redirects ← Cloudflare Pages / Netlify SPA fallback
|
|
23
|
+
└── _headers ← cache rules
|
|
18
24
|
```
|
|
19
25
|
|
|
20
26
|
`out/` можно деплоить на nginx, Cloudflare Pages, Netlify, S3/CloudFront или
|
|
21
|
-
GitHub Pages.
|
|
27
|
+
GitHub Pages. Не деплой `dist/`: это внутренний output для dev/build.
|
|
22
28
|
|
|
23
29
|
## Preview
|
|
24
30
|
|
|
@@ -27,8 +33,10 @@ mado release
|
|
|
27
33
|
mado preview
|
|
28
34
|
```
|
|
29
35
|
|
|
30
|
-
`mado preview` сервит `out/` как
|
|
31
|
-
|
|
36
|
+
`mado preview` сервит финальный `out/` как обычный static host: сначала реальные
|
|
37
|
+
файлы (`/<route>/index.html`, если route был baked), потом SPA fallback в
|
|
38
|
+
`index.html`. Preview больше не делает отдельную виртуальную подстановку из
|
|
39
|
+
`out/baked/`, поэтому он проверяет ровно то, что будет загружено на хостинг.
|
|
32
40
|
|
|
33
41
|
## VPS + nginx
|
|
34
42
|
|
|
@@ -37,8 +45,8 @@ mado release
|
|
|
37
45
|
rsync -avz --delete out/ user@server:/var/www/myapp/
|
|
38
46
|
```
|
|
39
47
|
|
|
40
|
-
В репозитории есть production `nginx.conf`: hashed bundles кешируются
|
|
41
|
-
|
|
48
|
+
В репозитории есть production `nginx.conf`: hashed bundles кешируются immutable,
|
|
49
|
+
HTML идет с `no-cache`, deep links работают через SPA fallback.
|
|
42
50
|
|
|
43
51
|
## Cloudflare / Netlify
|
|
44
52
|
|
|
@@ -47,9 +55,11 @@ mado release
|
|
|
47
55
|
npx wrangler pages deploy out --project-name=myapp
|
|
48
56
|
```
|
|
49
57
|
|
|
50
|
-
`_redirects` и `_headers` генерируются
|
|
58
|
+
`_redirects` и `_headers` генерируются автоматически, если ты не положил свои.
|
|
59
|
+
Baked routes промотируются в реальные файлы (`out/<route>/index.html`), поэтому
|
|
60
|
+
static host отдаст их до SPA fallback.
|
|
51
61
|
|
|
52
|
-
## Cache
|
|
62
|
+
## Cache Rules
|
|
53
63
|
|
|
54
64
|
| Path | Cache-Control |
|
|
55
65
|
|---|---|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
# Bake
|
|
1
|
+
# Bake Cookbook
|
|
2
2
|
|
|
3
3
|
`mado bake` рендерит выбранные роуты в статический HTML. Это для SEO и быстрого
|
|
4
4
|
первого ответа, не SSR с hydration.
|
|
5
5
|
|
|
6
|
-
## Минимальная
|
|
6
|
+
## Минимальная Страница
|
|
7
7
|
|
|
8
8
|
```ts
|
|
9
9
|
export default page({
|
|
@@ -23,9 +23,9 @@ export default page({
|
|
|
23
23
|
```
|
|
24
24
|
|
|
25
25
|
В baked views лучше использовать обычные массивы (`items.map(...)`). Runtime
|
|
26
|
-
директивы вроде `each()` нужны браузеру.
|
|
26
|
+
директивы вроде keyed `each()` нужны браузеру.
|
|
27
27
|
|
|
28
|
-
## Dynamic
|
|
28
|
+
## Dynamic Routes
|
|
29
29
|
|
|
30
30
|
```ts
|
|
31
31
|
export default page<{ slug: string }>({
|
|
@@ -40,16 +40,50 @@ export default page<{ slug: string }>({
|
|
|
40
40
|
|
|
41
41
|
`unsafeHTML()` используй только для доверенного или заранее очищенного HTML.
|
|
42
42
|
|
|
43
|
-
## Manifest
|
|
43
|
+
## Route Manifest
|
|
44
|
+
|
|
45
|
+
`mado bake` нужен source manifest:
|
|
44
46
|
|
|
45
47
|
```ts
|
|
46
48
|
export const manifest = {
|
|
47
49
|
"/": () => import("./pages/home.js"),
|
|
48
50
|
"/blog/:slug": () => import("./pages/blog-post.js"),
|
|
49
51
|
};
|
|
52
|
+
|
|
50
53
|
export default routes(manifest);
|
|
51
54
|
```
|
|
52
55
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
+
## Output
|
|
57
|
+
|
|
58
|
+
Standalone `mado bake` по умолчанию пишет baked pages в `out/baked/`.
|
|
59
|
+
`mado release` использует bundled production shell, оставляет копию в
|
|
60
|
+
`out/baked/` для inspection, промотирует HTML в реальные route paths внутри
|
|
61
|
+
`out/` и копирует sitemap в `out/sitemap.xml`.
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
mado release
|
|
65
|
+
tree out
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Deployable folder — `out/`, не `dist/`.
|
|
69
|
+
|
|
70
|
+
## Client Boot
|
|
71
|
+
|
|
72
|
+
Baked HTML помечает `#app` атрибутом `data-mado-baked`. Это не hydration:
|
|
73
|
+
клиентский `render()` заменяет baked DOM живыми bindings при старте приложения.
|
|
74
|
+
Так первый ответ содержит SEO/first-paint HTML, а SPA после загрузки работает
|
|
75
|
+
как обычно.
|
|
76
|
+
|
|
77
|
+
## Unsupported Values
|
|
78
|
+
|
|
79
|
+
Bake намеренно падает громко вместо записи `[object Object]`. Если baked view
|
|
80
|
+
ругается на unsupported directive:
|
|
81
|
+
|
|
82
|
+
- замени `each()` на `items.map(...)` в baked markup;
|
|
83
|
+
- интерактивные виджеты оставь в client-only routes;
|
|
84
|
+
- убедись, что все значения сериализуются в статический HTML.
|
|
85
|
+
|
|
86
|
+
## Canonical Links
|
|
87
|
+
|
|
88
|
+
Передай `--base-url` или задай `bake.baseUrl` в `mado.config.json`, чтобы
|
|
89
|
+
canonical links и sitemap указывали на production.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@madojs/mado",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.1",
|
|
4
4
|
"description": "Mado — a calm browser-native SPA framework for internal tools, admin panels and business apps. Routing, forms, state and data fetching without frontend infrastructure overhead.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
package/scripts/bake.mjs
CHANGED
|
@@ -533,7 +533,10 @@ function buildHtml({ template, bodyHtml, head, bakedData, revalidate, canonical
|
|
|
533
533
|
}
|
|
534
534
|
|
|
535
535
|
const app = document.getElementById("app");
|
|
536
|
-
if (app)
|
|
536
|
+
if (app) {
|
|
537
|
+
app.setAttribute("data-mado-baked", "");
|
|
538
|
+
app.innerHTML = bodyHtml;
|
|
539
|
+
}
|
|
537
540
|
|
|
538
541
|
return "<!doctype html>\n" + document.documentElement.outerHTML;
|
|
539
542
|
}
|
package/scripts/cli.mjs
CHANGED
|
@@ -241,15 +241,28 @@ async function runRelease(rawArgs) {
|
|
|
241
241
|
// → rm -rf out/ (unless --no-clean)
|
|
242
242
|
// → mado typecheck
|
|
243
243
|
// → mado build (tsc → dist/)
|
|
244
|
-
// → mado bundle (esbuild → out/assets/, also
|
|
245
|
-
// → mado bake (HTML
|
|
244
|
+
// → mado bundle (esbuild → out/assets/, also writes out/index.html)
|
|
245
|
+
// → mado bake (HTML → out/baked/, using bundled out/index.html)
|
|
246
246
|
// → copy public/* → out/
|
|
247
|
+
// → promote baked HTML + sitemap into out/ route paths
|
|
247
248
|
//
|
|
248
249
|
// Flags are forwarded to bake/bundle.
|
|
249
250
|
const { flags: releaseFlags } = parseFlags(rawArgs);
|
|
250
251
|
const cfg = loadConfig({ projectRoot: PROJECT_ROOT });
|
|
251
|
-
const outDir = resolve(
|
|
252
|
+
const outDir = resolve(
|
|
253
|
+
cfg.projectRoot,
|
|
254
|
+
typeof releaseFlags.out === "string" ? releaseFlags.out : cfg.build.out ?? "out",
|
|
255
|
+
);
|
|
252
256
|
const publicDir = resolve(cfg.projectRoot, cfg.build.publicDir ?? "public");
|
|
257
|
+
const bundledHtml = join(outDir, "index.html");
|
|
258
|
+
const bakedDir = resolve(
|
|
259
|
+
cfg.projectRoot,
|
|
260
|
+
cfg.bake.outDir ??
|
|
261
|
+
join(
|
|
262
|
+
typeof releaseFlags.out === "string" ? releaseFlags.out : cfg.build.out ?? "out",
|
|
263
|
+
"baked",
|
|
264
|
+
),
|
|
265
|
+
);
|
|
253
266
|
|
|
254
267
|
console.log(`[release] context: ${cfg.context}`);
|
|
255
268
|
console.log(`[release] artifact: ${outDir}`);
|
|
@@ -276,8 +289,14 @@ async function runRelease(rawArgs) {
|
|
|
276
289
|
console.log("[release] step 3/5 bundle (esbuild → out/assets/)");
|
|
277
290
|
await runNodeScript("scripts/bundle.mjs", rawArgs);
|
|
278
291
|
|
|
279
|
-
console.log("[release] step 4/5 bake (out/baked
|
|
280
|
-
await runNodeScript("scripts/bake.mjs",
|
|
292
|
+
console.log("[release] step 4/5 bake (out/baked/, bundled shell)");
|
|
293
|
+
await runNodeScript("scripts/bake.mjs", [
|
|
294
|
+
...rawArgs,
|
|
295
|
+
"--template",
|
|
296
|
+
bundledHtml,
|
|
297
|
+
"--out",
|
|
298
|
+
bakedDir,
|
|
299
|
+
]);
|
|
281
300
|
|
|
282
301
|
console.log("[release] step 5/5 copy public/ → out/");
|
|
283
302
|
if (existsSync(publicDir)) {
|
|
@@ -288,6 +307,14 @@ async function runRelease(rawArgs) {
|
|
|
288
307
|
console.log(`[release] no ${publicDir}, skipping`);
|
|
289
308
|
}
|
|
290
309
|
|
|
310
|
+
const promoted = await promoteBakedHtml(bakedDir, outDir);
|
|
311
|
+
if (promoted.html > 0) {
|
|
312
|
+
console.log(`[release] promoted ${promoted.html} baked HTML page(s) into out/`);
|
|
313
|
+
}
|
|
314
|
+
if (promoted.sitemap) {
|
|
315
|
+
console.log(`[release] copied sitemap.xml → ${join(outDir, "sitemap.xml")}`);
|
|
316
|
+
}
|
|
317
|
+
|
|
291
318
|
// Optional CDN config files. Generated only when not already provided.
|
|
292
319
|
await writeIfMissing(
|
|
293
320
|
join(outDir, "_redirects"),
|
|
@@ -319,6 +346,40 @@ async function writeIfMissing(path, content) {
|
|
|
319
346
|
console.log(`[release] wrote ${path}`);
|
|
320
347
|
}
|
|
321
348
|
|
|
349
|
+
async function promoteBakedHtml(bakedDir, outDir) {
|
|
350
|
+
if (!existsSync(bakedDir)) return { html: 0, sitemap: false };
|
|
351
|
+
|
|
352
|
+
let html = 0;
|
|
353
|
+
|
|
354
|
+
async function walk(dir, rel = "") {
|
|
355
|
+
for (const entry of await readdir(dir, { withFileTypes: true })) {
|
|
356
|
+
const nextRel = rel ? `${rel}/${entry.name}` : entry.name;
|
|
357
|
+
const source = join(dir, entry.name);
|
|
358
|
+
if (entry.isDirectory()) {
|
|
359
|
+
await walk(source, nextRel);
|
|
360
|
+
continue;
|
|
361
|
+
}
|
|
362
|
+
if (!entry.isFile() || !entry.name.endsWith(".html")) continue;
|
|
363
|
+
const target = join(outDir, nextRel);
|
|
364
|
+
await mkdir(dirname(target), { recursive: true });
|
|
365
|
+
await copyFile(source, target);
|
|
366
|
+
html++;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
await walk(bakedDir);
|
|
371
|
+
|
|
372
|
+
const bakedSitemap = join(bakedDir, "sitemap.xml");
|
|
373
|
+
const rootSitemap = join(outDir, "sitemap.xml");
|
|
374
|
+
let sitemap = false;
|
|
375
|
+
if (existsSync(bakedSitemap)) {
|
|
376
|
+
await copyFile(bakedSitemap, rootSitemap);
|
|
377
|
+
sitemap = true;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
return { html, sitemap };
|
|
381
|
+
}
|
|
382
|
+
|
|
322
383
|
async function copyCanonicalAgentFiles(target) {
|
|
323
384
|
for (const file of ["AGENTS.md", "llms.txt"]) {
|
|
324
385
|
const source = join(PACKAGE_ROOT, file);
|
package/scripts/preview.mjs
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
// 3. Starts a static server with:
|
|
12
12
|
// - immutable cache for hashed bundles;
|
|
13
13
|
// - SPA fallback to index.html;
|
|
14
|
-
// -
|
|
14
|
+
// - exact `out/` route files before SPA fallback;
|
|
15
15
|
// - precompressed .gz / .br serving via Accept-Encoding.
|
|
16
16
|
//
|
|
17
17
|
// Goal: see production-like output locally without Docker/nginx, identical to
|
|
@@ -58,9 +58,9 @@ const OUT = resolve(
|
|
|
58
58
|
process.env.OUT_DIR ?? cfg.build.out ?? "out",
|
|
59
59
|
);
|
|
60
60
|
// Baked HTML lives in <out>/baked/ by default (see scripts/bake.mjs and
|
|
61
|
-
// mado.config.json bake.outDir).
|
|
62
|
-
//
|
|
63
|
-
// instead of
|
|
61
|
+
// mado.config.json bake.outDir). `mado release` promotes those HTML files into
|
|
62
|
+
// real route paths inside <out>/, so preview can serve exactly what a static
|
|
63
|
+
// host sees instead of applying a preview-only virtual mapping.
|
|
64
64
|
const BAKED = resolve(
|
|
65
65
|
ROOT,
|
|
66
66
|
process.env.BAKED_DIR ?? cfg.bake?.outDir ?? join(cfg.build.out ?? "out", "baked"),
|
|
@@ -215,30 +215,10 @@ function basenameSafe(p) {
|
|
|
215
215
|
async function resolveTarget(pathname) {
|
|
216
216
|
if (pathname === "/") pathname = "/index.html";
|
|
217
217
|
|
|
218
|
-
// 1) Baked HTML wins. `mado bake` writes prerendered pages into
|
|
219
|
-
// <out>/baked/<path>/index.html. Serve them with priority over the
|
|
220
|
-
// SPA shell so search engines AND human users hitting a prerendered
|
|
221
|
-
// URL see real content immediately. Without this branch preview
|
|
222
|
-
// served the empty SPA shell for every URL, which looked like a
|
|
223
|
-
// "blank page" bug even when bake had succeeded.
|
|
224
|
-
if (await exists(BAKED)) {
|
|
225
|
-
if (!extname(pathname) || pathname.endsWith("/index.html")) {
|
|
226
|
-
const bakedDir = join(BAKED, pathname.replace(/\/index\.html$/, ""));
|
|
227
|
-
const bakedIdx = join(bakedDir, "index.html");
|
|
228
|
-
if (await exists(bakedIdx)) return bakedIdx;
|
|
229
|
-
}
|
|
230
|
-
// Direct file (sitemap.xml etc.) from the baked dir.
|
|
231
|
-
const bakedFile = resolve(join(BAKED, pathname));
|
|
232
|
-
if (bakedFile.startsWith(BAKED + sep) && (await exists(bakedFile))) {
|
|
233
|
-
const s = await stat(bakedFile);
|
|
234
|
-
if (!s.isDirectory()) return bakedFile;
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
|
|
238
218
|
const candidate = resolve(join(OUT, pathname));
|
|
239
219
|
if (!candidate.startsWith(OUT + sep) && candidate !== OUT) return null;
|
|
240
220
|
|
|
241
|
-
//
|
|
221
|
+
// 1) Exact match inside out/.
|
|
242
222
|
if (await exists(candidate)) {
|
|
243
223
|
const s = await stat(candidate);
|
|
244
224
|
if (s.isDirectory()) {
|
|
@@ -249,13 +229,13 @@ async function resolveTarget(pathname) {
|
|
|
249
229
|
}
|
|
250
230
|
}
|
|
251
231
|
|
|
252
|
-
//
|
|
232
|
+
// 2) /foo → /foo/index.html (for sub-folders without trailing slash).
|
|
253
233
|
if (!extname(pathname)) {
|
|
254
234
|
const asDir = join(OUT, pathname, "index.html");
|
|
255
235
|
if (await exists(asDir)) return asDir;
|
|
256
236
|
}
|
|
257
237
|
|
|
258
|
-
//
|
|
238
|
+
// 3) SPA-fallback: any non-asset path falls back to the SPA shell so
|
|
259
239
|
// client-side routing handles it. Asset-looking paths (with an
|
|
260
240
|
// extension) deliberately 404 instead — otherwise a 200 on
|
|
261
241
|
// /missing.png would mask real bugs.
|
package/starters/admin/README.md
CHANGED
|
@@ -28,7 +28,7 @@ npm run build # tsc → dist/
|
|
|
28
28
|
npm run typecheck # tsc --noEmit
|
|
29
29
|
npm run bundle # esbuild → out/assets/
|
|
30
30
|
npm run bake # prerender baked routes → out/baked/
|
|
31
|
-
npm run release # typecheck + build + bundle + bake + copy public/ → out/
|
|
31
|
+
npm run release # typecheck + build + bundle + bake + promote baked HTML + copy public/ → out/
|
|
32
32
|
npm run preview # serve out/ locally (production rehearsal)
|
|
33
33
|
```
|
|
34
34
|
|
|
@@ -60,4 +60,4 @@ Change `mado.config.json#dev.proxy` to point at your backend in development.
|
|
|
60
60
|
See the framework docs:
|
|
61
61
|
[`docs/en/11-layouts.md`](https://github.com/madojs/mado/blob/main/docs/en/11-layouts.md),
|
|
62
62
|
[`docs/en/12-auth-and-api.md`](https://github.com/madojs/mado/blob/main/docs/en/12-auth-and-api.md),
|
|
63
|
-
[`docs/en/13-deployment.md`](https://github.com/madojs/mado/blob/main/docs/en/13-deployment.md).
|
|
63
|
+
[`docs/en/13-deployment.md`](https://github.com/madojs/mado/blob/main/docs/en/13-deployment.md).
|