@madojs/mado 0.10.1 → 0.11.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 (217) hide show
  1. package/AGENTS.md +24 -26
  2. package/CHANGELOG.md +68 -0
  3. package/README.md +18 -45
  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/index.d.ts +11 -6
  12. package/dist/src/index.js +5 -3
  13. package/dist/src/index.js.map +1 -1
  14. package/dist/src/lazy.d.ts +1 -1
  15. package/dist/src/lazy.js +1 -1
  16. package/dist/src/lazy.js.map +1 -1
  17. package/dist/src/page.d.ts +17 -21
  18. package/dist/src/page.js +7 -12
  19. package/dist/src/page.js.map +1 -1
  20. package/dist/src/router/manifest.d.ts +1 -1
  21. package/dist/src/router/manifest.js +21 -13
  22. package/dist/src/router/manifest.js.map +1 -1
  23. package/dist/src/router/match.d.ts +2 -2
  24. package/dist/src/router/match.js +3 -3
  25. package/dist/src/router/match.js.map +1 -1
  26. package/dist/src/router/navigation.js +1 -1
  27. package/dist/src/router/navigation.js.map +1 -1
  28. package/dist/src/vite/index.d.ts +10 -0
  29. package/dist/src/vite/index.js +33 -0
  30. package/dist/src/vite/index.js.map +1 -0
  31. package/docs/en/00-the-mado-way.md +25 -12
  32. package/docs/en/01-routing.md +90 -142
  33. package/docs/en/02-project-layout.md +59 -53
  34. package/docs/en/03-static-bake.md +5 -6
  35. package/docs/en/05-why-mado.md +6 -6
  36. package/docs/en/06-for-backenders.md +18 -22
  37. package/docs/en/08-llm-zero-history-test.md +9 -14
  38. package/docs/en/09-shadow-vs-light-dom.md +28 -36
  39. package/docs/en/10-app-architecture.md +158 -96
  40. package/docs/en/11-layouts.md +22 -24
  41. package/docs/en/12-auth-and-api.md +89 -182
  42. package/docs/en/13-deployment.md +18 -22
  43. package/docs/en/14-testing.md +4 -4
  44. package/docs/en/16-bake-cookbook.md +11 -12
  45. package/docs/en/18-api-freeze-map.md +6 -4
  46. package/docs/en/20-v1-stability.md +1 -1
  47. package/docs/fr/00-the-mado-way.md +55 -90
  48. package/docs/fr/01-routing.md +70 -152
  49. package/docs/fr/02-project-layout.md +61 -42
  50. package/docs/fr/03-static-bake.md +1 -1
  51. package/docs/fr/05-why-mado.md +6 -6
  52. package/docs/fr/06-for-backenders.md +7 -7
  53. package/docs/fr/08-llm-zero-history-test.md +21 -48
  54. package/docs/fr/09-shadow-vs-light-dom.md +43 -162
  55. package/docs/fr/10-app-architecture.md +110 -33
  56. package/docs/fr/11-layouts.md +24 -12
  57. package/docs/fr/12-auth-and-api.md +63 -22
  58. package/docs/fr/13-deployment.md +7 -10
  59. package/docs/fr/14-testing.md +1 -1
  60. package/docs/fr/16-bake-cookbook.md +2 -2
  61. package/docs/fr/18-api-freeze-map.md +1 -1
  62. package/docs/fr/20-v1-stability.md +1 -1
  63. package/docs/recipes/nginx/README.md +13 -0
  64. package/docs/ru/00-the-mado-way.md +53 -75
  65. package/docs/ru/01-routing.md +68 -143
  66. package/docs/ru/02-project-layout.md +61 -41
  67. package/docs/ru/03-static-bake.md +2 -2
  68. package/docs/ru/05-why-mado.md +6 -6
  69. package/docs/ru/06-for-backenders.md +7 -7
  70. package/docs/ru/08-llm-zero-history-test.md +9 -14
  71. package/docs/ru/09-shadow-vs-light-dom.md +43 -178
  72. package/docs/ru/10-app-architecture.md +115 -63
  73. package/docs/ru/11-layouts.md +24 -24
  74. package/docs/ru/12-auth-and-api.md +57 -35
  75. package/docs/ru/13-deployment.md +7 -11
  76. package/docs/ru/14-testing.md +1 -1
  77. package/docs/ru/16-bake-cookbook.md +12 -6
  78. package/docs/ru/18-api-freeze-map.md +5 -3
  79. package/docs/ru/20-v1-stability.md +1 -1
  80. package/docs/uk/00-the-mado-way.md +70 -44
  81. package/docs/uk/01-routing.md +41 -47
  82. package/docs/uk/02-project-layout.md +68 -41
  83. package/docs/uk/03-static-bake.md +1 -2
  84. package/docs/uk/06-for-backenders.md +3 -3
  85. package/docs/uk/08-llm-zero-history-test.md +22 -24
  86. package/docs/uk/09-shadow-vs-light-dom.md +37 -86
  87. package/docs/uk/10-app-architecture.md +72 -31
  88. package/docs/uk/11-layouts.md +25 -12
  89. package/docs/uk/12-auth-and-api.md +58 -22
  90. package/docs/uk/13-deployment.md +4 -3
  91. package/docs/uk/14-testing.md +1 -1
  92. package/docs/uk/18-api-freeze-map.md +1 -1
  93. package/docs/uk/20-v1-stability.md +1 -1
  94. package/llms.txt +14 -15
  95. package/package.json +18 -11
  96. package/scripts/_config.mjs +15 -161
  97. package/scripts/bake.mjs +67 -57
  98. package/scripts/cli/generate.mjs +348 -0
  99. package/scripts/cli/help.mjs +27 -0
  100. package/scripts/cli/index.mjs +79 -0
  101. package/scripts/cli/init.mjs +153 -0
  102. package/scripts/cli/release.mjs +152 -0
  103. package/scripts/cli/run.mjs +96 -0
  104. package/scripts/cli.mjs +2 -621
  105. package/scripts/package-smoke.mjs +4 -1
  106. package/scripts/preview.mjs +13 -37
  107. package/scripts/size-budget.mjs +5 -2
  108. package/scripts/vite.default.mjs +11 -0
  109. package/starters/default/.editorconfig +12 -0
  110. package/starters/default/README.md +74 -0
  111. package/starters/default/eslint.config.mjs +256 -0
  112. package/starters/default/index.html +13 -0
  113. package/starters/default/package.json +30 -0
  114. package/starters/default/public/favicon.svg +4 -0
  115. package/starters/default/src/app.routes.ts +39 -0
  116. package/starters/default/src/layouts/app-shell.layout.ts +35 -0
  117. package/starters/default/src/layouts/auth-shell.layout.ts +17 -0
  118. package/starters/default/src/main.ts +16 -0
  119. package/starters/default/src/modules/auth/_contracts/auth-api.types.ts +17 -0
  120. package/starters/default/src/modules/auth/auth.connector.ts +45 -0
  121. package/starters/default/src/modules/auth/auth.guard.ts +22 -0
  122. package/starters/default/src/modules/auth/auth.public.ts +9 -0
  123. package/starters/default/src/modules/auth/auth.routes.ts +8 -0
  124. package/starters/default/src/modules/auth/auth.service.ts +71 -0
  125. package/starters/default/src/modules/auth/auth.types.ts +15 -0
  126. package/starters/default/src/modules/auth/login.page.ts +62 -0
  127. package/starters/default/src/modules/billing/_contracts/stripe.types.ts +17 -0
  128. package/starters/default/src/modules/billing/api/stripe.connector.ts +71 -0
  129. package/starters/default/src/modules/billing/billing.public.ts +5 -0
  130. package/starters/default/src/modules/billing/billing.routes.ts +9 -0
  131. package/starters/default/src/modules/billing/billing.types.ts +15 -0
  132. package/starters/default/src/modules/billing/components/invoice-status-badge.component.ts +43 -0
  133. package/starters/default/src/modules/billing/data/invoices.resource.ts +35 -0
  134. package/starters/default/src/modules/billing/pages/invoice-detail.page.ts +70 -0
  135. package/starters/default/src/modules/billing/pages/invoices-list.page.ts +73 -0
  136. package/starters/default/src/modules/home/home.page.ts +34 -0
  137. package/starters/default/src/modules/home/not-found.page.ts +11 -0
  138. package/starters/default/src/shared/http/http-client.ts +86 -0
  139. package/starters/default/src/shared/http/http-error.ts +37 -0
  140. package/starters/default/src/shared/http/interceptors.ts +59 -0
  141. package/starters/default/src/shared/lib/format-date.ts +19 -0
  142. package/starters/default/src/shared/styles/content.css +70 -0
  143. package/starters/default/src/shared/styles/reset.css +32 -0
  144. package/starters/default/src/shared/styles/shell.css +57 -0
  145. package/starters/default/src/shared/styles/tokens.css +44 -0
  146. package/starters/default/src/shared/ui/x-button.component.ts +49 -0
  147. package/starters/default/src/shared/ui/x-spinner.component.ts +22 -0
  148. package/starters/default/src/styles.d.ts +1 -0
  149. package/starters/default/src/vite-env.d.ts +1 -0
  150. package/starters/default/tsconfig.json +24 -0
  151. package/starters/default/vite.config.ts +9 -0
  152. package/MADO_V1_PLAN.md +0 -179
  153. package/ROADMAP.md +0 -178
  154. package/dist/src/html.d.ts +0 -18
  155. package/dist/src/html.js +0 -17
  156. package/dist/src/html.js.map +0 -1
  157. package/dist/src/router.d.ts +0 -13
  158. package/dist/src/router.js +0 -13
  159. package/dist/src/router.js.map +0 -1
  160. package/scripts/bundle.mjs +0 -212
  161. package/scripts/llm-zero-history-smoke.mjs +0 -93
  162. package/scripts/new.mjs +0 -80
  163. package/scripts/showcase-regression.mjs +0 -392
  164. package/server/serve.mjs +0 -455
  165. package/starters/admin/README.md +0 -63
  166. package/starters/admin/index.html +0 -28
  167. package/starters/admin/mado.config.json +0 -22
  168. package/starters/admin/package.json +0 -24
  169. package/starters/admin/public/favicon.svg +0 -4
  170. package/starters/admin/src/components/x-button.ts +0 -82
  171. package/starters/admin/src/components/x-input.ts +0 -105
  172. package/starters/admin/src/layouts/app.ts +0 -101
  173. package/starters/admin/src/layouts/auth.ts +0 -41
  174. package/starters/admin/src/lib/api.ts +0 -184
  175. package/starters/admin/src/lib/auth.ts +0 -83
  176. package/starters/admin/src/main.ts +0 -15
  177. package/starters/admin/src/pages/admin/dashboard.ts +0 -48
  178. package/starters/admin/src/pages/admin/order-detail.ts +0 -80
  179. package/starters/admin/src/pages/admin/orders.ts +0 -117
  180. package/starters/admin/src/pages/home.ts +0 -34
  181. package/starters/admin/src/pages/login.ts +0 -70
  182. package/starters/admin/src/pages/not-found.ts +0 -12
  183. package/starters/admin/src/routes.ts +0 -40
  184. package/starters/admin/src/styles/global.ts +0 -86
  185. package/starters/admin/tsconfig.json +0 -15
  186. package/starters/crud/README.md +0 -33
  187. package/starters/crud/index.html +0 -28
  188. package/starters/crud/mado.config.json +0 -20
  189. package/starters/crud/package.json +0 -24
  190. package/starters/crud/src/components/app-shell.ts +0 -56
  191. package/starters/crud/src/components/ticket-detail.ts +0 -33
  192. package/starters/crud/src/components/ticket-form.ts +0 -69
  193. package/starters/crud/src/components/ticket-list.ts +0 -66
  194. package/starters/crud/src/lib/api.ts +0 -76
  195. package/starters/crud/src/main.ts +0 -9
  196. package/starters/crud/src/pages/home.ts +0 -34
  197. package/starters/crud/src/pages/not-found.ts +0 -12
  198. package/starters/crud/src/pages/ticket-detail.ts +0 -7
  199. package/starters/crud/src/pages/ticket-new.ts +0 -7
  200. package/starters/crud/src/pages/tickets.ts +0 -7
  201. package/starters/crud/src/routes.ts +0 -11
  202. package/starters/crud/src/styles/global.ts +0 -155
  203. package/starters/crud/tsconfig.json +0 -15
  204. package/starters/minimal/README.md +0 -21
  205. package/starters/minimal/index.html +0 -28
  206. package/starters/minimal/mado.config.json +0 -20
  207. package/starters/minimal/package.json +0 -24
  208. package/starters/minimal/src/components/app-counter.ts +0 -31
  209. package/starters/minimal/src/main.ts +0 -9
  210. package/starters/minimal/src/pages/home.ts +0 -35
  211. package/starters/minimal/src/pages/not-found.ts +0 -14
  212. package/starters/minimal/src/routes.ts +0 -8
  213. package/starters/minimal/src/styles/global.ts +0 -60
  214. package/starters/minimal/tsconfig.json +0 -15
  215. package/templates/page-detail.ts +0 -63
  216. package/templates/page-form.ts +0 -94
  217. package/templates/page-list.ts +0 -79
