@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
@@ -1,20 +1,5 @@
1
- // Mado configuration loader.
2
- //
3
- // Single source of project configuration for `mado dev`, `mado build`,
4
- // `mado bundle`, `mado bake`, `mado preview`, `mado release`.
5
- //
6
- // Lookup order (first hit wins):
7
- // 1. `mado.config.json` in PROJECT_ROOT (recommended)
8
- // 2. built-in defaults
9
- //
10
- // CLI flags always override file values, file values override defaults.
11
- //
12
- // Context detection:
13
- // - "app" : a user project that depends on `@madojs/mado`
14
- // - "repo" : the framework repository itself (has src/index.ts and examples/)
15
- //
16
- // In app-mode, defaults assume the canonical layout from MADO_V1_PLAN.md:
17
- // src/routes.ts index.html public/ dist/ out/
1
+ // Small shared CLI helpers. Mado app configuration lives in vite.config.ts;
2
+ // this file intentionally does not read any project config file.
18
3
 
19
4
  import { existsSync, readFileSync } from "node:fs";
20
5
  import { dirname, join, resolve } from "node:path";
@@ -22,177 +7,38 @@ import { fileURLToPath } from "node:url";
22
7
 
23
8
  const PACKAGE_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..");
24
9
 
25
- /**
26
- * @typedef {Object} MadoDevConfig
27
- * @property {number} [port]
28
- * @property {Record<string,string>} [proxy] // path → upstream base URL
29
- *
30
- * @typedef {Object} MadoBuildConfig
31
- * @property {string} [out] // deploy artifact dir (default: "out")
32
- * @property {string} [dist] // tsc output dir (default: "dist")
33
- * @property {string} [publicDir] // static assets dir (default: "public")
34
- *
35
- * @typedef {Object} MadoBakeConfig
36
- * @property {string} [entry] // routes module (default: "src/routes.ts")
37
- * @property {string} [template] // SPA shell html (default: "index.html")
38
- * @property {string} [baseUrl] // canonical/sitemap base
39
- * @property {string} [outDir] // override (default: build.out + "/baked")
40
- *
41
- * @typedef {Object} MadoBundleConfig
42
- * @property {boolean} [splitting]
43
- * @property {Array<"gz"|"br">} [compress]
44
- *
45
- * @typedef {Object} MadoConfig
46
- * @property {"app"|"repo"} context
47
- * @property {string} projectRoot
48
- * @property {MadoDevConfig} dev
49
- * @property {MadoBuildConfig} build
50
- * @property {MadoBakeConfig} bake
51
- * @property {MadoBundleConfig} bundle
52
- */
53
-
54
10
  /**
55
11
  * Detect whether we are inside the framework repository or inside a user app.
56
12
  *
57
- * Heuristic: the framework repo has both `src/index.ts` and an `examples/`
58
- * directory at PROJECT_ROOT. Anything else is treated as an app.
59
- *
60
13
  * @param {string} projectRoot
61
14
  * @returns {"app"|"repo"}
62
15
  */
63
16
  export function detectContext(projectRoot) {
64
17
  const looksLikeRepo =
65
18
  existsSync(join(projectRoot, "src/index.ts")) &&
66
- existsSync(join(projectRoot, "examples")) &&
67
19
  existsSync(join(projectRoot, "package.json")) &&
68
20
  safeReadJson(join(projectRoot, "package.json"))?.name === "@madojs/mado";
69
21
  return looksLikeRepo ? "repo" : "app";
70
22
  }
71
23
 
