@madojs/mado 0.10.1 → 0.11.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.
Files changed (219) hide show
  1. package/AGENTS.md +24 -26
  2. package/CHANGELOG.md +95 -0
  3. package/README.md +22 -47
  4. package/TODO.md +52 -48
  5. package/dist/src/component.d.ts +2 -1
  6. package/dist/src/component.js +5 -2
  7. package/dist/src/component.js.map +1 -1
  8. package/dist/src/each.d.ts +1 -1
  9. package/dist/src/each.js +1 -1
  10. package/dist/src/each.js.map +1 -1
  11. package/dist/src/html/bindings.js +3 -3
  12. package/dist/src/html/bindings.js.map +1 -1
  13. package/dist/src/index.d.ts +11 -6
  14. package/dist/src/index.js +5 -3
  15. package/dist/src/index.js.map +1 -1
  16. package/dist/src/lazy.d.ts +1 -1
  17. package/dist/src/lazy.js +1 -1
  18. package/dist/src/lazy.js.map +1 -1
  19. package/dist/src/page.d.ts +17 -21
  20. package/dist/src/page.js +7 -12
  21. package/dist/src/page.js.map +1 -1
  22. package/dist/src/router/manifest.d.ts +1 -1
  23. package/dist/src/router/manifest.js +21 -13
  24. package/dist/src/router/manifest.js.map +1 -1
  25. package/dist/src/router/match.d.ts +2 -2
  26. package/dist/src/router/match.js +3 -3
  27. package/dist/src/router/match.js.map +1 -1
  28. package/dist/src/router/navigation.js +1 -1
  29. package/dist/src/router/navigation.js.map +1 -1
  30. package/dist/src/vite/index.d.ts +10 -0
  31. package/dist/src/vite/index.js +33 -0
  32. package/dist/src/vite/index.js.map +1 -0
  33. package/docs/en/00-the-mado-way.md +25 -12
  34. package/docs/en/01-routing.md +90 -142
  35. package/docs/en/02-project-layout.md +59 -53
  36. package/docs/en/03-static-bake.md +5 -6
  37. package/docs/en/05-why-mado.md +6 -6
  38. package/docs/en/06-for-backenders.md +18 -22
  39. package/docs/en/08-llm-zero-history-test.md +9 -14
  40. package/docs/en/09-shadow-vs-light-dom.md +28 -36
  41. package/docs/en/10-app-architecture.md +158 -96
  42. package/docs/en/11-layouts.md +22 -24
  43. package/docs/en/12-auth-and-api.md +89 -182
  44. package/docs/en/13-deployment.md +18 -22
  45. package/docs/en/14-testing.md +4 -4
  46. package/docs/en/16-bake-cookbook.md +11 -12
  47. package/docs/en/18-api-freeze-map.md +6 -4
  48. package/docs/en/20-v1-stability.md +1 -1
  49. package/docs/fr/00-the-mado-way.md +55 -90
  50. package/docs/fr/01-routing.md +70 -152
  51. package/docs/fr/02-project-layout.md +61 -42
  52. package/docs/fr/03-static-bake.md +1 -1
  53. package/docs/fr/05-why-mado.md +6 -6
  54. package/docs/fr/06-for-backenders.md +7 -7
  55. package/docs/fr/08-llm-zero-history-test.md +21 -48
  56. package/docs/fr/09-shadow-vs-light-dom.md +43 -162
  57. package/docs/fr/10-app-architecture.md +110 -33
  58. package/docs/fr/11-layouts.md +24 -12
  59. package/docs/fr/12-auth-and-api.md +63 -22
  60. package/docs/fr/13-deployment.md +7 -10
  61. package/docs/fr/14-testing.md +1 -1
  62. package/docs/fr/16-bake-cookbook.md +2 -2
  63. package/docs/fr/18-api-freeze-map.md +1 -1
  64. package/docs/fr/20-v1-stability.md +1 -1
  65. package/docs/recipes/nginx/README.md +13 -0
  66. package/docs/ru/00-the-mado-way.md +53 -75
  67. package/docs/ru/01-routing.md +68 -143
  68. package/docs/ru/02-project-layout.md +61 -41
  69. package/docs/ru/03-static-bake.md +2 -2
  70. package/docs/ru/05-why-mado.md +6 -6
  71. package/docs/ru/06-for-backenders.md +7 -7
  72. package/docs/ru/08-llm-zero-history-test.md +9 -14
  73. package/docs/ru/09-shadow-vs-light-dom.md +43 -178
  74. package/docs/ru/10-app-architecture.md +115 -63
  75. package/docs/ru/11-layouts.md +24 -24
  76. package/docs/ru/12-auth-and-api.md +57 -35
  77. package/docs/ru/13-deployment.md +7 -11
  78. package/docs/ru/14-testing.md +1 -1
  79. package/docs/ru/16-bake-cookbook.md +12 -6
  80. package/docs/ru/18-api-freeze-map.md +5 -3
  81. package/docs/ru/20-v1-stability.md +1 -1
  82. package/docs/uk/00-the-mado-way.md +70 -44
  83. package/docs/uk/01-routing.md +41 -47
  84. package/docs/uk/02-project-layout.md +68 -41
  85. package/docs/uk/03-static-bake.md +1 -2
  86. package/docs/uk/06-for-backenders.md +3 -3
  87. package/docs/uk/08-llm-zero-history-test.md +22 -24
  88. package/docs/uk/09-shadow-vs-light-dom.md +37 -86
  89. package/docs/uk/10-app-architecture.md +72 -31
  90. package/docs/uk/11-layouts.md +25 -12
  91. package/docs/uk/12-auth-and-api.md +58 -22
  92. package/docs/uk/13-deployment.md +4 -3
  93. package/docs/uk/14-testing.md +1 -1
  94. package/docs/uk/18-api-freeze-map.md +1 -1
  95. package/docs/uk/20-v1-stability.md +1 -1
  96. package/llms.txt +14 -15
  97. package/package.json +18 -11
  98. package/scripts/_config.mjs +15 -161
  99. package/scripts/bake.mjs +74 -63
  100. package/scripts/cli/generate.mjs +348 -0
  101. package/scripts/cli/help.mjs +27 -0
  102. package/scripts/cli/index.mjs +79 -0
  103. package/scripts/cli/init.mjs +153 -0
  104. package/scripts/cli/release.mjs +152 -0
  105. package/scripts/cli/run.mjs +96 -0
  106. package/scripts/cli.mjs +2 -621
  107. package/scripts/package-smoke.mjs +4 -1
  108. package/scripts/preview.mjs +13 -37
  109. package/scripts/size-budget.mjs +5 -2
  110. package/scripts/vite.default.mjs +11 -0
  111. package/starters/default/.editorconfig +12 -0
  112. package/starters/default/README.md +74 -0
  113. package/starters/default/eslint.config.mjs +256 -0
  114. package/starters/default/index.html +13 -0
  115. package/starters/default/package.json +30 -0
  116. package/starters/default/public/favicon.svg +4 -0
  117. package/starters/default/src/app.routes.ts +39 -0
  118. package/starters/default/src/layouts/app-shell.layout.ts +35 -0
  119. package/starters/default/src/layouts/auth-shell.layout.ts +17 -0
  120. package/starters/default/src/main.ts +16 -0
  121. package/starters/default/src/modules/auth/_contracts/auth-api.types.ts +17 -0
  122. package/starters/default/src/modules/auth/auth.connector.ts +45 -0
  123. package/starters/default/src/modules/auth/auth.guard.ts +22 -0
  124. package/starters/default/src/modules/auth/auth.public.ts +9 -0
  125. package/starters/default/src/modules/auth/auth.routes.ts +8 -0
  126. package/starters/default/src/modules/auth/auth.service.ts +71 -0
  127. package/starters/default/src/modules/auth/auth.types.ts +15 -0
  128. package/starters/default/src/modules/auth/login.page.ts +62 -0
  129. package/starters/default/src/modules/billing/_contracts/stripe.types.ts +17 -0
  130. package/starters/default/src/modules/billing/api/stripe.connector.ts +71 -0
  131. package/starters/default/src/modules/billing/billing.public.ts +5 -0
  132. package/starters/default/src/modules/billing/billing.routes.ts +9 -0
  133. package/starters/default/src/modules/billing/billing.types.ts +15 -0
  134. package/starters/default/src/modules/billing/components/invoice-status-badge.component.ts +43 -0
  135. package/starters/default/src/modules/billing/data/invoices.resource.ts +35 -0
  136. package/starters/default/src/modules/billing/pages/invoice-detail.page.ts +70 -0
  137. package/starters/default/src/modules/billing/pages/invoices-list.page.ts +73 -0
  138. package/starters/default/src/modules/home/home.page.ts +34 -0
  139. package/starters/default/src/modules/home/not-found.page.ts +11 -0
  140. package/starters/default/src/shared/http/http-client.ts +86 -0
  141. package/starters/default/src/shared/http/http-error.ts +37 -0
  142. package/starters/default/src/shared/http/interceptors.ts +59 -0
  143. package/starters/default/src/shared/lib/format-date.ts +19 -0
  144. package/starters/default/src/shared/styles/content.css +70 -0
  145. package/starters/default/src/shared/styles/reset.css +32 -0
  146. package/starters/default/src/shared/styles/shell.css +57 -0
  147. package/starters/default/src/shared/styles/tokens.css +44 -0
  148. package/starters/default/src/shared/ui/x-button.component.ts +49 -0
  149. package/starters/default/src/shared/ui/x-spinner.component.ts +22 -0
  150. package/starters/default/src/styles.d.ts +1 -0
  151. package/starters/default/src/vite-env.d.ts +1 -0
  152. package/starters/default/tsconfig.json +24 -0
  153. package/starters/default/vite.config.ts +9 -0
  154. package/MADO_V1_PLAN.md +0 -179
  155. package/ROADMAP.md +0 -178
  156. package/dist/src/html.d.ts +0 -18
  157. package/dist/src/html.js +0 -17
  158. package/dist/src/html.js.map +0 -1
  159. package/dist/src/router.d.ts +0 -13
  160. package/dist/src/router.js +0 -13
  161. package/dist/src/router.js.map +0 -1
  162. package/scripts/bundle.mjs +0 -212
  163. package/scripts/llm-zero-history-smoke.mjs +0 -93
  164. package/scripts/new.mjs +0 -80
  165. package/scripts/showcase-regression.mjs +0 -392
  166. package/server/serve.mjs +0 -455
  167. package/starters/admin/README.md +0 -63
  168. package/starters/admin/index.html +0 -28
  169. package/starters/admin/mado.config.json +0 -22
  170. package/starters/admin/package.json +0 -24
  171. package/starters/admin/public/favicon.svg +0 -4
  172. package/starters/admin/src/components/x-button.ts +0 -82
  173. package/starters/admin/src/components/x-input.ts +0 -105
  174. package/starters/admin/src/layouts/app.ts +0 -101
  175. package/starters/admin/src/layouts/auth.ts +0 -41
  176. package/starters/admin/src/lib/api.ts +0 -184
  177. package/starters/admin/src/lib/auth.ts +0 -83
  178. package/starters/admin/src/main.ts +0 -15
  179. package/starters/admin/src/pages/admin/dashboard.ts +0 -48
  180. package/starters/admin/src/pages/admin/order-detail.ts +0 -80
  181. package/starters/admin/src/pages/admin/orders.ts +0 -117
  182. package/starters/admin/src/pages/home.ts +0 -34
  183. package/starters/admin/src/pages/login.ts +0 -70
  184. package/starters/admin/src/pages/not-found.ts +0 -12
  185. package/starters/admin/src/routes.ts +0 -40
  186. package/starters/admin/src/styles/global.ts +0 -86
  187. package/starters/admin/tsconfig.json +0 -15
  188. package/starters/crud/README.md +0 -33
  189. package/starters/crud/index.html +0 -28
  190. package/starters/crud/mado.config.json +0 -20
  191. package/starters/crud/package.json +0 -24
  192. package/starters/crud/src/components/app-shell.ts +0 -56
  193. package/starters/crud/src/components/ticket-detail.ts +0 -33
  194. package/starters/crud/src/components/ticket-form.ts +0 -69
  195. package/starters/crud/src/components/ticket-list.ts +0 -66
  196. package/starters/crud/src/lib/api.ts +0 -76
  197. package/starters/crud/src/main.ts +0 -9
  198. package/starters/crud/src/pages/home.ts +0 -34
  199. package/starters/crud/src/pages/not-found.ts +0 -12
  200. package/starters/crud/src/pages/ticket-detail.ts +0 -7
  201. package/starters/crud/src/pages/ticket-new.ts +0 -7
  202. package/starters/crud/src/pages/tickets.ts +0 -7
  203. package/starters/crud/src/routes.ts +0 -11
  204. package/starters/crud/src/styles/global.ts +0 -155
  205. package/starters/crud/tsconfig.json +0 -15
  206. package/starters/minimal/README.md +0 -21
  207. package/starters/minimal/index.html +0 -28
  208. package/starters/minimal/mado.config.json +0 -20
  209. package/starters/minimal/package.json +0 -24
  210. package/starters/minimal/src/components/app-counter.ts +0 -31
  211. package/starters/minimal/src/main.ts +0 -9
  212. package/starters/minimal/src/pages/home.ts +0 -35
  213. package/starters/minimal/src/pages/not-found.ts +0 -14
  214. package/starters/minimal/src/routes.ts +0 -8
  215. package/starters/minimal/src/styles/global.ts +0 -60
  216. package/starters/minimal/tsconfig.json +0 -15
  217. package/templates/page-detail.ts +0 -63
  218. package/templates/page-form.ts +0 -94
  219. package/templates/page-list.ts +0 -79