@@ -1,204 +1,152 @@
1
1
  # Routing
2
2
 
3
- > One manifest file. No folder scanners. No special characters.
3
+ > One app map. No folder scanners. No special path syntax.
4
4
 
5
- ## Why not file-based
5
+ Mado does not infer routes from files. The browser sees files as files; route
6
+ composition should be readable in one place.
6
7
 
7
- In Next/SvelteKit/SolidStart routes appear "magically" from file names. This has
8
- advantages (URL structure visible in `pages/`), but in production it means:
8
+ ## App Manifest
9
9
 
10
- - An invisible plugin-scanner in the build. Without it the files are just files.
11
- - Special characters in paths: `[id]`, `(group)`, `_layout`, `+page.svelte`, `...slug`.
12
- - Server-routes and client-routes get confused.
13
- - Testing routing is a pain: you need a build-tool emulator.
10
+ Use `src/app.routes.ts` as the application map:
14
11
 
15
- Mado considers this **too much magic**. We do it differently.
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
+ ```
16
34
 
17
- ## Manifest
35
+ Open `app.routes.ts` and you can see the whole app: public pages, auth zone,
36
+ protected app zones, guards and shells.
18
37
 
19
- One file `src/routes.ts`. One object. Read top to bottom.
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.
20
44
 
21
45
  ```ts
