@madojs/mado 0.11.1 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (219) hide show
  1. package/AGENTS.md +102 -25
  2. package/CHANGELOG.md +158 -1
  3. package/README.md +135 -172
  4. package/dist/src/component.d.ts +3 -2
  5. package/dist/src/component.js +98 -6
  6. package/dist/src/component.js.map +1 -1
  7. package/dist/src/css.js +34 -5
  8. package/dist/src/css.js.map +1 -1
  9. package/dist/src/head.d.ts +6 -6
  10. package/dist/src/head.js +6 -6
  11. package/dist/src/html/parser.d.ts +1 -1
  12. package/dist/src/html/parser.js +1 -1
  13. package/dist/src/html/template.js +24 -10
  14. package/dist/src/html/template.js.map +1 -1
  15. package/dist/src/index.d.ts +8 -4
  16. package/dist/src/index.js +13 -3
  17. package/dist/src/index.js.map +1 -1
  18. package/dist/src/page.d.ts +62 -28
  19. package/dist/src/page.js +5 -0
  20. package/dist/src/page.js.map +1 -1
  21. package/dist/src/resource.js +2 -1
  22. package/dist/src/resource.js.map +1 -1
  23. package/dist/src/router/base.d.ts +65 -0
  24. package/dist/src/router/base.js +168 -0
  25. package/dist/src/router/base.js.map +1 -0
  26. package/dist/src/router/manifest.js +75 -31
  27. package/dist/src/router/manifest.js.map +1 -1
  28. package/dist/src/router/match.d.ts +16 -2
  29. package/dist/src/router/match.js +41 -1
  30. package/dist/src/router/match.js.map +1 -1
  31. package/dist/src/router/navigation.js +58 -9
  32. package/dist/src/router/navigation.js.map +1 -1
  33. package/dist/src/static-runtime.d.ts +81 -0
  34. package/dist/src/static-runtime.js +209 -0
  35. package/dist/src/static-runtime.js.map +1 -0
  36. package/dist/src/vite/index.d.ts +31 -3
  37. package/dist/src/vite/index.js +49 -18
  38. package/dist/src/vite/index.js.map +1 -1
  39. package/docs/README.md +5 -8
  40. package/docs/architecture/adr/0001-browser-static-snapshots.md +132 -0
  41. package/docs/en/00-the-mado-way.md +1 -1
  42. package/docs/en/01-quickstart.md +183 -0
  43. package/docs/en/10-pages-and-components.md +271 -0
  44. package/docs/en/11-templates-and-signals.md +211 -0
  45. package/docs/en/12-routing.md +229 -0
  46. package/docs/en/13-data.md +225 -0
  47. package/docs/en/14-forms.md +244 -0
  48. package/docs/en/15-static-snapshots.md +181 -0
  49. package/docs/en/{10-app-architecture.md → 16-app-architecture.md} +53 -2
  50. package/docs/en/{13-deployment.md → 20-deployment.md} +59 -10
  51. package/docs/en/{14-testing.md → 22-testing.md} +6 -5
  52. package/docs/en/23-cookbook.md +292 -0
  53. package/docs/en/{18-api-freeze-map.md → 30-api-freeze-map.md} +12 -3
  54. package/docs/en/{20-v1-stability.md → 32-v1-stability.md} +3 -3
  55. package/docs/en/40-llm-guide.md +776 -0
  56. package/docs/en/{06-for-backenders.md → 41-for-backenders.md} +15 -16
  57. package/docs/en/{05-why-mado.md → 42-why-mado.md} +1 -1
  58. package/docs/en/README.md +97 -27
  59. package/docs/recipes/nginx/Containerfile +26 -0
  60. package/docs/recipes/nginx/nginx.conf +42 -0
  61. package/llms.txt +246 -211
  62. package/package.json +17 -10
  63. package/scripts/bake.mjs +4 -569
  64. package/scripts/cli/generate.mjs +55 -5
  65. package/scripts/cli/help.mjs +11 -5
  66. package/scripts/cli/index.mjs +20 -4
  67. package/scripts/cli/init.mjs +7 -2
  68. package/scripts/cli/release.mjs +20 -68
  69. package/scripts/docs-lint.mjs +170 -0
  70. package/scripts/package-smoke.mjs +67 -9
  71. package/scripts/preview.mjs +95 -6
  72. package/scripts/size-budget.mjs +5 -1
  73. package/scripts/static/browser.mjs +654 -0
  74. package/scripts/static/discover.mjs +281 -0
  75. package/scripts/static/output.mjs +141 -0
  76. package/scripts/static/serialize.mjs +212 -0
  77. package/scripts/static/server.mjs +167 -0
  78. package/scripts/static.mjs +191 -0
  79. package/starters/default/README.md +57 -55
  80. package/starters/default/package.json +3 -9
  81. package/starters/default/src/app.routes.ts +20 -33
  82. package/starters/default/src/components/feature-card.component.ts +40 -0
  83. package/starters/default/src/components/live-counter.component.ts +57 -0
  84. package/starters/default/src/content/guides.ts +55 -0
  85. package/starters/default/src/main.ts +9 -13
  86. package/starters/default/src/pages/app.page.ts +41 -0
  87. package/starters/default/src/pages/guide.page.ts +40 -0
  88. package/starters/default/src/pages/home.page.ts +49 -0
  89. package/starters/default/src/pages/not-found.page.ts +18 -0
  90. package/starters/default/src/styles/document.css +39 -0
  91. package/starters/default/src/styles/reset.css +37 -0
  92. package/starters/default/src/styles/tokens.css +30 -0
  93. package/starters/default/src/styles.d.ts +3 -1
  94. package/starters/default/src/vite-env.d.ts +1 -1
  95. package/starters/default/vite.config.ts +14 -1
  96. package/starters/modular/README.md +142 -0
  97. package/starters/modular/index.html +13 -0
  98. package/starters/modular/package.json +30 -0
  99. package/starters/modular/src/app.routes.ts +39 -0
  100. package/starters/{default → modular}/src/layouts/app-shell.layout.ts +8 -5
  101. package/starters/{default → modular}/src/layouts/auth-shell.layout.ts +3 -3
  102. package/starters/modular/src/main.ts +16 -0
  103. package/starters/{default → modular}/src/modules/billing/pages/invoices-list.page.ts +3 -3
  104. package/starters/{default → modular}/src/modules/home/home.page.ts +5 -7
  105. package/starters/modular/src/modules/home/not-found.page.ts +14 -0
  106. package/starters/modular/src/styles.d.ts +1 -0
  107. package/starters/modular/src/vite-env.d.ts +1 -0
  108. package/starters/modular/tsconfig.json +24 -0
  109. package/starters/modular/vite.config.ts +131 -0
  110. package/TODO.md +0 -79
  111. package/docs/en/01-routing.md +0 -152
  112. package/docs/en/02-project-layout.md +0 -124
  113. package/docs/en/03-static-bake.md +0 -249
  114. package/docs/en/04-ide-setup.md +0 -162
  115. package/docs/en/07-llm-pitfalls.md +0 -724
  116. package/docs/en/08-llm-zero-history-test.md +0 -56
  117. package/docs/en/09-shadow-vs-light-dom.md +0 -174
  118. package/docs/en/11-layouts.md +0 -113
  119. package/docs/en/12-auth-and-api.md +0 -124
  120. package/docs/en/16-bake-cookbook.md +0 -100
  121. package/docs/en/17-shadow-dom-forms.md +0 -192
  122. package/docs/fr/00-the-mado-way.md +0 -85
  123. package/docs/fr/01-routing.md +0 -120
  124. package/docs/fr/02-project-layout.md +0 -84
  125. package/docs/fr/03-static-bake.md +0 -289
  126. package/docs/fr/04-ide-setup.md +0 -162
  127. package/docs/fr/05-why-mado.md +0 -193
  128. package/docs/fr/06-for-backenders.md +0 -438
  129. package/docs/fr/07-llm-pitfalls.md +0 -625
  130. package/docs/fr/08-llm-zero-history-test.md +0 -38
  131. package/docs/fr/09-shadow-vs-light-dom.md +0 -65
  132. package/docs/fr/10-app-architecture.md +0 -138
  133. package/docs/fr/11-layouts.md +0 -47
  134. package/docs/fr/12-auth-and-api.md +0 -76
  135. package/docs/fr/13-deployment.md +0 -57
  136. package/docs/fr/14-testing.md +0 -41
  137. package/docs/fr/15-error-handling.md +0 -50
  138. package/docs/fr/16-bake-cookbook.md +0 -88
  139. package/docs/fr/17-shadow-dom-forms.md +0 -196
  140. package/docs/fr/18-api-freeze-map.md +0 -63
  141. package/docs/fr/19-reactivity-ordering.md +0 -97
  142. package/docs/fr/20-v1-stability.md +0 -88
  143. package/docs/fr/README.md +0 -27
  144. package/docs/ru/00-the-mado-way.md +0 -84
  145. package/docs/ru/01-routing.md +0 -119
  146. package/docs/ru/02-project-layout.md +0 -84
  147. package/docs/ru/03-static-bake.md +0 -250
  148. package/docs/ru/04-ide-setup.md +0 -144
  149. package/docs/ru/05-why-mado.md +0 -193
  150. package/docs/ru/06-for-backenders.md +0 -428
  151. package/docs/ru/07-llm-pitfalls.md +0 -624
  152. package/docs/ru/08-llm-zero-history-test.md +0 -57
  153. package/docs/ru/09-shadow-vs-light-dom.md +0 -63
  154. package/docs/ru/10-app-architecture.md +0 -152
  155. package/docs/ru/11-layouts.md +0 -47
  156. package/docs/ru/12-auth-and-api.md +0 -75
  157. package/docs/ru/13-deployment.md +0 -66
  158. package/docs/ru/14-testing.md +0 -50
  159. package/docs/ru/15-error-handling.md +0 -56
  160. package/docs/ru/16-bake-cookbook.md +0 -95
  161. package/docs/ru/17-shadow-dom-forms.md +0 -193
  162. package/docs/ru/18-api-freeze-map.md +0 -64
  163. package/docs/ru/19-reactivity-ordering.md +0 -95
  164. package/docs/ru/20-v1-stability.md +0 -82
  165. package/docs/ru/README.md +0 -25
  166. package/docs/uk/00-the-mado-way.md +0 -82
  167. package/docs/uk/01-routing.md +0 -76
  168. package/docs/uk/02-project-layout.md +0 -73
  169. package/docs/uk/03-static-bake.md +0 -48
  170. package/docs/uk/04-ide-setup.md +0 -26
  171. package/docs/uk/05-why-mado.md +0 -34
  172. package/docs/uk/06-for-backenders.md +0 -55
  173. package/docs/uk/07-llm-pitfalls.md +0 -145
  174. package/docs/uk/08-llm-zero-history-test.md +0 -34
  175. package/docs/uk/09-shadow-vs-light-dom.md +0 -58
  176. package/docs/uk/10-app-architecture.md +0 -97
  177. package/docs/uk/11-layouts.md +0 -47
  178. package/docs/uk/12-auth-and-api.md +0 -70
  179. package/docs/uk/13-deployment.md +0 -40
  180. package/docs/uk/14-testing.md +0 -34
  181. package/docs/uk/15-error-handling.md +0 -32
  182. package/docs/uk/16-bake-cookbook.md +0 -36
  183. package/docs/uk/17-shadow-dom-forms.md +0 -193
  184. package/docs/uk/18-api-freeze-map.md +0 -61
  185. package/docs/uk/19-reactivity-ordering.md +0 -95
  186. package/docs/uk/20-v1-stability.md +0 -83
  187. package/docs/uk/README.md +0 -27
  188. package/starters/default/src/modules/home/not-found.page.ts +0 -11
  189. /package/docs/en/{15-error-handling.md → 21-error-handling.md} +0 -0
  190. /package/docs/en/{19-reactivity-ordering.md → 31-reactivity-ordering.md} +0 -0
  191. /package/starters/{default → modular}/.editorconfig +0 -0
  192. /package/starters/{default → modular}/eslint.config.mjs +0 -0
  193. /package/starters/{default → modular}/public/favicon.svg +0 -0
  194. /package/starters/{default → modular}/src/modules/auth/_contracts/auth-api.types.ts +0 -0
  195. /package/starters/{default → modular}/src/modules/auth/auth.connector.ts +0 -0
  196. /package/starters/{default → modular}/src/modules/auth/auth.guard.ts +0 -0
  197. /package/starters/{default → modular}/src/modules/auth/auth.public.ts +0 -0
  198. /package/starters/{default → modular}/src/modules/auth/auth.routes.ts +0 -0
  199. /package/starters/{default → modular}/src/modules/auth/auth.service.ts +0 -0
  200. /package/starters/{default → modular}/src/modules/auth/auth.types.ts +0 -0
  201. /package/starters/{default → modular}/src/modules/auth/login.page.ts +0 -0
  202. /package/starters/{default → modular}/src/modules/billing/_contracts/stripe.types.ts +0 -0
  203. /package/starters/{default → modular}/src/modules/billing/api/stripe.connector.ts +0 -0
  204. /package/starters/{default → modular}/src/modules/billing/billing.public.ts +0 -0
  205. /package/starters/{default → modular}/src/modules/billing/billing.routes.ts +0 -0
  206. /package/starters/{default → modular}/src/modules/billing/billing.types.ts +0 -0
  207. /package/starters/{default → modular}/src/modules/billing/components/invoice-status-badge.component.ts +0 -0
  208. /package/starters/{default → modular}/src/modules/billing/data/invoices.resource.ts +0 -0
  209. /package/starters/{default → modular}/src/modules/billing/pages/invoice-detail.page.ts +0 -0
  210. /package/starters/{default → modular}/src/shared/http/http-client.ts +0 -0
  211. /package/starters/{default → modular}/src/shared/http/http-error.ts +0 -0
  212. /package/starters/{default → modular}/src/shared/http/interceptors.ts +0 -0
  213. /package/starters/{default → modular}/src/shared/lib/format-date.ts +0 -0
  214. /package/starters/{default → modular}/src/shared/styles/content.css +0 -0
  215. /package/starters/{default → modular}/src/shared/styles/reset.css +0 -0
  216. /package/starters/{default → modular}/src/shared/styles/shell.css +0 -0
  217. /package/starters/{default → modular}/src/shared/styles/tokens.css +0 -0
  218. /package/starters/{default → modular}/src/shared/ui/x-button.component.ts +0 -0
  219. /package/starters/{default → modular}/src/shared/ui/x-spinner.component.ts +0 -0