72
24
  /**
73
- * Built-in defaults per context.
74
- *
75
- * @param {"app"|"repo"} context
76
- * @returns {MadoConfig}
77
- */
78
- function defaults(context) {
79
- if (context === "repo") {
80
- return {
81
- context,
82
- projectRoot: "",
83
- dev: { port: 5173, proxy: {} },
84
- build: { out: "out", dist: "dist", publicDir: "public" },
85
- bake: {
86
- entry: "examples/basic/routes.ts",
87
- template: "examples/index.html",
88
- baseUrl: "https://example.com",
89
- },
90
- bundle: { splitting: true, compress: ["gz", "br"] },
91
- };
92
- }
93
- return {
94
- context,
95
- projectRoot: "",
96
- dev: { port: 5173, proxy: {} },
97
- build: { out: "out", dist: "dist", publicDir: "public" },
98
- bake: {
99
- entry: "src/routes.ts",
100
- template: "index.html",
101
- baseUrl: "https://example.com",
102
- },
103
- bundle: { splitting: true, compress: ["gz", "br"] },
104
- };
105
- }
106
-
107
- /**
108
- * Load and merge configuration for the given project root.
109
- *
110
- * Precedence (low → high): defaults, mado.config.json, CLI overrides.
25
+ * Resolve a project-relative path against `projectRoot`.
111
26
  *
112
- * @param {Object} [opts]
113
- * @param {string} [opts.projectRoot=process.cwd()]
114
- * @param {Partial<MadoConfig>} [opts.overrides]
115
- * @returns {MadoConfig}
116
- */
117
- export function loadConfig(opts = {}) {
118
- const projectRoot = resolve(opts.projectRoot ?? process.cwd());
119
- const context = detectContext(projectRoot);
120
- const base = defaults(context);
121
- base.projectRoot = projectRoot;
122
-
123
- const file = join(projectRoot, "mado.config.json");
124
- const fromFile = existsSync(file) ? safeReadJson(file) ?? {} : {};
125
-
126
- const merged = deepMerge(base, fromFile);
127
- if (opts.overrides) deepMerge(merged, opts.overrides);
128
-
129
- merged.context = context;
130
- merged.projectRoot = projectRoot;
131
- return merged;
132
- }
133
-
134
- /**
135
- * Resolve a project-relative path against `projectRoot`. Absolute paths are
136
- * returned unchanged.
137
- *
138
- * @param {MadoConfig} cfg
27
+ * @param {string} projectRoot
139
28
  * @param {string} p
140
29
  * @returns {string}
141
30
  */
142
- export function resolveProjectPath(cfg, p) {
143
- if (!p) return cfg.projectRoot;
144
- return resolve(cfg.projectRoot, p);
31
+ export function resolveProjectPath(projectRoot, p) {
32
+ return resolve(projectRoot, p);
145
33
  }
146
34
 
147
- /**
148
- * @param {MadoConfig} cfg
149
- * @returns {string}
150
- */
151
35
  export function getPackageRoot() {
152
36
  return PACKAGE_ROOT;
153
37
  }
154
38
 
155
- // ---------- helpers ----------
156
-
157
- function safeReadJson(path) {
158
- try {
159
- return JSON.parse(readFileSync(path, "utf8"));
160
- } catch (err) {
161
- console.warn(`[mado] failed to parse ${path}: ${err.message}`);
162
- return null;
163
- }
164
- }
165
-
166
- /**
167
- * Shallow-deep merge: objects are merged recursively, arrays and primitives
168
- * are replaced. Mutates `target` and returns it.
169
- */
170
- function deepMerge(target, source) {
171
- if (!source || typeof source !== "object") return target;
172
- for (const [k, v] of Object.entries(source)) {
173
- if (v === undefined) continue;
174
- if (
175
- v !== null &&
176
- typeof v === "object" &&
177
- !Array.isArray(v) &&
178
- target[k] &&
179
- typeof target[k] === "object" &&
180
- !Array.isArray(target[k])
181
- ) {
182
- deepMerge(target[k], v);
183
- } else {
184
- target[k] = v;
185
- }
186
- }
187
- return target;
188
- }
189
-
190
39
  /**
191
40
  * Tiny argv parser shared by CLI subcommands.
192
41
  *
193
- * Recognises `--key=value`, `--key value`, and `--flag` (boolean).
194
- * Unknown leading positionals are returned in `positional`.
195
- *
196
42
  * @param {string[]} argv
197
43
  * @returns {{ flags: Record<string, string|boolean>, positional: string[] }}
198
44
  */
@@ -221,4 +67,12 @@ export function parseFlags(argv) {
221
67
  }
222
68
  }
223
69
  return { flags, positional };
224
- }
70
+ }
71
+
72
+ function safeReadJson(path) {
73
+ try {
74
+ return JSON.parse(readFileSync(path, "utf8"));
75
+ } catch {
76
+ return null;
77
+ }
78
+ }
package/scripts/bake.mjs CHANGED
@@ -2,14 +2,16 @@
2
2
  //