22
- // src/routes.ts
23
- import { routes } from '@madojs/mado';
24
-
25
- export default routes({
26
- '/': () => import('./pages/home.js'),
27
- '/about': () => import('./pages/about.js'),
28
- '/users/:id': () => import('./pages/user-profile.js'),
29
- '/users/:id/edit':() => import('./pages/user-edit.js'),
30
- '*': () => import('./pages/not-found.js'),
31
- });
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
+ };
32
51
  ```
33
52
 
34
- Want to see all routes? Open `routes.ts`. No surprises.
35
-
36
- ## What goes on the right side of a path
53
+ The prefix is applied by `src/app.routes.ts` when the module is mounted under
54
+ `"/billing"`.
37
55
 
38
- Every entry is **one of three things**:
56
+ ## What Goes On The Right Side
39
57
 
40
- ### 1. Lazy import (recommended)
58
+ ### Lazy page import
41
59
 
42
60
  ```ts
43
- '/posts': () => import('./pages/posts.js'),
61
+ "/users/:id": () => import("./modules/users/pages/user-profile.page.js"),
44
62
  ```
45
63
 
46
- - The browser makes its own chunk when bundling (esbuild --bundle --splitting).
47
- - The module is loaded only when the user visits the route.
48
- - Subsequent navigations use the cached result.
64
+ Vite creates a separate chunk for dynamic imports in production.
49
65
 
50
- ### 2. Ready Page (eager)
66
+ ### Eager page
51
67
 
52
68
  ```ts