@@ -0,0 +1,16 @@
1
+ // App entry point. The ONLY file allowed to import CSS at runtime.
2
+ import "./shared/styles/tokens.css";
3
+ import "./shared/styles/reset.css";
4
+ import "./shared/styles/shell.css";
5
+ import "./shared/styles/content.css";
6
+
7
+ import { html, render } from "@madojs/mado";
8
+
9
+ import { init as initAuth } from "./modules/auth/auth.service";
10
+ import appRoutes from "./app.routes";
11
+
12
+ // One-off boot order: init cross-cutting modules first, then render the
13
+ // router view into #app.
14
+ await initAuth();
15
+
16
+ render(html`${appRoutes.view}`, document.getElementById("app")!);
@@ -1,4 +1,4 @@
1
- import { each, html, page, signal, untracked } from "@madojs/mado";
1
+ import { each, html, page, routeUrl, signal, untracked } from "@madojs/mado";
2
2
 
3
3
  import { formatDate, formatMoney } from "../../../shared/lib/format-date";
4
4
  import "../../../shared/ui/x-spinner.component";
@@ -50,7 +50,7 @@ export default page({
50
50
  (i) => html`
51
51
  <tr>
52
52
  <td>
53
- <a href=${`/billing/invoices/${i.id}`}>${i.number}</a>
53
+ <a data-link href=${routeUrl(`/billing/invoices/${i.id}`)}>${i.number}</a>
54
54
  </td>
55
55
  <td>${i.customerEmail}</td>
56
56
  <td>${formatMoney(i.amount, i.currency)}</td>
@@ -59,7 +59,7 @@ export default page({
59
59
  </td>
60
60
  <td>${formatDate(i.issuedAt)}</td>
61
61
  <td>
62
- <a href=${`/billing/invoices/${i.id}`}>Open</a>
62
+ <a data-link href=${routeUrl(`/billing/invoices/${i.id}`)}>Open</a>
63
63
  </td>
64
64
  </tr>
65
65
  `,
@@ -6,7 +6,7 @@
6
6
  //
7
7
  // A page should be read top-to-bottom and understood without jumping files.
8
8
 
9
- import { html, page } from "@madojs/mado";
9
+ import { html, page, routeUrl } from "@madojs/mado";
10
10
 
11
11
  // 1. LOCAL STATE — none
12
12
  // 2. DATA — none
@@ -14,20 +14,18 @@ import { html, page } from "@madojs/mado";
14
14
 
15
15
  // 4. VIEW
16
16
  export default page({
17
+ static: true,
17
18
  title: "Home",
18
19
  head: () => ({
19
20
  description: "A modular Mado application.",
20
21
  }),
21
- bake: {
22
- paths: () => [{}],
23
- data: () => ({}),
24
- },
25
22
  view: () => html`
26
23
  <section>
27
24
  <h1>Mado App</h1>
28
25
  <p>
29
- Welcome. Try <a href="/billing/invoices">billing</a> or
30
- <a href="/login">sign in</a>.
26
+ Welcome. Try
27
+ <a data-link href=${routeUrl("/billing/invoices")}>billing</a>
28
+ or <a data-link href=${routeUrl("/login")}>sign in</a>.
31
29
  </p>
32
30
  </section>
33
31
  `,
@@ -0,0 +1,14 @@
1
+ import { html, page, routeUrl } from "@madojs/mado";
2
+
3
+ export default page({
4
+ title: "Not Found",
5
+ view: () => html`
6
+ <section>
7
+ <h1>404</h1>
8
+ <p>
9
+ This route does not exist.
10
+ <a data-link href=${routeUrl("/")}>Go home</a>.
11
+ </p>
12
+ </section>
13
+ `,
14
+ });
@@ -0,0 +1 @@
1
+ declare module "*.css";
@@ -0,0 +1 @@
1
+ /// <reference types="vite/client" />
@@ -0,0 +1,24 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ES2022",
5
+ "moduleResolution": "Bundler",
6
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
7
+ "strict": true,
8
+ "noImplicitOverride": true,
9
+ "noUncheckedIndexedAccess": true,
10
+ "exactOptionalPropertyTypes": true,
11
+ "noFallthroughCasesInSwitch": true,
12
+ "forceConsistentCasingInFileNames": true,
13
+ "esModuleInterop": true,
14
+ "resolveJsonModule": true,
15
+ "skipLibCheck": true,
16
+ "useDefineForClassFields": true,
17
+ "experimentalDecorators": false,
18
+ "isolatedModules": true,
19
+ "verbatimModuleSyntax": true,
20
+ "noEmit": true
21
+ },
22
+ "include": ["src/**/*.ts", "src/**/*.d.ts"],
23
+ "exclude": ["node_modules", "out"]
24
+ }
@@ -0,0 +1,131 @@
1
+ import { defineConfig, type Plugin } from "vite";
2
+ import { mado } from "@madojs/mado/vite";
3
+
4
+ /**
5
+ * Tiny in-memory mock for the auth + billing API used by this starter.
6
+ *
7
+ * Mounted only in `dev` so `mado release` ships a clean bundle that
8
+ * talks to your real backend. The endpoint shape mirrors what
9
+ * `auth.connector.ts` and `stripe.connector.ts` expect: any change in
10
+ * one must be mirrored in the other.
11
+ *
12
+ * Disable by removing this plugin or setting `MADO_MOCK_API=0`.
13
+ */
14
+ function devApiMock(): Plugin {
15
+ const users = new Map<string, { password: string; user: unknown }>([
16
+ [
17
+ "demo@mado.dev",
18
+ {
19
+ password: "demo",
20
+ user: {
21
+ id: "u_demo",
22
+ email: "demo@mado.dev",
23
+ roles: ["user"],
24
+ permissions: ["billing.read"],
25
+ },
26
+ },
27
+ ],
28
+ ]);
29
+ const invoices = Array.from({ length: 6 }, (_, i) => ({
30
+ id: `in_${1000 + i}`,
31
+ number: `INV-${1000 + i}`,
32
+ customer_email: "demo@mado.dev",
33
+ amount_due: 1000 * (i + 1),
34
+ currency: "usd",
35
+ status: i % 3 === 0 ? "paid" : i % 3 === 1 ? "pending" : "draft",
36
+ created: Math.floor(Date.now() / 1000) - i * 86_400,
37
+ }));
38
+ let token: string | null = null;
39
+ const json = (res: import("http").ServerResponse, status: number, body: unknown) => {
40
+ res.statusCode = status;
41
+ res.setHeader("content-type", "application/json");
42
+ res.end(JSON.stringify(body));
43
+ };
44
+ const readJson = (req: import("http").IncomingMessage) =>
45
+ new Promise<Record<string, unknown>>((resolve) => {
46
+ let buf = "";
47
+ req.on("data", (chunk) => (buf += chunk));
48
+ req.on("end", () => {
49
+ try {
50
+ resolve(buf ? JSON.parse(buf) : {});
51
+ } catch {
52
+ resolve({});
53
+ }
54
+ });
55
+ });
56
+
57
+ return {
58
+ name: "starter:dev-api-mock",
59
+ apply: "serve",
60
+ configureServer(server) {
61
+ if (process.env.MADO_MOCK_API === "0") return;
62
+ server.middlewares.use(async (req, res, next) => {
63
+ const url = req.url ?? "";
64
+ if (!url.startsWith("/api/")) return next();
65
+
66
+ // ---- auth -----------------------------------------------------
67
+ if (url === "/api/auth/login" && req.method === "POST") {
68
+ const body = await readJson(req);
69
+ const entry = users.get(String(body.email ?? ""));
70
+ if (!entry || entry.password !== body.password) {
71
+ return json(res, 401, { error: "invalid_credentials" });
72
+ }
73
+ token = `tok_${Math.random().toString(36).slice(2)}`;
74
+ return json(res, 200, { token, user: entry.user });
75
+ }
76
+ if (url === "/api/auth/me" && req.method === "GET") {
77
+ if (!token) return json(res, 401, { error: "unauthenticated" });
78
+ const entry = users.values().next().value;
79
+ return json(res, 200, entry.user);
80
+ }
81
+ if (url === "/api/auth/logout" && req.method === "POST") {
82
+ token = null;
83
+ return json(res, 204, null);
84
+ }
85
+
86
+ // ---- billing --------------------------------------------------
87
+ if (url.startsWith("/api/billing/stripe/invoices")) {
88
+ const id = url.match(/\/invoices\/([^/?]+)/)?.[1];
89
+ if (req.method === "POST" && url.endsWith("/pay") && id) {
90
+ const inv = invoices.find((x) => x.id === id);
91
+ if (!inv) return json(res, 404, { error: "not_found" });
92
+ inv.status = "paid";
93
+ return json(res, 200, inv);
94
+ }
95
+ if (id && req.method === "GET") {
96
+ const inv = invoices.find((x) => x.id === id);
97
+ return inv
98
+ ? json(res, 200, inv)
99
+ : json(res, 404, { error: "not_found" });
100
+ }
101
+ if (req.method === "GET") {
102
+ return json(res, 200, { data: invoices, has_more: false });
103
+ }
104
+ }
105
+
106
+ return next();
107
+ });
108
+ },
109
+ };
110
+ }
111
+
112
+ export default defineConfig({
113
+ plugins: [
114
+ devApiMock(),
115
+ mado({
116
+ // Public origin used to build absolute URLs for static snapshots
117
+ // (sitemap, canonical, OpenGraph). Combined with Vite's `base`,
118
+ // the canonical for a route is `site + base + pathname`.
119
+ //
120
+ // REQUIRED when any page declares `static`. Set to your deployment
121
+ // origin (e.g. https://your-app.example) before running
122
+ // `mado release`. Override per environment via:
123
+ // mado release --base-url https://staging.example
124
+ // MADO_SITE=https://staging.example mado release
125
+ // site: "https://your-app.example",
126
+ }),
127
+ ],
128
+ css: {
129
+ transformer: "lightningcss",
130
+ },
131
+ });
package/TODO.md DELETED
@@ -1,79 +0,0 @@
1
- # TODO
2
-
3
- Backlog, not a roadmap. Keep this file small and honest: ideas stay here only
4
- while they are plausible next work. Promote concrete work to issues or release
5
- notes when it becomes real.
6
-
7
- ## Before Public Demo / Release
8
-
9
- - [ ] Run the default starter as a real demo app and feed every friction point
10
- back into docs, CLI generators, or starter structure.
11
- - [ ] Add focused tests for every `mado new` generator template.
12
- - [ ] Audit EN/RU/FR docs after each tooling/runtime change; no old `dist/`,
13
- `tsc-only`, custom-bundler, or legacy example wording should remain.
14
- - [ ] Keep `mado preview` boring and reliable inside generated apps.
15
- - [ ] Add a short maintainer-only release checklist if manual npm publish stays
16
- in use.
17
-
18
- ## CLI / DX
19
-
20
- - [ ] Decide whether `npx mado create my-app` is worth adding after package
21
- naming and install friction are clear.
22
- - [ ] Consider short aliases over `mado new` only after real usage shows they
23
- save enough typing (`mado page`, `mado component`, etc.).
24
- - [ ] Split preview/static-server internals only if `scripts/preview.mjs` grows.
25
- - [ ] Typed navigation inferred from route patterns.
26
- - [ ] Editor highlighting docs should prefer existing tooling first; build a
27
- Mado-specific VS Code extension only if current HTML/CSS template support is
28
- not enough.
29
-
30
- ## Runtime / App API
31
-
32
- - [ ] Mutation rollback helper on top of `resource().mutate()` and `mutation()`.
33
- - [ ] Document loading/error shell patterns before adding an `<x-async>` API.
34
- - [ ] `liveResource()` over SSE / WebSocket after a real app needs live data.
35
- - [ ] i18n helper once browser-native message formatting is realistic.
36
- - [ ] Accessibility helpers: focus trap, live region, click outside.
37
-
38
- ## Styling
39
-
40
- - [ ] Docs for `::part`, `::slotted()` and `:host-context()`.
41
- - [ ] Document Vite CSS options, including `css.transformer: "lightningcss"`,
42
- for apps that want Lightning CSS.
43
- - [ ] Optional tiny utilities stylesheet only if the starter/demo repeats the
44
- same small classes often enough to justify it.
45
- - [ ] CSP-friendly style mode.
46
-
47
- ## Bake / Smart Static
48
-
49
- - [ ] Add bake regression coverage for layout groups, dynamic params, sitemap,
50
- and hard-refresh deploy paths.
51
- - [ ] Incremental bake cache after a real content-heavy app needs it.
52
- - [ ] Webhook re-bake endpoint only after incremental bake has a concrete use.
53
- - [ ] RSS / Atom feed generator for docs/blog/demo content.
54
-
55
- ## Operations
56
-
57
- - [ ] Benchmark against Lit / Solid / Preact.
58
- - [ ] Source-map notes and a minimal dev error overlay.
59
- - [ ] `eslint-plugin-mado` rules only if docs/starter conventions are not enough
60
- to prevent recurring mistakes.
61
- - [ ] Timeout / retry options for resource fetchers after real API friction.
62
- - [ ] Schema validation hook for resources after choosing a dependency-free
63
- story or a clearly optional integration.
64
- - [ ] PWA scaffold only if the public demo or a real app needs offline behavior.
65
-
66
- ## Community / GitHub
67
-
68
- - [ ] Minimal landing site built with Mado.
69
- - [ ] Public live demo: CRUD starter and showcase hosted from Mado itself.
70
- - [ ] Case study: real React page -> Mado + bake.
71
- - [ ] Convert contribution-ready TODO items into GitHub issues with labels
72
- (`good first issue`, `help wanted`, `docs`, `cli`, `ci`) after the first
73
- public feedback round.
74
- - [ ] Configure npm Trusted Publishing for GitHub Actions after verifying the
75
- first manual publishes.
76
- - [ ] Decide the release flow: continue manual npm publish for patch releases or
77
- publish from signed `v*` tags through `.github/workflows/release.yml`.
78
- - [ ] Improve generated release notes with PR links/authors when releases start
79
- going through pull requests.
@@ -1,152 +0,0 @@
1
- # Routing
2
-
3
- > One app map. No folder scanners. No special path syntax.
4
-
5
- Mado does not infer routes from files. The browser sees files as files; route
6
- composition should be readable in one place.
7
-
8
- ## App Manifest
9
-
10
- Use `src/app.routes.ts` as the application map:
11
-
12
- ```ts
13
- import { layout, routes } from "@madojs/mado";
14
- import { requireAuth } from "./modules/auth/auth.public";
15
- import { authRoutes } from "./modules/auth/auth.routes";
16
- import { billingRoutes } from "./modules/billing/billing.routes";
17
-
18
- export const manifest = {
19
- "/": () => import("./modules/home/home.page.js"),
20
- "/login": layout({
21
- layout: () => import("./layouts/auth-shell.layout.js"),
22
- routes: authRoutes,
23
- }),
24
- "/billing": layout({
25
- layout: () => import("./layouts/app-shell.layout.js"),
26
- guard: requireAuth,
27
- routes: billingRoutes,
28
- }),
29
- "*": () => import("./modules/home/not-found.page.js"),
30
- };
31
-
32
- export default routes(manifest);
33
- ```
34
-
35
- Open `app.routes.ts` and you can see the whole app: public pages, auth zone,
36
- protected app zones, guards and shells.
37
-
38
- Exporting `manifest` is important because `mado bake` reads it.
39
-
40
- ## Module Routes
41
-
42
- Modules export plain route maps. They do not call `layout()` and they do not
43
- decide which shell wraps them.
44
-
45
- ```ts
46
- // src/modules/billing/billing.routes.ts
47
- export const billingRoutes = {
48
- "/invoices": () => import("./pages/invoices-list.page.js"),
49
- "/invoices/:id": () => import("./pages/invoice-detail.page.js"),
50
- };
51
- ```
52
-
53
- The prefix is applied by `src/app.routes.ts` when the module is mounted under
54
- `"/billing"`.
55
-
56
- ## What Goes On The Right Side
57
-
58
- ### Lazy page import
59
-
60
- ```ts
61
- "/users/:id": () => import("./modules/users/pages/user-profile.page.js"),
62
- ```
63
-
64
- Vite creates a separate chunk for dynamic imports in production.
65
-
66
- ### Eager page
67
-
68
- ```ts
69
- import home from "./modules/home/home.page.js";
70
-
71
- export const manifest = {
72
- "/": home,
73
- };
74
- ```
75
-
76
- Use this only for tiny critical pages.
77
-
78
- ### Layout group
79
-
80
- ```ts
81
- "/admin": layout({
82
- layout: () => import("./layouts/app-shell.layout.js"),
83
- guard: requireAuth,
84
- routes: adminRoutes,
85
- }),
86
- ```
87
-
88
- A layout is a normal `page({...})` file:
89
-
90
- ```ts
91
- import { html, page } from "@madojs/mado";
92
-
93
- export default page({
94
- view: ({ child }) => html`
95
- <div class="layout layout--app">
96
- <main class="app-main">${child}</main>
97
- </div>
98
- `,
99
- });
100
- ```
101
-
102
- Keep layout views stateless. Put page-specific signals, resources and forms in
103
- pages/components/resources, not in layout locals.
104
-
105
- ## Page Contract
106
-
107
- ```ts
108
- import { html, page } from "@madojs/mado";
109
-
110
- export default page<{ id: string }>({
111
- title: ({ id }) => `User ${id}`,
112
- view: ({ params }) => html`<h1>${params.id}</h1>`,
113
- });
114
- ```
115
-
116
- If a lazy route does not default-export `page({...})`, `routes()` throws a clear
117
- error.
118
-
119
- ## Navigation
120
-
121
- ```ts
122
- import appRoutes from "./app.routes.js";
123
-
124
- appRoutes.navigate("/billing/invoices");
125
- appRoutes.navigate("/billing/invoices?page=2");
126
- appRoutes.navigate("/login", { replace: true });
127
- ```
128
-
129
- Clicks on `<a href="/foo">` are intercepted for same-origin SPA navigation.
130
- External links still use normal browser navigation.
131
-
132
- ## Query Parameters
133
-
134
- ```ts
135
- import { queryParam } from "@madojs/mado";
136
-
137
- const page = queryParam("page", "1");
138
- page();
139
- page.set("2");
140
- page.set(null);
141
- page.set("3", { push: true });
142
- ```
143
-
144
- `queryParam()` is a signal. Use it in pages or components when URL state is part
145
- of the UI.
146
-
147
- ## What Is Intentionally Absent
148
-
149
- - No auto-scan of page folders.
150
- - No special filesystem route syntax like `[id]`, `(group)`, `_layout`.
151
- - No server routes in the client manifest.
152
- - No hidden layout discovery. App zones are explicit in `app.routes.ts`.
@@ -1,124 +0,0 @@
1
- # Project layout
2
-
3
- Every Mado app uses the same shape. This is a **mandatory** convention — it
4
- exists so that you, your teammates, and any LLM assistant always know where
5
- things live.
6
-
7
- ```
8
- my-app/
9
- ├── package.json # exactly one runtime dep: @madojs/mado
10
- ├── tsconfig.json # strict TS, ES2022, Bundler resolution
11
- ├── vite.config.ts # optional; use mado() from @madojs/mado/vite
12
- ├── index.html # Vite entry + SPA shell
13
- ├── public/ # static assets (favicons, images, robots.txt)
14
- └── src/
15
- ├── main.ts # entry: mount router into #app
16
- ├── app.routes.ts # one app map (default + named `manifest`)
17
- ├── layouts/ # app-zone layouts, not domain modules
18
- ├── shared/ # ui, http, lib, styles
19
- └── modules/ # bounded contexts
20
- └── billing/
21
- ├── billing.routes.ts
22
- ├── billing.public.ts
23
- ├── billing.types.ts
24
- ├── pages/
25
- ├── data/
26
- ├── api/
27
- └── _contracts/
28
- ```
29
-
30
- ## The three artifact states (read this once, never wonder again)
31
-
32
- | Folder | What it is | Who writes | Who reads | Deploy? |
33
- |-------------|----------------------------------------------------------------|-------------------|----------------------------|-------------------|
34
- | `src/` | your source (TypeScript) | you | Vite, `tsc --noEmit` | ❌ no |
35
- | `public/` | static assets copied as-is (favicon, images, robots.txt) | you | Vite build | ✅ via `out/` |
36
- | `out/` | **the deploy artifact**: SPA shell + assets + baked HTML | `mado release` | nginx / CDN / Cloudflare | ✅ **yes** |
37
-
38
- One-liner to remember:
39
- > Develop with `mado dev`. To ship: run `mado release`, then upload `out/`.
40
-
41
- `mado release` = `typecheck` + Vite build (`out/index.html`, `out/assets/`,
42
- `public/*`) + `bake` directly into deployable route paths + `sitemap.xml` +
43
- precompressed assets and CDN helper files.
44
-
45
- `index.html` belongs at the project root because Vite treats it as an entry
46
- template, not as a static public file. Put only copy-as-is files in `public/`.
47
-
48
- ### Quick deployment matrix
49
-
50
- | Target | Command | Where it goes |
51
- |-----------------------|--------------------------------------|----------------------------|
52
- | VPS + nginx | `mado release && rsync -avz out/ …` | `/var/www/<app>/` |
53
- | Cloudflare Pages | `mado release && wrangler pages deploy out` | CF Pages |
54
- | Netlify / S3 / GH Pages | `mado release && upload out/*` | any static host |
55
-
56
- See `docs/en/13-deployment.md` for full recipes.
57
-
58
- ## Where to put a new file?
59
-
60
- | What | Where |
61
- |-------------------------------------|------------------------------------------------------|
62
- | Page for a new URL | `src/modules/<module>/pages/<name>.page.ts` + module routes |
63
- | Module route map | `src/modules/<module>/<module>.routes.ts` |
64
- | App shell/layout | `src/layouts/<zone>.layout.ts` |
65
- | Reusable shared UI widget | `src/shared/ui/<x-name>.component.ts` |
66
- | Module-only UI widget | `src/modules/<module>/components/<name>.component.ts` |
67
- | API connector | `src/modules/<module>/api/<provider>.connector.ts` |
68
- | Data resource/mutation | `src/modules/<module>/data/<name>.resource.ts` |
69
- | Auth/session | `src/modules/auth/` |
70
- | Public module surface | `src/modules/<module>/<module>.public.ts` |
71
- | Pure function with no UI | `src/shared/lib/<name>.ts` |
72
- | Static image / favicon | `public/<file>` |
73
- | App-zone shell CSS | `src/shared/styles/shell.css` |
74
- | Page-level table/form/prose CSS | `src/shared/styles/content.css` |
75
-
76
- If you don't know where — that is a signal that **the architecture is
77
- suffering**. Ask the team and **record** the answer in `docs/`. Don't invent a
78
- new top-level folder.
79
-
80
- ## Naming rules
81
-
82
- | What | Style | Example |
83
- |-------------------------------------|----------------------|------------------------|
84
- | File | kebab-case | `user-profile.ts` |
85
- | Component tag | `x-` + kebab | `<x-user-profile>` |
86
- | Context | PascalCase + `Ctx` | `ThemeCtx`, `AuthCtx` |
87
- | Signal | camelCase | `userId`, `isLoggedIn` |
88
- | Page-internal element | `x-<route>-page` | `<x-posts-page>` |
89
-
90
- ## Vite config
91
-
92
- App/dev/build settings live in `vite.config.ts`.
93
-
94
- ```ts
95
- import { defineConfig } from "vite";
96
- import { mado } from "@madojs/mado/vite";
97
-
98
- export default defineConfig({
99
- plugins: [mado()],
100
- css: {
101
- transformer: "lightningcss",
102
- },
103
- server: {
104
- proxy: { "/api": "http://localhost:3000" },
105
- },
106
- });
107
- ```
108
-
109
- The default starter opts into Vite's Lightning CSS transformer. CSS
110
- minification is already handled by Vite; the explicit transformer keeps
111
- prefixing and modern CSS lowering in Vite instead of in Mado.
112
-
113
- `mado bake` uses conventions by default: `src/app.routes.ts` first,
114
- then `src/routes.ts`, `index.html` as the template, and `out/` as output.
115
- Use CLI flags (`--entry`, `--template`, `--out`, `--base-url`) for the few
116
- values that are specific to prerendering.
117
-
118
- ## What does NOT go in `src/`
119
-
120
- - ❌ Extra build tool configs — use `vite.config.ts` with `mado()` when needed.
121
- - ❌ `.env` files — read env in `src/shared/lib/config.ts` from `import.meta.env` /
122
- `process.env` and import that one module everywhere.
123
- - ❌ Tests mixed with code — put them in `test/`.
124
- - ❌ `examples/` folder — keep large demos outside the app repo.