package/MADO_V1_PLAN.md DELETED
@@ -1,179 +0,0 @@
1
- # MadoJS — Road to v1 (completed v0.6 tracker)
2
-
3
- > Completed tracker for the v0.6 product-surface push. The work agreed during
4
- > the v1 audit is now closed and squash-merged into `main` as
5
- > `feat: add v1 product surface`.
6
- >
7
- > Convention: every commit/PR that advances v1 references task IDs in its message,
8
- > e.g. `[v1 F1.2] bake.mjs: app-mode defaults + --entry/--template/--out flags`.
9
- > When a task is completed, tick its box here in the same commit.
10
-
11
- ---
12
-
13
- ## 0. Mental model (write this into docs first)
14
-
15
- Three artifact states. One deploy artifact. End of story.
16
-
17
- | Folder | What it is | Who writes | Who reads | Deployed? |
18
- |-------------|----------------------------------------------------------------|-------------------|----------------------------|-------------------|
19
- | `src/` | your source (TS) | you | `tsc`, `esbuild` | no |
20
- | `dist/` | `tsc` output (native ESM JS for the browser) | `mado build` | `mado dev`, dev browser | no (internal) |
21
- | `public/` | static assets (favicons, images, robots.txt) | you | `mado bundle` copies it | as part of `out/` |
22
- | `out/` | **the only deploy artifact**: SPA shell + bundles + baked HTML | `mado release` | nginx / CDN / CF / VPS | ✅ yes |
23
-
24
- One-liner for users:
25
- > Develop with `mado dev`. To deploy: run `mado release`, then upload `out/` anywhere.
26
-
27
- `mado release` = `typecheck` + `build` + `bundle` + `bake` + copy `public/*` → `out/`.
28
- One command. One artifact. Zero questions about where things live.
29
-
30
- ---
31
-
32
- ## 1. Problem map (consolidated)
33
-
34
- ### Layer A — Core (from the audit)
35
- - **A1** Computed leaks: no refcount/owner; once read, computed stays subscribed to deps forever.
36
- - **A2** No cycle-detection in `effect` (e.g. `effect(() => x.set(x()+1))` runs forever).
37
- - **A3** No `equals` option on `computed`.
38
- - **A4** No template directives: `unsafeHTML`, `ref`, `classMap`, `styleMap`.
39
- - **A5** `useForm`: no async validators, no field-arrays.
40
- - **A6** `resource`/`invalidate`: no typed key relationship; pattern miss is silent.
41
- - **A7** Router: no scroll restoration / focus management / error boundary.
42
- - **A8** `head()`: not obvious how dedup/cleanup happens across navigation.
43
-
44
- ### Layer B — DX, tooling, product surface (from dogfooding)
45
- - **B1** CLI/bake hardcoded to `examples/` and alias `@madojs/mado → src/index.ts` — repo-mode leaks into user apps.
46
- - **B2** No single blessed way for **layouts** (LLM and humans both guess).
47
- - **B3** No blessed way for **auth + API client + dev-proxy**.
48
- - **B4** No single config file (`mado.config.json`); everything is env vars.
49
- - **B5** Unclear model for `dist/` vs `out/` vs `public/`.
50
- - **B6** No `mado release` (one deploy artifact).
51
- - **B7** Bake silently renders `[object Object]` for `each()`.
52
- - **B8** Bake lives "next to" the app instead of inside `mado dev`.
53
- - **B9** No route guard API.
54
- - **B10** `crud` starter looks like raw scaffold; no admin-template.
55
- - **B11** Docs missing: app-architecture, layouts, auth-and-api, deployment, testing, error-handling, bake-cookbook.
56
- - **B12** Starter scaffolds contain `examples/` legacy.
57
-
58
- ---
59
-
60
- ## 2. Phases (deliver in order; phase 5 runs in parallel)
61
-
62
- ### 🟦 Phase 1 — Repo-vs-app split (P0)
63
-
64
- Goal: kill `examples/` leakage; make app-mode the default.
65
-
66
- ### 🟩 Phase 2 — One blessed way (P0)
67
-
68
- Goal: conventions over flexibility. Layouts, guards, auth, api — one recipe each.
69
-
70
- ### 🟨 Phase 3 — Bake first-class + Release pipeline (P0)
71
-
72
- Goal: pragmatic path from `mado dev` to production on VPS / Cloudflare / static CDN.
73
-
74
- ### 🟪 Phase 4 — Core hardening (P1)
75
-
76
- Goal: long-term correctness of signals, html, router, forms, head.
77
-
78
- ### 🟫 Phase 5 — Documentation (P0/P1, in parallel)
79
-
80
- Goal: a backender can ship an admin app reading only the docs.
81
-
82
- ---
83
-
84
- ## 3. Acceptance scenario for v1
85
-
86
- ```bash
87
- npm exec --package @madojs/mado@latest -- mado init dashboard --starter admin
88
- cd dashboard
89
- npm install
90
- mado dev
91
- # / → public landing
92
- # /admin → redirected to /login (guard works)
93
- # log in → admin shell with sidebar / topbar / dashboard / orders table
94
-
95
- mado release
96
- rsync -avz out/ user@vps:/var/www/dashboard/
97
- # done
98
- ```
99
-
100
- If a backend developer reaches production **without reading `bake.mjs`,
101
- without setting env vars, without asking "where does layout live?",
102
- and without an `examples/` folder in their app** — v1 is done.
103
-
104
- ---
105
-
106
- ## 4. Tracker
107
-
108
- ### Phase 1 — Repo-vs-app split ✅
109
- - [x] **F1.1** `mado.config.json` schema + loader (`scripts/_config.mjs`)
110
- - [x] **F1.2** `scripts/bake.mjs`: flags `--entry/--template/--out/--base-url`, app-mode defaults, no `examples/` alias in app-mode, loud errors instead of silent `[object Object]`
111
- - [x] **F1.3** `scripts/cli.mjs`: detect repo vs app, read `mado.config.json`, redesigned help, new `mado release` command
112
- - [x] **F1.4** Starters cleanup: `mado.config.json` and blessed CLI scripts in `starters/minimal` and `starters/crud`; no `examples/` references
113
- - [x] **F1.5** `docs/en/02-project-layout.md` rewritten around the canonical `src/`/`dist/`/`public/`/`out/` model and `mado.config.json`
114
- - [x] **F1.6** `test/config-loader.test.mjs` + `test/bake-cli.test.mjs`.
115
-
116
- ### Phase 2 — One blessed way ✅
117
- - [x] **F2.1** `layout()` factory (alias of `nested()`) exported from `src/page.ts`; flatten propagates layouts outer → inner.
118
- - [x] **F2.2** `Guard` type + guard chain in `src/router/manifest.ts`. Verdicts: void / `{ halt: true }` / `{ redirect, replace? }`. Async-aware with sync fast path; throwing guard is treated as `halt` with a `console.error`.
119
- - [x] **F2.3** `layout`, `Guard`, `GuardResult`, `nested`, `navigate` exported from `src/index.ts`.
120
- - [x] **F2.4** New starter `starters/admin/` with `layouts/{app,auth}.ts`, `lib/{api,auth}.ts`, `components/{x-button,x-input}.ts`, `pages/{home,login,not-found,admin/{dashboard,orders,order-detail}}.ts`, `styles/global.ts`, `mado.config.json`, `public/favicon.svg`. End-to-end scaffold + typecheck + build verified locally.
121
- - [x] **F2.5** CLI advertises `--starter admin` (`mado init my-app --starter admin`) and adds it to the help screen.
122
- - [x] **F2.6** Doc `docs/en/11-layouts.md` (one path + 2 alternatives with caveats).
123
- - [x] **F2.7** Doc `docs/en/12-auth-and-api.md` (blessed `api`/`auth` recipes, backend contract, dev-proxy hint).
124
- - [x] **F2.8** `test/guards-layouts.test.mjs` covers the public `layout()` alias, sync pass/halt/redirect verdicts, async guards, parent→page guard order, and throwing-guard fallback (7 tests, all green).
125
-
126
- ### Phase 3 — Bake first-class + Release pipeline ✅ (core)
127
- - [x] **F3.1** `mado release` command: typecheck + build + bundle + bake + copy `public/` → `out/` + write `_headers` / `_redirects` (`scripts/cli.mjs`).
128
- - [x] **F3.2** Deferred to v0.7: `mado dev` serves baked routes inline. Current v1 path is `mado release && mado preview`.
129
- - [x] **F3.3** `mado preview` reads `mado.config.json`, refuses to auto-build in app-mode (opt in with `PREVIEW_AUTOBUILD=1`), and emulates nginx fallback (`scripts/preview.mjs`).
130
- - [x] **F3.4** Bake raises a loud, file/route-targeted error on unsupported render values (e.g. `each()` directive objects). Covered by `test/bake-cli.test.mjs`.
131
- - [x] **F3.5** Deferred to v0.7: `mado check` runs bake-safety scan on `bake:` routes. Current v1 coverage is the loud-error contract from F3.4.
132
- - [x] **F3.6** Dev-proxy in `server/serve.mjs`: reads `dev.proxy` from `mado.config.json` and forwards matching prefixes to the upstream backend without external deps.
133
- - [x] **F3.7** `mado release` generates `out/_redirects` (`/* /index.html 200`) and `out/_headers` (immutable for `/assets/*`, no-cache for `/*.html`) when those files do not exist.
134
- - [x] **F3.8** Doc `docs/en/13-deployment.md` (VPS + nginx, Cloudflare Pages, S3/CloudFront, Netlify, GitHub Pages, CI sketch, cache matrix, troubleshooting).
135
- - [x] **F3.9** Production `nginx.conf` next to the docs (already shipped, with `gzip_static`, immutable hashed bundles, SPA fallback). Verified referenced from the deployment doc.
136
- - [x] **F3.10** `test/release-pipeline.test.mjs`: end-to-end CLI test scaffolds an app, runs `mado release`, and asserts `out/index.html`, `out/baked/index.html`, `out/_headers`, `out/_redirects`, public-asset copy, and sitemap. Also `scripts/bundle.mjs` is app-mode aware (no `examples/`-leak when run from a user app).
137
-
138
- ### Phase 4 — Core hardening
139
- - [x] **F4.1** Refcount/GC for `computed` in `src/signal.ts` + tests (`computed` releases dep subscriptions after unobserved reads and after the last subscriber is disposed).
140
- - [x] **F4.2** Cycle-detection in `effect` + `test/signal-cycle.test.mjs`
141
- - [x] **F4.3** `equals` option on `computed`
142
- - [x] **F4.4** Directives: `unsafeHTML`, `ref`, `classMap`, `styleMap` in `src/html/bindings.ts` + public exports + `test/html-directives.test.mjs`.
143
- - [x] **F4.5** `useForm`: async validators (`validateAsync`, field-level `validateAsync`, `validating`) + field arrays (`form.array()`, dotted paths, wildcard schema) + tests.
144
- - [x] **F4.6** Router: scroll restoration + focus management
145
- - [x] **F4.7** Router: `errorPage:` in manifest + error boundary
146
- - [x] **F4.8** `src/head.ts`: guaranteed cleanup on navigation (`data-mado-head` removed before re-applying)
147
- - [x] **F4.9** Tests for everything above: `test/computed-lazy.test.mjs` (F4.1/F4.3), `test/signal-cycle.test.mjs` (F4.2), `test/html-directives.test.mjs` (F4.4), `test/forms.test.mjs` (F4.5), `test/router-isolation.test.mjs` (F4.6), `test/router-error-boundary.test.mjs` (F4.7), `test/head.test.mjs` (F4.8). Final `typecheck + build + test` pass verified.
148
-
149
- ### Phase 5 — Documentation (parallel to phases 2-4)
150
- English docs for project layout, layouts, auth/API and deployment exist now.
151
- The remaining work is deeper recipes plus translation sync.
152
-
153
- - [x] **F5.1** `docs/en/10-app-architecture.md` (end-to-end admin)
154
- - [x] **F5.2** `docs/en/14-testing.md`
155
- - [x] **F5.3** `docs/en/15-error-handling.md`
156
- - [x] **F5.4** `docs/en/16-bake-cookbook.md`
157
- - [x] **F5.5** `AGENTS.md`: section "App architecture for LLM"
158
- - [x] **F5.6** `llms.txt`: new anchors
159
- - [x] **F5.7** ru/fr/uk translations for new docs (`10` through `16`)
160
-
161
- ---
162
-
163
- ## 5. Working loop
164
-
165
- 1. Pick the first unchecked box.
166
- 2. Implement it (small, focused change).
167
- 3. Run `npm test` (and `npm run typecheck` when touching `src/`).
168
- 4. Tick the box in this file in the same commit.
169
- 5. Add a one-line entry under "Unreleased" in `CHANGELOG.md`.
170
- 6. After each phase finishes, pause and review.
171
-
172
- ## 6. Context-loss safety
173
-
174
- - This file = ground truth on disk.
175
- - `task_progress` in chat = current phase/task.
176
- - Git commits tagged `[v1 Fx.y]` = time machine.
177
-
178
- If future work resumes from this plan, treat it as archive context. New tasks
179
- belong in `ROADMAP.md`, `TODO.md`, or a fresh tracker.
package/ROADMAP.md DELETED
@@ -1,178 +0,0 @@
1
- # Roadmap to v1
2
-
3
- Mado is a calm frontend stack for internal tools, admin panels and business
4
- SPA. The path to v1 is not about adding features — it is about proving the
5
- framework works reliably for its intended use case and earning trust through
6
- demonstrated quality.
7
-
8
- ## Positioning
9
-
10
- **For:** small teams and solo developers building admin panels, internal tools,
11
- backoffice apps and CRUD-heavy SPA.
12
-
13
- **Promise:** easy to build, boring to maintain. Complete app stack (routing,
14
- forms, state, data, prerender) without frontend infrastructure overhead.
15
-
16
- **Not for:** SEO-heavy sites, large teams optimizing for hiring, projects
17
- needing SSR hydration or mature plugin ecosystems.
18
-
19
- ## Current state: v0.8
20
-
21
- The core API is feature-complete for the target use case:
22
- - Signals, computed, effects with GC and cycle detection
23
- - Tagged-template `html` with directives (ref, classMap, styleMap, unsafeHTML)
24
- - Web Components with Shadow DOM
25
- - Router with nested routes, guards, lazy loading, error boundary, scroll restoration
26
- - Resource + mutation with cache, invalidation, abort, optimistic updates
27
- - Forms with schema validation, async validators, field arrays
28
- - Persisted state, context/DI, head management
29
- - Static prerender (bake) for SEO without hydration
30
- - CLI: init, dev, build, release, preview
31
- - Starters: minimal, crud, admin
32
-
33
- The API surface is feature-complete — but a line-by-line audit
34
- ([`FABLE_REPORT.md`](./FABLE_REPORT.md)) found correctness defects that break
35
- three central promises (*keyed `each` preserves state*, *glitch-free signals*,
36
- *cross-tab sync works*) plus one re-bind model that is cheaper to fix before
37
- the API freeze than after. So what is missing is, in order: **correctness,
38
- then surface lockdown, then proof and polish.**
39
-
40
- ## Milestone 0: Correctness & surface lockdown (gate to v1)
41
-
42
- Before any of the product-surface milestones below mean anything, the
43
- foundation must be correct and the public surface must be freezable. This work
44
- is tracked task-by-task (TDD: reproducing test → fix) in
45
- [`MADO_V1_TRACKER.md`](./MADO_V1_TRACKER.md):
46
-
47
- - [ ] **v0.9 — Correctness release:** fix audit findings C1–C8 (each-reorder
48
- teardown, persisted cross-tab loop, `update()` nested re-bind, `equals` +
49
- `batch` atomicity, stale form async validation, `mutation` abort semantics,
50
- silent parser drops, lifecycle/router defect pack). Fixes only, zero features,
51
- each with a regression test that was red first.
52
- - [ ] **v0.10 — Surface & cleanup:** remove legacy attribute→property reflection
53
- and `observedAttributes`, replace `exports "./*"` with an explicit subpath map,
54
- close `_testHooks`, publish the API freeze map, add CI size budgets and a
55
- package-level export test, sync `llms.txt`/`AGENTS.md` with the real API.
56
-
57
- The milestones below (live demo, reliability, recipes, docs) execute **after**
58
- Milestone 0, during the `v1.0-rc` dogfooding phase.
59
-
60
- ## V1 milestones (in priority order)
61
-
62
-
63
- ### 1. Live demo — prove it works
64
-
65
- **Goal:** a deployed, publicly accessible app that looks like real work.
66
-
67
- The showcase already has: auth, tables with filters/sort/pagination, record
68
- details, forms, nested routes, context services, role guards, loading/error
69
- states. What it needs:
70
-
71
- - [ ] Deploy to a public URL (Cloudflare Pages or similar)
72
- - [ ] Add responsive sidebar + mobile breakpoints
73
- - [ ] Add toast/feedback system
74
- - [ ] Add optimistic update example
75
- - [ ] Add persisted filters example
76
- - [ ] Polish visual quality to "not embarrassing" level
77
- - [ ] Add a comparison section: bundle size, file count, cold start vs React+Vite equivalent
78
-
79
- This is the single most important deliverable. A working app proves more than
80
- any README.
81
-
82
- ### 2. Boring reliability — earn trust
83
-
84
- **Goal:** no surprises in production.
85
-
86
- - [ ] Browser compatibility pass: Chrome, Edge, Firefox, Safari (current)
87
- - [ ] Accessibility pass: focus management, ARIA, keyboard navigation in examples
88
- - [ ] Public API audit: names, warnings, lifecycle rules, docs coverage
89
- - [ ] Public exports audit: lock the root import; decide on subpaths
90
- - [ ] Error messages audit: every runtime error should point to a fix
91
- - [ ] Long session stability test: memory leaks, listener cleanup, route churn
92
- - [ ] Migration notes template: document how to upgrade between versions
93
-
94
- ### 3. Release hygiene — look professional
95
-
96
- **Goal:** signal stability and care.
97
-
98
- - [ ] npm provenance / Trusted Publishing
99
- - [ ] GitHub repo metadata: description, topics, social preview
100
- - [ ] Semantic versioning discipline with changelog
101
- - [ ] Size reporting in CI (ESM + bundled + gzip + brotli)
102
- - [ ] Compatibility matrix in docs (browsers, TS versions)
103
- - [ ] Issue labels and triage process
104
-
105
- ### 4. Business-app recipes — solve real problems
106
-
107
- **Goal:** answer "how do I..." for common admin/internal app patterns.
108
-
109
- - [ ] Auth recipe: login, token refresh, route guards, logout
110
- - [ ] Table recipe: pagination, sort, filters, empty state, loading skeleton
111
- - [ ] Form recipe: create/edit, validation, async submit, error display
112
- - [ ] CRUD recipe: list → detail → edit → delete with optimistic updates
113
- - [ ] API integration recipe: typed client, error handling, retry
114
- - [ ] Deployment recipe: VPS, Cloudflare Pages, static CDN, Docker
115
-
116
- ### 5. External validation — prove others can use it
117
-
118
- **Goal:** at least 3 people outside the author build real apps.
119
-
120
- - [ ] Invite 2-3 developers to build internal tools with Mado
121
- - [ ] Collect friction reports and fix blockers
122
- - [ ] Get at least 1 public case study or blog post
123
- - [ ] Address real issues that emerge from pressure testing
124
-
125
- ### 6. Documentation rewrite — job-to-be-done first
126
-
127
- **Goal:** docs answer "how to build X" not "how API Y works".
128
-
129
- - [ ] Getting started guide focused on building an admin panel
130
- - [ ] "Build a backoffice app" tutorial (30 min, end-to-end)
131
- - [ ] Troubleshooting guide for common mistakes
132
- - [ ] API reference (generated or manually curated)
133
- - [ ] Remove or archive docs that don't serve the target audience
134
-
135
- ## Explicitly out of scope
136
-
137
- These will not happen before v1. They are not weaknesses — they are focus.
138
-
139
- - Runtime dependencies
140
- - UI component library / design system
141
- - SSR with hydration
142
- - Plugin ecosystem
143
- - Framework-specific build tool
144
- - Broad general-purpose narrative
145
- - Synthetic benchmark marketing
146
-
147
- ## After v1
148
-
149
- Ideas live in `TODO.md`. They become commitments only when a real app or
150
- user issue proves the need. The general direction:
151
-
152
- - Optimistic mutation primitives
153
- - Live data (SSE/WebSocket) resource
154
- - i18n helper
155
- - Accessibility utilities (focus trap, live region)
156
- - Optional tiny utility stylesheet
157
- - PWA scaffold
158
- - eslint-plugin-mado
159
-
160
- ## Success criteria for v1
161
-
162
- Not stars or hype. Instead:
163
-
164
- 1. **3+ people** have built real internal/admin apps with Mado
165
- 2. **1 polished live demo** is deployed and maintained
166
- 3. **Zero known browser bugs** in Chrome/Edge/Firefox/Safari current
167
- 4. **API is stable** — no breaking changes needed for known use cases
168
- 5. **Docs answer 90% of questions** without asking the author
169
-
170
- ## Release History
171
-
172
- | Date | Version | Notes |
173
- |---|---|---|
174
- | 2026-06-03 | v0.1-v0.3 | Core stabilization, docs foundation, publish readiness, Cloudflare PoC |
175
- | 2026-06-05 | v0.4 | Showcase CRM pressure app, browser regression suite |
176
- | 2026-06-06 | v0.5 | Unified CLI, rebrand to @madojs/mado, first npm release |
177
- | 2026-06-07 | v0.6 | App-mode CLI/config, admin starter, release pipeline, core hardening |
178
- | 2026-06-09 | v0.8 | Product surface complete, positioning pivot to internal tools focus |
@@ -1,18 +0,0 @@
1
- /**
2
- * html module entry point. The implementation lives in `./html/`:
3
- *
4
- * ./html/parser.ts — state-machine tokenizer + ParsedTemplate cache
5
- * ./html/bindings.ts — bindChild / bindAttr + keyed each
6
- * ./html/template.ts — html`` tag, instantiate(), render()
7
- * ./html/template-types.ts — shared types (TemplateResult, InstantiatedTemplate)
8
- *
9
- * This file keeps compatibility for internal imports from `"./html.js"`.
10
- * The public barrel (`src/index.ts`) also goes through this file.
11
- *
12
- * If the shell is ever removed, switch imports to `./html/template.js`.
13
- * For now it has no runtime cost.
14
- */
15
- export { html, render, instantiate } from "./html/template.js";
16
- export { unsafeHTML, ref, classMap, styleMap, isHtmlDirective, } from "./html/bindings.js";
17
- export type { TemplateResult } from "./html/template-types.js";
18
- export type { HtmlDirective, UnsafeHTMLDirective, RefCallback, RefDirective, ClassMap, ClassMapDirective, StyleMap, StyleMapDirective, } from "./html/bindings.js";
package/dist/src/html.js DELETED
@@ -1,17 +0,0 @@
1
- /**
2
- * html module entry point. The implementation lives in `./html/`:
3
- *
4
- * ./html/parser.ts — state-machine tokenizer + ParsedTemplate cache
5
- * ./html/bindings.ts — bindChild / bindAttr + keyed each
6
- * ./html/template.ts — html`` tag, instantiate(), render()
7
- * ./html/template-types.ts — shared types (TemplateResult, InstantiatedTemplate)
8
- *
9
- * This file keeps compatibility for internal imports from `"./html.js"`.
10
- * The public barrel (`src/index.ts`) also goes through this file.
11
- *
12
- * If the shell is ever removed, switch imports to `./html/template.js`.
13
- * For now it has no runtime cost.
14
- */
15
- export { html, render, instantiate } from "./html/template.js";
16
- export { unsafeHTML, ref, classMap, styleMap, isHtmlDirective, } from "./html/bindings.js";
17
- //# sourceMappingURL=html.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"html.js","sourceRoot":"","sources":["../../src/html.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAC/D,OAAO,EACL,UAAU,EACV,GAAG,EACH,QAAQ,EACR,QAAQ,EACR,eAAe,GAChB,MAAM,oBAAoB,CAAC"}
@@ -1,13 +0,0 @@
1
- /**
2
- * Router module entry point. The implementation lives in `./router/`:
3
- *
4
- * ./router/match.ts — pure pattern matching (compile/flatten/normalize)
5
- * ./router/navigation.ts — router() + navigate() + queryParam (touches window/history)
6
- * ./router/manifest.ts — routes() + prefetchPath + lazy loading + sync-fast-path
7
- *
8
- * This file keeps compatibility for internal imports from `"./router.js"`.
9
- * The public barrel (`src/index.ts`) also goes through this file.
10
- */
11
- export { router, navigate, queryParam, type RouterApi, type RouterOptions, type QueryParam, type QuerySignal, } from "./router/navigation.js";
12
- export type { RouteHandler, RouteParams, Routes, RoutesMap, } from "./router/match.js";
13
- export { routes, prefetchPath, type RoutesOptions, } from "./router/manifest.js";
@@ -1,13 +0,0 @@
1
- /**
2
- * Router module entry point. The implementation lives in `./router/`:
3
- *
4
- * ./router/match.ts — pure pattern matching (compile/flatten/normalize)
5
- * ./router/navigation.ts — router() + navigate() + queryParam (touches window/history)
6
- * ./router/manifest.ts — routes() + prefetchPath + lazy loading + sync-fast-path
7
- *
8
- * This file keeps compatibility for internal imports from `"./router.js"`.
9
- * The public barrel (`src/index.ts`) also goes through this file.
10
- */
11
- export { router, navigate, queryParam, } from "./router/navigation.js";
12
- export { routes, prefetchPath, } from "./router/manifest.js";
13
- //# sourceMappingURL=router.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"router.js","sourceRoot":"","sources":["../../src/router.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EACL,MAAM,EACN,QAAQ,EACR,UAAU,GAKX,MAAM,wBAAwB,CAAC;AAShC,OAAO,EACL,MAAM,EACN,YAAY,GAEb,MAAM,sBAAsB,CAAC"}
@@ -1,212 +0,0 @@
1
- // Production bundle through esbuild. No build config files.
2
- //
3
- // Usage:
4
- // mado bundle
5
- // mado bundle --entry src/main.ts --html index.html --out out
6
- //
7
- // Configuration precedence: built-in defaults < mado.config.json < CLI flags
8
- // < legacy env vars (ENTRY, HTML, OUT_DIR).
9
- //
10
- // What it does:
11
- // 1. Bundles `entry` with code splitting (each dynamic import → chunk),
12
- // writing hashed `main-<hash>.js` and `chunk-<hash>.js` into <out>/assets/.
13
- // 2. Computes SRI for the entry bundle.
14
- // 3. Rewrites `html` so its <script type=module> points at the hashed entry,
15
- // removes the dev importmap, and adds <link rel=modulepreload> for the
16
- // entry and all chunks. Writes the result to <out>/index.html.
17
- // 4. Pre-compresses every .js into .gz and .br for nginx_gzip_static and
18
- // Cloudflare/Netlify Accept-Encoding.
19
- // 5. Copies optional `favicon.ico`/`favicon.svg`/`assets/` from the project
20
- // root if they exist (kept for backwards compatibility; new apps should
21
- // put public assets in `public/` so `mado release` copies them).
22
- //
23
- // In repo-mode (the framework repo itself) the defaults still point at
24
- // examples/showcase so the framework can dogfood its bundle pipeline against
25
- // its biggest example.
26
-
27
- import { build } from "esbuild";
28
- import { readFile, writeFile, mkdir, cp, stat, readdir, rm } from "node:fs/promises";
29
- import { createHash } from "node:crypto";
30
- import { gzipSync, brotliCompressSync, constants as zlibConst } from "node:zlib";
31
- import { join, basename, resolve, dirname } from "node:path";
32
- import { existsSync } from "node:fs";
33
-
34
- import { loadConfig, parseFlags, resolveProjectPath } from "./_config.mjs";
35
-
36
- const { flags } = parseFlags(process.argv.slice(2));
37
- const cfg = loadConfig({});
38
-
39
- // Defaults are context-aware: in repo-mode they continue to bundle the
40
- // showcase example; in app-mode they assume the canonical layout.
41
- const defaultEntry = cfg.context === "repo"
42
- ? "examples/showcase/main.ts"
43
- : "src/main.ts";
44
- const defaultHtml = cfg.context === "repo"
45
- ? "examples/showcase/index.html"
46
- : "index.html";
47
-
48
- const ENTRY = resolveProjectPath(
49
- cfg,
50
- typeof flags.entry === "string" ? flags.entry : process.env.ENTRY ?? defaultEntry,
51
- );
52
- const HTML = resolveProjectPath(
53
- cfg,
54
- typeof flags.html === "string" ? flags.html : process.env.HTML ?? defaultHtml,
55
- );
56
- const OUT_DIR = resolveProjectPath(
57
- cfg,
58
- typeof flags.out === "string" ? flags.out : process.env.OUT_DIR ?? cfg.build.out ?? "out",
59
- );
60
- // Where the hashed bundles land. Apps want them under /assets/* to match
61
- // nginx.conf and _headers; in repo-mode we keep the historical out/main-*.js
62
- // layout so existing showcase pages continue to work.
63
- const ASSETS_REL = cfg.context === "repo" ? "" : "assets";
64
- const ASSETS_DIR = ASSETS_REL ? join(OUT_DIR, ASSETS_REL) : OUT_DIR;
65
-
66
- if (!existsSync(ENTRY)) {
67
- console.error(`[bundle] entry not found: ${ENTRY}`);
68
- console.error("[bundle] set bundle entry in mado.config.json or pass --entry <file>");
69
- process.exit(1);
70
- }
71
- if (!existsSync(HTML)) {
72
- console.error(`[bundle] html template not found: ${HTML}`);
73
- console.error("[bundle] pass --html <file> or place index.html at the project root");
74
- process.exit(1);
75
- }
76
-
77
- // Clean stale assets from previous bundles.
78
- //
79
- // Without this step, hashed chunks from prior runs (main-<oldhash>.js,
80
- // chunk-<oldhash>.js) accumulate in ASSETS_DIR. We later list ASSETS_DIR
81
- // (via readdir below) and emit <link rel="modulepreload"> for every
82
- // .js file we find — so stale chunks would be preloaded as if they were
83
- // still part of the app, polluting the production HTML and shipping dead
84
- // code over the wire. SRI is also only computed for the fresh entry, so
85
- // stale preloads would lack integrity checks.
86
- //
87
- // In app-mode the entire <out>/assets/ folder is owned by the bundler,
88
- // so wiping it is safe. In repo-mode the historical layout drops assets
89
- // directly into <out>/ alongside non-bundle artifacts, so we only remove
90
- // the recognisable hashed files there.
91
- if (ASSETS_REL) {
92
- await rm(ASSETS_DIR, { recursive: true, force: true });
93
- } else if (existsSync(ASSETS_DIR)) {
94
- for (const f of await readdir(ASSETS_DIR)) {
95
- if (/^(main|chunk|asset)-[A-Z0-9]+\.(js|css)(\.map|\.gz|\.br)?$/i.test(f)) {
96
- await rm(join(ASSETS_DIR, f), { force: true });
97
- }
98
- }
99
- }
100
- await mkdir(ASSETS_DIR, { recursive: true });
101
-
102
- console.log(`[bundle] entry: ${ENTRY}`);
103
- console.log(`[bundle] html: ${HTML}`);
104
- console.log(`[bundle] out: ${OUT_DIR}`);
105
- if (ASSETS_REL) console.log(`[bundle] assets: ${ASSETS_DIR}`);
106
-
107
- const result = await build({
108
- entryPoints: [ENTRY],
109
- bundle: true,
110
- minify: true,
111
- sourcemap: true,
112
- format: "esm",
113
- target: "es2022",
114
- splitting: true,
115
- outdir: ASSETS_DIR,
116
- entryNames: "main-[hash]",
117
- chunkNames: "chunk-[hash]",
118
- assetNames: "asset-[hash]",
119
- metafile: true,
120
- legalComments: "none",
121
- });
122
-
123
- // With splitting: true, esbuild marks all dynamic-import chunks as having
124
- // entryPoint. We identify the real app entry by the `entryNames` prefix "main-".
125
- const mainBundle = (await readdir(ASSETS_DIR))
126
- .find((f) => f.startsWith("main-") && f.endsWith(".js") && !f.endsWith(".js.map"));
127
- if (!mainBundle) {
128
- console.error("[bundle] entry not found in outputs (no main-*.js in assets dir)");
129
- process.exit(1);
130
- }
131
-
132
- // Collect all js chunks in the assets dir.
133
- const allJs = (await readdir(ASSETS_DIR)).filter((f) => f.endsWith(".js"));
134
-
135
- // Pre-compress every .js into .gz and .br.
136
- let totalRaw = 0;
137
- let totalGz = 0;
138
- let totalBr = 0;
139
- for (const f of allJs) {
140
- const p = join(ASSETS_DIR, f);
141
- const buf = await readFile(p);
142
- totalRaw += buf.length;
143
- const gz = gzipSync(buf, { level: 9 });
144
- await writeFile(`${p}.gz`, gz);
145
- totalGz += gz.length;
146
- const br = brotliCompressSync(buf, { params: { [zlibConst.BROTLI_PARAM_QUALITY]: 11 } });
147
- await writeFile(`${p}.br`, br);
148
- totalBr += br.length;
149
- }
150
-
151
- // SRI for the main bundle.
152
- const mainBuf = await readFile(join(ASSETS_DIR, mainBundle));
153
- const sri = "sha384-" + createHash("sha384").update(mainBuf).digest("base64");
154
-
155
- // Rewrite HTML: drop dev importmap, swap the <script src>, add preloads.
156
- const urlPrefix = ASSETS_REL ? `/${ASSETS_REL}/` : "/";
157
- let html = await readFile(HTML, "utf8");
158
-
159
- html = html.replace(/<script type="importmap">[\s\S]*?<\/script>/, "");
160
-
161
- const preloads = allJs
162
- .map(
163
- (f) =>
164
- ` <link rel="modulepreload" href="${urlPrefix}${f}"${
165
- f === mainBundle ? ` integrity="${sri}" crossorigin="anonymous"` : ""
166
- } />`,
167
- )
168
- .join("\n");
169
-
170
- const scriptTag = `<script type="module" src="${urlPrefix}${mainBundle}" integrity="${sri}" crossorigin="anonymous"></script>`;
171
- if (/<script\s+type="module"\s+src="[^"]+"[^>]*><\/script>/.test(html)) {
172
- html = html.replace(
173
- /<script\s+type="module"\s+src="[^"]+"[^>]*><\/script>/,
174
- scriptTag,
175
- );
176
- } else {
177
- // No matching dev <script> in the template: inject one before </body>.
178
- html = html.replace(/<\/body>/i, ` ${scriptTag}\n </body>`);
179
- }
180
-
181
- html = html.replace(/<\/head>/, `${preloads}\n </head>`);
182
-
183
- await mkdir(OUT_DIR, { recursive: true });
184
- await writeFile(join(OUT_DIR, "index.html"), html);
185
-
186
- // Backwards-compatible asset copy (repo-mode only). In app-mode the
187
- // `mado release` command copies the entire `public/` tree, which is the
188
- // recommended path for new apps.
189
- if (cfg.context === "repo") {
190
- for (const name of ["favicon.ico", "favicon.svg", "assets"]) {
191
- const src = join(cfg.projectRoot, "examples", name);
192
- if (!existsSync(src)) continue;
193
- const s = await stat(src);
194
- if (s.isDirectory()) {
195
- await cp(src, join(OUT_DIR, name), { recursive: true });
196
- } else {
197
- await cp(src, join(OUT_DIR, name));
198
- }
199
- }
200
- }
201
-
202
- // Stats
203
- const kib = (n) => (n / 1024).toFixed(1);
204
- console.log(`[bundle] chunks: ${allJs.length}`);
205
- for (const f of allJs.sort()) {
206
- const sz = (await stat(join(ASSETS_DIR, f))).size;
207
- const gz = (await stat(join(ASSETS_DIR, `${f}.gz`))).size;
208
- const star = f === mainBundle ? " *" : "";
209
- console.log(` ${f.padEnd(24)} ${kib(sz).padStart(6)} KB raw, ${kib(gz).padStart(5)} KB gz${star}`);
210
- }
211
- console.log(`[bundle] total: ${kib(totalRaw)} KB raw / ${kib(totalGz)} KB gz / ${kib(totalBr)} KB br`);
212
- console.log(`[bundle] entry SRI: ${sri}`);