53
- import about from './pages/about.js';
69
+ import home from "./modules/home/home.page.js";
54
70
 
55
- '/about': about,
71
+ export const manifest = {
72
+ "/": home,
73
+ };
56
74
  ```
57
75
 
58
- In the bundle immediately, no delay. Use for critical pages (home, login).
76
+ Use this only for tiny critical pages.
59
77
 
60
- ### 3. Nested with layout
78
+ ### Layout group
61
79
 
62
80
  ```ts
63
- import { routes, nested } from '@madojs/mado';
64
-
65
- export default routes({
66
- '/': () => import('./pages/home.js'),
67
-
68
- '/admin/*': nested({
69
- layout: () => import('./layouts/admin.js'),
70
- routes: {
71
- '': () => import('./pages/admin/dashboard.js'),
72
- 'users': () => import('./pages/admin/users.js'),
73
- 'logs': () => import('./pages/admin/logs.js'),
74
- },
75
- }),
76
- });
81
+ "/admin": layout({
82
+ layout: () => import("./layouts/app-shell.layout.js"),
83
+ guard: requireAuth,
84
+ routes: adminRoutes,
85
+ }),
77
86
  ```
78
87
 
79
- A layout is just a regular `page({...})` that renders `ctx.child` wherever it wants:
88
+ A layout is a normal `page({...})` file:
80
89
 
81
90
  ```ts
82
- // src/layouts/admin.ts
83
- import { page, html, css, component } from '@madojs/mado';
91
+ import { html, page } from "@madojs/mado";
84
92
 
85
93
  export default page({
86
94
  view: ({ child }) => html`
87
- <div class="admin">
88
- <aside><nav>...</nav></aside>
89
- <main>${child}</main>
95
+ <div class="layout layout--app">
96
+ <main class="app-main">${child}</main>
90
97
  </div>
91
98
  `,
92
99
  });
93
100
  ```
94
101
 
95
- ## Page contract
102
+ Keep layout views stateless. Put page-specific signals, resources and forms in
103
+ pages/components/resources, not in layout locals.
96
104
 
97
- ```ts
98
- import { page, html, resource, jsonFetcher } from '@madojs/mado';
99
-
100
- export default page({
101
- title: ({ id }) => `User #${id}`, // string | (params) => string
102
- load: ({ id }) => resource(...), // optional, returns Resource or data
103
- view: ({ params, data, path, child }) => html`...`, // REQUIRED
104
- });
105
- ```
106
-
107
- Three slots, that's all. If you export something other than `page({...})`, a plain
108
- function for instance — `routes()` throws a clear error:
109
-
110
- ```
111
- [Mado] Lazy route did not return page({...}) as the default export.
112
- ```
113
-
114
- ## URL parameters
105
+ ## Page Contract
115
106
 
116
107
  ```ts
117
- '/users/:id': () => import('./pages/user.js'),
118
- ```
108
+ import { html, page } from "@madojs/mado";
119
109
 
120
- ```ts
121
110
  export default page<{ id: string }>({
122
111
  title: ({ id }) => `User ${id}`,
123
- view: ({ params }) => html`<h1>${params.id}</h1>`,
112
+ view: ({ params }) => html`<h1>${params.id}</h1>`,
124
113
  });
