@madojs/mado 0.10.1 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +24 -26
- package/CHANGELOG.md +68 -0
- package/README.md +18 -45
- package/TODO.md +52 -48
- package/dist/src/component.d.ts +2 -1
- package/dist/src/component.js +5 -2
- package/dist/src/component.js.map +1 -1
- package/dist/src/each.d.ts +1 -1
- package/dist/src/each.js +1 -1
- package/dist/src/each.js.map +1 -1
- package/dist/src/index.d.ts +11 -6
- package/dist/src/index.js +5 -3
- package/dist/src/index.js.map +1 -1
- package/dist/src/lazy.d.ts +1 -1
- package/dist/src/lazy.js +1 -1
- package/dist/src/lazy.js.map +1 -1
- package/dist/src/page.d.ts +17 -21
- package/dist/src/page.js +7 -12
- package/dist/src/page.js.map +1 -1
- package/dist/src/router/manifest.d.ts +1 -1
- package/dist/src/router/manifest.js +21 -13
- package/dist/src/router/manifest.js.map +1 -1
- package/dist/src/router/match.d.ts +2 -2
- package/dist/src/router/match.js +3 -3
- package/dist/src/router/match.js.map +1 -1
- package/dist/src/router/navigation.js +1 -1
- package/dist/src/router/navigation.js.map +1 -1
- package/dist/src/vite/index.d.ts +10 -0
- package/dist/src/vite/index.js +33 -0
- package/dist/src/vite/index.js.map +1 -0
- package/docs/en/00-the-mado-way.md +25 -12
- package/docs/en/01-routing.md +90 -142
- package/docs/en/02-project-layout.md +59 -53
- package/docs/en/03-static-bake.md +5 -6
- package/docs/en/05-why-mado.md +6 -6
- package/docs/en/06-for-backenders.md +18 -22
- package/docs/en/08-llm-zero-history-test.md +9 -14
- package/docs/en/09-shadow-vs-light-dom.md +28 -36
- package/docs/en/10-app-architecture.md +158 -96
- package/docs/en/11-layouts.md +22 -24
- package/docs/en/12-auth-and-api.md +89 -182
- package/docs/en/13-deployment.md +18 -22
- package/docs/en/14-testing.md +4 -4
- package/docs/en/16-bake-cookbook.md +11 -12
- package/docs/en/18-api-freeze-map.md +6 -4
- package/docs/en/20-v1-stability.md +1 -1
- package/docs/fr/00-the-mado-way.md +55 -90
- package/docs/fr/01-routing.md +70 -152
- package/docs/fr/02-project-layout.md +61 -42
- package/docs/fr/03-static-bake.md +1 -1
- package/docs/fr/05-why-mado.md +6 -6
- package/docs/fr/06-for-backenders.md +7 -7
- package/docs/fr/08-llm-zero-history-test.md +21 -48
- package/docs/fr/09-shadow-vs-light-dom.md +43 -162
- package/docs/fr/10-app-architecture.md +110 -33
- package/docs/fr/11-layouts.md +24 -12
- package/docs/fr/12-auth-and-api.md +63 -22
- package/docs/fr/13-deployment.md +7 -10
- package/docs/fr/14-testing.md +1 -1
- package/docs/fr/16-bake-cookbook.md +2 -2
- package/docs/fr/18-api-freeze-map.md +1 -1
- package/docs/fr/20-v1-stability.md +1 -1
- package/docs/recipes/nginx/README.md +13 -0
- package/docs/ru/00-the-mado-way.md +53 -75
- package/docs/ru/01-routing.md +68 -143
- package/docs/ru/02-project-layout.md +61 -41
- package/docs/ru/03-static-bake.md +2 -2
- package/docs/ru/05-why-mado.md +6 -6
- package/docs/ru/06-for-backenders.md +7 -7
- package/docs/ru/08-llm-zero-history-test.md +9 -14
- package/docs/ru/09-shadow-vs-light-dom.md +43 -178
- package/docs/ru/10-app-architecture.md +115 -63
- package/docs/ru/11-layouts.md +24 -24
- package/docs/ru/12-auth-and-api.md +57 -35
- package/docs/ru/13-deployment.md +7 -11
- package/docs/ru/14-testing.md +1 -1
- package/docs/ru/16-bake-cookbook.md +12 -6
- package/docs/ru/18-api-freeze-map.md +5 -3
- package/docs/ru/20-v1-stability.md +1 -1
- package/docs/uk/00-the-mado-way.md +70 -44
- package/docs/uk/01-routing.md +41 -47
- package/docs/uk/02-project-layout.md +68 -41
- package/docs/uk/03-static-bake.md +1 -2
- package/docs/uk/06-for-backenders.md +3 -3
- package/docs/uk/08-llm-zero-history-test.md +22 -24
- package/docs/uk/09-shadow-vs-light-dom.md +37 -86
- package/docs/uk/10-app-architecture.md +72 -31
- package/docs/uk/11-layouts.md +25 -12
- package/docs/uk/12-auth-and-api.md +58 -22
- package/docs/uk/13-deployment.md +4 -3
- package/docs/uk/14-testing.md +1 -1
- package/docs/uk/18-api-freeze-map.md +1 -1
- package/docs/uk/20-v1-stability.md +1 -1
- package/llms.txt +14 -15
- package/package.json +18 -11
- package/scripts/_config.mjs +15 -161
- package/scripts/bake.mjs +67 -57
- package/scripts/cli/generate.mjs +348 -0
- package/scripts/cli/help.mjs +27 -0
- package/scripts/cli/index.mjs +79 -0
- package/scripts/cli/init.mjs +153 -0
- package/scripts/cli/release.mjs +152 -0
- package/scripts/cli/run.mjs +96 -0
- package/scripts/cli.mjs +2 -621
- package/scripts/package-smoke.mjs +4 -1
- package/scripts/preview.mjs +13 -37
- package/scripts/size-budget.mjs +5 -2
- package/scripts/vite.default.mjs +11 -0
- package/starters/default/.editorconfig +12 -0
- package/starters/default/README.md +74 -0
- package/starters/default/eslint.config.mjs +256 -0
- package/starters/default/index.html +13 -0
- package/starters/default/package.json +30 -0
- package/starters/default/public/favicon.svg +4 -0
- package/starters/default/src/app.routes.ts +39 -0
- package/starters/default/src/layouts/app-shell.layout.ts +35 -0
- package/starters/default/src/layouts/auth-shell.layout.ts +17 -0
- package/starters/default/src/main.ts +16 -0
- package/starters/default/src/modules/auth/_contracts/auth-api.types.ts +17 -0
- package/starters/default/src/modules/auth/auth.connector.ts +45 -0
- package/starters/default/src/modules/auth/auth.guard.ts +22 -0
- package/starters/default/src/modules/auth/auth.public.ts +9 -0
- package/starters/default/src/modules/auth/auth.routes.ts +8 -0
- package/starters/default/src/modules/auth/auth.service.ts +71 -0
- package/starters/default/src/modules/auth/auth.types.ts +15 -0
- package/starters/default/src/modules/auth/login.page.ts +62 -0
- package/starters/default/src/modules/billing/_contracts/stripe.types.ts +17 -0
- package/starters/default/src/modules/billing/api/stripe.connector.ts +71 -0
- package/starters/default/src/modules/billing/billing.public.ts +5 -0
- package/starters/default/src/modules/billing/billing.routes.ts +9 -0
- package/starters/default/src/modules/billing/billing.types.ts +15 -0
- package/starters/default/src/modules/billing/components/invoice-status-badge.component.ts +43 -0
- package/starters/default/src/modules/billing/data/invoices.resource.ts +35 -0
- package/starters/default/src/modules/billing/pages/invoice-detail.page.ts +70 -0
- package/starters/default/src/modules/billing/pages/invoices-list.page.ts +73 -0
- package/starters/default/src/modules/home/home.page.ts +34 -0
- package/starters/default/src/modules/home/not-found.page.ts +11 -0
- package/starters/default/src/shared/http/http-client.ts +86 -0
- package/starters/default/src/shared/http/http-error.ts +37 -0
- package/starters/default/src/shared/http/interceptors.ts +59 -0
- package/starters/default/src/shared/lib/format-date.ts +19 -0
- package/starters/default/src/shared/styles/content.css +70 -0
- package/starters/default/src/shared/styles/reset.css +32 -0
- package/starters/default/src/shared/styles/shell.css +57 -0
- package/starters/default/src/shared/styles/tokens.css +44 -0
- package/starters/default/src/shared/ui/x-button.component.ts +49 -0
- package/starters/default/src/shared/ui/x-spinner.component.ts +22 -0
- package/starters/default/src/styles.d.ts +1 -0
- package/starters/default/src/vite-env.d.ts +1 -0
- package/starters/default/tsconfig.json +24 -0
- package/starters/default/vite.config.ts +9 -0
- package/MADO_V1_PLAN.md +0 -179
- package/ROADMAP.md +0 -178
- package/dist/src/html.d.ts +0 -18
- package/dist/src/html.js +0 -17
- package/dist/src/html.js.map +0 -1
- package/dist/src/router.d.ts +0 -13
- package/dist/src/router.js +0 -13
- package/dist/src/router.js.map +0 -1
- package/scripts/bundle.mjs +0 -212
- package/scripts/llm-zero-history-smoke.mjs +0 -93
- package/scripts/new.mjs +0 -80
- package/scripts/showcase-regression.mjs +0 -392
- package/server/serve.mjs +0 -455
- package/starters/admin/README.md +0 -63
- package/starters/admin/index.html +0 -28
- package/starters/admin/mado.config.json +0 -22
- package/starters/admin/package.json +0 -24
- package/starters/admin/public/favicon.svg +0 -4
- package/starters/admin/src/components/x-button.ts +0 -82
- package/starters/admin/src/components/x-input.ts +0 -105
- package/starters/admin/src/layouts/app.ts +0 -101
- package/starters/admin/src/layouts/auth.ts +0 -41
- package/starters/admin/src/lib/api.ts +0 -184
- package/starters/admin/src/lib/auth.ts +0 -83
- package/starters/admin/src/main.ts +0 -15
- package/starters/admin/src/pages/admin/dashboard.ts +0 -48
- package/starters/admin/src/pages/admin/order-detail.ts +0 -80
- package/starters/admin/src/pages/admin/orders.ts +0 -117
- package/starters/admin/src/pages/home.ts +0 -34
- package/starters/admin/src/pages/login.ts +0 -70
- package/starters/admin/src/pages/not-found.ts +0 -12
- package/starters/admin/src/routes.ts +0 -40
- package/starters/admin/src/styles/global.ts +0 -86
- package/starters/admin/tsconfig.json +0 -15
- package/starters/crud/README.md +0 -33
- package/starters/crud/index.html +0 -28
- package/starters/crud/mado.config.json +0 -20
- package/starters/crud/package.json +0 -24
- package/starters/crud/src/components/app-shell.ts +0 -56
- package/starters/crud/src/components/ticket-detail.ts +0 -33
- package/starters/crud/src/components/ticket-form.ts +0 -69
- package/starters/crud/src/components/ticket-list.ts +0 -66
- package/starters/crud/src/lib/api.ts +0 -76
- package/starters/crud/src/main.ts +0 -9
- package/starters/crud/src/pages/home.ts +0 -34
- package/starters/crud/src/pages/not-found.ts +0 -12
- package/starters/crud/src/pages/ticket-detail.ts +0 -7
- package/starters/crud/src/pages/ticket-new.ts +0 -7
- package/starters/crud/src/pages/tickets.ts +0 -7
- package/starters/crud/src/routes.ts +0 -11
- package/starters/crud/src/styles/global.ts +0 -155
- package/starters/crud/tsconfig.json +0 -15
- package/starters/minimal/README.md +0 -21
- package/starters/minimal/index.html +0 -28
- package/starters/minimal/mado.config.json +0 -20
- package/starters/minimal/package.json +0 -24
- package/starters/minimal/src/components/app-counter.ts +0 -31
- package/starters/minimal/src/main.ts +0 -9
- package/starters/minimal/src/pages/home.ts +0 -35
- package/starters/minimal/src/pages/not-found.ts +0 -14
- package/starters/minimal/src/routes.ts +0 -8
- package/starters/minimal/src/styles/global.ts +0 -60
- package/starters/minimal/tsconfig.json +0 -15
- package/templates/page-detail.ts +0 -63
- package/templates/page-form.ts +0 -94
- package/templates/page-list.ts +0 -79
|
@@ -1,64 +1,84 @@
|
|
|
1
1
|
# Project Layout
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Каждое Mado-приложение использует одну каноничную форму. Это не примерная
|
|
4
|
+
рекомендация, а соглашение, чтобы люди и AI-ассистенты одинаково понимали,
|
|
5
|
+
куда класть код.
|
|
4
6
|
|
|
5
|
-
```
|
|
7
|
+
```txt
|
|
6
8
|
my-app/
|
|
7
9
|
├── package.json # runtime dep: @madojs/mado
|
|
8
10
|
├── tsconfig.json # strict TS, ES2022, Bundler resolution
|
|
9
|
-
├──
|
|
10
|
-
├── index.html #
|
|
11
|
-
├── public/ #
|
|
11
|
+
├── vite.config.ts # mado() from @madojs/mado/vite
|
|
12
|
+
├── index.html # Vite entry + SPA shell
|
|
13
|
+
├── public/ # статика: favicon, images, robots.txt
|
|
12
14
|
└── src/
|
|
13
|
-
├── main.ts #
|
|
14
|
-
├── routes.ts
|
|
15
|
-
├──
|
|
16
|
-
├──
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
15
|
+
├── main.ts # импорт CSS и mount router в #app
|
|
16
|
+
├── app.routes.ts # app map: manifest + default routes(...)
|
|
17
|
+
├── layouts/ # layouts app-зон, не доменные модули
|
|
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/
|
|
23
28
|
```
|
|
24
29
|
|
|
25
30
|
## Artifact States
|
|
26
31
|
|
|
27
32
|
| Folder | Что это | Кто пишет | Deploy? |
|
|
28
|
-
|
|
33
|
+
| --- | --- | --- | --- |
|
|
29
34
|
| `src/` | исходники TypeScript | ты | no |
|
|
30
|
-
| `
|
|
31
|
-
| `
|
|
32
|
-
|
|
35
|
+
| `public/` | статика, копируется как есть | ты | через `out/` |
|
|
36
|
+
| `out/` | deploy artifact: SPA shell + assets + baked HTML | `mado release` | yes |
|
|
37
|
+
|
|
38
|
+
`mado release` = `typecheck` + Vite build (`out/index.html`, `out/assets/`,
|
|
39
|
+
`public/*`) + `bake` прямо в route paths + `sitemap.xml` + precompression.
|
|
33
40
|
|
|
34
|
-
`
|
|
35
|
-
|
|
36
|
-
`sitemap.xml` в deployable `out/` paths + copy `public/*`.
|
|
41
|
+
`index.html` лежит в корне, потому что для Vite это entry template. В
|
|
42
|
+
`public/` кладите только файлы, которые нужно скопировать как есть.
|
|
37
43
|
|
|
38
44
|
## Куда положить новый файл?
|
|
39
45
|
|
|
40
46
|
| Что | Куда |
|
|
41
|
-
|
|
42
|
-
| Страница на новый URL | `src/pages
|
|
43
|
-
|
|
|
44
|
-
|
|
|
45
|
-
|
|
|
46
|
-
|
|
|
47
|
+
| --- | --- |
|
|
48
|
+
| Страница на новый URL | `src/modules/<module>/pages/<name>.page.ts` + module routes |
|
|
49
|
+
| Module route map | `src/modules/<module>/<module>.routes.ts` |
|
|
50
|
+
| App shell/layout | `src/layouts/<zone>.layout.ts` |
|
|
51
|
+
| Shared UI widget | `src/shared/ui/<x-name>.component.ts` |
|
|
52
|
+
| Module-only UI widget | `src/modules/<module>/components/<name>.component.ts` |
|
|
53
|
+
| API connector | `src/modules/<module>/api/<provider>.connector.ts` |
|
|
54
|
+
| Data resource/mutation | `src/modules/<module>/data/<name>.resource.ts` |
|
|
55
|
+
| Auth/session | `src/modules/auth/` |
|
|
56
|
+
| Public module surface | `src/modules/<module>/<module>.public.ts` |
|
|
57
|
+
| Pure function без UI | `src/shared/lib/<name>.ts` |
|
|
58
|
+
| Static image / favicon | `public/<file>` |
|
|
59
|
+
| App-zone shell CSS | `src/shared/styles/shell.css` |
|
|
60
|
+
| Page-level CSS | `src/shared/styles/content.css` |
|
|
47
61
|
|
|
48
|
-
|
|
62
|
+
## Vite Config
|
|
49
63
|
|
|
50
|
-
|
|
64
|
+
```ts
|
|
65
|
+
import { defineConfig } from "vite";
|
|
66
|
+
import { mado } from "@madojs/mado/vite";
|
|
67
|
+
|
|
68
|
+
export default defineConfig({
|
|
69
|
+
plugins: [mado()],
|
|
70
|
+
css: {
|
|
71
|
+
transformer: "lightningcss",
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
```
|
|
51
75
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
| Файл | kebab-case | `user-profile.ts` |
|
|
55
|
-
| Тэг компонента | `x-` + kebab | `<x-user-profile>` |
|
|
56
|
-
| Контекст | PascalCase + `Ctx` | `ThemeCtx`, `AuthCtx` |
|
|
57
|
-
| Сигнал | camelCase | `userId`, `isLoggedIn` |
|
|
58
|
-
| Page-функция (внутренний компонент) | `x-<route>-page` | `<x-posts-page>` |
|
|
76
|
+
Starter включает Vite Lightning CSS transformer. Mado не владеет prefixing,
|
|
77
|
+
CSS lowering или minification.
|
|
59
78
|
|
|
60
|
-
## Что НЕ кладём в src
|
|
79
|
+
## Что НЕ кладём в `src/`
|
|
61
80
|
|
|
62
|
-
-
|
|
63
|
-
-
|
|
64
|
-
|
|
81
|
+
- Лишние build-tool configs — настройка живет в `vite.config.ts`.
|
|
82
|
+
- `.env` files — читайте env в `src/shared/lib/config.ts` и импортируйте этот
|
|
83
|
+
модуль.
|
|
84
|
+
- Tests рядом с кодом — держите их в `test/`.
|
|
@@ -100,7 +100,7 @@ export default routes(manifest, { titleSuffix: " · MyShop" });
|
|
|
100
100
|
## Запуск
|
|
101
101
|
|
|
102
102
|
```bash
|
|
103
|
-
npm install -D linkedom
|
|
103
|
+
npm install -D linkedom vite
|
|
104
104
|
npm run build
|
|
105
105
|
npm run bake
|
|
106
106
|
```
|
|
@@ -144,7 +144,7 @@ out/
|
|
|
144
144
|
{"slug":"mado-mug","name":"Mado-кружка","price":12,"..."}
|
|
145
145
|
</script>
|
|
146
146
|
|
|
147
|
-
<script type="module" src="/
|
|
147
|
+
<script type="module" src="/assets/index-HASH.js"></script>
|
|
148
148
|
</body>
|
|
149
149
|
```
|
|
150
150
|
|
package/docs/ru/05-why-mado.md
CHANGED
|
@@ -31,11 +31,11 @@ Mado — не «убийца» React/Vue/Svelte. Это узкоспециали
|
|
|
31
31
|
| Размер | ~6 КБ | ~16 КБ |
|
|
32
32
|
| Возраст / поддержка | ~10 лет, Google | 6 месяцев, один автор |
|
|
33
33
|
| Реактивность | декораторы `@property` + ручной `requestUpdate` | сигналы (`signal`/`computed`/`effect`) из коробки |
|
|
34
|
-
| Роутер | нет, нужно искать (`@lit-labs/router`, etc) | в комплекте: `routes()` +
|
|
34
|
+
| Роутер | нет, нужно искать (`@lit-labs/router`, etc) | в комплекте: `routes()` + layout groups + prefetch |
|
|
35
35
|
| Data fetching | нет, нужно собирать | `resource()` + `mutation()` + glob-инвалидация |
|
|
36
36
|
| Формы | нет | `useForm()` с HTML-like constraints |
|
|
37
37
|
| SEO / static | сложно (`@lit-labs/ssr`) | `bake` (linkedom) + edge-prerender |
|
|
38
|
-
| Билд |
|
|
38
|
+
| Билд | нужны framework-specific build plugins | Vite transport + native runtime |
|
|
39
39
|
| Стиль кода | классы + декораторы | функции + tagged templates |
|
|
40
40
|
| Экосистема | реальная (Shoelace, Material Web, и т.д.) | нет |
|
|
41
41
|
| Когда выбрать | пишете дизайн-систему / Web-Components-библиотеку для встраивания | пишете приложение целиком, хотите всё в одной коробке |
|
|
@@ -55,14 +55,14 @@ Mado — не «убийца» React/Vue/Svelte. Это узкоспециали
|
|
|
55
55
|
| Реактивность | сигналы (тот же класс идей) | сигналы |
|
|
56
56
|
| Шаблоны | JSX (компилируется в реактивные expressions) | tagged template `html\`\`` |
|
|
57
57
|
| Компонентная модель | функции, виртуальные узлы Solid | Web Components |
|
|
58
|
-
| Билд | Vite + babel-plugin-solid обязательны |
|
|
58
|
+
| Билд | Vite + babel-plugin-solid обязательны | Vite для dev/build, без runtime-зависимостей |
|
|
59
59
|
| Роутер | `@solidjs/router` | в комплекте |
|
|
60
60
|
| Data | `createResource` | `resource()` |
|
|
61
61
|
| SSR | поддерживается серьёзно (SolidStart) | намеренно нет |
|
|
62
62
|
| Экосистема | растёт, ~50 пакетов | нет |
|
|
63
|
-
| Когда выбрать | нужен топовый перф + JSX + готов настраивать билд |
|
|
63
|
+
| Когда выбрать | нужен топовый перф + JSX + готов настраивать билд | нужен browser-native runtime с простым tooling |
|
|
64
64
|
|
|
65
|
-
**Честный pitch:** *«Solid технически быстрее и зрелее.
|
|
65
|
+
**Честный pitch:** *«Solid технически быстрее и зрелее. Mado проще концептуально: browser-native runtime, Web Components, tagged templates, а Vite забирает на себя скучный dev/build. Если нужны JSX и большая экосистема — берите Solid.»*
|
|
66
66
|
|
|
67
67
|
---
|
|
68
68
|
|
|
@@ -153,7 +153,7 @@ Mado — не «убийца» React/Vue/Svelte. Это узкоспециали
|
|
|
153
153
|
|
|
154
154
|
Не размер, не перф, не сигналы — у всего этого есть конкуренты лучше.
|
|
155
155
|
|
|
156
|
-
> **«Открой исходник и прочитай его за вечер. ~3500 строк,
|
|
156
|
+
> **«Открой исходник и прочитай его за вечер. ~3500 строк, маленькие модули. Если что-то сломалось — ты не идёшь в issue на 3000 комментариев. Ты идёшь в `src/router/` и читаешь код.»**
|
|
157
157
|
|
|
158
158
|
Это называется **ownership** — ты владеешь кодом, а не зависишь от чужого.
|
|
159
159
|
|
|
@@ -13,7 +13,7 @@ Mado устроен **как HTTP-сервер**. Серьёзно:
|
|
|
13
13
|
|---|---|
|
|
14
14
|
| HTTP-роутер (chi, axum, mux) | `routes()` — манифест путей |
|
|
15
15
|
| Handler `func(req, resp)` | `page({ view: (ctx) => html\`...\` })` |
|
|
16
|
-
| Middleware |
|
|
16
|
+
| Middleware | route group через `layout()` (оборачивает handler) |
|
|
17
17
|
| Шаблонизатор (Jinja, Handlebars) | `html\`\`` tagged template |
|
|
18
18
|
| HTTP-клиент с кешем | `resource()` — fetch + cache + invalidation |
|
|
19
19
|
| Reactive variable / atom | `signal()` — реактивный геттер |
|
|
@@ -350,17 +350,17 @@ export default page({
|
|
|
350
350
|
```
|
|
351
351
|
|
|
352
352
|
```ts
|
|
353
|
-
// src/routes.ts
|
|
354
|
-
import {
|
|
353
|
+
// src/app.routes.ts
|
|
354
|
+
import { layout, routes } from "@madojs/mado";
|
|
355
355
|
|
|
356
356
|
export default routes({
|
|
357
357
|
"/login": () => import("./pages/login.js"),
|
|
358
358
|
|
|
359
|
-
"/app
|
|
359
|
+
"/app": layout({
|
|
360
360
|
layout: () => import("./layouts/auth-layout.js"),
|
|
361
361
|
routes: {
|
|
362
|
-
"dashboard": () => import("./pages/dashboard.js"),
|
|
363
|
-
"users": () => import("./pages/users.js"),
|
|
362
|
+
"/dashboard": () => import("./pages/dashboard.js"),
|
|
363
|
+
"/users": () => import("./pages/users.js"),
|
|
364
364
|
},
|
|
365
365
|
}),
|
|
366
366
|
});
|
|
@@ -423,6 +423,6 @@ export const api = new ApiClient("/api");
|
|
|
423
423
|
- **[`01-routing.md`](./01-routing.md)** — детально про роутер.
|
|
424
424
|
- **[`02-project-layout.md`](./02-project-layout.md)** — структура проекта.
|
|
425
425
|
- **[`03-static-bake.md`](./03-static-bake.md)** — SEO без SSR.
|
|
426
|
-
-
|
|
426
|
+
- **Внешний workspace `madojs-examples`** — полные демо (landing + admin).
|
|
427
427
|
|
|
428
428
|
Если что-то непонятно — open issue, или просто открой исходник. Это правда читается за вечер.
|
|
@@ -13,15 +13,15 @@
|
|
|
13
13
|
- `AGENTS.md`
|
|
14
14
|
- `README.md`
|
|
15
15
|
- `docs/en/07-llm-pitfalls.md`
|
|
16
|
-
- `examples
|
|
17
|
-
|
|
16
|
+
- файлы из внешнего workspace `madojs-examples` только если агент сам попросит
|
|
17
|
+
паттерн более крупного приложения
|
|
18
18
|
|
|
19
19
|
Агент может искать целевые API в `src/` когда заблокирован, но не должен
|
|
20
20
|
загружать весь фреймворк в контекст.
|
|
21
21
|
|
|
22
22
|
## Задание
|
|
23
23
|
|
|
24
|
-
Построить
|
|
24
|
+
Построить маленький ticket-admin SPA для соло/бекенд-разработчика.
|
|
25
25
|
|
|
26
26
|
Требуемое поведение:
|
|
27
27
|
|
|
@@ -48,15 +48,10 @@
|
|
|
48
48
|
|
|
49
49
|
## Заметки по результатам
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
Историческая реализация tickets живет во внешнем workspace `madojs-examples`.
|
|
52
|
+
Core-репозиторий больше не поставляет этот артефакт и отдельную smoke-команду;
|
|
53
|
+
используйте этот документ как ручной сценарий оценки при обновлении LLM-guidance.
|
|
53
54
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
failure patterns, затем собирает проект и запускает `test/tickets-smoke.test.mjs`.
|
|
58
|
-
|
|
59
|
-
Основная болевая точка в документации остается lifecycle: старые примеры могут
|
|
60
|
-
создать впечатление, что создание `resource()` прямо в `page.view()` допустимо.
|
|
61
|
-
Пример tickets использует page-level wrapper-компоненты вместо этого, поэтому
|
|
62
|
-
ресурсы регистрируются внутри component setup и очищаются вместе с компонентом.
|
|
55
|
+
Основная болевая точка в документации остается lifecycle: примеры не должны
|
|
56
|
+
создавать впечатление, что long-lived `resource()` можно случайно держать на
|
|
57
|
+
module scope или в route-коде без cleanup.
|
|
@@ -1,198 +1,63 @@
|
|
|
1
1
|
# Shadow DOM vs Light DOM
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
самодостаточных виджетов, но
|
|
3
|
+
Mado components используют Shadow DOM по умолчанию. Это хороший default для
|
|
4
|
+
самодостаточных виджетов, но app zones и страницы обычно должны оставаться
|
|
5
|
+
простыми light DOM templates.
|
|
5
6
|
|
|
6
|
-
## Правило
|
|
7
|
+
## Правило
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
часть UI-дерева — app shell, sidebar, modal, table, page section — по умолчанию
|
|
10
|
-
делайте Web Component через `component()`.
|
|
11
|
-
|
|
12
|
-
Обычные функции оставляйте для маленьких inline helpers:
|
|
9
|
+
Используйте route layouts для app zones:
|
|
13
10
|
|
|
14
11
|
```ts
|
|
15
|
-
|
|
12
|
+
export default page({
|
|
13
|
+
view: ({ child }) => html`<main class="app-main">${child}</main>`,
|
|
14
|
+
});
|
|
16
15
|
```
|
|
17
16
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
Используйте **Shadow DOM** для leaf-виджетов:
|
|
22
|
-
|
|
23
|
-
- кнопки, бейджи, карточки, метрики;
|
|
24
|
-
- модалы, тосты, маленькие визуальные компоненты;
|
|
25
|
-
- embed-виджеты, которые не должны наследовать CSS приложения случайно;
|
|
26
|
-
- компоненты, стили которых принадлежат самому компоненту.
|
|
27
|
-
|
|
28
|
-
Используйте **Light DOM** (`{ shadow: false }`) для структуры приложения,
|
|
29
|
-
которая хочет разделять глобальные CSS-утилиты:
|
|
30
|
-
|
|
31
|
-
- route/page компоненты;
|
|
32
|
-
- admin-экраны с плотными таблицами/формами;
|
|
33
|
-
- data-heavy экраны с таблицами и формами;
|
|
34
|
-
- компоненты, которые намеренно используют глобальные layout/form/table утилиты;
|
|
35
|
-
- места, где children должны оставаться обычным document DOM.
|
|
36
|
-
|
|
37
|
-
Используйте **Shadow DOM** для slot-based layouts:
|
|
38
|
-
|
|
39
|
-
- app shells с `<slot>`;
|
|
40
|
-
- sidebar/content wrappers;
|
|
41
|
-
- reusable layout frames, которые владеют своим grid/header/sidebar CSS.
|
|
42
|
-
|
|
43
|
-
`<slot>` — это feature Shadow DOM. В компоненте с `shadow: false` тег `<slot>`
|
|
44
|
-
становится обычным DOM-элементом и не переносит children в это место layout.
|
|
45
|
-
|
|
46
|
-
## Подвох
|
|
47
|
-
|
|
48
|
-
Глобальный CSS не пересекает границу Shadow DOM.
|
|
49
|
-
|
|
50
|
-
```ts
|
|
51
|
-
// global.ts
|
|
52
|
-
export const globalStyles = css`
|
|
53
|
-
.page-head {
|
|
54
|
-
display: flex;
|
|
55
|
-
justify-content: space-between;
|
|
56
|
-
}
|
|
57
|
-
.metric-grid {
|
|
58
|
-
display: grid;
|
|
59
|
-
grid-template-columns: repeat(4, 1fr);
|
|
60
|
-
}
|
|
61
|
-
`;
|
|
62
|
-
|
|
63
|
-
// ❌ .page-head и .metric-grid НЕ применятся внутри shadowRoot x-dashboard
|
|
64
|
-
component(
|
|
65
|
-
"x-dashboard",
|
|
66
|
-
() => () => html`
|
|
67
|
-
<header class="page-head">...</header>
|
|
68
|
-
<div class="metric-grid">...</div>
|
|
69
|
-
`,
|
|
70
|
-
);
|
|
71
|
-
```
|
|
17
|
+
Такие файлы живут в `src/layouts/` и подключаются в `src/app.routes.ts` через
|
|
18
|
+
`layout()`. Они стилизуются обычным CSS из `src/shared/styles/shell.css`.
|
|
72
19
|
|
|
73
|
-
|
|
20
|
+
Используйте page files для screens:
|
|
74
21
|
|
|
75
22
|
```ts
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
<header class="page-head">...</header>
|
|
80
|
-
<div class="metric-grid">...</div>
|
|
81
|
-
`,
|
|
82
|
-
{
|
|
83
|
-
shadow: false,
|
|
84
|
-
styles: css`
|
|
85
|
-
x-dashboard {
|
|
86
|
-
display: block;
|
|
87
|
-
}
|
|
88
|
-
x-dashboard .panel {
|
|
89
|
-
padding: 1rem;
|
|
90
|
-
}
|
|
91
|
-
`,
|
|
92
|
-
},
|
|
93
|
-
);
|
|
23
|
+
export default page({
|
|
24
|
+
view: () => html`<section><h1>Users</h1></section>`,
|
|
25
|
+
});
|
|
94
26
|
```
|
|
95
27
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
## Как ведут себя стили
|
|
28
|
+
Page-level tables, forms, prose and simple states стилизуются из
|
|
29
|
+
`src/shared/styles/content.css`.
|
|
99
30
|
|
|
100
|
-
|
|
101
|
-
- `styles: css\`\``с`shadow: false` скоупится по имени тега и адоптируется
|
|
102
|
-
глобально.
|
|
103
|
-
- CSS custom properties (`--accent`, `--bg` и т.д.) пересекают границу Shadow DOM.
|
|
104
|
-
- Селекторы по классу (`.btn`, `.form-grid`, `.page-head`) **не** пересекают
|
|
105
|
-
границу Shadow DOM.
|
|
106
|
-
- Slotted-children сохраняют свои стили из документа; shadow-компонент может
|
|
107
|
-
таргетировать их только через `::slotted(...)`.
|
|
108
|
-
- `<slot>` проецирует children только в Shadow DOM. В компоненте с `shadow: false`
|
|
109
|
-
это обычный `<slot>` элемент, который не перемещает children в своё место.
|
|
31
|
+
Используйте Shadow DOM components для leaf widgets:
|
|
110
32
|
|
|
111
|
-
|
|
33
|
+
- buttons, badges, cards, metrics;
|
|
34
|
+
- spinners, modals, toasts;
|
|
35
|
+
- widgets, которые должны владеть своим CSS.
|
|
112
36
|
|
|
113
37
|
```ts
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
component("x-stat-card", setup);
|
|
124
|
-
component("x-toast-stack", setup);
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
Это даёт backend-admin экранам предсказуемый CSS, сохраняя инкапсуляцию
|
|
128
|
-
для переиспользуемых виджетов и slot-based shells.
|
|
129
|
-
|
|
130
|
-
Import model специально browser-native:
|
|
131
|
-
|
|
132
|
-
```ts
|
|
133
|
-
import "./components/app-layout.js";
|
|
134
|
-
|
|
135
|
-
render(html`<x-app-layout>${router.view}</x-app-layout>`, app);
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
Import регистрирует custom element через `customElements.define()`. Template
|
|
139
|
-
создаёт `<x-app-layout>` элемент. Дальше браузер сам связывает тег с классом
|
|
140
|
-
компонента. Тут нет React-style component value, который передаётся как функция.
|
|
141
|
-
|
|
142
|
-
Если layout не нуждается в slot projection и должен стилизоваться полностью
|
|
143
|
-
глобальным CSS, `shadow: false` — хороший выбор. Если он содержит `<slot>`,
|
|
144
|
-
оставьте Shadow DOM и поместите стили shell в этот компонент.
|
|
145
|
-
|
|
146
|
-
## Маршрутизация и ссылки
|
|
147
|
-
|
|
148
|
-
`data-link` работает внутри Shadow DOM. Роутер использует `event.composedPath()`,
|
|
149
|
-
поэтому перехват кликов и hover-prefetch видят ссылки из open shadow roots.
|
|
150
|
-
|
|
151
|
-
```ts
|
|
152
|
-
component(
|
|
153
|
-
"x-card-link",
|
|
154
|
-
() => () => html` <a href="/app/accounts" data-link>Accounts</a> `,
|
|
155
|
-
);
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
Ссылка может быть внутри Shadow DOM — навигация всё равно остаётся SPA.
|
|
159
|
-
|
|
160
|
-
## Где импортировать компоненты
|
|
161
|
-
|
|
162
|
-
Custom elements становятся глобальными после регистрации, но регистрация всё
|
|
163
|
-
равно остаётся явным JavaScript import.
|
|
164
|
-
|
|
165
|
-
```ts
|
|
166
|
-
// main.ts: global app frame
|
|
167
|
-
import "./components/app-shell.js";
|
|
168
|
-
|
|
169
|
-
// pages/tickets.ts: component, которым владеет эта page
|
|
170
|
-
import "../components/ticket-list.js";
|
|
38
|
+
component("x-status-badge", ({ attr }) => {
|
|
39
|
+
const status = attr("status", "draft");
|
|
40
|
+
return () => html`<span>${status}</span>`;
|
|
41
|
+
}, {
|
|
42
|
+
styles: css`
|
|
43
|
+
:host { display: inline-block; }
|
|
44
|
+
span { color: var(--color-text-muted); }
|
|
45
|
+
`,
|
|
46
|
+
});
|
|
171
47
|
```
|
|
172
48
|
|
|
173
|
-
|
|
174
|
-
`<ticket-list>`. Файл должен быть где-то импортирован. После import он вызывает
|
|
175
|
-
`customElements.define(...)`, и тег становится известен текущему document.
|
|
176
|
-
|
|
177
|
-
Не стоит bulk-import всех компонентов в `main.ts` «на всякий случай». Для маленьких
|
|
178
|
-
demo это работает, но прячет ownership и ломает lazy route loading. Лучше:
|
|
179
|
-
|
|
180
|
-
- global app shell/providers импортировать в `main.ts`;
|
|
181
|
-
- components, которыми владеет одна page, импортировать в этой page;
|
|
182
|
-
- shared components feature-а импортировать в feature entry page;
|
|
183
|
-
- truly global leaf components импортировать в `main.ts` только если они реально
|
|
184
|
-
используются везде.
|
|
185
|
-
|
|
186
|
-
## Урок из showcase
|
|
187
|
-
|
|
188
|
-
`examples/showcase` использует это разделение намеренно:
|
|
189
|
-
|
|
190
|
-
- `x-app` и CRM route pages — Light DOM;
|
|
191
|
-
- `x-app-layout` остаётся в Shadow DOM, потому что владеет slot-based
|
|
192
|
-
sidebar/content shell;
|
|
193
|
-
- table/form/page утилиты живут в `styles/global.ts`;
|
|
194
|
-
- leaf-компоненты типа `x-stat-card`, `x-status-badge`, `x-modal` и
|
|
195
|
-
`x-toast-stack` остаются в Shadow DOM.
|
|
49
|
+
## Как ведут себя стили
|
|
196
50
|
|
|
197
|
-
|
|
198
|
-
|
|
51
|
+
- `tokens.css` задает CSS custom properties; `var(...)` проходит через Shadow
|
|
52
|
+
DOM.
|
|
53
|
+
- `reset.css`, `shell.css`, `content.css` применяются только к document/light
|
|
54
|
+
DOM.
|
|
55
|
+
- Class selectors вроде `.data`, `.app-main`, `.error` не проходят внутрь
|
|
56
|
+
Shadow DOM.
|
|
57
|
+
- Component-local styles пишутся в ``css`...` `` внутри `component()` options.
|
|
58
|
+
- Если Shadow component принимает children, используйте `<slot>` и стилизуйте
|
|
59
|
+
frame внутри component styles.
|
|
60
|
+
|
|
61
|
+
Если page выглядит без стилей, почти всегда вы использовали global classes
|
|
62
|
+
внутри Shadow DOM component. Вынесите markup в page/layout или перенесите CSS в
|
|
63
|
+
component styles.
|