@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.
- package/AGENTS.md +24 -26
- package/CHANGELOG.md +68 -0
- package/README.md +18 -45
- package/TODO.md +52 -48
- package/dist/src/component.d.ts +2 -1
- package/dist/src/component.js +5 -2
- package/dist/src/component.js.map +1 -1
- package/dist/src/each.d.ts +1 -1
- package/dist/src/each.js +1 -1
- package/dist/src/each.js.map +1 -1
- package/dist/src/index.d.ts +11 -6
- package/dist/src/index.js +5 -3
- package/dist/src/index.js.map +1 -1
- package/dist/src/lazy.d.ts +1 -1
- package/dist/src/lazy.js +1 -1
- package/dist/src/lazy.js.map +1 -1
- package/dist/src/page.d.ts +17 -21
- package/dist/src/page.js +7 -12
- package/dist/src/page.js.map +1 -1
- package/dist/src/router/manifest.d.ts +1 -1
- package/dist/src/router/manifest.js +21 -13
- package/dist/src/router/manifest.js.map +1 -1
- package/dist/src/router/match.d.ts +2 -2
- package/dist/src/router/match.js +3 -3
- package/dist/src/router/match.js.map +1 -1
- package/dist/src/router/navigation.js +1 -1
- package/dist/src/router/navigation.js.map +1 -1
- package/dist/src/vite/index.d.ts +10 -0
- package/dist/src/vite/index.js +33 -0
- package/dist/src/vite/index.js.map +1 -0
- package/docs/en/00-the-mado-way.md +25 -12
- package/docs/en/01-routing.md +90 -142
- package/docs/en/02-project-layout.md +59 -53
- package/docs/en/03-static-bake.md +5 -6
- package/docs/en/05-why-mado.md +6 -6
- package/docs/en/06-for-backenders.md +18 -22
- package/docs/en/08-llm-zero-history-test.md +9 -14
- package/docs/en/09-shadow-vs-light-dom.md +28 -36
- package/docs/en/10-app-architecture.md +158 -96
- package/docs/en/11-layouts.md +22 -24
- package/docs/en/12-auth-and-api.md +89 -182
- package/docs/en/13-deployment.md +18 -22
- package/docs/en/14-testing.md +4 -4
- package/docs/en/16-bake-cookbook.md +11 -12
- package/docs/en/18-api-freeze-map.md +6 -4
- package/docs/en/20-v1-stability.md +1 -1
- package/docs/fr/00-the-mado-way.md +55 -90
- package/docs/fr/01-routing.md +70 -152
- package/docs/fr/02-project-layout.md +61 -42
- package/docs/fr/03-static-bake.md +1 -1
- package/docs/fr/05-why-mado.md +6 -6
- package/docs/fr/06-for-backenders.md +7 -7
- package/docs/fr/08-llm-zero-history-test.md +21 -48
- package/docs/fr/09-shadow-vs-light-dom.md +43 -162
- package/docs/fr/10-app-architecture.md +110 -33
- package/docs/fr/11-layouts.md +24 -12
- package/docs/fr/12-auth-and-api.md +63 -22
- package/docs/fr/13-deployment.md +7 -10
- package/docs/fr/14-testing.md +1 -1
- package/docs/fr/16-bake-cookbook.md +2 -2
- package/docs/fr/18-api-freeze-map.md +1 -1
- package/docs/fr/20-v1-stability.md +1 -1
- package/docs/recipes/nginx/README.md +13 -0
- package/docs/ru/00-the-mado-way.md +53 -75
- package/docs/ru/01-routing.md +68 -143
- package/docs/ru/02-project-layout.md +61 -41
- package/docs/ru/03-static-bake.md +2 -2
- package/docs/ru/05-why-mado.md +6 -6
- package/docs/ru/06-for-backenders.md +7 -7
- package/docs/ru/08-llm-zero-history-test.md +9 -14
- package/docs/ru/09-shadow-vs-light-dom.md +43 -178
- package/docs/ru/10-app-architecture.md +115 -63
- package/docs/ru/11-layouts.md +24 -24
- package/docs/ru/12-auth-and-api.md +57 -35
- package/docs/ru/13-deployment.md +7 -11
- package/docs/ru/14-testing.md +1 -1
- package/docs/ru/16-bake-cookbook.md +12 -6
- package/docs/ru/18-api-freeze-map.md +5 -3
- package/docs/ru/20-v1-stability.md +1 -1
- package/docs/uk/00-the-mado-way.md +70 -44
- package/docs/uk/01-routing.md +41 -47
- package/docs/uk/02-project-layout.md +68 -41
- package/docs/uk/03-static-bake.md +1 -2
- package/docs/uk/06-for-backenders.md +3 -3
- package/docs/uk/08-llm-zero-history-test.md +22 -24
- package/docs/uk/09-shadow-vs-light-dom.md +37 -86
- package/docs/uk/10-app-architecture.md +72 -31
- package/docs/uk/11-layouts.md +25 -12
- package/docs/uk/12-auth-and-api.md +58 -22
- package/docs/uk/13-deployment.md +4 -3
- package/docs/uk/14-testing.md +1 -1
- package/docs/uk/18-api-freeze-map.md +1 -1
- package/docs/uk/20-v1-stability.md +1 -1
- package/llms.txt +14 -15
- package/package.json +18 -11
- package/scripts/_config.mjs +15 -161
- package/scripts/bake.mjs +67 -57
- package/scripts/cli/generate.mjs +348 -0
- package/scripts/cli/help.mjs +27 -0
- package/scripts/cli/index.mjs +79 -0
- package/scripts/cli/init.mjs +153 -0
- package/scripts/cli/release.mjs +152 -0
- package/scripts/cli/run.mjs +96 -0
- package/scripts/cli.mjs +2 -621
- package/scripts/package-smoke.mjs +4 -1
- package/scripts/preview.mjs +13 -37
- package/scripts/size-budget.mjs +5 -2
- package/scripts/vite.default.mjs +11 -0
- package/starters/default/.editorconfig +12 -0
- package/starters/default/README.md +74 -0
- package/starters/default/eslint.config.mjs +256 -0
- package/starters/default/index.html +13 -0
- package/starters/default/package.json +30 -0
- package/starters/default/public/favicon.svg +4 -0
- package/starters/default/src/app.routes.ts +39 -0
- package/starters/default/src/layouts/app-shell.layout.ts +35 -0
- package/starters/default/src/layouts/auth-shell.layout.ts +17 -0
- package/starters/default/src/main.ts +16 -0
- package/starters/default/src/modules/auth/_contracts/auth-api.types.ts +17 -0
- package/starters/default/src/modules/auth/auth.connector.ts +45 -0
- package/starters/default/src/modules/auth/auth.guard.ts +22 -0
- package/starters/default/src/modules/auth/auth.public.ts +9 -0
- package/starters/default/src/modules/auth/auth.routes.ts +8 -0
- package/starters/default/src/modules/auth/auth.service.ts +71 -0
- package/starters/default/src/modules/auth/auth.types.ts +15 -0
- package/starters/default/src/modules/auth/login.page.ts +62 -0
- package/starters/default/src/modules/billing/_contracts/stripe.types.ts +17 -0
- package/starters/default/src/modules/billing/api/stripe.connector.ts +71 -0
- package/starters/default/src/modules/billing/billing.public.ts +5 -0
- package/starters/default/src/modules/billing/billing.routes.ts +9 -0
- package/starters/default/src/modules/billing/billing.types.ts +15 -0
- package/starters/default/src/modules/billing/components/invoice-status-badge.component.ts +43 -0
- package/starters/default/src/modules/billing/data/invoices.resource.ts +35 -0
- package/starters/default/src/modules/billing/pages/invoice-detail.page.ts +70 -0
- package/starters/default/src/modules/billing/pages/invoices-list.page.ts +73 -0
- package/starters/default/src/modules/home/home.page.ts +34 -0
- package/starters/default/src/modules/home/not-found.page.ts +11 -0
- package/starters/default/src/shared/http/http-client.ts +86 -0
- package/starters/default/src/shared/http/http-error.ts +37 -0
- package/starters/default/src/shared/http/interceptors.ts +59 -0
- package/starters/default/src/shared/lib/format-date.ts +19 -0
- package/starters/default/src/shared/styles/content.css +70 -0
- package/starters/default/src/shared/styles/reset.css +32 -0
- package/starters/default/src/shared/styles/shell.css +57 -0
- package/starters/default/src/shared/styles/tokens.css +44 -0
- package/starters/default/src/shared/ui/x-button.component.ts +49 -0
- package/starters/default/src/shared/ui/x-spinner.component.ts +22 -0
- package/starters/default/src/styles.d.ts +1 -0
- package/starters/default/src/vite-env.d.ts +1 -0
- package/starters/default/tsconfig.json +24 -0
- package/starters/default/vite.config.ts +9 -0
- package/MADO_V1_PLAN.md +0 -179
- package/ROADMAP.md +0 -178
- package/dist/src/html.d.ts +0 -18
- package/dist/src/html.js +0 -17
- package/dist/src/html.js.map +0 -1
- package/dist/src/router.d.ts +0 -13
- package/dist/src/router.js +0 -13
- package/dist/src/router.js.map +0 -1
- package/scripts/bundle.mjs +0 -212
- package/scripts/llm-zero-history-smoke.mjs +0 -93
- package/scripts/new.mjs +0 -80
- package/scripts/showcase-regression.mjs +0 -392
- package/server/serve.mjs +0 -455
- package/starters/admin/README.md +0 -63
- package/starters/admin/index.html +0 -28
- package/starters/admin/mado.config.json +0 -22
- package/starters/admin/package.json +0 -24
- package/starters/admin/public/favicon.svg +0 -4
- package/starters/admin/src/components/x-button.ts +0 -82
- package/starters/admin/src/components/x-input.ts +0 -105
- package/starters/admin/src/layouts/app.ts +0 -101
- package/starters/admin/src/layouts/auth.ts +0 -41
- package/starters/admin/src/lib/api.ts +0 -184
- package/starters/admin/src/lib/auth.ts +0 -83
- package/starters/admin/src/main.ts +0 -15
- package/starters/admin/src/pages/admin/dashboard.ts +0 -48
- package/starters/admin/src/pages/admin/order-detail.ts +0 -80
- package/starters/admin/src/pages/admin/orders.ts +0 -117
- package/starters/admin/src/pages/home.ts +0 -34
- package/starters/admin/src/pages/login.ts +0 -70
- package/starters/admin/src/pages/not-found.ts +0 -12
- package/starters/admin/src/routes.ts +0 -40
- package/starters/admin/src/styles/global.ts +0 -86
- package/starters/admin/tsconfig.json +0 -15
- package/starters/crud/README.md +0 -33
- package/starters/crud/index.html +0 -28
- package/starters/crud/mado.config.json +0 -20
- package/starters/crud/package.json +0 -24
- package/starters/crud/src/components/app-shell.ts +0 -56
- package/starters/crud/src/components/ticket-detail.ts +0 -33
- package/starters/crud/src/components/ticket-form.ts +0 -69
- package/starters/crud/src/components/ticket-list.ts +0 -66
- package/starters/crud/src/lib/api.ts +0 -76
- package/starters/crud/src/main.ts +0 -9
- package/starters/crud/src/pages/home.ts +0 -34
- package/starters/crud/src/pages/not-found.ts +0 -12
- package/starters/crud/src/pages/ticket-detail.ts +0 -7
- package/starters/crud/src/pages/ticket-new.ts +0 -7
- package/starters/crud/src/pages/tickets.ts +0 -7
- package/starters/crud/src/routes.ts +0 -11
- package/starters/crud/src/styles/global.ts +0 -155
- package/starters/crud/tsconfig.json +0 -15
- package/starters/minimal/README.md +0 -21
- package/starters/minimal/index.html +0 -28
- package/starters/minimal/mado.config.json +0 -20
- package/starters/minimal/package.json +0 -24
- package/starters/minimal/src/components/app-counter.ts +0 -31
- package/starters/minimal/src/main.ts +0 -9
- package/starters/minimal/src/pages/home.ts +0 -35
- package/starters/minimal/src/pages/not-found.ts +0 -14
- package/starters/minimal/src/routes.ts +0 -8
- package/starters/minimal/src/styles/global.ts +0 -60
- package/starters/minimal/tsconfig.json +0 -15
- package/templates/page-detail.ts +0 -63
- package/templates/page-form.ts +0 -94
- package/templates/page-list.ts +0 -79
package/docs/en/01-routing.md
CHANGED
|
@@ -1,204 +1,152 @@
|
|
|
1
1
|
# Routing
|
|
2
2
|
|
|
3
|
-
> One
|
|
3
|
+
> One app map. No folder scanners. No special path syntax.
|
|
4
4
|
|
|
5
|
-
|
|
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
|
-
|
|
8
|
-
advantages (URL structure visible in `pages/`), but in production it means:
|
|
8
|
+
## App Manifest
|
|
9
9
|
|
|
10
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
|
|
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
|
-
|
|
56
|
+
## What Goes On The Right Side
|
|
39
57
|
|
|
40
|
-
###
|
|
58
|
+
### Lazy page import
|
|
41
59
|
|
|
42
60
|
```ts
|
|
43
|
-
|
|
61
|
+
"/users/:id": () => import("./modules/users/pages/user-profile.page.js"),
|
|
44
62
|
```
|
|
45
63
|
|
|
46
|
-
|
|
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
|
-
###
|
|
66
|
+
### Eager page
|
|
51
67
|
|
|
52
68
|
```ts
|
|
53
|
-
import
|
|
69
|
+
import home from "./modules/home/home.page.js";
|
|
54
70
|
|
|
55
|
-
|
|
71
|
+
export const manifest = {
|
|
72
|
+
"/": home,
|
|
73
|
+
};
|
|
56
74
|
```
|
|
57
75
|
|
|
58
|
-
|
|
76
|
+
Use this only for tiny critical pages.
|
|
59
77
|
|
|
60
|
-
###
|
|
78
|
+
### Layout group
|
|
61
79
|
|
|
62
80
|
```ts
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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
|
|
88
|
+
A layout is a normal `page({...})` file:
|
|
80
89
|
|
|
81
90
|
```ts
|
|
82
|
-
|
|
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="
|
|
88
|
-
<
|
|
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
|
-
|
|
102
|
+
Keep layout views stateless. Put page-specific signals, resources and forms in
|
|
103
|
+
pages/components/resources, not in layout locals.
|
|
96
104
|
|
|
97
|
-
|
|
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
|
-
|
|
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:
|
|
112
|
+
view: ({ params }) => html`<h1>${params.id}</h1>`,
|
|
124
113
|
});
|
|
125
114
|
```
|
|
126
115
|
|
|
127
|
-
|
|
128
|
-
|
|
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
|
-
##
|
|
119
|
+
## Navigation
|
|
144
120
|
|
|
145
121
|
```ts
|
|
146
|
-
import
|
|
122
|
+
import appRoutes from "./app.routes.js";
|
|
147
123
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
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"
|
|
154
|
-
|
|
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
|
|
132
|
+
## Query Parameters
|
|
157
133
|
|
|
158
134
|
```ts
|
|
159
|
-
import { queryParam } from
|
|
135
|
+
import { queryParam } from "@madojs/mado";
|
|
160
136
|
|
|
161
|
-
const page = queryParam(
|
|
162
|
-
page();
|
|
163
|
-
page.set(
|
|
164
|
-
page.set(null);
|
|
165
|
-
page.set(
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
203
|
-
|
|
204
|
-
|
|
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
|
-
├──
|
|
12
|
-
├── index.html #
|
|
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
|
|
17
|
-
├── layouts/ #
|
|
18
|
-
├──
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
|
31
|
-
| `
|
|
32
|
-
| `
|
|
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` +
|
|
39
|
-
|
|
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.
|
|
41
44
|
|
|
42
|
-
|
|
43
|
-
|
|
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` +
|
|
61
|
-
|
|
|
62
|
-
|
|
|
63
|
-
|
|
|
64
|
-
|
|
|
65
|
-
|
|
|
66
|
-
|
|
|
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
|
-
##
|
|
90
|
+
## Vite config
|
|
84
91
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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
|
-
|
|
97
|
-
"
|
|
98
|
-
"template": "index.html",
|
|
99
|
-
"baseUrl": "https://example.com"
|
|
103
|
+
server: {
|
|
104
|
+
proxy: { "/api": "http://localhost:3000" },
|
|
100
105
|
},
|
|
101
|
-
|
|
102
|
-
"splitting": true,
|
|
103
|
-
"compress": ["gz", "br"]
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
+
});
|
|
106
107
|
```
|
|
107
108
|
|
|
108
|
-
|
|
109
|
-
|
|
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
|
-
- ❌
|
|
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 —
|
|
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
|
|
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="/
|
|
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
|
|
package/docs/en/05-why-mado.md
CHANGED
|
@@ -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()` +
|
|
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
|
|
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 |
|
|
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
|
|
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.
|
|
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,
|
|
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
|
|
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 {
|
|
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
|
|
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
|
-
###
|
|
388
|
+
### Shared HTTP client (like a small transport package in Go)
|
|
389
389
|
|
|
390
390
|
```ts
|
|
391
|
-
// src/
|
|
392
|
-
export
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
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
|
-
|
|
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
|
-
- **
|
|
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.
|