125
114
  ```
126
115
 
127
- Types are passed in `page<Params>` `tsc` verifies that you don't access
128
- `params.foo` which doesn't exist in the route.
129
-
130
- ## Global options
131
-
132
- ```ts
133
- export default routes(
134
- { '/': home, '/about': about, '*': nf },
135
- {
136
- titleSuffix: ' · MyApp', // → "Home · MyApp"
137
- loading: () => html`<x-spinner/>`, // while module loads
138
- error: (err) => html`<x-fatal-error .err=${err}/>`,
139
- },
140
- );
141
- ```
116
+ If a lazy route does not default-export `page({...})`, `routes()` throws a clear
117
+ error.
142
118
 
143
- ## Programmatic navigation
119
+ ## Navigation
144
120
 
145
121
  ```ts
146
- import route from './routes.js';
122
+ import appRoutes from "./app.routes.js";
147
123
 
148
- route.navigate('/posts');
149
- route.navigate('/posts?page=2');
150
- route.navigate('/posts', { replace: true });
124
+ appRoutes.navigate("/billing/invoices");
125
+ appRoutes.navigate("/billing/invoices?page=2");
126
+ appRoutes.navigate("/login", { replace: true });
151
127
  ```
152
128
 
153
- Clicks on `<a href="/foo" data-link>` are intercepted globally (without the
154
- attribute the browser does a full reload, as expected for external links).
129
+ Clicks on `<a href="/foo">` are intercepted for same-origin SPA navigation.
130
+ External links still use normal browser navigation.
155
131
 
156
- ## Query parameters
132
+ ## Query Parameters
157
133
 
158
134
  ```ts
159
- import { queryParam } from '@madojs/mado';
135
+ import { queryParam } from "@madojs/mado";
160
136
 
161
- const page = queryParam('page', '1');
162
- page(); // '1'
163
- page.set('2'); // history.replaceState + re-render
164
- page.set(null); // delete the parameter
165
- page.set('3', { push: true }); // history.pushState
137
+ const page = queryParam("page", "1");
138
+ page();
139
+ page.set("2");
140
+ page.set(null);
141
+ page.set("3", { push: true });
166
142
  ```
167
143
 
168
- `queryParam` is a normal signal. Use it anywhere: in pages, components, computed.
169
-
170
- ## What is intentionally absent
171
-
172
- - ❌ Auto-scan of `pages/`. **One explicit manifest file**.
173
- - ❌ Special characters in paths (`[id]`, `(group)`, `_layout`). **Parameters are
174
- `:name` only, nothing else**.
175
- - ❌ Server-side routing in the same manifest. Mado is a client-side framework.
176
- - ❌ Auto-prefetch on hover. If you really need it — do it manually:
177
- `link.addEventListener('mouseenter', loader)`. Usually unnecessary.
178
-
179
- ## FAQ
180
-
181
- **What if I have 100 routes? Won't the file get huge?**
182
- It will grow to ~150 lines. That is still **one source of truth** versus a hundred
183
- files in `pages/` with magic names. In practice even large projects (1000+ pages)
184
- can split into feature manifests:
185
-
186
- ```ts
187
- import { routes } from '@madojs/mado';
188
- import adminRoutes from './features/admin/routes.js';
189
- import billingRoutes from './features/billing/routes.js';
190
-
191
- export default routes({
192
- ...adminRoutes,
193
- ...billingRoutes,
194
- '*': () => import('./pages/not-found.js'),
195
- });
196
- ```
144
+ `queryParam()` is a signal. Use it in pages or components when URL state is part
145
+ of the UI.
197
146
 
198
- **How do I test routing?**
199
- Import `routes.ts` — it is just an object. Substitute your mock router. No build
200
- tool emulation needed.
147
+ ## What Is Intentionally Absent
201
148
 
202
- **Does code splitting work?**
203
- Yes. With `esbuild --bundle --splitting --format=esm` every
204
- `() => import('./pages/x.js')` becomes its own chunk.
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`.
@@ -8,40 +8,42 @@ things live.
8
8
  my-app/
9
9
  ├── package.json # exactly one runtime dep: @madojs/mado
10
10
  ├── tsconfig.json # strict TS, ES2022, Bundler resolution
11
- ├── mado.config.json # single config file (dev/build/bake/bundle)
12
- ├── index.html # SPA shell (also the template for `mado bake`)
11
+ ├── vite.config.ts # optional; use mado() from @madojs/mado/vite
12
+ ├── index.html # Vite entry + SPA shell
13
13
  ├── public/ # static assets (favicons, images, robots.txt)
14
14
  └── src/
15
15
  ├── main.ts # entry: mount router into #app
