@madojs/mado 0.7.0 → 0.9.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 +29 -3
- package/CHANGELOG.md +164 -1
- package/README.md +168 -242
- package/ROADMAP.md +174 -79
- package/TODO.md +8 -5
- package/dist/src/component.js +48 -3
- package/dist/src/component.js.map +1 -1
- package/dist/src/forms.js +21 -1
- package/dist/src/forms.js.map +1 -1
- package/dist/src/html/bindings.js +32 -3
- package/dist/src/html/bindings.js.map +1 -1
- package/dist/src/html/parser.js +60 -3
- package/dist/src/html/parser.js.map +1 -1
- package/dist/src/lifecycle.js +18 -0
- package/dist/src/lifecycle.js.map +1 -1
- package/dist/src/page.d.ts +12 -0
- package/dist/src/page.js.map +1 -1
- package/dist/src/persisted.js +43 -9
- package/dist/src/persisted.js.map +1 -1
- package/dist/src/resource.d.ts +10 -0
- package/dist/src/resource.js +24 -6
- package/dist/src/resource.js.map +1 -1
- package/dist/src/router/manifest.js +29 -3
- package/dist/src/router/manifest.js.map +1 -1
- package/dist/src/router/navigation.js +56 -2
- package/dist/src/router/navigation.js.map +1 -1
- package/dist/src/signal.js +55 -7
- package/dist/src/signal.js.map +1 -1
- package/docs/en/00-the-mado-way.md +23 -12
- package/docs/en/05-why-mado.md +78 -68
- package/docs/en/06-for-backenders.md +75 -55
- package/docs/en/07-llm-pitfalls.md +101 -0
- package/docs/fr/00-the-mado-way.md +25 -13
- package/docs/fr/07-llm-pitfalls.md +2 -0
- package/docs/ru/00-the-mado-way.md +24 -11
- package/docs/ru/07-llm-pitfalls.md +2 -0
- package/docs/uk/00-the-mado-way.md +3 -1
- package/docs/uk/07-llm-pitfalls.md +2 -0
- package/llms.txt +7 -5
- package/package.json +3 -3
- package/scripts/bundle.mjs +6 -6
|
@@ -2,9 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
> Une seule bonne façon. Des contrats stricts. Pas de magie.
|
|
4
4
|
|
|
5
|
-
Mado
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
Mado est un framework pour les équipes qui construisent des panneaux d'admin,
|
|
6
|
+
des outils internes et des SPA métier — des apps qui doivent être simples à
|
|
7
|
+
créer et ennuyeuses à maintenir. Pour cela, il impose un **ensemble de
|
|
8
|
+
conventions**. Si vous les respectez, le projet reste compréhensible même avec
|
|
9
|
+
200 écrans et 5 développeurs. Si vous les enfreignez — les types et le linter
|
|
10
|
+
vous le diront immédiatement.
|
|
8
11
|
|
|
9
12
|
## Principes
|
|
10
13
|
|
|
@@ -42,13 +45,21 @@ tous écrire de la même manière.
|
|
|
42
45
|
|
|
43
46
|
```ts
|
|
44
47
|
// src/components/user-card.ts
|
|
45
|
-
import { component, html, css } from
|
|
46
|
-
|
|
47
|
-
component(
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}
|
|
48
|
+
import { component, html, css } from "@madojs/mado";
|
|
49
|
+
|
|
50
|
+
component(
|
|
51
|
+
"x-user-card",
|
|
52
|
+
() => {
|
|
53
|
+
return () => html`<div class="card"><slot /></div>`;
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
styles: css`
|
|
57
|
+
.card {
|
|
58
|
+
padding: 1rem;
|
|
59
|
+
}
|
|
60
|
+
`,
|
|
61
|
+
},
|
|
62
|
+
);
|
|
52
63
|
```
|
|
53
64
|
|
|
54
65
|
`import './components/user-card.js'` **enregistre** le composant via
|
|
@@ -63,7 +74,7 @@ component('x-user-card', () => {
|
|
|
63
74
|
const user = resource(() => `/api/users/${id()}`, jsonFetcher());
|
|
64
75
|
|
|
65
76
|
// écriture → mutation
|
|
66
|
-
const save = mutation(api.save, { invalidates: [
|
|
77
|
+
const save = mutation(api.save, { invalidates: ["/api/users*"] });
|
|
67
78
|
```
|
|
68
79
|
|
|
69
80
|
Cela fournit la mise en cache, l'annulation, la gestion des erreurs et l'invalidation automatique.
|
|
@@ -72,11 +83,11 @@ Cela fournit la mise en cache, l'annulation, la gestion des erreurs et l'invalid
|
|
|
72
83
|
|
|
73
84
|
```ts
|
|
74
85
|
// src/pages/user-profile.ts
|
|
75
|
-
import { page, html, resource, jsonFetcher } from
|
|
86
|
+
import { page, html, resource, jsonFetcher } from "@madojs/mado";
|
|
76
87
|
|
|
77
88
|
export default page({
|
|
78
89
|
title: ({ id }) => `Utilisateur #${id}`,
|
|
79
|
-
view:
|
|
90
|
+
view: ({ params }) => html`...`,
|
|
80
91
|
});
|
|
81
92
|
```
|
|
82
93
|
|
|
@@ -101,6 +112,7 @@ Voir [`01-routing.md`](./01-routing.md).
|
|
|
101
112
|
## En cas de doute
|
|
102
113
|
|
|
103
114
|
Si vous vous demandez "quelle est la meilleure façon ici ?" — c'est un signal que :
|
|
115
|
+
|
|
104
116
|
1. Soit il existe un helper intégré que vous ne connaissez pas (consultez `docs/`).
|
|
105
117
|
2. Soit c'est une nouvelle situation — discutez-en et **consignez-la** dans ce document
|
|
106
118
|
comme une convention supplémentaire.
|
|
@@ -619,5 +619,7 @@ Plus de détails : [`17-shadow-dom-forms.md`](./17-shadow-dom-forms.md).
|
|
|
619
619
|
| `@customElement('x')` | `component('x-name', setup)` |
|
|
620
620
|
| `host.getAttribute('x')` dans render | `ctx.attr('x', default)` (réactif) |
|
|
621
621
|
| `jsonFetcher()` avec auth | `apiFetcher()` (attache le Bearer token) |
|
|
622
|
+
| `setInterval` dans page view | `onDispose(() => clearInterval(id))` |
|
|
623
|
+
| lecture signal dans async init view() | `untracked(() => cursor())` |
|
|
622
624
|
|
|
623
625
|
Si quelque chose ne rentre pas dans cette liste — ouvrez `src/` et **lisez 500 lignes**. Sérieusement. Mado est intentionnellement petit pour être lisible.
|
|
@@ -2,7 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
> Один правильный путь. Жёсткие контракты. Никакой магии.
|
|
4
4
|
|
|
5
|
-
Mado —
|
|
5
|
+
Mado — фреймворк для команд, которые строят админки, внутренние инструменты
|
|
6
|
+
и бизнес-SPA — приложения, которые должны быть просты в разработке и скучны
|
|
7
|
+
в поддержке. Для этого он задаёт **набор соглашений**. Если ты следуешь им,
|
|
8
|
+
проект остаётся понятным даже когда в нём 200 экранов и 5 разработчиков. Если
|
|
9
|
+
нарушаешь — типы и линтер скажут об этом сразу.
|
|
6
10
|
|
|
7
11
|
## Принципы
|
|
8
12
|
|
|
@@ -32,13 +36,21 @@ src/
|
|
|
32
36
|
|
|
33
37
|
```ts
|
|
34
38
|
// src/components/user-card.ts
|
|
35
|
-
import { component, html, css } from
|
|
36
|
-
|
|
37
|
-
component(
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
39
|
+
import { component, html, css } from "@madojs/mado";
|
|
40
|
+
|
|
41
|
+
component(
|
|
42
|
+
"x-user-card",
|
|
43
|
+
() => {
|
|
44
|
+
return () => html`<div class="card"><slot /></div>`;
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
styles: css`
|
|
48
|
+
.card {
|
|
49
|
+
padding: 1rem;
|
|
50
|
+
}
|
|
51
|
+
`,
|
|
52
|
+
},
|
|
53
|
+
);
|
|
42
54
|
```
|
|
43
55
|
|
|
44
56
|
Импорт `import './components/user-card.js'` **регистрирует** компонент через `customElements.define`. Это side-effect. Где компонент нужен — там и импортируем.
|
|
@@ -52,7 +64,7 @@ component('x-user-card', () => {
|
|
|
52
64
|
const user = resource(() => `/api/users/${id()}`, jsonFetcher());
|
|
53
65
|
|
|
54
66
|
// запись → mutation
|
|
55
|
-
const save = mutation(api.save, { invalidates: [
|
|
67
|
+
const save = mutation(api.save, { invalidates: ["/api/users*"] });
|
|
56
68
|
```
|
|
57
69
|
|
|
58
70
|
Это даёт кеш, отмену, обработку ошибок, авто-инвалидацию.
|
|
@@ -61,11 +73,11 @@ const save = mutation(api.save, { invalidates: ['/api/users*'] });
|
|
|
61
73
|
|
|
62
74
|
```ts
|
|
63
75
|
// src/pages/user-profile.ts
|
|
64
|
-
import { page, html, resource, jsonFetcher } from
|
|
76
|
+
import { page, html, resource, jsonFetcher } from "@madojs/mado";
|
|
65
77
|
|
|
66
78
|
export default page({
|
|
67
79
|
title: ({ id }) => `User #${id}`,
|
|
68
|
-
view:
|
|
80
|
+
view: ({ params }) => html`...`,
|
|
69
81
|
});
|
|
70
82
|
```
|
|
71
83
|
|
|
@@ -87,6 +99,7 @@ export default page({
|
|
|
87
99
|
## Когда сомневаешься
|
|
88
100
|
|
|
89
101
|
Если ты задаёшься вопросом "а как тут лучше?" — это сигнал, что:
|
|
102
|
+
|
|
90
103
|
1. Либо есть встроенный хелпер, который ты не знаешь (загляни в `docs/`).
|
|
91
104
|
2. Либо это новая ситуация — её надо обсудить и **зафиксировать** в этом документе как ещё одно соглашение.
|
|
92
105
|
|
|
@@ -618,5 +618,7 @@ component("x-input", ({ host, attr }) => {
|
|
|
618
618
|
| `@customElement('x')` | `component('x-name', setup)` |
|
|
619
619
|
| `host.getAttribute('x')` в render | `ctx.attr('x', default)` (реактивно) |
|
|
620
620
|
| `jsonFetcher()` с авторизацией | `apiFetcher()` (прикрепляет Bearer токен) |
|
|
621
|
+
| `setInterval` в page view | `onDispose(() => clearInterval(id))` |
|
|
622
|
+
| чтение сигнала в async init view() | `untracked(() => cursor())` |
|
|
621
623
|
|
|
622
624
|
Если что-то не подходит из этого списка — открой `src/` и **прочитай 500 строк**. Это серьёзно. Mado специально маленький, чтобы быть читаемым.
|
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
> Один зрозумілий шлях. Жорсткі контракти. Мінімум магії.
|
|
4
4
|
|
|
5
|
-
Mado —
|
|
5
|
+
Mado — фреймворк для команд, що будують адмін-панелі, внутрішні інструменти
|
|
6
|
+
та бізнес-SPA — застосунки, які мають бути простими у розробці та нудними в
|
|
7
|
+
підтримці. Для цього він задає **набір домовленостей**. Якщо їх дотримуватись,
|
|
6
8
|
проєкт залишається читабельним навіть тоді, коли в ньому десятки сторінок і
|
|
7
9
|
кілька розробників.
|
|
8
10
|
|
|
@@ -141,3 +141,5 @@ Object.defineProperty(host, "value", {
|
|
|
141
141
|
| `class extends HTMLElement` | `component('x-name', setup)` |
|
|
142
142
|
| `host.getAttribute('x')` | `ctx.attr('x', default)` |
|
|
143
143
|
| `jsonFetcher()` з auth | `apiFetcher()` |
|
|
144
|
+
| `setInterval` в page view | `onDispose(() => clearInterval(id))` |
|
|
145
|
+
| читання сигналу в async init | `untracked(() => cursor())` |
|
package/llms.txt
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# Mado
|
|
2
2
|
|
|
3
|
-
>
|
|
4
|
-
>
|
|
3
|
+
> A calm browser-native SPA framework for internal tools, admin panels and business apps.
|
|
4
|
+
> Routing, forms, state, data fetching and prerendering. TypeScript-only build (`tsc`), zero runtime dependencies.
|
|
5
5
|
|
|
6
|
-
Mado is a
|
|
6
|
+
Mado is a focused frontend framework for admin panels, internal tools and CRUD-heavy SPA. It deliberately avoids React patterns (no JSX, no hooks, no VDOM, no Vite). Target audience: backend developers and small teams who want a complete app stack without frontend infrastructure overhead.
|
|
7
7
|
|
|
8
8
|
## Key things an AI assistant needs to know
|
|
9
9
|
|
|
@@ -13,7 +13,9 @@ Mado is a narrowly-focused frontend framework that deliberately avoids React pat
|
|
|
13
13
|
- **Components are Web Components.** Registered via `component('x-name', setupFn, options)`. Names must include a hyphen (`x-foo`, `my-button`).
|
|
14
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", ...)`.
|
|
15
15
|
- **Cleanup via `ctx.onDispose(fn)`** in setup, not via return from effect.
|
|
16
|
-
- **
|
|
16
|
+
- **Page cleanup via `onDispose`** in view: `view: ({ onDispose }) => { ... onDispose(() => cleanup()); }`. Only needed for raw APIs (setInterval, WebSocket). `resource()`/`effect()` auto-cleanup.
|
|
17
|
+
- **`untracked()` in page view async** — functions called synchronously in `view()` that read signals must wrap reads in `untracked()` to avoid effect cycles with the router.
|
|
18
|
+
- **Reactive attributes via `ctx.attr(name, default?)`** — returns a Signal<string> that auto-updates when the attribute changes. Uses `observedAttributes` when available, falls back to a per-instance `MutationObserver` for attrs registered during setup. No boilerplate needed.
|
|
17
19
|
|
|
18
20
|
## Critical template rules
|
|
19
21
|
|
|
@@ -219,7 +221,7 @@ export default page({
|
|
|
219
221
|
|
|
220
222
|
## Version
|
|
221
223
|
|
|
222
|
-
`0.
|
|
224
|
+
`0.8.0` — pre-1.0 product-surface release. API may still change before 1.0.
|
|
223
225
|
Semver is not guaranteed on minor versions before 1.0.
|
|
224
226
|
|
|
225
227
|
## License
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@madojs/mado",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Mado — a
|
|
3
|
+
"version": "0.9.0",
|
|
4
|
+
"description": "Mado — a calm browser-native SPA framework for internal tools, admin panels and business apps. Routing, forms, state and data fetching without frontend infrastructure overhead.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"keywords": [
|
|
@@ -80,4 +80,4 @@
|
|
|
80
80
|
"engines": {
|
|
81
81
|
"node": ">=20"
|
|
82
82
|
}
|
|
83
|
-
}
|
|
83
|
+
}
|
package/scripts/bundle.mjs
CHANGED
|
@@ -120,14 +120,14 @@ const result = await build({
|
|
|
120
120
|
legalComments: "none",
|
|
121
121
|
});
|
|
122
122
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
)
|
|
126
|
-
|
|
127
|
-
|
|
123
|
+
// With splitting: true, esbuild marks all dynamic-import chunks as having
|
|
124
|
+
// entryPoint. We identify the real app entry by the `entryNames` prefix "main-".
|
|
125
|
+
const mainBundle = (await readdir(ASSETS_DIR))
|
|
126
|
+
.find((f) => f.startsWith("main-") && f.endsWith(".js") && !f.endsWith(".js.map"));
|
|
127
|
+
if (!mainBundle) {
|
|
128
|
+
console.error("[bundle] entry not found in outputs (no main-*.js in assets dir)");
|
|
128
129
|
process.exit(1);
|
|
129
130
|
}
|
|
130
|
-
const mainBundle = basename(entryOutput[0]);
|
|
131
131
|
|
|
132
132
|
// Collect all js chunks in the assets dir.
|
|
133
133
|
const allJs = (await readdir(ASSETS_DIR)).filter((f) => f.endsWith(".js"));
|