@madojs/mado 0.11.1 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +102 -25
- package/CHANGELOG.md +158 -1
- package/README.md +135 -172
- package/dist/src/component.d.ts +3 -2
- package/dist/src/component.js +98 -6
- package/dist/src/component.js.map +1 -1
- package/dist/src/css.js +34 -5
- package/dist/src/css.js.map +1 -1
- package/dist/src/head.d.ts +6 -6
- package/dist/src/head.js +6 -6
- package/dist/src/html/parser.d.ts +1 -1
- package/dist/src/html/parser.js +1 -1
- package/dist/src/html/template.js +24 -10
- package/dist/src/html/template.js.map +1 -1
- package/dist/src/index.d.ts +8 -4
- package/dist/src/index.js +13 -3
- package/dist/src/index.js.map +1 -1
- package/dist/src/page.d.ts +62 -28
- package/dist/src/page.js +5 -0
- package/dist/src/page.js.map +1 -1
- package/dist/src/resource.js +2 -1
- package/dist/src/resource.js.map +1 -1
- package/dist/src/router/base.d.ts +65 -0
- package/dist/src/router/base.js +168 -0
- package/dist/src/router/base.js.map +1 -0
- package/dist/src/router/manifest.js +75 -31
- package/dist/src/router/manifest.js.map +1 -1
- package/dist/src/router/match.d.ts +16 -2
- package/dist/src/router/match.js +41 -1
- package/dist/src/router/match.js.map +1 -1
- package/dist/src/router/navigation.js +58 -9
- package/dist/src/router/navigation.js.map +1 -1
- package/dist/src/static-runtime.d.ts +81 -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 +31 -3
- package/dist/src/vite/index.js +49 -18
- 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/en/00-the-mado-way.md +1 -1
- package/docs/en/01-quickstart.md +183 -0
- package/docs/en/10-pages-and-components.md +271 -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 +225 -0
- package/docs/en/14-forms.md +244 -0
- package/docs/en/15-static-snapshots.md +181 -0
- package/docs/en/{10-app-architecture.md → 16-app-architecture.md} +53 -2
- package/docs/en/{13-deployment.md → 20-deployment.md} +59 -10
- package/docs/en/{14-testing.md → 22-testing.md} +6 -5
- package/docs/en/23-cookbook.md +292 -0
- package/docs/en/{18-api-freeze-map.md → 30-api-freeze-map.md} +12 -3
- package/docs/en/{20-v1-stability.md → 32-v1-stability.md} +3 -3
- package/docs/en/40-llm-guide.md +776 -0
- package/docs/en/{06-for-backenders.md → 41-for-backenders.md} +15 -16
- package/docs/en/{05-why-mado.md → 42-why-mado.md} +1 -1
- package/docs/en/README.md +97 -27
- package/docs/recipes/nginx/Containerfile +26 -0
- package/docs/recipes/nginx/nginx.conf +42 -0
- package/llms.txt +246 -211
- package/package.json +17 -10
- package/scripts/bake.mjs +4 -569
- package/scripts/cli/generate.mjs +55 -5
- package/scripts/cli/help.mjs +11 -5
- package/scripts/cli/index.mjs +20 -4
- package/scripts/cli/init.mjs +7 -2
- package/scripts/cli/release.mjs +20 -68
- package/scripts/docs-lint.mjs +170 -0
- package/scripts/package-smoke.mjs +67 -9
- package/scripts/preview.mjs +95 -6
- package/scripts/size-budget.mjs +5 -1
- package/scripts/static/browser.mjs +654 -0
- package/scripts/static/discover.mjs +281 -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 +191 -0
- package/starters/default/README.md +57 -55
- package/starters/default/package.json +3 -9
- 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/vite.config.ts +14 -1
- 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 +8 -5
- 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/billing/pages/invoices-list.page.ts +3 -3
- 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/modular/src/styles.d.ts +1 -0
- package/starters/modular/src/vite-env.d.ts +1 -0
- package/starters/modular/tsconfig.json +24 -0
- package/starters/modular/vite.config.ts +131 -0
- package/TODO.md +0 -79
- 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/starters/default/src/modules/home/not-found.page.ts +0 -11
- /package/docs/en/{15-error-handling.md → 21-error-handling.md} +0 -0
- /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/auth/login.page.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/modules/billing/pages/invoice-detail.page.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/reset.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-button.component.ts +0 -0
- /package/starters/{default → modular}/src/shared/ui/x-spinner.component.ts +0 -0
package/docs/uk/13-deployment.md
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
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
|
-
├── <route>/index.html
|
|
16
|
-
├── sitemap.xml
|
|
17
|
-
├── _redirects
|
|
18
|
-
└── _headers
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
`out/` можна деплоїти на nginx, Cloudflare Pages, Netlify, S3/CloudFront або
|
|
22
|
-
GitHub Pages.
|
|
23
|
-
|
|
24
|
-
```bash
|
|
25
|
-
mado release
|
|
26
|
-
mado preview
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
`mado preview` показує `out/` як статичний хост: baked HTML має пріоритет,
|
|
30
|
-
потім SPA fallback.
|
|
31
|
-
|
|
32
|
-
Для VPS + nginx:
|
|
33
|
-
|
|
34
|
-
```bash
|
|
35
|
-
mado release
|
|
36
|
-
rsync -avz --delete out/ user@server:/var/www/myapp/
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
Опційний nginx recipe живе в `docs/recipes/nginx/`: immutable cache для
|
|
40
|
-
`/assets/*`, no-cache для HTML та fallback для deep links.
|
package/docs/uk/14-testing.md
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
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/template.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`.
|
|
@@ -1,32 +0,0 @@
|
|
|
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()`.
|
|
@@ -1,36 +0,0 @@
|
|
|
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.
|
|
@@ -1,193 +0,0 @@
|
|
|
1
|
-
# Shadow DOM + форми
|
|
2
|
-
|
|
3
|
-
Використання `useForm()` з кастомними input-компонентами у Shadow DOM потребує
|
|
4
|
-
знання двох поведінок на рівні браузера:
|
|
5
|
-
|
|
6
|
-
1. **Ретаргетинг подій** — події, що спливають із Shadow DOM, мають
|
|
7
|
-
`e.target`, перенаправлений на host-елемент. `useForm().onInput` читає
|
|
8
|
-
`e.target.name` та `e.target.value`, але host-елемент `<x-input>`
|
|
9
|
-
не має цих властивостей нативно.
|
|
10
|
-
|
|
11
|
-
2. **Асоціація з формою** — `<button type="submit">` всередині Shadow Root
|
|
12
|
-
НЕ бере участі в алгоритмі form-owner для `<form>` у Light DOM. Клік по ній
|
|
13
|
-
не тригерить submit форми.
|
|
14
|
-
|
|
15
|
-
Обидва обмеження — на рівні специфікації, не баги Mado. Але фреймворк надає
|
|
16
|
-
патерни, що роблять їх безболісними.
|
|
17
|
-
|
|
18
|
-
## Патерн: Proxy-властивості на input-компонентах
|
|
19
|
-
|
|
20
|
-
Обгортаючи `<input>` у Shadow DOM компонент, експонуйте `name` та `value`
|
|
21
|
-
як DOM-властивості на host, щоб `useForm().onInput` працював після ретаргетингу:
|
|
22
|
-
|
|
23
|
-
```ts
|
|
24
|
-
import { component, css, html } from "@madojs/mado";
|
|
25
|
-
|
|
26
|
-
component("x-input", ({ host, attr }) => {
|
|
27
|
-
const name = attr("name", "");
|
|
28
|
-
const type = attr("type", "text");
|
|
29
|
-
const value = attr("value", "");
|
|
30
|
-
|
|
31
|
-
// Proxy-властивості для сумісності з useForm().
|
|
32
|
-
// Після ретаргетингу Shadow DOM e.target з <input> → <x-input>,
|
|
33
|
-
// useForm читає e.target.name / e.target.value — ці геттери будують міст.
|
|
34
|
-
Object.defineProperty(host, "name", {
|
|
35
|
-
get: () => host.getAttribute("name") ?? "",
|
|
36
|
-
configurable: true,
|
|
37
|
-
});
|
|
38
|
-
Object.defineProperty(host, "value", {
|
|
39
|
-
get: () => host.shadowRoot?.querySelector("input")?.value ?? "",
|
|
40
|
-
set: (v: string) => {
|
|
41
|
-
const input = host.shadowRoot?.querySelector("input");
|
|
42
|
-
if (input) input.value = v;
|
|
43
|
-
},
|
|
44
|
-
configurable: true,
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
return () => html`<input name=${name} type=${type} .value=${value} />`;
|
|
48
|
-
});
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
Подія `input` від внутрішнього `<input>` має `composed: true` за замовчуванням,
|
|
52
|
-
тому вона спливає через межу shadow. Після ретаргетингу `e.target` —
|
|
53
|
-
це `<x-input>`, але тепер у нього є геттери `.name` та `.value` → `useForm`
|
|
54
|
-
працює.
|
|
55
|
-
|
|
56
|
-
## Патерн: Submit форми з Shadow DOM кнопок
|
|
57
|
-
|
|
58
|
-
`<button type="submit">` всередині Shadow DOM не може тригерити submit `<form>`
|
|
59
|
-
у Light DOM. Міст через `requestSubmit()`:
|
|
60
|
-
|
|
61
|
-
```ts
|
|
62
|
-
import { component, css, html } from "@madojs/mado";
|
|
63
|
-
|
|
64
|
-
component("x-button", ({ host, attr }) => {
|
|
65
|
-
const disabled = attr("disabled");
|
|
66
|
-
|
|
67
|
-
const handleClick = () => {
|
|
68
|
-
const typeAttr = host.getAttribute("type");
|
|
69
|
-
if (typeAttr === "button" || typeAttr === "reset") return;
|
|
70
|
-
const form = host.closest("form");
|
|
71
|
-
if (form && !host.hasAttribute("disabled")) form.requestSubmit();
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
return () => html`
|
|
75
|
-
<button ?disabled=${() => disabled() !== ""} @click=${handleClick}>
|
|
76
|
-
<slot></slot>
|
|
77
|
-
</button>
|
|
78
|
-
`;
|
|
79
|
-
});
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
`host.closest("form")` працює, бо сам host-елемент живе в Light DOM
|
|
83
|
-
(тільки його внутрішні елементи у тіні). `requestSubmit()` тригерить валідацію
|
|
84
|
-
та подію `submit` точно так, ніби користувач клікнув нативну submit-кнопку
|
|
85
|
-
всередині форми.
|
|
86
|
-
|
|
87
|
-
## Патерн: Реактивні атрибути через ctx.attr()
|
|
88
|
-
|
|
89
|
-
З версії 0.7 `ctx.attr(name, defaultValue?)` повертає `Signal<string>`,
|
|
90
|
-
який автоматично оновлюється при зміні атрибута на host. Ніякого
|
|
91
|
-
`MutationObserver` бойлерплейту:
|
|
92
|
-
|
|
93
|
-
```ts
|
|
94
|
-
component("x-badge", ({ attr }) => {
|
|
95
|
-
const variant = attr("variant", "default"); // Signal<string>
|
|
96
|
-
|
|
97
|
-
return () =>
|
|
98
|
-
html`<span class=${() => `badge badge-${variant()}`}>
|
|
99
|
-
<slot></slot>
|
|
100
|
-
</span>`;
|
|
101
|
-
});
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
Батько може використовувати `?disabled=${() => !form.isValid()}` (boolean атрибут)
|
|
105
|
-
або `.variant=${"danger"}` — компонент перерендерюється реактивно в обох випадках.
|
|
106
|
-
|
|
107
|
-
## Повний приклад форми
|
|
108
|
-
|
|
109
|
-
```ts
|
|
110
|
-
import { page, html, useForm, navigate } from "@madojs/mado";
|
|
111
|
-
import "../components/x-input.js";
|
|
112
|
-
import "../components/x-button.js";
|
|
113
|
-
|
|
114
|
-
export default page({
|
|
115
|
-
title: "Вхід",
|
|
116
|
-
view: () => {
|
|
117
|
-
const form = useForm({
|
|
118
|
-
email: { required: true, type: "email" },
|
|
119
|
-
password: { required: true, minLength: 6 },
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
const handleLogin = async (values) => {
|
|
123
|
-
await api("/auth/login", { method: "POST", json: values });
|
|
124
|
-
navigate("/admin");
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
return html`
|
|
128
|
-
<form @submit=${form.onSubmit(handleLogin)}>
|
|
129
|
-
<x-input
|
|
130
|
-
name="email"
|
|
131
|
-
type="email"
|
|
132
|
-
label="Email"
|
|
133
|
-
required
|
|
134
|
-
@input=${form.onInput}
|
|
135
|
-
@blur=${form.onBlur}
|
|
136
|
-
></x-input>
|
|
137
|
-
${() =>
|
|
138
|
-
form.errors().email
|
|
139
|
-
? html`<small class="err">${form.errors().email}</small>`
|
|
140
|
-
: null}
|
|
141
|
-
|
|
142
|
-
<x-input
|
|
143
|
-
name="password"
|
|
144
|
-
type="password"
|
|
145
|
-
label="Пароль"
|
|
146
|
-
required
|
|
147
|
-
@input=${form.onInput}
|
|
148
|
-
@blur=${form.onBlur}
|
|
149
|
-
></x-input>
|
|
150
|
-
|
|
151
|
-
<x-button type="submit" ?disabled=${() => !form.isValid()}>
|
|
152
|
-
Увійти
|
|
153
|
-
</x-button>
|
|
154
|
-
</form>
|
|
155
|
-
`;
|
|
156
|
-
},
|
|
157
|
-
});
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
## Коли використовувати Light DOM
|
|
161
|
-
|
|
162
|
-
Якщо ваш input-компонент — це просто стилізована обгортка без потреби в
|
|
163
|
-
інкапсуляції, `shadow: false` уникає обох проблем (ретаргетинг та
|
|
164
|
-
form-association):
|
|
165
|
-
|
|
166
|
-
```ts
|
|
167
|
-
component(
|
|
168
|
-
"x-field",
|
|
169
|
-
({ attr }) => {
|
|
170
|
-
const label = attr("label", "");
|
|
171
|
-
return () => html`
|
|
172
|
-
<label>
|
|
173
|
-
<span>${label}</span>
|
|
174
|
-
<slot></slot>
|
|
175
|
-
</label>
|
|
176
|
-
`;
|
|
177
|
-
},
|
|
178
|
-
{ shadow: false },
|
|
179
|
-
);
|
|
180
|
-
```
|
|
181
|
-
|
|
182
|
-
З Light DOM нативний `<input>` — частина дерева документа, події не
|
|
183
|
-
ретаргетуються, і submit форми працює нативно. Компроміс: стилі не
|
|
184
|
-
інкапсульовані (треба скоупити самостійно).
|
|
185
|
-
|
|
186
|
-
## Підсумок
|
|
187
|
-
|
|
188
|
-
| Задача | Рішення Shadow DOM | Альтернатива Light DOM |
|
|
189
|
-
| --------------------------- | -------------------------------------- | -------------------------- |
|
|
190
|
-
| `useForm` + кастомний input | Proxy `name`/`value` на host | Нативний `<input>` у slot |
|
|
191
|
-
| Submit форми | `form.requestSubmit()` у click handler | Нативна кнопка працює |
|
|
192
|
-
| Реактивні атрибути | `ctx.attr()` → авто-сигнал | `ctx.attr()` працює скрізь |
|
|
193
|
-
| Інкапсуляція стилів | Так (автоматично) | Ручний `@scope` або BEM |
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
# Карта замороження API
|
|
2
|
-
|
|
3
|
-
> Що є публічним, що внутрішнім, і що SemVer захищатиме у v1.
|
|
4
|
-
|
|
5
|
-
Контракт Mado v1 навмисно невеликий. Код застосунку імпортує API з кореня
|
|
6
|
-
пакета:
|
|
7
|
-
|
|
8
|
-
```ts
|
|
9
|
-
import { component, html, resource, routes, signal } from "@madojs/mado";
|
|
10
|
-
```
|
|
11
|
-
|
|
12
|
-
Єдиний публічний subpath — side-effect модуль devtools:
|
|
13
|
-
|
|
14
|
-
```ts
|
|
15
|
-
import "@madojs/mado/devtools.js";
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
Усе інше під `dist/src/` — деталь реалізації, навіть якщо файл видно в
|
|
19
|
-
репозиторії.
|
|
20
|
-
|
|
21
|
-
## Стабільний публічний API
|
|
22
|
-
|
|
23
|
-
Ці імена публічні й захищаються SemVer після v1:
|
|
24
|
-
|
|
25
|
-
- Reactivity: `signal`, `computed`, `effect`, `untracked`, `batch`,
|
|
26
|
-
`flushSync`.
|
|
27
|
-
- Templates і directives: `html`, `render`, `each`, `list`, `unsafeHTML`,
|
|
28
|
-
`ref`, `classMap`, `styleMap`.
|
|
29
|
-
- Components і CSS: `component`, `css`, `cssVars`.
|
|
30
|
-
- Routing і pages: `routes`, `router`, `page`, `layout`,
|
|
31
|
-
`navigate`, `queryParam`, `prefetchPath`.
|
|
32
|
-
- Data: `resource`, `mutation`, `invalidate`, `jsonFetcher`, `HttpError`.
|
|
33
|
-
- Forms: `useForm`.
|
|
34
|
-
- Head і persistence: `applyHead`, `persisted`.
|
|
35
|
-
- Context: `createContext`, `provide`, `inject`.
|
|
36
|
-
- Advanced lifecycle helpers: `createLifecycle`, `runInLifecycle`,
|
|
37
|
-
`getCurrentLifecycle`.
|
|
38
|
-
- Публічні TypeScript-типи, експортовані з `@madojs/mado`.
|
|
39
|
-
|
|
40
|
-
## Внутрішнє або нестабільне
|
|
41
|
-
|
|
42
|
-
Це не публічний API:
|
|
43
|
-
|
|
44
|
-
- Package subpaths крім `@madojs/mado` і `@madojs/mado/devtools.js`.
|
|
45
|
-
- Internals parser/binding: `html/parser.js`, `html/bindings.js`,
|
|
46
|
-
`ChildState`, `EachEntry`.
|
|
47
|
-
- Internals router: `router/match.js`, `router/navigation.js`,
|
|
48
|
-
`router/manifest.js`.
|
|
49
|
-
- Diagnostics internals і всі `_testHooks`.
|
|
50
|
-
- Точний текст bundle, назви chunks і внутрішня структура файлів.
|
|
51
|
-
|
|
52
|
-
Тести репозиторію можуть імпортувати internal files через відносні шляхи
|
|
53
|
-
`dist/`. Код застосунків не повинен цього робити.
|
|
54
|
-
|
|
55
|
-
## Що може змінюватися
|
|
56
|
-
|
|
57
|
-
Patch і minor releases можуть додавати root exports, options, diagnostics, docs
|
|
58
|
-
або starter files. Вони також можуть змінювати internals, форму bundle і деталі
|
|
59
|
-
реалізації, якщо stable API та задокументована поведінка лишаються сумісними.
|
|
60
|
-
|
|
61
|
-
Breaking changes стабільного API потребують major version.
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
# Порядок reactivity
|
|
2
|
-
|
|
3
|
-
> Малий набір ordering-гарантій, які Mado вважає публічною поведінкою.
|
|
4
|
-
|
|
5
|
-
Reactivity у Mado синхронна для читання і планована для side effects. Мета —
|
|
6
|
-
передбачувані UI updates без великої scheduling-моделі.
|
|
7
|
-
|
|
8
|
-
## Signals
|
|
9
|
-
|
|
10
|
-
`signal(value)` повертає getter-функцію. `set(next)` змінює значення одразу,
|
|
11
|
-
якщо `Object.is(previous, next)` не дорівнює `true`.
|
|
12
|
-
|
|
13
|
-
```ts
|
|
14
|
-
const count = signal(0);
|
|
15
|
-
count.set(1);
|
|
16
|
-
count(); // 1, одразу
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
Computed values позначаються до запуску effects, тому effect, який читає
|
|
20
|
-
computed, бачить актуальні dependencies, а не застарілий cache.
|
|
21
|
-
|
|
22
|
-
## Effects
|
|
23
|
-
|
|
24
|
-
`effect(fn)` запускається один раз одразу. Подальші зміни dependencies планують
|
|
25
|
-
один запуск effect у microtask. Тести можуть викликати `flushSync()`, щоб
|
|
26
|
-
синхронно очистити чергу.
|
|
27
|
-
|
|
28
|
-
Якщо effect повертає cleanup-функцію, Mado запускає її перед наступним запуском
|
|
29
|
-
effect і ще раз при виклику disposer.
|
|
30
|
-
|
|
31
|
-
```ts
|
|
32
|
-
const stop = effect(() => {
|
|
33
|
-
const id = setInterval(tick, 1000);
|
|
34
|
-
return () => clearInterval(id);
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
stop();
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
У компонентах і pages для unmount cleanup надавайте перевагу
|
|
41
|
-
`ctx.onDispose()` / page `onDispose()`. Cleanup effect — це cleanup між runs.
|
|
42
|
-
|
|
43
|
-
## Batch
|
|
44
|
-
|
|
45
|
-
`batch(fn)` групує записи signals в один subscriber pass. Effects не
|
|
46
|
-
запускаються до виходу з найзовнішнього batch, включно з вкладеними batches.
|
|
47
|
-
|
|
48
|
-
```ts
|
|
49
|
-
batch(() => {
|
|
50
|
-
first.set("Ada");
|
|
51
|
-
batch(() => last.set("Lovelace"));
|
|
52
|
-
});
|
|
53
|
-
// effects бачать тільки фінальну пару
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
Спостережувані `computed({ equals })` також зберігають batch atomicity: вони
|
|
57
|
-
перераховуються один раз після зовнішнього batch на повністю застосованому
|
|
58
|
-
state. Вони не повинні бачити напівзастосований batch на кшталт
|
|
59
|
-
`(new x, old y)`.
|
|
60
|
-
|
|
61
|
-
## DOM updates
|
|
62
|
-
|
|
63
|
-
`render(result, container)` перевикористовує наявний template instance, коли
|
|
64
|
-
наступний render має ті самі template strings. Для child bindings, що
|
|
65
|
-
повертають вкладений `html```, діє те саме правило: ті самі strings оновлюються
|
|
66
|
-
на місці, інші strings перебудовують гілку.
|
|
67
|
-
|
|
68
|
-
Це означає, що непов'язані зміни signals не пересоздають `<input>` у
|
|
69
|
-
стабільному вкладеному template, тому focus, DOM state і listeners
|
|
70
|
-
зберігаються.
|
|
71
|
-
|
|
72
|
-
Списки повинні використовувати `each(items, key, renderItem)`. Keys задають DOM
|
|
73
|
-
identity. Duplicate keys попереджають у development і отримують positional
|
|
74
|
-
suffix, щоб кожен item все одно рендерився, але duplicate keys — це data bug.
|
|
75
|
-
|
|
76
|
-
## Teardown компонентів
|
|
77
|
-
|
|
78
|
-
Custom elements можуть отримати `disconnectedCallback()`, а потім
|
|
79
|
-
`connectedCallback()` під час same-tick move. Mado відкладає teardown компонента
|
|
80
|
-
до microtask і скасовує його при reconnect, тому keyed reorders зберігають
|
|
81
|
-
state компонента. Справжнє видалення все одно запускає lifecycle cleanup на
|
|
82
|
-
наступній microtask.
|
|
83
|
-
|
|
84
|
-
## Не гарантується
|
|
85
|
-
|
|
86
|
-
Mado не гарантує точну кількість internal scheduler microtasks, порядок
|
|
87
|
-
незалежних effects без спільних dependencies, форму generated bundle або
|
|
88
|
-
внутрішній module layout. Це implementation details.
|
|
89
|
-
|
|
90
|
-
Invariant tests для цього контракту:
|
|
91
|
-
|
|
92
|
-
- `test/reactivity-ordering.test.mjs`
|
|
93
|
-
- `test/signal-batch-equals.test.mjs`
|
|
94
|
-
- `test/update-nested-reuse.test.mjs`
|
|
95
|
-
- `test/each-component-state.test.mjs`
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
# Стабільність v1
|
|
2
|
-
|
|
3
|
-
> Що Mado обіцяє після v1, і що лишається вільним для розвитку.
|
|
4
|
-
|
|
5
|
-
Mado v1 означає, що публічний app-facing contract достатньо стабільний для
|
|
6
|
-
реальних business apps. Це не означає, що кожен internal file, generated byte,
|
|
7
|
-
starter copy або diagnostic string заморожені назавжди.
|
|
8
|
-
|
|
9
|
-
Читайте разом із:
|
|
10
|
-
|
|
11
|
-
- [Карта замороження API](./18-api-freeze-map.md)
|
|
12
|
-
- [Порядок reactivity](./19-reactivity-ordering.md)
|
|
13
|
-
|
|
14
|
-
## Стабільно під SemVer
|
|
15
|
-
|
|
16
|
-
Після v1 Mado вважає SemVer-protected:
|
|
17
|
-
|
|
18
|
-
- Public exports з `@madojs/mado`.
|
|
19
|
-
- Public TypeScript types з `@madojs/mado`.
|
|
20
|
-
- Side-effect subpath `@madojs/mado/devtools.js`.
|
|
21
|
-
- Template binding syntax: child `${}`, `@event`, `.prop`, `?boolean`,
|
|
22
|
-
attribute bindings, directives і `each()`.
|
|
23
|
-
- Signal semantics, описані в reactivity ordering guide.
|
|
24
|
-
- Component lifecycle semantics: setup один раз за connection lifetime,
|
|
25
|
-
deferred teardown для same-tick moves, cleanup через `ctx.onDispose`.
|
|
26
|
-
- Router/page/resource/form contracts, описані в English docs.
|
|
27
|
-
- Імена CLI commands і широкий сенс команд (`build`, `dev`, `release`, `bake`,
|
|
28
|
-
`preview`, `init`, `new`).
|
|
29
|
-
|
|
30
|
-
Ламати це можна тільки в major version.
|
|
31
|
-
|
|
32
|
-
## Дозволено в minor releases
|
|
33
|
-
|
|
34
|
-
Minor releases можуть додавати:
|
|
35
|
-
|
|
36
|
-
- New root exports.
|
|
37
|
-
- New options на наявних API.
|
|
38
|
-
- New diagnostics і warnings.
|
|
39
|
-
- New starters, examples, docs і CLI flags.
|
|
40
|
-
- Performance improvements і internal implementation rewrites.
|
|
41
|
-
|
|
42
|
-
Minor release не має вимагати змін у вже коректних apps.
|
|
43
|
-
|
|
44
|
-
## Дозволено в patch releases
|
|
45
|
-
|
|
46
|
-
Patch releases можуть виправляти bugs, посилювати diagnostics, покращувати docs
|
|
47
|
-
і робити сумісні implementation changes. Patch може змінити timing тільки якщо
|
|
48
|
-
старий timing був незадокументованим bug і зміна зберігає reactivity ordering
|
|
49
|
-
contract.
|
|
50
|
-
|
|
51
|
-
## Нестабільно
|
|
52
|
-
|
|
53
|
-
Це навмисно не захищено SemVer:
|
|
54
|
-
|
|
55
|
-
- Internal package subpaths крім `@madojs/mado/devtools.js`.
|
|
56
|
-
- Файли під `src/`, `dist/src/` і implementation module boundaries.
|
|
57
|
-
- `_testHooks`, diagnostics internals і warning codes.
|
|
58
|
-
- Точний generated JavaScript text, chunk names, sourcemap content і bundle
|
|
59
|
-
byte layout.
|
|
60
|
-
- Internal parser, binding, router і resource cache data structures.
|
|
61
|
-
- Starter app visual copy і demo data.
|
|
62
|
-
|
|
63
|
-
Apps не повинні імпортувати internal files або перевіряти точний bundle output.
|
|
64
|
-
|
|
65
|
-
## Bundle і release output
|
|
66
|
-
|
|
67
|
-
Mado триматиме size budget і deterministic release tests, але v1 stability не
|
|
68
|
-
заморожує byte-for-byte bundler output. Hashes, chunk boundaries і generated
|
|
69
|
-
asset names можуть змінюватися, якщо задокументований deployment contract
|
|
70
|
-
продовжує працювати.
|
|
71
|
-
|
|
72
|
-
## Якщо release вас зламав
|
|
73
|
-
|
|
74
|
-
Якщо update ламає код, який використовує тільки public exports і
|
|
75
|
-
задокументовану поведінку, вважайте це bug. Відкрийте issue з:
|
|
76
|
-
|
|
77
|
-
- версією Mado до і після;
|
|
78
|
-
- задіяним public API;
|
|
79
|
-
- мінімальною репродукцією;
|
|
80
|
-
- чи це runtime behaviour, TypeScript types, CLI output або docs.
|
|
81
|
-
|
|
82
|
-
Якщо поломка залежить від internal subpath або точного generated output, про це
|
|
83
|
-
все одно варто повідомити, але це не вважається SemVer break.
|
package/docs/uk/README.md
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
# Документація Mado — Українська
|
|
2
|
-
|
|
3
|
-
Український комплект документації.
|
|
4
|
-
|
|
5
|
-
| Розділ | Файл |
|
|
6
|
-
| ----------------------- | ------------------------------------------------------------ |
|
|
7
|
-
| Шлях Mado | [00-the-mado-way.md](./00-the-mado-way.md) |
|
|
8
|
-
| Маршрутизація | [01-routing.md](./01-routing.md) |
|
|
9
|
-
| Структура проєкту | [02-project-layout.md](./02-project-layout.md) |
|
|
10
|
-
| Статичний bake & SEO | [03-static-bake.md](./03-static-bake.md) |
|
|
11
|
-
| Налаштування IDE | [04-ide-setup.md](./04-ide-setup.md) |
|
|
12
|
-
| Чому Mado | [05-why-mado.md](./05-why-mado.md) |
|
|
13
|
-
| Для бекендерів | [06-for-backenders.md](./06-for-backenders.md) |
|
|
14
|
-
| Типові помилки LLM | [07-llm-pitfalls.md](./07-llm-pitfalls.md) |
|
|
15
|
-
| LLM zero-history тест | [08-llm-zero-history-test.md](./08-llm-zero-history-test.md) |
|
|
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
|
-
| Розгортання | [13-deployment.md](./13-deployment.md) |
|
|
21
|
-
| Тестування | [14-testing.md](./14-testing.md) |
|
|
22
|
-
| Обробка помилок | [15-error-handling.md](./15-error-handling.md) |
|
|
23
|
-
| Рецепти bake | [16-bake-cookbook.md](./16-bake-cookbook.md) |
|
|
24
|
-
| Shadow DOM + форми | [17-shadow-dom-forms.md](./17-shadow-dom-forms.md) |
|
|
25
|
-
| Карта замороження API | [18-api-freeze-map.md](./18-api-freeze-map.md) |
|
|
26
|
-
| Порядок reactivity | [19-reactivity-ordering.md](./19-reactivity-ordering.md) |
|
|
27
|
-
| Стабільність v1 | [20-v1-stability.md](./20-v1-stability.md) |
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|