@madojs/mado 0.11.1 → 0.13.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 +116 -32
- package/CHANGELOG.md +216 -1
- package/README.md +158 -173
- package/dist/src/component.d.ts +6 -3
- package/dist/src/component.d.ts.map +1 -0
- package/dist/src/component.js +163 -36
- package/dist/src/component.js.map +1 -1
- package/dist/src/context.d.ts +13 -32
- package/dist/src/context.d.ts.map +1 -0
- package/dist/src/context.js +73 -48
- package/dist/src/context.js.map +1 -1
- package/dist/src/css.d.ts +1 -0
- package/dist/src/css.d.ts.map +1 -0
- package/dist/src/css.js +34 -5
- package/dist/src/css.js.map +1 -1
- package/dist/src/devtools-hook.d.ts +14 -0
- package/dist/src/devtools-hook.d.ts.map +1 -0
- package/dist/src/devtools-hook.js +27 -0
- package/dist/src/devtools-hook.js.map +1 -0
- package/dist/src/devtools.d.ts +24 -22
- package/dist/src/devtools.d.ts.map +1 -0
- package/dist/src/devtools.js +230 -58
- package/dist/src/devtools.js.map +1 -1
- package/dist/src/diagnostics.d.ts +12 -0
- package/dist/src/diagnostics.d.ts.map +1 -0
- package/dist/src/diagnostics.js +68 -8
- package/dist/src/diagnostics.js.map +1 -1
- package/dist/src/each.d.ts +1 -5
- package/dist/src/each.d.ts.map +1 -0
- package/dist/src/each.js +0 -8
- package/dist/src/each.js.map +1 -1
- package/dist/src/forms.d.ts +27 -96
- package/dist/src/forms.d.ts.map +1 -0
- package/dist/src/forms.js +184 -394
- package/dist/src/forms.js.map +1 -1
- package/dist/src/head.d.ts +7 -6
- package/dist/src/head.d.ts.map +1 -0
- package/dist/src/head.js +8 -7
- package/dist/src/head.js.map +1 -1
- package/dist/src/html/bindings.d.ts +1 -0
- package/dist/src/html/bindings.d.ts.map +1 -0
- package/dist/src/html/bindings.js.map +1 -1
- package/dist/src/html/parser.d.ts +4 -5
- package/dist/src/html/parser.d.ts.map +1 -0
- package/dist/src/html/parser.js +64 -15
- package/dist/src/html/parser.js.map +1 -1
- package/dist/src/html/template-types.d.ts +1 -0
- package/dist/src/html/template-types.d.ts.map +1 -0
- package/dist/src/html/template-types.js.map +1 -1
- package/dist/src/html/template.d.ts +5 -1
- package/dist/src/html/template.d.ts.map +1 -0
- package/dist/src/html/template.js +102 -13
- package/dist/src/html/template.js.map +1 -1
- package/dist/src/index.d.ts +15 -12
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +18 -10
- package/dist/src/index.js.map +1 -1
- package/dist/src/json.d.ts +3 -0
- package/dist/src/json.d.ts.map +1 -0
- package/dist/src/json.js +14 -0
- package/dist/src/json.js.map +1 -0
- package/dist/src/lifecycle.d.ts +1 -0
- package/dist/src/lifecycle.d.ts.map +1 -0
- package/dist/src/lifecycle.js +3 -2
- package/dist/src/lifecycle.js.map +1 -1
- package/dist/src/page.d.ts +63 -28
- package/dist/src/page.d.ts.map +1 -0
- package/dist/src/page.js +5 -0
- package/dist/src/page.js.map +1 -1
- package/dist/src/persisted.d.ts +6 -1
- package/dist/src/persisted.d.ts.map +1 -0
- package/dist/src/persisted.js +17 -11
- package/dist/src/persisted.js.map +1 -1
- package/dist/src/resource.d.ts +2 -1
- package/dist/src/resource.d.ts.map +1 -0
- package/dist/src/resource.js +128 -50
- package/dist/src/resource.js.map +1 -1
- package/dist/src/router/base.d.ts +66 -0
- package/dist/src/router/base.d.ts.map +1 -0
- package/dist/src/router/base.js +168 -0
- package/dist/src/router/base.js.map +1 -0
- package/dist/src/router/manifest.d.ts +1 -0
- package/dist/src/router/manifest.d.ts.map +1 -0
- package/dist/src/router/manifest.js +156 -171
- package/dist/src/router/manifest.js.map +1 -1
- package/dist/src/router/match.d.ts +17 -2
- package/dist/src/router/match.d.ts.map +1 -0
- package/dist/src/router/match.js +60 -6
- package/dist/src/router/match.js.map +1 -1
- package/dist/src/router/navigation.d.ts +1 -0
- package/dist/src/router/navigation.d.ts.map +1 -0
- package/dist/src/router/navigation.js +58 -9
- package/dist/src/router/navigation.js.map +1 -1
- package/dist/src/signal.d.ts +1 -0
- package/dist/src/signal.d.ts.map +1 -0
- package/dist/src/signal.js +46 -17
- package/dist/src/signal.js.map +1 -1
- package/dist/src/static-runtime.d.ts +82 -0
- package/dist/src/static-runtime.d.ts.map +1 -0
- package/dist/src/static-runtime.js +209 -0
- package/dist/src/static-runtime.js.map +1 -0
- package/dist/src/vite/index.d.ts +32 -3
- package/dist/src/vite/index.d.ts.map +1 -0
- package/dist/src/vite/index.js +49 -17
- package/dist/src/vite/index.js.map +1 -1
- package/docs/README.md +5 -8
- package/docs/architecture/adr/0001-browser-static-snapshots.md +132 -0
- package/docs/architecture/v1-roadmap.md +19 -0
- package/docs/en/00-the-mado-way.md +1 -1
- package/docs/en/01-quickstart.md +183 -0
- package/docs/en/10-pages-and-components.md +279 -0
- package/docs/en/11-templates-and-signals.md +211 -0
- package/docs/en/12-routing.md +229 -0
- package/docs/en/13-data.md +227 -0
- package/docs/en/14-forms.md +91 -0
- package/docs/en/15-static-snapshots.md +181 -0
- package/docs/en/{10-app-architecture.md → 16-app-architecture.md} +56 -4
- package/docs/en/{13-deployment.md → 20-deployment.md} +79 -17
- package/docs/en/{15-error-handling.md → 21-error-handling.md} +9 -6
- package/docs/en/{14-testing.md → 22-testing.md} +6 -5
- package/docs/en/23-cookbook.md +292 -0
- package/docs/en/24-devtools-and-diagnostics.md +45 -0
- package/docs/en/{18-api-freeze-map.md → 30-api-freeze-map.md} +17 -9
- package/docs/en/{20-v1-stability.md → 32-v1-stability.md} +5 -5
- package/docs/en/33-migration-0.12-0.13.md +56 -0
- package/docs/en/40-llm-guide.md +774 -0
- package/docs/en/{06-for-backenders.md → 41-for-backenders.md} +28 -25
- package/docs/en/{05-why-mado.md → 42-why-mado.md} +3 -3
- package/docs/en/README.md +99 -27
- package/docs/recipes/nginx/Containerfile +26 -0
- package/docs/recipes/nginx/nginx.conf +42 -0
- package/llms.txt +263 -210
- package/package.json +51 -23
- package/scripts/clean.mjs +8 -0
- package/scripts/cli/generate.mjs +57 -10
- package/scripts/cli/help.mjs +13 -5
- package/scripts/cli/index.mjs +31 -7
- package/scripts/cli/init.mjs +19 -19
- package/scripts/cli/release.mjs +38 -83
- package/scripts/cli/run.mjs +5 -3
- package/scripts/logger.mjs +117 -0
- package/scripts/output-guard.mjs +128 -0
- package/scripts/preview.mjs +105 -24
- package/scripts/static/browser.mjs +695 -0
- package/scripts/static/discover.mjs +284 -0
- package/scripts/static/output.mjs +141 -0
- package/scripts/static/serialize.mjs +212 -0
- package/scripts/static/server.mjs +167 -0
- package/scripts/static.mjs +205 -0
- package/starters/default/README.md +57 -55
- package/starters/default/package.json +6 -12
- package/starters/default/src/app.routes.ts +20 -33
- package/starters/default/src/components/feature-card.component.ts +40 -0
- package/starters/default/src/components/live-counter.component.ts +57 -0
- package/starters/default/src/content/guides.ts +55 -0
- package/starters/default/src/main.ts +9 -13
- package/starters/default/src/pages/app.page.ts +41 -0
- package/starters/default/src/pages/guide.page.ts +40 -0
- package/starters/default/src/pages/home.page.ts +49 -0
- package/starters/default/src/pages/not-found.page.ts +18 -0
- package/starters/default/src/styles/document.css +39 -0
- package/starters/default/src/styles/reset.css +37 -0
- package/starters/default/src/styles/tokens.css +30 -0
- package/starters/default/src/styles.d.ts +3 -1
- package/starters/default/src/vite-env.d.ts +1 -1
- package/starters/default/tsconfig.node.json +13 -0
- package/starters/default/vite.config.ts +14 -4
- package/starters/modular/README.md +142 -0
- package/starters/modular/index.html +13 -0
- package/starters/modular/package.json +30 -0
- package/starters/modular/src/app.routes.ts +39 -0
- package/starters/{default → modular}/src/layouts/app-shell.layout.ts +9 -7
- package/starters/{default → modular}/src/layouts/auth-shell.layout.ts +3 -3
- package/starters/modular/src/main.ts +16 -0
- package/starters/{default → modular}/src/modules/auth/login.page.ts +5 -12
- package/starters/{default → modular}/src/modules/billing/pages/invoice-detail.page.ts +3 -4
- package/starters/{default → modular}/src/modules/billing/pages/invoices-list.page.ts +4 -4
- package/starters/{default → modular}/src/modules/home/home.page.ts +5 -7
- package/starters/modular/src/modules/home/not-found.page.ts +14 -0
- package/starters/{default → modular}/src/shared/styles/reset.css +21 -1
- package/starters/modular/src/styles.d.ts +1 -0
- package/starters/modular/src/vite-env.d.ts +1 -0
- package/starters/modular/tsconfig.json +33 -0
- package/starters/modular/tsconfig.node.json +13 -0
- package/starters/modular/vite.config.ts +132 -0
- package/TODO.md +0 -79
- package/dist/src/lazy.d.ts +0 -38
- package/dist/src/lazy.js +0 -73
- package/dist/src/lazy.js.map +0 -1
- package/docs/en/01-routing.md +0 -152
- package/docs/en/02-project-layout.md +0 -124
- package/docs/en/03-static-bake.md +0 -249
- package/docs/en/04-ide-setup.md +0 -162
- package/docs/en/07-llm-pitfalls.md +0 -724
- package/docs/en/08-llm-zero-history-test.md +0 -56
- package/docs/en/09-shadow-vs-light-dom.md +0 -174
- package/docs/en/11-layouts.md +0 -113
- package/docs/en/12-auth-and-api.md +0 -124
- package/docs/en/16-bake-cookbook.md +0 -100
- package/docs/en/17-shadow-dom-forms.md +0 -192
- package/docs/fr/00-the-mado-way.md +0 -85
- package/docs/fr/01-routing.md +0 -120
- package/docs/fr/02-project-layout.md +0 -84
- package/docs/fr/03-static-bake.md +0 -289
- package/docs/fr/04-ide-setup.md +0 -162
- package/docs/fr/05-why-mado.md +0 -193
- package/docs/fr/06-for-backenders.md +0 -438
- package/docs/fr/07-llm-pitfalls.md +0 -625
- package/docs/fr/08-llm-zero-history-test.md +0 -38
- package/docs/fr/09-shadow-vs-light-dom.md +0 -65
- package/docs/fr/10-app-architecture.md +0 -138
- package/docs/fr/11-layouts.md +0 -47
- package/docs/fr/12-auth-and-api.md +0 -76
- package/docs/fr/13-deployment.md +0 -57
- package/docs/fr/14-testing.md +0 -41
- package/docs/fr/15-error-handling.md +0 -50
- package/docs/fr/16-bake-cookbook.md +0 -88
- package/docs/fr/17-shadow-dom-forms.md +0 -196
- package/docs/fr/18-api-freeze-map.md +0 -63
- package/docs/fr/19-reactivity-ordering.md +0 -97
- package/docs/fr/20-v1-stability.md +0 -88
- package/docs/fr/README.md +0 -27
- package/docs/ru/00-the-mado-way.md +0 -84
- package/docs/ru/01-routing.md +0 -119
- package/docs/ru/02-project-layout.md +0 -84
- package/docs/ru/03-static-bake.md +0 -250
- package/docs/ru/04-ide-setup.md +0 -144
- package/docs/ru/05-why-mado.md +0 -193
- package/docs/ru/06-for-backenders.md +0 -428
- package/docs/ru/07-llm-pitfalls.md +0 -624
- package/docs/ru/08-llm-zero-history-test.md +0 -57
- package/docs/ru/09-shadow-vs-light-dom.md +0 -63
- package/docs/ru/10-app-architecture.md +0 -152
- package/docs/ru/11-layouts.md +0 -47
- package/docs/ru/12-auth-and-api.md +0 -75
- package/docs/ru/13-deployment.md +0 -66
- package/docs/ru/14-testing.md +0 -50
- package/docs/ru/15-error-handling.md +0 -56
- package/docs/ru/16-bake-cookbook.md +0 -95
- package/docs/ru/17-shadow-dom-forms.md +0 -193
- package/docs/ru/18-api-freeze-map.md +0 -64
- package/docs/ru/19-reactivity-ordering.md +0 -95
- package/docs/ru/20-v1-stability.md +0 -82
- package/docs/ru/README.md +0 -25
- package/docs/uk/00-the-mado-way.md +0 -82
- package/docs/uk/01-routing.md +0 -76
- package/docs/uk/02-project-layout.md +0 -73
- package/docs/uk/03-static-bake.md +0 -48
- package/docs/uk/04-ide-setup.md +0 -26
- package/docs/uk/05-why-mado.md +0 -34
- package/docs/uk/06-for-backenders.md +0 -55
- package/docs/uk/07-llm-pitfalls.md +0 -145
- package/docs/uk/08-llm-zero-history-test.md +0 -34
- package/docs/uk/09-shadow-vs-light-dom.md +0 -58
- package/docs/uk/10-app-architecture.md +0 -97
- package/docs/uk/11-layouts.md +0 -47
- package/docs/uk/12-auth-and-api.md +0 -70
- package/docs/uk/13-deployment.md +0 -40
- package/docs/uk/14-testing.md +0 -34
- package/docs/uk/15-error-handling.md +0 -32
- package/docs/uk/16-bake-cookbook.md +0 -36
- package/docs/uk/17-shadow-dom-forms.md +0 -193
- package/docs/uk/18-api-freeze-map.md +0 -61
- package/docs/uk/19-reactivity-ordering.md +0 -95
- package/docs/uk/20-v1-stability.md +0 -83
- package/docs/uk/README.md +0 -27
- package/scripts/bake.mjs +0 -570
- package/scripts/package-smoke.mjs +0 -77
- package/scripts/release-notes.mjs +0 -66
- package/scripts/size-budget.mjs +0 -91
- package/starters/default/src/modules/home/not-found.page.ts +0 -11
- package/starters/default/src/shared/ui/x-button.component.ts +0 -49
- /package/docs/en/{19-reactivity-ordering.md → 31-reactivity-ordering.md} +0 -0
- /package/starters/{default → modular}/.editorconfig +0 -0
- /package/starters/{default → modular}/eslint.config.mjs +0 -0
- /package/starters/{default → modular}/public/favicon.svg +0 -0
- /package/starters/{default → modular}/src/modules/auth/_contracts/auth-api.types.ts +0 -0
- /package/starters/{default → modular}/src/modules/auth/auth.connector.ts +0 -0
- /package/starters/{default → modular}/src/modules/auth/auth.guard.ts +0 -0
- /package/starters/{default → modular}/src/modules/auth/auth.public.ts +0 -0
- /package/starters/{default → modular}/src/modules/auth/auth.routes.ts +0 -0
- /package/starters/{default → modular}/src/modules/auth/auth.service.ts +0 -0
- /package/starters/{default → modular}/src/modules/auth/auth.types.ts +0 -0
- /package/starters/{default → modular}/src/modules/billing/_contracts/stripe.types.ts +0 -0
- /package/starters/{default → modular}/src/modules/billing/api/stripe.connector.ts +0 -0
- /package/starters/{default → modular}/src/modules/billing/billing.public.ts +0 -0
- /package/starters/{default → modular}/src/modules/billing/billing.routes.ts +0 -0
- /package/starters/{default → modular}/src/modules/billing/billing.types.ts +0 -0
- /package/starters/{default → modular}/src/modules/billing/components/invoice-status-badge.component.ts +0 -0
- /package/starters/{default → modular}/src/modules/billing/data/invoices.resource.ts +0 -0
- /package/starters/{default → modular}/src/shared/http/http-client.ts +0 -0
- /package/starters/{default → modular}/src/shared/http/http-error.ts +0 -0
- /package/starters/{default → modular}/src/shared/http/interceptors.ts +0 -0
- /package/starters/{default → modular}/src/shared/lib/format-date.ts +0 -0
- /package/starters/{default → modular}/src/shared/styles/content.css +0 -0
- /package/starters/{default → modular}/src/shared/styles/shell.css +0 -0
- /package/starters/{default → modular}/src/shared/styles/tokens.css +0 -0
- /package/starters/{default → modular}/src/shared/ui/x-spinner.component.ts +0 -0
package/docs/en/01-routing.md
DELETED
|
@@ -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.
|
|
@@ -1,249 +0,0 @@
|
|
|
1
|
-
# Smart Static (`bake`)
|
|
2
|
-
|
|
3
|
-
> Baking HTML for SEO without runtime SSR. **Idea: data and view in one file, static output.**
|
|
4
|
-
|
|
5
|
-
`bake` is a **build-time prerender**, not SSR. The output is static `*.html` files that any nginx/Cloudflare serves as regular static content. On the client, Web Components come alive and SPA navigation continues to work.
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## When `bake` is suitable
|
|
10
|
-
|
|
11
|
-
- **Marketing pages**: landing pages, sub-landings for advertising campaigns, product pages.
|
|
12
|
-
- **Catalog with a relatively stable set of pages**: blog, documentation, portfolio, e-commerce up to **~10k SKUs** with updates less than once an hour.
|
|
13
|
-
- **Content that is the same for all users**: the entire page is identical for a guest and an authenticated user (or authentication is rendered on the client via `effect()`).
|
|
14
|
-
- You need **good SEO** (rich snippets, OG, JSON-LD) and **fast first paint** without running node servers in production.
|
|
15
|
-
|
|
16
|
-
## When `bake` is **not** suitable
|
|
17
|
-
|
|
18
|
-
- **Hundreds of thousands of pages with frequent changes**. `bake` traverses all `paths` synchronously in one run. For 100k+ pages this means minutes of rebuild, and invalidating one page either requires a full rebake or separate CI logic (see below on targeted revalidation).
|
|
19
|
-
- **Personalized content in HTML**. If the page should show "Hello, Ivan" in the `<title>` or in meta — that's not for `bake`. Authenticated dashboards, personal feeds, a cart with real prices for the user — keep these as SPA.
|
|
20
|
-
- **Server-only APIs are needed in render**: cookies, headers, real network requests to private APIs. On the bake side only `linkedom` is available, no Node environment for components.
|
|
21
|
-
- **A/B tests and flags that change markup on the first paint**. `bake` will lock in one variant. Handle dynamic behavior on the client via `effect()`.
|
|
22
|
-
- **Real-time / frequently changing data** (stock quotes, warehouse stock by the minute). `bake.revalidate` is metadata, not runtime: the framework does not re-bake anything itself.
|
|
23
|
-
- **Content behind authentication** (admin, internal tools). No need; use SPA mode.
|
|
24
|
-
|
|
25
|
-
---
|
|
26
|
-
|
|
27
|
-
## Concept
|
|
28
|
-
|
|
29
|
-
`page({...})` has four optional slots related to `bake`:
|
|
30
|
-
|
|
31
|
-
- `head` — meta, OG, JSON-LD.
|
|
32
|
-
- `bake.paths` — list of URL parameters for generation (build-time, can be `async`).
|
|
33
|
-
- `bake.data` — data for a specific URL (build-time, can be `async`).
|
|
34
|
-
- `bake.revalidate` — after how many seconds the cache is stale (written to `<meta>`, real invalidation is handled by your CI/CDN).
|
|
35
|
-
|
|
36
|
-
The `npm run bake` command traverses all `page` entries with `bake`, generates HTML via `linkedom`, and places it in `out/<path>/index.html`. **No Chromium needed** — `linkedom` is ~50 KB.
|
|
37
|
-
|
|
38
|
-
---
|
|
39
|
-
|
|
40
|
-
## Example
|
|
41
|
-
|
|
42
|
-
```ts
|
|
43
|
-
// src/modules/<module>/pages/product.ts
|
|
44
|
-
import { page, component, html } from "@madojs/mado";
|
|
45
|
-
import { findProduct, products, type Product } from "../lib/products.js";
|
|
46
|
-
|
|
47
|
-
component("x-product-page", ({ host }) => {
|
|
48
|
-
return () => {
|
|
49
|
-
const p = findProduct(host.dataset.slug);
|
|
50
|
-
return p
|
|
51
|
-
? html`<h1>${p.name}</h1><p>${p.description}</p>`
|
|
52
|
-
: html`<p>Not found.</p>`;
|
|
53
|
-
};
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
export default page<{ slug: string }, Product | undefined>({
|
|
57
|
-
title: ({ slug }) => `${findProduct(slug)?.name} — MyShop`,
|
|
58
|
-
|
|
59
|
-
head: ({ slug }, baked) => {
|
|
60
|
-
const p = baked ?? findProduct(slug);
|
|
61
|
-
if (!p) return {};
|
|
62
|
-
return {
|
|
63
|
-
description: p.description,
|
|
64
|
-
canonical: `/product/${p.slug}`,
|
|
65
|
-
og: { title: p.name, image: p.image, type: "product" },
|
|
66
|
-
jsonLd: {
|
|
67
|
-
"@context": "https://schema.org",
|
|
68
|
-
"@type": "Product",
|
|
69
|
-
name: p.name,
|
|
70
|
-
offers: { "@type": "Offer", price: p.price, priceCurrency: p.currency },
|
|
71
|
-
},
|
|
72
|
-
};
|
|
73
|
-
},
|
|
74
|
-
|
|
75
|
-
bake: {
|
|
76
|
-
paths: () => products.map((p) => ({ slug: p.slug })),
|
|
77
|
-
data: ({ slug }) => findProduct(slug),
|
|
78
|
-
revalidate: 3600,
|
|
79
|
-
},
|
|
80
|
-
|
|
81
|
-
view: ({ params }) =>
|
|
82
|
-
html`<x-product-page data-slug=${params.slug}></x-product-page>`,
|
|
83
|
-
});
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
```ts
|
|
87
|
-
// src/app.routes.ts
|
|
88
|
-
import { routes, type RoutesMap } from "@madojs/mado";
|
|
89
|
-
|
|
90
|
-
// Export BOTH default (RouterApi for runtime) AND manifest (for the bake script).
|
|
91
|
-
export const manifest: RoutesMap = {
|
|
92
|
-
"/": () => import("./pages/home.js"),
|
|
93
|
-
"/product/:slug": () => import("./pages/product.js"),
|
|
94
|
-
"*": () => import("./pages/not-found.js"),
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
export default routes(manifest, { titleSuffix: " · MyShop" });
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
## Running
|
|
101
|
-
|
|
102
|
-
```bash
|
|
103
|
-
npm install -D linkedom vite
|
|
104
|
-
npm run bake
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
You get:
|
|
108
|
-
|
|
109
|
-
```
|
|
110
|
-
out/
|
|
111
|
-
├── product/
|
|
112
|
-
│ ├── mado-mug/index.html ← HTML with meta + JSON-LD
|
|
113
|
-
│ ├── raw-bundler/index.html
|
|
114
|
-
│ └── shadow-dom/index.html
|
|
115
|
-
└── sitemap.xml
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
## What's Inside the Generated HTML
|
|
119
|
-
|
|
120
|
-
```html
|
|
121
|
-
<head>
|
|
122
|
-
<title>Mado Mug — MyShop</title>
|
|
123
|
-
<meta name="description" content="..." data-mado-head="baked">
|
|
124
|
-
<link rel="canonical" href="/product/mado-mug" data-mado-head="baked">
|
|
125
|
-
<meta property="og:title" content="Mado Mug" data-mado-head="baked">
|
|
126
|
-
<meta property="og:image" content="..." data-mado-head="baked">
|
|
127
|
-
<script type="application/ld+json" data-mado-head="baked">
|
|
128
|
-
{"@context":"https://schema.org","@type":"Product","..."}
|
|
129
|
-
</script>
|
|
130
|
-
<meta name="bake-revalidate" content="3600" data-mado-head="baked">
|
|
131
|
-
</head>
|
|
132
|
-
<body>
|
|
133
|
-
<div id="app">
|
|
134
|
-
<x-product-page data-slug="mado-mug">
|
|
135
|
-
<h1>Mado Mug</h1>
|
|
136
|
-
<p>A mug with the inscription "zero dependencies".</p>
|
|
137
|
-
<strong>12 EUR</strong>
|
|
138
|
-
</x-product-page>
|
|
139
|
-
</div>
|
|
140
|
-
|
|
141
|
-
<!-- preloaded data for hydration -->
|
|
142
|
-
<script id="bake" type="application/json">
|
|
143
|
-
{"slug":"mado-mug","name":"Mado Mug","price":12,"..."}
|
|
144
|
-
</script>
|
|
145
|
-
|
|
146
|
-
<script type="module" src="/assets/index-HASH.js"></script>
|
|
147
|
-
</body>
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
After JS loads:
|
|
151
|
-
|
|
152
|
-
1. Web Components `<x-product-page>` come alive (the browser already knows the custom elements).
|
|
153
|
-
2. `page.load()` receives `baked` as initialData — no unnecessary `fetch` calls.
|
|
154
|
-
3. SPA navigation continues to work as normal.
|
|
155
|
-
|
|
156
|
-
---
|
|
157
|
-
|
|
158
|
-
## Cookbook: Typical Scenarios
|
|
159
|
-
|
|
160
|
-
### Blog (Markdown → bake)
|
|
161
|
-
|
|
162
|
-
```ts
|
|
163
|
-
// src/lib/posts.ts
|
|
164
|
-
import { readdirSync, readFileSync } from "node:fs";
|
|
165
|
-
// (this module is imported only from the bake script,
|
|
166
|
-
// it must not end up in the browser graph — put it in lib/server/ or ifdef it out)
|
|
167
|
-
|
|
168
|
-
export const allPosts = () =>
|
|
169
|
-
readdirSync("content/blog").map((file) => ({
|
|
170
|
-
slug: file.replace(/\.md$/, ""),
|
|
171
|
-
...parseFrontmatter(readFileSync(`content/blog/${file}`, "utf-8")),
|
|
172
|
-
}));
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
```ts
|
|
176
|
-
// src/modules/<module>/pages/blog-post.ts
|
|
177
|
-
import { page, html } from "@madojs/mado";
|
|
178
|
-
import { allPosts } from "../lib/posts.js";
|
|
179
|
-
|
|
180
|
-
export default page<{ slug: string }>({
|
|
181
|
-
title: ({ slug }) => allPosts().find((p) => p.slug === slug)?.title ?? slug,
|
|
182
|
-
head: ({ slug }, post) => ({
|
|
183
|
-
description: post?.excerpt,
|
|
184
|
-
canonical: `/blog/${slug}`,
|
|
185
|
-
og: { title: post?.title, type: "article" },
|
|
186
|
-
}),
|
|
187
|
-
bake: {
|
|
188
|
-
paths: () => allPosts().map(({ slug }) => ({ slug })),
|
|
189
|
-
data: ({ slug }) => allPosts().find((p) => p.slug === slug),
|
|
190
|
-
},
|
|
191
|
-
view: ({ params }) =>
|
|
192
|
-
html`<x-blog-post data-slug=${params.slug}></x-blog-post>`,
|
|
193
|
-
});
|
|
194
|
-
```
|
|
195
|
-
|
|
196
|
-
### Product Catalog (e-commerce, ≤ 10k SKUs)
|
|
197
|
-
|
|
198
|
-
- `bake.paths` → SELECT slug FROM products
|
|
199
|
-
- `bake.data` → SELECT * FROM products WHERE slug=?
|
|
200
|
-
- Full invalidation every N hours via cron + `npm run bake`
|
|
201
|
-
- Targeted invalidation of a single product: webhook → rebuild only that `paths` entry
|
|
202
|
-
|
|
203
|
-
### Documentation
|
|
204
|
-
|
|
205
|
-
- `bake.paths` — traversal of the file system `docs/**/*.md`
|
|
206
|
-
- `bake.data` — Markdown parsing
|
|
207
|
-
- `head.jsonLd` — `TechArticle`
|
|
208
|
-
|
|
209
|
-
---
|
|
210
|
-
|
|
211
|
-
## Revalidate / CDN
|
|
212
|
-
|
|
213
|
-
`bake.revalidate: 3600` writes `<meta name="bake-revalidate" content="3600">` to the HTML. This is **metadata** — the framework does not re-bake anything itself. Strategies:
|
|
214
|
-
|
|
215
|
-
1. **Simplest option**: cron in CI — `npm run bake && rsync out/ origin:/var/www/`.
|
|
216
|
-
2. **Via CDN** (Cloudflare/Fastly): serve HTML with `Cache-Control: max-age=3600`. CDN invalidates itself.
|
|
217
|
-
3. **Webhook trigger**: shop API → POST `/_revalidate?path=/product/mado-mug` → CI re-bakes only that page (you can implement `bake.paths` to return a targeted list based on an env parameter).
|
|
218
|
-
|
|
219
|
-
---
|
|
220
|
-
|
|
221
|
-
## Comparison with Alternatives
|
|
222
|
-
|
|
223
|
-
| | Next.js SSG/ISR | playwright-prerender | **mado bake** |
|
|
224
|
-
|--------------------------|--------------------|----------------------|-----------------|
|
|
225
|
-
| Chrome required in CI | no | **yes** (~300 MB) | **no** |
|
|
226
|
-
| Node required in production | for ISR | no | **no** |
|
|
227
|
-
| Time for 1000 pages | minutes | minutes | **seconds** |
|
|
228
|
-
| Magic level | high | low | **zero** |
|
|
229
|
-
| HTML parser | React renderer | browser | linkedom (~50 KB) |
|
|
230
|
-
| Configuration | next.config.js + … | single script | single script |
|
|
231
|
-
| Source of truth view+data | separate | page | **one page** |
|
|
232
|
-
|
|
233
|
-
---
|
|
234
|
-
|
|
235
|
-
## Limitations and Gotchas
|
|
236
|
-
|
|
237
|
-
- **There is no browser on the bake side.** The following do not work: `setTimeout` (technically it works, but bake finishes before it fires), `fetch` to relative URLs, any `effect()`/`signal()` side effects, real `requestAnimationFrame`. The render function must be deterministic based on `params` / `data`.
|
|
238
|
-
- **`linkedom` ≠ browser.** Not all DOM APIs are supported (for example, `HTMLElement.click()` behaves more simply). Heavy logic in Web Components will only execute in the browser after `connectedCallback`; only what rendered synchronously on the first pass will end up in the baked HTML.
|
|
239
|
-
- **Render dynamic content on the client.** Current time, A/B tests, geo-banners, the user's cart — these must not be in the baked HTML. Use `effect()` for client-side rendering.
|
|
240
|
-
- **Server-side imports must not end up in the client graph.** If `lib/posts.ts` imports `node:fs`, it cannot be imported from `view`. Keep such modules in a separate folder (`lib/build/`) and use them only from `bake.paths`/`bake.data`.
|
|
241
|
-
- **`paths` and `data` are executed on every bake run.** If they involve a heavy database query — cache at the script level.
|
|
242
|
-
|
|
243
|
-
---
|
|
244
|
-
|
|
245
|
-
## TL;DR
|
|
246
|
-
|
|
247
|
-
If a page is **the same for all users**, has **a relatively stable set of URLs**, and **SEO + first paint** matter — add `bake: { paths, data }` and get static HTML with meta/JSON-LD/sitemap in milliseconds. No node server, no Chrome, no magic.
|
|
248
|
-
|
|
249
|
-
If the page is personalized, or there are millions of URLs, or content changes in real time — `bake` is not your tool. Keep it as SPA or bring in a separate SSR framework.
|
package/docs/en/04-ide-setup.md
DELETED
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
# IDE support for `html\`\`` and `css\`\``
|
|
2
|
-
|
|
3
|
-
Out of the box `html\`...\`` and `css\`...\`` are just tagged-template strings.
|
|
4
|
-
TypeScript doesn't know about them, the IDE doesn't highlight them. This is a
|
|
5
|
-
**deliberate trade-off** in favour of zero runtime dependencies and no build plugins.
|
|
6
|
-
|
|
7
|
-
The good news: Mado uses the same conventions as [lit](https://lit.dev), so the
|
|
8
|
-
**ready-made** IDE tools from the lit ecosystem work out of the box.
|
|
9
|
-
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
## VS Code (recommended setup)
|
|
13
|
-
|
|
14
|
-
### 1. Install [lit-plugin](https://marketplace.visualstudio.com/items?itemName=runem.lit-plugin)
|
|
15
|
-
|
|
16
|
-
VS Code → Extensions → search **"lit-plugin"** (by runem) → Install.
|
|
17
|
-
|
|
18
|
-
What you get:
|
|
19
|
-
|
|
20
|
-
- HTML highlighting inside `html\`\``.
|
|
21
|
-
- CSS highlighting inside `css\`\``.
|
|
22
|
-
- Auto-complete for HTML tags, attributes, and events.
|
|
23
|
-
- Typo checking in attributes.
|
|
24
|
-
- Go-to-definition for custom elements (if described via `customElements.json` or JSDoc).
|
|
25
|
-
- Diagnostics on invalid bindings.
|
|
26
|
-
|
|
27
|
-
### 2. Specify tag names
|
|
28
|
-
|
|
29
|
-
`lit-plugin` looks for the `html` and `css` identifiers in imports. If you don't
|
|
30
|
-
rename them on import — configuration is zero, everything works:
|
|
31
|
-
|
|
32
|
-
```ts
|
|
33
|
-
import { html, css } from "@madojs/mado";
|
|
34
|
-
|
|
35
|
-
const tpl = html`<button @click=${fn}>${label}</button>`;
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
### 3. (Optional) Your own `customElements.json`
|
|
39
|
-
|
|
40
|
-
If you want auto-complete for your own `<x-*>` components, describe them via
|
|
41
|
-
[Custom Elements Manifest](https://github.com/webcomponents/custom-elements-manifest):
|
|
42
|
-
|
|
43
|
-
```bash
|
|
44
|
-
npm install --save-dev @custom-elements-manifest/analyzer
|
|
45
|
-
npx cem analyze --globs "src/components/**/*.ts"
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
This creates `custom-elements.json`, which `lit-plugin` picks up automatically.
|
|
49
|
-
|
|
50
|
-
---
|
|
51
|
-
|
|
52
|
-
## WebStorm / IntelliJ
|
|
53
|
-
|
|
54
|
-
WebStorm understands `html\`\`` and `css\`\`` **out of the box** — native support
|
|
55
|
-
for lit-style template literals since 2021. No plugins needed.
|
|
56
|
-
|
|
57
|
-
If highlighting doesn't appear:
|
|
58
|
-
|
|
59
|
-
- Settings → Languages & Frameworks → JavaScript → verify "Use types from server" is on
|
|
60
|
-
- Restart the TS server: ⌘+⇧+P → "Restart TypeScript Server"
|
|
61
|
-
|
|
62
|
-
---
|
|
63
|
-
|
|
64
|
-
## Neovim / Helix
|
|
65
|
-
|
|
66
|
-
Use [`lit-html-server`](https://github.com/runem/lit-analyzer/tree/master/packages/lit-html-server)
|
|
67
|
-
(LSP server by the same author as lit-plugin):
|
|
68
|
-
|
|
69
|
-
```bash
|
|
70
|
-
npm install -g lit-html-server
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
`init.lua` (for `lspconfig`):
|
|
74
|
-
|
|
75
|
-
```lua
|
|
76
|
-
require('lspconfig').lit_html.setup{
|
|
77
|
-
cmd = { 'lit-html-server', '--stdio' },
|
|
78
|
-
filetypes = { 'typescript', 'javascript' },
|
|
79
|
-
}
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
---
|
|
83
|
-
|
|
84
|
-
## What does NOT work (known limitations)
|
|
85
|
-
|
|
86
|
-
- **Type-checking of signal bindings.** `html\`<input .value=${count}>\`` — `lit-plugin`
|
|
87
|
-
expects a string, but `count` is a `Signal<number>`. Suppress with `// @ts-expect-error`
|
|
88
|
-
or `<!-- @ts-ignore -->`. Will be improved in Phase 3+.
|
|
89
|
-
- **Custom directives (`each`)** are recognised as plain functions — without special
|
|
90
|
-
semantics in the plugin.
|
|
91
|
-
- **Attributes with `@`, `.`, `?` prefixes** are sometimes flagged as errors if
|
|
92
|
-
`lit-plugin` has `"no-unknown-attribute": false` disabled. In `.vscode/settings.json`:
|
|
93
|
-
|
|
94
|
-
```json
|
|
95
|
-
{
|
|
96
|
-
"lit-plugin.rules": {
|
|
97
|
-
"no-unknown-attribute": "off",
|
|
98
|
-
"no-incompatible-type-binding": "off"
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
---
|
|
104
|
-
|
|
105
|
-
## JSDoc typing for components
|
|
106
|
-
|
|
107
|
-
To make the IDE pick up custom elements inside `html\`\``, annotate the `component()`
|
|
108
|
-
definition via JSDoc:
|
|
109
|
-
|
|
110
|
-
```ts
|
|
111
|
-
/**
|
|
112
|
-
* @element x-counter
|
|
113
|
-
* @attr {number} initial - starting value
|
|
114
|
-
* @fires {CustomEvent<number>} change - on every change
|
|
115
|
-
*/
|
|
116
|
-
component("x-counter", () => {
|
|
117
|
-
/* ... */
|
|
118
|
-
});
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
`lit-plugin` recognises this and suggests attributes when you type `<x-counter ...>`.
|
|
122
|
-
|
|
123
|
-
---
|
|
124
|
-
|
|
125
|
-
## Prettier / formatting
|
|
126
|
-
|
|
127
|
-
Prettier 3.0+ formats `html\`\`` via [`@prettier/plugin-xml`](https://github.com/prettier/plugin-xml)
|
|
128
|
-
or the built-in mode. Minimal `.prettierrc`:
|
|
129
|
-
|
|
130
|
-
```json
|
|
131
|
-
{
|
|
132
|
-
"semi": true,
|
|
133
|
-
"singleQuote": false,
|
|
134
|
-
"embeddedLanguageFormatting": "auto"
|
|
135
|
-
}
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
`embeddedLanguageFormatting: "auto"` (default) formats the content of tagged-template
|
|
139
|
-
literals with known tags (`html`, `css`).
|
|
140
|
-
|
|
141
|
-
---
|
|
142
|
-
|
|
143
|
-
## ESLint
|
|
144
|
-
|
|
145
|
-
If you use ESLint, the [`eslint-plugin-lit`](https://github.com/43081j/eslint-plugin-lit)
|
|
146
|
-
plugin provides rules specific to tagged-template HTML, and
|
|
147
|
-
[`eslint-plugin-wc`](https://github.com/43081j/eslint-plugin-wc) covers Web Components
|
|
148
|
-
in general. Configuration is up to you, not required.
|
|
149
|
-
|
|
150
|
-
---
|
|
151
|
-
|
|
152
|
-
## TL;DR
|
|
153
|
-
|
|
154
|
-
| Editor | Setup | DX level |
|
|
155
|
-
|---|---|---|
|
|
156
|
-
| **VS Code** | install `lit-plugin` | ★★★★ |
|
|
157
|
-
| **WebStorm** | nothing | ★★★★ |
|
|
158
|
-
| **Neovim/Helix** | `lit-html-server` via LSP | ★★★ |
|
|
159
|
-
| **Vim without LSP** | manual | ★ |
|
|
160
|
-
|
|
161
|
-
Mado works without an IDE plugin too: `html\`\`` remains valid TS code, everything
|
|
162
|
-
compiles and runs. The string inside is just highlighted as a string.
|