3
3
  // Usage:
4
4
  // mado bake
5
- // mado bake --entry src/routes.ts --template index.html --out out/baked
5
+ // mado bake --entry src/routes.ts --template index.html --out out
6
6
  // mado bake --base-url https://example.com
7
7
  //
8
- // Configuration precedence (low → high):
9
- // built-in defaults < mado.config.json (bake.*) < CLI flags < env vars
8
+ // Defaults:
9
+ // entry: src/app.routes.ts, then src/routes.ts
10
+ // template: index.html
11
+ // out: out/
10
12
  //
11
13
  // What it does:
12
- // 1. Bundles `entry` (routes module) with esbuild for Node consumption.
14
+ // 1. Loads `entry` (routes module) through Vite SSR/module loading.
13
15
  // 2. For every route whose page has `bake`:
14
16
  // a) gets params via `bake.paths()`,
15
17
  // b) gets data per params via `bake.data(params)`,
@@ -25,36 +27,32 @@
25
27
  // - In repo-mode (the framework repository itself) it aliases
26
28
  // `@madojs/mado` → ./src/index.ts so the framework can dogfood itself.
27
29
  //
28
- // Required dev deps: linkedom, esbuild. We print a clear error if missing.
30
+ // Required dev deps: linkedom, vite. We print a clear error if missing.
29
31
 
30
- import { readFile, writeFile, mkdir, rm } from "node:fs/promises";
32
+ import { readFile, writeFile, mkdir } from "node:fs/promises";
31
33
  import { existsSync, writeSync } from "node:fs";
32
34
  import { join, dirname, resolve } from "node:path";
33
- import { pathToFileURL } from "node:url";
34
- import { tmpdir } from "node:os";
35
35
 
36
- import { loadConfig, parseFlags, resolveProjectPath } from "./_config.mjs";
36
+ import { detectContext, parseFlags, resolveProjectPath } from "./_config.mjs";
37
37
 
38
38
  // ---------- Resolve options from config + flags + env ----------
39
39
 
40
40
  const { flags } = parseFlags(process.argv.slice(2));
41
- const cfg = loadConfig({
42
- overrides: {
43
- bake: {
44
- entry: typeof flags.entry === "string" ? flags.entry : undefined,
45
- template: typeof flags.template === "string" ? flags.template : undefined,
46
- baseUrl: typeof flags["base-url"] === "string" ? flags["base-url"] : undefined,
47
- outDir: typeof flags.out === "string" ? flags.out : undefined,
48
- },
49
- },
50
- });
51
-
52
- // Env vars are legacy escape hatches (kept so old CI keeps working).
53
- const ENTRY = process.env.ENTRY ?? resolveProjectPath(cfg, cfg.bake.entry);
54
- const TEMPLATE = process.env.TEMPLATE ?? resolveProjectPath(cfg, cfg.bake.template);
55
- const BASE_URL = process.env.BASE_URL ?? cfg.bake.baseUrl;
56
- const OUT_DIR = process.env.OUT_DIR
57
- ?? resolveProjectPath(cfg, cfg.bake.outDir ?? join(cfg.build.out, "baked"));
41
+ const PROJECT_ROOT = resolve(process.cwd());
42
+ const CONTEXT = detectContext(PROJECT_ROOT);
43
+ const ENTRY = resolveProjectPath(
44
+ PROJECT_ROOT,
45
+ typeof flags.entry === "string" ? flags.entry : pickDefaultEntry(PROJECT_ROOT),
46
+ );
47
+ const TEMPLATE = resolveProjectPath(
48
+ PROJECT_ROOT,
49
+ typeof flags.template === "string" ? flags.template : "index.html",
50
+ );
51
+ const BASE_URL = typeof flags["base-url"] === "string" ? flags["base-url"] : "https://example.com";
52
+ const OUT_DIR = resolveProjectPath(
53
+ PROJECT_ROOT,
54
+ typeof flags.out === "string" ? flags.out : "out",
55
+ );
58
56
 
59
57
  /** Write message to stderr and exit. Sync write keeps CI/execFile output reliable. */
