@madojs/mado 0.5.0 → 0.6.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 +49 -1
- package/CHANGELOG.md +188 -0
- package/MADO_V1_PLAN.md +179 -0
- package/README.md +53 -14
- package/ROADMAP.md +36 -5
- package/TODO.md +72 -0
- package/dist/src/forms.d.ts +41 -7
- package/dist/src/forms.js +334 -59
- package/dist/src/forms.js.map +1 -1
- package/dist/src/html/bindings.d.ts +41 -0
- package/dist/src/html/bindings.js +163 -6
- package/dist/src/html/bindings.js.map +1 -1
- package/dist/src/html.d.ts +2 -0
- package/dist/src/html.js +1 -0
- package/dist/src/html.js.map +1 -1
- package/dist/src/index.d.ts +6 -6
- package/dist/src/index.js +2 -2
- package/dist/src/index.js.map +1 -1
- package/dist/src/page.d.ts +56 -0
- package/dist/src/page.js +17 -0
- package/dist/src/page.js.map +1 -1
- package/dist/src/router/manifest.d.ts +16 -1
- package/dist/src/router/manifest.js +181 -38
- package/dist/src/router/manifest.js.map +1 -1
- package/dist/src/router/match.d.ts +7 -2
- package/dist/src/router/match.js +14 -4
- package/dist/src/router/match.js.map +1 -1
- package/dist/src/router/navigation.d.ts +10 -0
- package/dist/src/router/navigation.js +73 -12
- package/dist/src/router/navigation.js.map +1 -1
- package/dist/src/signal.d.ts +15 -1
- package/dist/src/signal.js +112 -16
- package/dist/src/signal.js.map +1 -1
- package/docs/en/02-project-layout.md +99 -40
- package/docs/en/05-why-mado.md +1 -1
- package/docs/en/06-for-backenders.md +1 -1
- package/docs/en/07-llm-pitfalls.md +1 -1
- package/docs/en/09-shadow-vs-light-dom.md +60 -0
- package/docs/en/10-app-architecture.md +141 -0
- package/docs/en/11-layouts.md +115 -0
- package/docs/en/12-auth-and-api.md +217 -0
- package/docs/en/13-deployment.md +192 -0
- package/docs/en/14-testing.md +82 -0
- package/docs/en/15-error-handling.md +100 -0
- package/docs/en/16-bake-cookbook.md +93 -0
- package/docs/en/README.md +7 -0
- package/docs/fr/05-why-mado.md +1 -1
- package/docs/fr/06-for-backenders.md +1 -1
- package/docs/fr/07-llm-pitfalls.md +1 -1
- package/docs/fr/09-shadow-vs-light-dom.md +63 -0
- package/docs/fr/10-app-architecture.md +61 -0
- package/docs/fr/11-layouts.md +35 -0
- package/docs/fr/12-auth-and-api.md +35 -0
- package/docs/fr/13-deployment.md +39 -0
- package/docs/fr/14-testing.md +41 -0
- package/docs/fr/15-error-handling.md +50 -0
- package/docs/fr/16-bake-cookbook.md +35 -0
- package/docs/fr/README.md +7 -0
- package/docs/ru/05-why-mado.md +2 -2
- package/docs/ru/06-for-backenders.md +1 -1
- package/docs/ru/09-shadow-vs-light-dom.md +60 -0
- package/docs/ru/10-app-architecture.md +100 -0
- package/docs/ru/11-layouts.md +47 -0
- package/docs/ru/12-auth-and-api.md +53 -0
- package/docs/ru/13-deployment.md +60 -0
- package/docs/ru/14-testing.md +50 -0
- package/docs/ru/15-error-handling.md +56 -0
- package/docs/ru/16-bake-cookbook.md +55 -0
- package/docs/ru/README.md +7 -0
- package/docs/uk/06-for-backenders.md +2 -2
- package/docs/uk/09-shadow-vs-light-dom.md +91 -24
- package/docs/uk/10-app-architecture.md +56 -0
- package/docs/uk/11-layouts.md +34 -0
- package/docs/uk/12-auth-and-api.md +34 -0
- package/docs/uk/13-deployment.md +39 -0
- package/docs/uk/14-testing.md +34 -0
- package/docs/uk/15-error-handling.md +32 -0
- package/docs/uk/16-bake-cookbook.md +36 -0
- package/docs/uk/README.md +7 -0
- package/llms.txt +24 -1
- package/package.json +3 -1
- package/scripts/_config.mjs +224 -0
- package/scripts/bake.mjs +217 -120
- package/scripts/bundle.mjs +110 -67
- package/scripts/cli.mjs +127 -16
- package/scripts/preview.mjs +22 -12
- package/server/serve.mjs +101 -11
- package/starters/admin/README.md +63 -0
- package/starters/admin/index.html +21 -0
- package/starters/admin/mado.config.json +22 -0
- package/starters/admin/package.json +22 -0
- package/starters/admin/public/favicon.svg +4 -0
- package/starters/admin/src/components/x-button.ts +55 -0
- package/starters/admin/src/components/x-input.ts +74 -0
- package/starters/admin/src/layouts/app.ts +101 -0
- package/starters/admin/src/layouts/auth.ts +41 -0
- package/starters/admin/src/lib/api.ts +133 -0
- package/starters/admin/src/lib/auth.ts +83 -0
- package/starters/admin/src/main.ts +15 -0
- package/starters/admin/src/pages/admin/dashboard.ts +48 -0
- package/starters/admin/src/pages/admin/order-detail.ts +78 -0
- package/starters/admin/src/pages/admin/orders.ts +117 -0
- package/starters/admin/src/pages/home.ts +25 -0
- package/starters/admin/src/pages/login.ts +70 -0
- package/starters/admin/src/pages/not-found.ts +12 -0
- package/starters/admin/src/routes.ts +40 -0
- package/starters/admin/src/styles/global.ts +86 -0
- package/starters/admin/tsconfig.json +15 -0
- package/starters/crud/README.md +14 -2
- package/starters/crud/mado.config.json +20 -0
- package/starters/crud/package.json +9 -4
- package/starters/crud/src/components/app-shell.ts +13 -8
- package/starters/crud/src/main.ts +1 -4
- package/starters/crud/src/pages/ticket-detail.ts +1 -0
- package/starters/crud/src/pages/ticket-new.ts +1 -0
- package/starters/crud/src/pages/tickets.ts +1 -0
- package/starters/crud/src/routes.ts +4 -2
- package/starters/minimal/README.md +4 -2
- package/starters/minimal/mado.config.json +20 -0
- package/starters/minimal/package.json +8 -3
- package/starters/minimal/src/components/app-counter.ts +1 -1
- package/starters/minimal/src/routes.ts +4 -2
|
@@ -1,40 +1,107 @@
|
|
|
1
1
|
# Shadow DOM vs Light DOM
|
|
2
2
|
|
|
3
|
-
Mado використовує Shadow DOM за
|
|
4
|
-
|
|
3
|
+
Mado використовує Shadow DOM за замовчуванням. Це хороший default для
|
|
4
|
+
самодостатніх widgets, але не для кожного компонента в app.
|
|
5
5
|
|
|
6
|
-
##
|
|
6
|
+
## Практичне правило
|
|
7
|
+
|
|
8
|
+
У Mado layout — це теж component. Якщо файл описує видиму reusable частину
|
|
9
|
+
UI-дерева — app shell, sidebar, modal, table, page section — за замовчуванням
|
|
10
|
+
робіть Web Component через `component()`.
|
|
7
11
|
|
|
8
|
-
|
|
9
|
-
- isolated widgets;
|
|
10
|
-
- badges, cards, modals, buttons;
|
|
11
|
-
- компонентів, які мають не ламатися від зовнішнього CSS.
|
|
12
|
+
Звичайні функції залишайте для маленьких inline helpers:
|
|
12
13
|
|
|
13
14
|
```ts
|
|
14
|
-
|
|
15
|
-
styles: css`:host { display: inline-block; }`,
|
|
16
|
-
});
|
|
15
|
+
const money = (value: number) => html`<span>${formatMoney(value)}</span>`;
|
|
17
16
|
```
|
|
18
17
|
|
|
19
|
-
|
|
18
|
+
Не варто робити app shell функцією в public examples. Це працює, але ховає
|
|
19
|
+
browser model замість того, щоб її пояснювати.
|
|
20
|
+
|
|
21
|
+
Використовуйте **Shadow DOM** для leaf widgets:
|
|
22
|
+
|
|
23
|
+
- buttons, badges, cards, metrics;
|
|
24
|
+
- modals, toasts, small visual components;
|
|
25
|
+
- embedded widgets, які не мають випадково успадковувати app CSS;
|
|
26
|
+
- components, чиї styles належать самому component.
|
|
27
|
+
|
|
28
|
+
Використовуйте **Light DOM** (`{ shadow: false }`) для app structure, якій
|
|
29
|
+
потрібні global CSS utilities:
|
|
30
|
+
|
|
31
|
+
- route/page components;
|
|
32
|
+
- admin screens з dense table/form layouts;
|
|
33
|
+
- data-heavy screens з tables and forms;
|
|
34
|
+
- місця, де children мають залишатися normal document DOM.
|
|
35
|
+
|
|
36
|
+
Використовуйте **Shadow DOM** для slot-based layouts:
|
|
20
37
|
|
|
21
|
-
- app
|
|
22
|
-
-
|
|
23
|
-
-
|
|
24
|
-
|
|
25
|
-
|
|
38
|
+
- app shells, які render `<slot>`;
|
|
39
|
+
- sidebar/content wrappers;
|
|
40
|
+
- reusable layout frames, які володіють своїм grid/header/sidebar CSS.
|
|
41
|
+
|
|
42
|
+
`<slot>` — це feature Shadow DOM. У component з `shadow: false` тег `<slot>` є
|
|
43
|
+
звичайним DOM element і не переносить children у це місце layout.
|
|
44
|
+
|
|
45
|
+
## Як працює import
|
|
26
46
|
|
|
27
47
|
```ts
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
});
|
|
48
|
+
import "./components/app-layout.js";
|
|
49
|
+
|
|
50
|
+
render(html`<x-app-layout>${router.view}</x-app-layout>`, app);
|
|
32
51
|
```
|
|
33
52
|
|
|
34
|
-
|
|
53
|
+
Import реєструє custom element через `customElements.define()`. Template створює
|
|
54
|
+
`<x-app-layout>` element. Далі browser сам з'єднує tag з component class. Це не
|
|
55
|
+
React-style component value, який передається як function.
|
|
35
56
|
|
|
36
|
-
|
|
37
|
-
app layout або page composition, часто краще Light DOM.
|
|
57
|
+
## Routing and links
|
|
38
58
|
|
|
39
|
-
|
|
59
|
+
`data-link` працює всередині Shadow DOM, бо router використовує
|
|
40
60
|
`event.composedPath()`.
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
component("x-card-link", () => () => html`
|
|
64
|
+
<a href="/app/accounts" data-link>Accounts</a>
|
|
65
|
+
`);
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Link може бути в Shadow DOM; navigation все одно залишається SPA.
|
|
69
|
+
|
|
70
|
+
## Де імпортувати компоненти
|
|
71
|
+
|
|
72
|
+
Custom elements стають global після registration, але registration все одно є
|
|
73
|
+
явним JavaScript import.
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
// main.ts: global app frame
|
|
77
|
+
import "./components/app-shell.js";
|
|
78
|
+
|
|
79
|
+
// pages/tickets.ts: component, яким володіє ця page
|
|
80
|
+
import "../components/ticket-list.js";
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Browser **не** завантажує `ticket-list.js` лише тому, що побачив
|
|
84
|
+
`<ticket-list>`. File має бути imported десь першим. Після import він викликає
|
|
85
|
+
`customElements.define(...)`, і tag стає відомим у поточному document.
|
|
86
|
+
|
|
87
|
+
Не робіть bulk-import усіх components у `main.ts` "just in case". Це працює в
|
|
88
|
+
tiny demos, але ховає ownership і ламає lazy route loading. Краще:
|
|
89
|
+
|
|
90
|
+
- global app shell/providers імпортувати в `main.ts`;
|
|
91
|
+
- components, якими володіє одна page, імпортувати в цій page;
|
|
92
|
+
- shared feature components імпортувати у feature entry page;
|
|
93
|
+
- truly global leaf components імпортувати в `main.ts` лише якщо вони реально
|
|
94
|
+
використовуються всюди.
|
|
95
|
+
|
|
96
|
+
## Showcase lesson
|
|
97
|
+
|
|
98
|
+
`examples/showcase` використовує цей split навмисно:
|
|
99
|
+
|
|
100
|
+
- `x-app` і CRM route pages — Light DOM;
|
|
101
|
+
- `x-app-layout` — Shadow DOM, бо він володіє slot-based sidebar/content shell;
|
|
102
|
+
- table/form/page utilities живуть у `styles/global.ts`;
|
|
103
|
+
- leaf components на кшталт `x-stat-card`, `x-status-badge`, `x-modal`,
|
|
104
|
+
`x-toast-stack` залишають Shadow DOM.
|
|
105
|
+
|
|
106
|
+
Якщо page раптом виглядає unstyled, перевірте, чи не використовує вона global
|
|
107
|
+
classes всередині Shadow DOM component. Зазвичай проблема саме в цьому.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Архітектура застосунку
|
|
2
|
+
|
|
3
|
+
Production-застосунок на Mado має бути простим: один manifest маршрутів, один
|
|
4
|
+
shell, один API-клієнт, один auth-модуль і сторінки, які імпортують власні
|
|
5
|
+
компоненти.
|
|
6
|
+
|
|
7
|
+
```txt
|
|
8
|
+
src/
|
|
9
|
+
├── main.ts
|
|
10
|
+
├── routes.ts
|
|
11
|
+
├── layouts/
|
|
12
|
+
├── pages/
|
|
13
|
+
├── components/
|
|
14
|
+
├── lib/
|
|
15
|
+
└── styles/
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
`lib/` — бізнес-логіка, `layouts/` — обгортки груп маршрутів, `components/` —
|
|
19
|
+
повторні UI-теги, `pages/` — один файл на сторінку.
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
import { html, render } from "@madojs/mado";
|
|
23
|
+
import "./styles/global.js";
|
|
24
|
+
import routesApi from "./routes.js";
|
|
25
|
+
|
|
26
|
+
render(html`${routesApi.view}`, document.getElementById("app")!);
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Feature-компоненти імпортує сторінка, яка їх рендерить.
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
import { layout, routes } from "@madojs/mado";
|
|
33
|
+
import { requireAuth } from "./lib/auth.js";
|
|
34
|
+
|
|
35
|
+
export const manifest = {
|
|
36
|
+
"/": () => import("./pages/home.js"),
|
|
37
|
+
"/admin": layout({
|
|
38
|
+
layout: () => import("./layouts/app.js"),
|
|
39
|
+
guard: requireAuth,
|
|
40
|
+
routes: { "/": () => import("./pages/admin/dashboard.js") },
|
|
41
|
+
}),
|
|
42
|
+
"*": () => import("./pages/not-found.js"),
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export default routes(manifest);
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
`manifest` потрібен для `mado bake`. Для читання використовуй `resource()`, для
|
|
49
|
+
запису `mutation(..., { invalidates })`, для форм `useForm()`.
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
mado dev
|
|
53
|
+
mado release
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Деплоїться `out/`, а не `dist/`.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Layouts
|
|
2
|
+
|
|
3
|
+
Рекомендований спосіб layout у Mado — вкладена група маршрутів у `routes.ts`.
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
import { layout, routes } from "@madojs/mado";
|
|
7
|
+
import { requireAuth } from "./lib/auth.js";
|
|
8
|
+
|
|
9
|
+
export const manifest = {
|
|
10
|
+
"/": () => import("./pages/home.js"),
|
|
11
|
+
"/login": layout({
|
|
12
|
+
layout: () => import("./layouts/auth.js"),
|
|
13
|
+
routes: { "/": () => import("./pages/login.js") },
|
|
14
|
+
}),
|
|
15
|
+
"/admin": layout({
|
|
16
|
+
layout: () => import("./layouts/app.js"),
|
|
17
|
+
guard: requireAuth,
|
|
18
|
+
routes: { "/": () => import("./pages/admin/dashboard.js") },
|
|
19
|
+
}),
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export default routes(manifest);
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Layout — це `page({ view })`, яка рендерить `child`:
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
export default page({
|
|
29
|
+
view: ({ child }) => html`<x-app-shell>${child}</x-app-shell>`,
|
|
30
|
+
});
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Один shell на групу, не на кожну сторінку. Guard на групі захищає все
|
|
34
|
+
піддерево.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Auth та API
|
|
2
|
+
|
|
3
|
+
Starter `admin` містить рекомендований рецепт:
|
|
4
|
+
|
|
5
|
+
- `src/lib/api.ts` — один HTTP-клієнт, `ApiError`, refresh після 401;
|
|
6
|
+
- `src/lib/auth.ts` — `accessToken`, `restoreSession()`, `login()`,
|
|
7
|
+
`logout()`, `requireAuth`.
|
|
8
|
+
|
|
9
|
+
Модель:
|
|
10
|
+
|
|
11
|
+
- access token у пам'яті через `signal`, не в `localStorage`;
|
|
12
|
+
- HttpOnly refresh cookie відновлює сесію;
|
|
13
|
+
- усі запити проходять через API-клієнт;
|
|
14
|
+
- захищені routes використовують group guard.
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
export const requireAuth: Guard = async ({ path }) => {
|
|
18
|
+
if (accessToken()) return;
|
|
19
|
+
if (await restoreSession()) return;
|
|
20
|
+
return { redirect: `/login?return=${encodeURIComponent(path)}`, replace: true };
|
|
21
|
+
};
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Dev proxy:
|
|
25
|
+
|
|
26
|
+
```jsonc
|
|
27
|
+
{
|
|
28
|
+
"dev": {
|
|
29
|
+
"proxy": { "/api": "http://localhost:3000" }
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Якщо backend має іншу auth-схему, змінюй `api.ts`/`auth.ts`, а не сторінки.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Deployment
|
|
2
|
+
|
|
3
|
+
Одна команда, один artifact:
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
mado release
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Результат:
|
|
10
|
+
|
|
11
|
+
```txt
|
|
12
|
+
out/
|
|
13
|
+
├── index.html
|
|
14
|
+
├── assets/
|
|
15
|
+
├── baked/
|
|
16
|
+
├── _redirects
|
|
17
|
+
└── _headers
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
`out/` можна деплоїти на nginx, Cloudflare Pages, Netlify, S3/CloudFront або
|
|
21
|
+
GitHub Pages.
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
mado release
|
|
25
|
+
mado preview
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
`mado preview` показує `out/` як статичний хост: baked HTML має пріоритет,
|
|
29
|
+
потім SPA fallback.
|
|
30
|
+
|
|
31
|
+
Для VPS + nginx:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
mado release
|
|
35
|
+
rsync -avz --delete out/ user@server:/var/www/myapp/
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Наданий `nginx.conf` налаштовує immutable cache для hash bundles, no-cache для
|
|
39
|
+
HTML та fallback для deep links.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Тестування
|
|
2
|
+
|
|
3
|
+
Mado — це TypeScript і browser APIs. У репозиторії фреймворка тести йдуть через
|
|
4
|
+
Node test runner та `linkedom`.
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
npm run typecheck
|
|
8
|
+
npm run build
|
|
9
|
+
npm test
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
DOM-тест:
|
|
13
|
+
|
|
14
|
+
```js
|
|
15
|
+
import test from "node:test";
|
|
16
|
+
import assert from "node:assert/strict";
|
|
17
|
+
|
|
18
|
+
const { parseHTML } = await import("linkedom");
|
|
19
|
+
const { window } = parseHTML("<!doctype html><html><body></body></html>");
|
|
20
|
+
globalThis.window = window;
|
|
21
|
+
globalThis.document = window.document;
|
|
22
|
+
|
|
23
|
+
const { html, render } = await import("../dist/src/html.js");
|
|
24
|
+
|
|
25
|
+
test("renders", () => {
|
|
26
|
+
const root = document.createElement("div");
|
|
27
|
+
render(html`<p>${"hello"}</p>`, root);
|
|
28
|
+
assert.equal(root.querySelector("p").textContent, "hello");
|
|
29
|
+
});
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Покривай signals/computed/effect, HTML bindings, guards/redirects, scroll/focus,
|
|
33
|
+
error boundaries, forms, resources/mutations і CLI-команди `mado release`,
|
|
34
|
+
`mado bake`, `mado preview`.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Обробка помилок
|
|
2
|
+
|
|
3
|
+
Обробляй помилки там, де користувач може відновитися: маршрути, дані, дії
|
|
4
|
+
користувача.
|
|
5
|
+
|
|
6
|
+
```ts
|
|
7
|
+
export default routes(manifest, {
|
|
8
|
+
errorPage: (err) => html`
|
|
9
|
+
<main>
|
|
10
|
+
<h1>Щось пішло не так</h1>
|
|
11
|
+
<pre>${err.message}</pre>
|
|
12
|
+
<a data-link href="/">На головну</a>
|
|
13
|
+
</main>
|
|
14
|
+
`,
|
|
15
|
+
});
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
`page({ errorView })` має пріоритет над глобальною boundary.
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
const users = resource(() => "/api/users", jsonFetcher<User[]>());
|
|
22
|
+
|
|
23
|
+
html`
|
|
24
|
+
${() => users.error()
|
|
25
|
+
? html`<p role="alert">${users.error()!.message}</p>
|
|
26
|
+
<button @click=${users.refresh}>Повторити</button>`
|
|
27
|
+
: null}
|
|
28
|
+
`;
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Валідація належить `useForm()`, помилки запису — поруч із submit-кнопкою.
|
|
32
|
+
Зовнішні browser subscriptions очищай через `ctx.onDispose()`.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Bake cookbook
|
|
2
|
+
|
|
3
|
+
`mado bake` рендерить вибрані маршрути у статичний HTML. Це для SEO та швидкого
|
|
4
|
+
першого рендеру, не SSR з hydration.
|
|
5
|
+
|
|
6
|
+
```ts
|
|
7
|
+
export default page({
|
|
8
|
+
head: () => ({ title: "Products", description: "Catalog" }),
|
|
9
|
+
view: ({ data }) => html`
|
|
10
|
+
<main>
|
|
11
|
+
<h1>Products</h1>
|
|
12
|
+
${data.products.map((p) => html`<article><h2>${p.name}</h2></article>`)}
|
|
13
|
+
</main>
|
|
14
|
+
`,
|
|
15
|
+
bake: {
|
|
16
|
+
paths: () => [{}],
|
|
17
|
+
data: async () => ({ products: await api.products() }),
|
|
18
|
+
revalidate: 3600,
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
У baked views краще використовувати звичайні масиви (`items.map(...)`).
|
|
24
|
+
Runtime-директиви на кшталт `each()` потрібні браузеру.
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
export const manifest = {
|
|
28
|
+
"/": () => import("./pages/home.js"),
|
|
29
|
+
"/blog/:slug": () => import("./pages/blog-post.js"),
|
|
30
|
+
};
|
|
31
|
+
export default routes(manifest);
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
`mado release` створює deploy artifact у `out/`. Якщо bake повідомляє про
|
|
35
|
+
unsupported value, винеси runtime-only частину в клієнт або заміни її на
|
|
36
|
+
серіалізований HTML.
|
package/docs/uk/README.md
CHANGED
|
@@ -14,3 +14,10 @@
|
|
|
14
14
|
| Типові помилки LLM | [07-llm-pitfalls.md](./07-llm-pitfalls.md) |
|
|
15
15
|
| LLM zero-history тест | [08-llm-zero-history-test.md](./08-llm-zero-history-test.md) |
|
|
16
16
|
| Shadow DOM vs Light DOM | [09-shadow-vs-light-dom.md](./09-shadow-vs-light-dom.md) |
|
|
17
|
+
| Архітектура застосунку | [10-app-architecture.md](./10-app-architecture.md) |
|
|
18
|
+
| Layouts | [11-layouts.md](./11-layouts.md) |
|
|
19
|
+
| Auth та API | [12-auth-and-api.md](./12-auth-and-api.md) |
|
|
20
|
+
| Deployment | [13-deployment.md](./13-deployment.md) |
|
|
21
|
+
| Тестування | [14-testing.md](./14-testing.md) |
|
|
22
|
+
| Обробка помилок | [15-error-handling.md](./15-error-handling.md) |
|
|
23
|
+
| Bake cookbook | [16-bake-cookbook.md](./16-bake-cookbook.md) |
|
package/llms.txt
CHANGED
|
@@ -11,6 +11,7 @@ Mado is a narrowly-focused frontend framework that deliberately avoids React pat
|
|
|
11
11
|
- **All templates are tagged template `html\`...\``** (not JSX). Allowed bindings inside: `${value}`, `attr=${v}`, `@evt=${fn}`, `.prop=${v}`, `?attr=${flag}`.
|
|
12
12
|
- **Reactivity via signals.** `signal()`, `computed()`, `effect()`. A signal is a getter function (`count()`), not an object field (`count.value`).
|
|
13
13
|
- **Components are Web Components.** Registered via `component('x-name', setupFn, options)`. Names must include a hyphen (`x-foo`, `my-button`).
|
|
14
|
+
- **Component files register tags as side effects.** The browser does not auto-import files by tag name. If `<x-card>` works, some imported module already ran `customElements.define("x-card", ...)`.
|
|
14
15
|
- **Cleanup via `ctx.onDispose(fn)`** in setup, not via return from effect.
|
|
15
16
|
|
|
16
17
|
## Critical template rules
|
|
@@ -94,6 +95,20 @@ export default page({
|
|
|
94
95
|
});
|
|
95
96
|
```
|
|
96
97
|
|
|
98
|
+
## Component registration imports
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
// main.ts: global shell/provider components only
|
|
102
|
+
import "./components/app-shell.js";
|
|
103
|
+
|
|
104
|
+
// pages/tickets.ts: feature/page-owned components
|
|
105
|
+
import "../components/ticket-list.js";
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Do not bulk-import every component in `main.ts` "just in case". Import the
|
|
109
|
+
registration near the route/page that owns the tag. This preserves lazy loading
|
|
110
|
+
and makes the code readable to humans and LLMs.
|
|
111
|
+
|
|
97
112
|
## Canonical CRUD pattern (for backend developers)
|
|
98
113
|
|
|
99
114
|
```ts
|
|
@@ -128,6 +143,13 @@ export default page({
|
|
|
128
143
|
- docs/en/05-why-mado.md — honest comparison with Lit / Solid / Svelte / htmx / Alpine / React
|
|
129
144
|
- docs/en/06-for-backenders.md — mental model in 10 minutes for Go/Rust/.NET/Java developers
|
|
130
145
|
- docs/en/07-llm-pitfalls.md — common mistakes when generating Mado code
|
|
146
|
+
- docs/en/10-app-architecture.md — canonical app file layout, routes, auth/API and release shape
|
|
147
|
+
- docs/en/11-layouts.md — blessed layout recipe
|
|
148
|
+
- docs/en/12-auth-and-api.md — blessed auth/API client recipe
|
|
149
|
+
- docs/en/13-deployment.md — deployment recipes and cache rules
|
|
150
|
+
- docs/en/14-testing.md — testing strategy and commands
|
|
151
|
+
- docs/en/15-error-handling.md — route/data/action error boundaries
|
|
152
|
+
- docs/en/16-bake-cookbook.md — static bake recipes and failure modes
|
|
131
153
|
- examples/basic/ — minimal API tour
|
|
132
154
|
- examples/tickets/ — LLM zero-history CRUD validation
|
|
133
155
|
- examples/showcase/ — flagship CRM pressure app (auth, nested routes, forms, mutations)
|
|
@@ -148,7 +170,8 @@ export default page({
|
|
|
148
170
|
|
|
149
171
|
## Version
|
|
150
172
|
|
|
151
|
-
`0.
|
|
173
|
+
`0.6.0` — pre-1.0 product-surface release. API may still change before 1.0.
|
|
174
|
+
Semver is not guaranteed on minor versions before 1.0.
|
|
152
175
|
|
|
153
176
|
## License
|
|
154
177
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@madojs/mado",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Mado — a small native-web SPA framework with Web Components, signals, tagged-template html, router, resources, and forms. TypeScript-only build, zero runtime dependencies.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -48,7 +48,9 @@
|
|
|
48
48
|
"README.md",
|
|
49
49
|
"LICENSE",
|
|
50
50
|
"CHANGELOG.md",
|
|
51
|
+
"MADO_V1_PLAN.md",
|
|
51
52
|
"ROADMAP.md",
|
|
53
|
+
"TODO.md",
|
|
52
54
|
"AGENTS.md",
|
|
53
55
|
"llms.txt",
|
|
54
56
|
"docs/**/*.md"
|