16
- ├── routes.ts # route manifest (default + named `manifest`)
17
- ├── layouts/ # `page({ child })` layouts for nested routes
18
- ├── pages/ # one page = one file
19
- ├── components/ # reusable x-* Web Components
20
- └── lib/
21
- ├── api.ts # API client + error type
22
- ├── auth.ts # auth recipe (token + guard)
23
- └── ... # contexts, helpers, business rules
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/
24
28
  ```
25
29
 
26
30
  ## The three artifact states (read this once, never wonder again)
27
31
 
28
32
  | Folder | What it is | Who writes | Who reads | Deploy? |
29
33
  |-------------|----------------------------------------------------------------|-------------------|----------------------------|-------------------|
30
- | `src/` | your source (TypeScript) | you | `tsc`, `esbuild` | ❌ no |
31
- | `dist/` | `tsc` output native ESM `.js` for the browser | `mado build` | `mado dev` (during dev) | ❌ no (internal) |
32
- | `public/` | static assets you authored (favicon, images, robots.txt) | you | `mado release` copies it | ✅ via `out/` |
33
- | `out/` | **the deploy artifact**: SPA shell + bundles + baked HTML | `mado release` | nginx / CDN / Cloudflare | ✅ **yes** |
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** |
34
37
 
35
38
  One-liner to remember:
36
39
  > Develop with `mado dev`. To ship: run `mado release`, then upload `out/`.
37
40
 
38
- `mado release` = `typecheck` + `build` (tsc → `dist/`) + `bundle` (esbuild
39
- `out/assets/`) + `bake` (HTML `out/baked/`) + promote baked HTML and
40
- `sitemap.xml` into deployable `out/` paths + copy `public/*` → `out/`.
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.
41
44
 
42
- You almost never need to look inside `dist/`. It exists so the dev browser can
43
- load native ESM modules without a bundler during development. In production
44
- the equivalent code is bundled and hashed into `out/assets/`.
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/`.
45
47
 
46
48
  ### Quick deployment matrix
47
49
 
@@ -57,14 +59,19 @@ See `docs/en/13-deployment.md` for full recipes.
57
59
 
58
60
  | What | Where |
59
61
  |-------------------------------------|------------------------------------------------------|
60
- | Page for a new URL | `src/pages/<name>.ts` + add to `src/routes.ts` |
61
- | Layout for a group of routes | `src/layouts/<name>.ts` (referenced from `routes.ts`)|
62
- | Reusable UI widget | `src/components/<x-name>.ts` |
63
- | API call | `src/lib/api.ts` (add a method) |
64
- | Auth/session | `src/lib/auth.ts` |
65
- | Global context (theme, user, i18n) | `src/lib/<name>.ts` |
66
- | Pure function with no UI | `src/lib/util/<name>.ts` |
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` |
67
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` |
68
75
 
69
76
  If you don't know where — that is a signal that **the architecture is
70
77
  suffering**. Ask the team and **record** the answer in `docs/`. Don't invent a
@@ -80,39 +87,38 @@ new top-level folder.
80
87
  | Signal | camelCase | `userId`, `isLoggedIn` |
81
88
  | Page-internal element | `x-<route>-page` | `<x-posts-page>` |
82
89
 
83
- ## `mado.config.json` in one screen
90
+ ## Vite config
84
91
 
85
- ```jsonc
86
- {
87
- "dev": {
88
- "port": 5173,
89
- "proxy": { "/api": "http://localhost:3000" } // dev → backend
90
- },
91
- "build": {
92
- "out": "out",
93
- "dist": "dist",
94
- "publicDir": "public"
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",
95
102
  },
96
- "bake": {
97
- "entry": "src/routes.ts",
98
- "template": "index.html",
99
- "baseUrl": "https://example.com"
103
+ server: {
104
+ proxy: { "/api": "http://localhost:3000" },
100
105
  },
101
- "bundle": {
102
- "splitting": true,
103
- "compress": ["gz", "br"]
104
- }
105
- }
106
+ });
106
107
  ```
107
108
 
108
- Precedence: built-in defaults < `mado.config.json` < CLI flags
109
- (< legacy env vars). All keys are optional.
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.
110
117
 
111
118
  ## What does NOT go in `src/`
112
119
 
113
- - ❌ Build tool configs (webpack, rollup, vite) we don't have any.
114
- - ❌ `.env` files — read env in `src/lib/config.ts` from `import.meta.env` /
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` /
115
122
  `process.env` and import that one module everywhere.
116
123
  - ❌ Tests mixed with code — put them in `test/`.
117
- - ❌ `examples/` folder — the framework repository has examples, your app
118
- does not need one.
124
+ - ❌ `examples/` folder — keep large demos outside the app repo.
@@ -40,7 +40,7 @@ The `npm run bake` command traverses all `page` entries with `bake`, generates H
40
40
  ## Example
41
41
 
42
42
  ```ts
43
- // src/pages/product.ts
43
+ // src/modules/<module>/pages/product.ts
44
44
  import { page, component, html } from "@madojs/mado";
45
45
  import { findProduct, products, type Product } from "../lib/products.js";
46
46
 
@@ -84,7 +84,7 @@ export default page<{ slug: string }, Product | undefined>({
84
84
  ```
85
85
 
86
86
  ```ts
87
- // src/routes.ts
87
+ // src/app.routes.ts
88
88
  import { routes, type RoutesMap } from "@madojs/mado";
89
89
 
90
90
  // Export BOTH default (RouterApi for runtime) AND manifest (for the bake script).
@@ -100,8 +100,7 @@ export default routes(manifest, { titleSuffix: " · MyShop" });
100
100
  ## Running
101
101
 
102
102
  ```bash
103
- npm install -D linkedom esbuild
104
- npm run build
103
+ npm install -D linkedom vite
105
104
  npm run bake
106
105
  ```
107
106
 
@@ -144,7 +143,7 @@ out/
144
143
  {"slug":"mado-mug","name":"Mado Mug","price":12,"..."}
145
144
  </script>
146
145
 
147
- <script type="module" src="/dist/examples/main.js"></script>
146
+ <script type="module" src="/assets/index-HASH.js"></script>
148
147
  </body>
149
148
  ```
150
149
 
@@ -174,7 +173,7 @@ export const allPosts = () =>
174
173
  ```
175
174
 
176
175
  ```ts
177
- // src/pages/blog-post.ts
176
+ // src/modules/<module>/pages/blog-post.ts
178
177
  import { page, html } from "@madojs/mado";
179
178
  import { allPosts } from "../lib/posts.js";
180
179
 
@@ -36,11 +36,11 @@ If your case does not fall into the last point — Mado is most likely not the b
36
36
  | Size | ~6 KB | ~16 KB |
37
37
  | Age / support | ~10 years, Google | 6 months, single author |
38
38
  | Reactivity | `@property` decorators + manual `requestUpdate` | signals (`signal`/`computed`/`effect`) out of the box |
39
- | Router | none, you need to find one (`@lit-labs/router`, etc) | included: `routes()` + nested + prefetch + sync-cache |
39
+ | Router | none, you need to find one (`@lit-labs/router`, etc) | included: `routes()` + layout groups + prefetch |
40
40
  | Data fetching | none, you need to assemble it | `resource()` + `mutation()` + glob invalidation |
41
41
  | Forms | none | `useForm()` with HTML-like constraints |
42
42
  | SEO / static | complex (`@lit-labs/ssr`) | `bake` (linkedom) + edge-prerender |
43
- | Build | needs esbuild/rollup/webpack | `tsc` is enough |
43
+ | Build | needs framework-specific build plugins | Vite transport + native runtime |
44
44
  | Code style | classes + decorators | functions + tagged templates |
45
45
  | Ecosystem | real (Shoelace, Material Web, etc.) | none |
46
46
  | When to choose | writing a design system / Web Components library for embedding | writing a full application, want everything in one box |
@@ -60,14 +60,14 @@ If your case does not fall into the last point — Mado is most likely not the b
60
60
  | Reactivity | signals (same class of ideas) | signals |
61
61
  | Templates | JSX (compiled to reactive expressions) | tagged template `html\`\`` |
62
62
  | Component model | functions, Solid virtual nodes | Web Components |
63
- | Build | Vite + babel-plugin-solid required | `tsc` only |
63
+ | Build | Vite + babel-plugin-solid required | Vite for dev/build, no runtime dependencies |
64
64
  | Router | `@solidjs/router` | included |
65
65
  | Data | `createResource` | `resource()` |
66
66
  | SSR | seriously supported (SolidStart) | intentionally none |
67
67
  | Ecosystem | growing, ~50 packages | none |
68
- | When to choose | need top performance + JSX + willing to configure the build | want to run without a build / minimal infrastructure |
68
+ | When to choose | need top performance + JSX + willing to configure the build | want browser-native runtime with simple tooling |
69
69
 
70
- **Honest pitch:** _"Solid is technically faster and more mature. But Solid requires Vite + a babel plugin. Mado requires nothing but `tsc` it's 'open VS Code, F5, and work'. If that difference isn't critical go with Solid."_
70
+ **Honest pitch:** _"Solid is technically faster and more mature. Mado is smaller in concept: browser-native runtime, Web Components, tagged templates, and Vite doing the boring dev/build work. If you want JSX and a larger ecosystem, go with Solid."_
71
71
 
72
72
  ---
73
73
 
@@ -162,7 +162,7 @@ I won't dwell on this for long, because React is in a **different weight class**
162
162
 
163
163
  Not size, not performance, not signals — everything has better competitors.
164
164
 
165
- > **"Open the source and read it in an evening. ~3500 lines, 12 modules. If something breaks — you don't go to an issue with 3000 comments. You go to `src/router.ts` and read 500 lines."**
165
+ > **"Open the source and read it in an evening. ~3500 lines, small modules. If something breaks — you don't go to an issue with 3000 comments. You go to `src/router/` and read the code."**
166
166
 
167
167
  This is called **ownership** — you own the code, rather than depending on someone else's.
168
168
 
@@ -14,7 +14,7 @@ Mado is structured **like an HTTP server**. Seriously:
14
14
  | ----------------------------------- | ---------------------------------------------- |
15
15
  | HTTP router (chi, axum, mux) | `routes()` — path manifest |
16
16
  | Handler `func(req, resp)` | `page({ view: (ctx) => html\`...\` })` |
17
- | Middleware | `layout` in `nested()` (wraps the handler) |
17
+ | Middleware | `layout()` route group (wraps the handler) |
18
18
  | Template engine (Jinja, Handlebars) | `html\`\`` tagged template |
19
19
  | HTTP client with cache | `resource()` — fetch + cache + invalidation |
20
20
  | Reactive variable / atom | `signal()` — reactive getter |
@@ -61,7 +61,7 @@ http.ListenAndServe(":8080", r)
61
61
  ### Mado — the same thing
62
62
 
63
63
  ```ts
64
- // src/routes.ts
64
+ // src/app.routes.ts
65
65
  import { routes } from "@madojs/mado";
66
66
 
67
67
  export default routes({
@@ -71,7 +71,7 @@ export default routes({
71
71
  ```
72
72
 
73
73
  ```ts
74
- // src/pages/home.ts
74
+ // src/modules/<module>/pages/home.ts
75
75
  import { page, html } from "@madojs/mado";
76
76
  export default page({
77
77
  view: () => html`<h1>Hello</h1>`,
@@ -79,7 +79,7 @@ export default page({
79
79
  ```
80
80
 
81
81
  ```ts
82
- // src/pages/user.ts
82
+ // src/modules/<module>/pages/user.ts
83
83
  import { page, html } from "@madojs/mado";
84
84
  export default page<{ id: string }>({
85
85
  view: ({ params }) => html`<h1>User ${params.id}</h1>`,
@@ -266,7 +266,7 @@ This is like `context.WithValue` / `ctx.Value` in Go, but reactive.
266
266
  If you're used to server-side rendering for SEO, in Mado this is solved differently: **prerender at build time**.
267
267
 
268
268
  ```ts
269
- // src/pages/product.ts
269
+ // src/modules/<module>/pages/product.ts
270
270
  export default page({
271
271
  bake: {
272
272
  paths: () => api.allProductSlugs(), // build-time fetch
@@ -369,37 +369,33 @@ export default page({
369
369
  ```
370
370
 
371
371
  ```ts
372
- // src/routes.ts
373
- import { routes, nested } from "@madojs/mado";
372
+ // src/app.routes.ts
373
+ import { layout, routes } from "@madojs/mado";
374
374
 
375
375
  export default routes({
376
376
  "/login": () => import("./pages/login.js"),
377
377
 
378
- "/app/*": nested({
378
+ "/app": layout({
379
379
  layout: () => import("./layouts/auth-layout.js"),
380
380
  routes: {
381
- dashboard: () => import("./pages/dashboard.js"),
382
- users: () => import("./pages/users.js"),
381
+ "/dashboard": () => import("./pages/dashboard.js"),
382
+ "/users": () => import("./pages/users.js"),
383
383
  },
384
384
  }),
385
385
  });
386
386
  ```
387
387
 
388
- ### Global API client (like a singleton in Go)
388
+ ### Shared HTTP client (like a small transport package in Go)
389
389
 
390
390
  ```ts
391
- // src/lib/api.ts
392
- export class ApiClient {
393
- constructor(private base: string) {}
394
- get<T>(path: string): Promise<T> {
395
- return fetch(this.base + path).then((r) => r.json());
396
- }
397
- }
398
-
399
- export const api = new ApiClient("/api");
391
+ // src/shared/http/http-client.ts
392
+ export const httpClient = {
393
+ get: <T>(path: string): Promise<T> =>
394
+ fetch(path).then((r) => r.json() as Promise<T>),
395
+ };
400
396
  ```
401
397
 
402
- Used directly via `import { api } from '...'` or through `createContext` for testability.
398
+ Module connectors build on this transport and map DTOs to domain types.
403
399
 
404
400
  ---
405
401
 
@@ -442,6 +438,6 @@ Everything else — standard browser + TypeScript.
442
438
  - **[`01-routing.md`](./01-routing.md)** — the router in detail.
443
439
  - **[`02-project-layout.md`](./02-project-layout.md)** — project structure.
444
440
  - **[`03-static-bake.md`](./03-static-bake.md)** — SEO without SSR.
445
- - **[`examples/showcase/`](../../examples/showcase/)** — full example (landing + admin).
441
+ - **External `madojs-examples` workspace** — full demos (landing + admin).
446
442
 
447
443
  If something is unclear — open an issue, or just open the source. It really is readable in an evening.