60
58
  function fatal(...msgs) {
@@ -69,13 +67,13 @@ function error(...msgs) {
69
67
  if (!existsSync(ENTRY)) {
70
68
  fatal(
71
69
  `[bake] entry not found: ${ENTRY}`,
72
- `[bake] set bake.entry in mado.config.json or pass --entry <file>`,
70
+ `[bake] expected src/app.routes.ts or src/routes.ts; pass --entry <file> to override`,
73
71
  );
74
72
  }
75
73
  if (!existsSync(TEMPLATE)) {
76
74
  fatal(
77
75
  `[bake] template not found: ${TEMPLATE}`,
78
- `[bake] set bake.template in mado.config.json or pass --template <file>`,
76
+ `[bake] expected index.html; pass --template <file> to override`,
79
77
  );
80
78
  }
81
79
 
@@ -88,22 +86,22 @@ try {
88
86
  fatal(
89
87
  "[bake] package 'linkedom' is required.",
90
88
  "[bake] Install it as a dev dependency in this project:",
91
- "[bake] npm i -D linkedom esbuild",
92
- "[bake] (esbuild is also required, see next check).",
89
+ "[bake] npm i -D linkedom vite",
90
+ "[bake] (vite is also required, see next check).",
93
91
  "[bake] These are not bundled into @madojs/mado on purpose: bake is an",
94
92
  "[bake] optional build step and we don't want to add transitive deps to",
95
93
  "[bake] every Mado install.",
96
94
  );
97
95
  }
98
96
 
99
- let esbuild;
97
+ let createViteServer;
100
98
  try {
101
- esbuild = await import("esbuild");
99
+ ({ createServer: createViteServer } = await import("vite"));
102
100
  } catch {
103
101
  fatal(
104
- "[bake] package 'esbuild' is required.",
102
+ "[bake] package 'vite' is required.",
105
103
  "[bake] Install it as a dev dependency in this project:",
106
- "[bake] npm i -D esbuild linkedom",
104
+ "[bake] npm i -D vite linkedom",
107
105
  );
108
106
  }
109
107
 
@@ -139,46 +137,40 @@ if (!globalThis.queueMicrotask) {
139
137
  globalThis.queueMicrotask = (fn) => Promise.resolve().then(fn);
140
138
  }
141
139
 
142
- // ---------- Bundle the routes module for Node ----------
140
+ // ---------- Load the routes module through Vite ----------
143
141
  //
144
142
  // In repo-mode the framework dogfoods its own source; alias `@madojs/mado`
145
143
  // to ./src/index.ts. In app-mode the package is resolved from node_modules.
146
144
 
147
- const tmpFile = join(tmpdir(), `mado-bake-${Date.now()}.mjs`);
148
- const aliases = cfg.context === "repo"
149
- ? { "@madojs/mado": resolve(cfg.projectRoot, "src/index.ts") }
145
+ const aliases = CONTEXT === "repo"
146
+ ? { "@madojs/mado": resolve(PROJECT_ROOT, "src/index.ts") }
150
147
  : {};
151
148
 
152
- const tsconfigCandidate = join(cfg.projectRoot, "tsconfig.json");
153
- const tsconfig = existsSync(tsconfigCandidate) ? tsconfigCandidate : undefined;
154
-
155
- await esbuild.build({
156
- entryPoints: [ENTRY],
157
- bundle: true,
158
- format: "esm",
159
- platform: "node",
160
- target: "es2022",
161
- outfile: tmpFile,
162
- absWorkingDir: cfg.projectRoot,
163
- tsconfig,
164
- alias: aliases,
149
+ const viteServer = await createViteServer({
150
+ root: PROJECT_ROOT,
165
151
  logLevel: "error",
152
+ server: { middlewareMode: true },
153
+ appType: "custom",
154
+ resolve: { alias: aliases },
166
155
  });
167
156
 
168
- const routesUrl = pathToFileURL(tmpFile).href;
169
- const routesModule = await import(routesUrl);
170
- await rm(tmpFile).catch(() => { });
157
+ let routesModule;
158
+ try {
159
+ routesModule = await viteServer.ssrLoadModule(toViteId(ENTRY));
160
+ } catch (err) {
161
+ await closeAndFatal(`[bake] failed to load ${ENTRY}: ${err.message}`);
162
+ }
171
163
  const routeApi = routesModule.default;
172
164
 
173
165
  if (!routeApi) {
174
- fatal(`[bake] ${ENTRY} must default-export routes({...})`);
166
+ await closeAndFatal(`[bake] ${ENTRY} must default-export routes({...})`);
175
167
  }
176
168
 
177
169
  // Bake needs the source manifest (not the runtime RouterApi).
178
170
  // routes.ts must therefore also `export const manifest = {...}`.
179
171
  const manifest = routesModule.manifest;
180
172
  if (!manifest) {
181
- fatal(
173
+ await closeAndFatal(
182
174
  `[bake] ${ENTRY} must also \`export const manifest = {...}\` ` +
183
175
  "(the same object passed to routes()).",
184
176
  );
@@ -290,7 +282,7 @@ await writeFile(join(OUT_DIR, "sitemap.xml"), sitemap);
290
282
 
291
283
  console.log(`[bake] done: ${total} pages + sitemap.xml → ${OUT_DIR}`);
292
284
  if (bakedErrors > 0) {
293
- fatal(`[bake] ${bakedErrors} route(s) failed; see errors above.`);
285
+ await closeAndFatal(`[bake] ${bakedErrors} route(s) failed; see errors above.`);
294
286
  }
295
287
  // Loud diagnostic when the manifest exists but no page declares `bake`.
296
288
  // Previously bake silently produced 0 pages + an empty sitemap and exited
@@ -319,10 +311,13 @@ if (bakeablePages === 0) {
319
311
  // it. If you intentionally have an SPA-only deploy, drop `mado bake` from
320
312
  // the release pipeline (or set MADO_BAKE_ALLOW_EMPTY=1).
321
313
  if (process.env.MADO_BAKE_ALLOW_EMPTY !== "1") {
314
+ await viteServer.close();
322
315
  process.exit(1);
323
316
  }
324
317
  }
325
318
 
319
+ await viteServer.close();
320
+
326
321
  // ---------- Helpers ----------
327
322
 
328
323
  async function resolvePage(entry) {
@@ -339,6 +334,21 @@ async function resolvePage(entry) {
339
334
  return null;
340
335
  }
341
336
 
337
+ function toViteId(path) {
338
+ return path.split("\\").join("/");
339
+ }
340
+
341
+ function pickDefaultEntry(projectRoot) {
342
+ const appRoutes = "src/app.routes.ts";
343
+ if (existsSync(resolve(projectRoot, appRoutes))) return appRoutes;
344
+ return "src/routes.ts";
345
+ }
346
+
347
+ async function closeAndFatal(...msgs) {
348
+ await viteServer.close().catch(() => { });
349
+ fatal(...msgs);
350
+ }
351
+
342
352
  function applyParams(pattern, params) {
343
353
  return pattern.replace(/:([\w]+)/g, (_, k) => {
344
354
  const v = params[k];
@@ -358,13 +368,14 @@ function renderTemplate(tpl, ctx) {
358
368
  if (typeof tpl === "string") return escapeHtml(tpl);
359
369
  if (typeof tpl === "number") return String(tpl);
360
370
  if (Array.isArray(tpl)) return tpl.map((x) => renderTemplate(x, ctx)).join("");
361
- if (tpl && tpl._mado === true) return renderMadoTemplate(tpl, ctx);
362
- // Unknown shapes (e.g. each() directive results) must NOT silently render
363
- // as "[object Object]". Either each() unwraps to an array here, or we
364
- // throw with a meaningful location.
365
- if (tpl && typeof tpl === "object") {
371
+ if (typeof tpl === "object") {
372
+ if (tpl._mado === true) return renderMadoTemplate(tpl, ctx);
373
+
374
+ // Unknown shapes (e.g. each() directive results) must NOT silently render
375
+ // as "[object Object]". Either each() unwraps to an array here, or we
376
+ // throw with a meaningful location.
366
377
  throw new Error(
367
- `bake cannot render value of type "${tpl?._type ?? tpl?.constructor?.name ?? "object"}" ` +
378
+ `bake cannot render value of type "${tpl._type ?? tpl.constructor?.name ?? "object"}" ` +
368
379
  `in route ${ctx?.route ?? "?"}. ` +
369
380
  "Hint: each() and other directives are not yet supported in bake. " +
370
381
  "Use a plain array (items.map(render)) in baked views, or render this " +