@madojs/mado 0.8.0 → 0.10.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 +81 -4
- package/CHANGELOG.md +202 -1
- package/README.md +184 -242
- package/ROADMAP.md +174 -79
- package/TODO.md +8 -5
- package/dist/src/component.d.ts +2 -12
- package/dist/src/component.js +30 -29
- package/dist/src/component.js.map +1 -1
- package/dist/src/diagnostics.d.ts +0 -4
- package/dist/src/diagnostics.js +1 -0
- package/dist/src/diagnostics.js.map +1 -1
- package/dist/src/forms.js +17 -0
- package/dist/src/forms.js.map +1 -1
- package/dist/src/html/bindings.js +35 -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/persisted.js +43 -9
- package/dist/src/persisted.js.map +1 -1
- package/dist/src/resource.d.ts +13 -6
- package/dist/src/resource.js +83 -16
- package/dist/src/resource.js.map +1 -1
- package/dist/src/router/manifest.d.ts +0 -3
- package/dist/src/router/manifest.js +23 -2
- 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/router.d.ts +1 -1
- package/dist/src/router.js +1 -1
- package/dist/src/router.js.map +1 -1
- package/dist/src/signal.d.ts +0 -4
- package/dist/src/signal.js +56 -7
- package/dist/src/signal.js.map +1 -1
- package/docs/en/00-the-mado-way.md +23 -12
- package/docs/en/03-static-bake.md +1 -2
- package/docs/en/05-why-mado.md +78 -68
- package/docs/en/06-for-backenders.md +80 -55
- package/docs/en/07-llm-pitfalls.md +101 -0
- package/docs/en/08-llm-zero-history-test.md +5 -0
- package/docs/en/18-api-freeze-map.md +63 -0
- package/docs/en/19-reactivity-ordering.md +93 -0
- package/docs/en/20-v1-stability.md +83 -0
- package/docs/en/README.md +3 -0
- package/docs/fr/00-the-mado-way.md +25 -13
- package/docs/fr/03-static-bake.md +1 -2
- package/docs/fr/06-for-backenders.md +6 -0
- package/docs/fr/07-llm-pitfalls.md +2 -0
- package/docs/fr/08-llm-zero-history-test.md +5 -0
- package/docs/fr/18-api-freeze-map.md +63 -0
- package/docs/fr/19-reactivity-ordering.md +97 -0
- package/docs/fr/20-v1-stability.md +88 -0
- package/docs/fr/README.md +3 -0
- package/docs/ru/00-the-mado-way.md +24 -11
- package/docs/ru/03-static-bake.md +2 -3
- package/docs/ru/06-for-backenders.md +6 -0
- package/docs/ru/07-llm-pitfalls.md +2 -0
- package/docs/ru/08-llm-zero-history-test.md +5 -0
- package/docs/ru/18-api-freeze-map.md +62 -0
- package/docs/ru/19-reactivity-ordering.md +95 -0
- package/docs/ru/20-v1-stability.md +82 -0
- package/docs/ru/README.md +3 -0
- package/docs/uk/00-the-mado-way.md +3 -1
- package/docs/uk/06-for-backenders.md +5 -0
- package/docs/uk/07-llm-pitfalls.md +2 -0
- package/docs/uk/08-llm-zero-history-test.md +5 -0
- package/docs/uk/18-api-freeze-map.md +61 -0
- package/docs/uk/19-reactivity-ordering.md +95 -0
- package/docs/uk/20-v1-stability.md +83 -0
- package/docs/uk/README.md +3 -0
- package/llms.txt +63 -7
- package/package.json +10 -5
- package/scripts/bake.mjs +0 -1
- package/scripts/bundle.mjs +6 -6
- package/scripts/cli.mjs +17 -0
- package/scripts/llm-zero-history-smoke.mjs +93 -0
- package/scripts/new.mjs +1 -1
- package/scripts/package-smoke.mjs +74 -0
- package/scripts/size-budget.mjs +88 -0
- package/starters/admin/package.json +2 -2
- package/starters/crud/package.json +2 -2
- package/starters/minimal/package.json +2 -2
package/dist/src/signal.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"signal.js","sourceRoot":"","sources":["../../src/signal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAIH,MAAM,6BAA6B,GAAG,GAAG,CAAC;AAmB1C,IAAI,aAAa,GAAmB,IAAI,CAAC;AAEzC,MAAM,aAAc,SAAQ,GAAoB;IAGjB;IAFrB,cAAc,GAAG,KAAK,CAAC;IAE/B,YAA6B,OAAoB;QAC/C,KAAK,EAAE,CAAC;QADmB,YAAO,GAAP,OAAO,CAAa;IAEjD,CAAC;IAEQ,GAAG,CAAC,KAAsB;QACjC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACjB,IAAI,CAAC,cAAc,GAAG,KAAK,CAAC;QAC5B,OAAO,IAAI,CAAC;IACd,CAAC;IAEQ,MAAM,CAAC,KAAsB;QACpC,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACpC,IAAI,OAAO;YAAE,IAAI,CAAC,UAAU,EAAE,CAAC;QAC/B,OAAO,OAAO,CAAC;IACjB,CAAC;IAEQ,KAAK;QACZ,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC;QACjC,KAAK,CAAC,KAAK,EAAE,CAAC;QACd,IAAI,UAAU;YAAE,IAAI,CAAC,UAAU,EAAE,CAAC;IACpC,CAAC;IAEO,UAAU;QAChB,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,IAAI,GAAG,CAAC,IAAI,IAAI,CAAC,cAAc;YAAE,OAAO;QAClE,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;QAC3B,cAAc,CAAC,GAAG,EAAE;YAClB,IAAI,CAAC,cAAc,GAAG,KAAK,CAAC;YAC5B,IAAI,IAAI,CAAC,IAAI,KAAK,CAAC;gBAAE,IAAI,CAAC,OAAO,EAAE,EAAE,CAAC;QACxC,CAAC,CAAC,CAAC;IACL,CAAC;CACF;AAED,SAAS,cAAc,CAAC,OAAgB;IACtC,KAAK,MAAM,GAAG,IAAI,OAAO,CAAC,IAAI;QAAE,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAC1D,OAAO,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;AACvB,CAAC;AAED,kCAAkC;AAElC,MAAM,OAAO,GAAG,IAAI,GAAG,EAAc,CAAC;AACtC,IAAI,UAAU,GAAG,CAAC,CAAC;AACnB,IAAI,cAAc,GAAG,KAAK,CAAC;AAE3B,SAAS,QAAQ,CAAC,GAAe;IAC/B,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACjB,IAAI,UAAU,GAAG,CAAC;QAAE,OAAO;IAC3B,IAAI,cAAc;QAAE,OAAO;IAC3B,cAAc,GAAG,IAAI,CAAC;IACtB,cAAc,CAAC,KAAK,CAAC,CAAC;AACxB,CAAC;AAED,SAAS,KAAK;IACZ,cAAc,GAAG,KAAK,CAAC;IACvB,MAAM,SAAS,GAAG,IAAI,GAAG,EAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"signal.js","sourceRoot":"","sources":["../../src/signal.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
> One right way. Strict contracts. No magic.
|
|
4
4
|
|
|
5
|
-
Mado is
|
|
6
|
-
|
|
5
|
+
Mado is a framework for teams building admin panels, internal tools and
|
|
6
|
+
business SPA — apps that should be easy to build and boring to maintain. To
|
|
7
|
+
achieve that, it enforces a **set of conventions**. If you follow them, the
|
|
8
|
+
project stays understandable even with 200 screens and 5 developers. If you
|
|
7
9
|
break them — types and the linter will tell you immediately.
|
|
8
10
|
|
|
9
11
|
## Principles
|
|
@@ -40,13 +42,21 @@ all write the same way.
|
|
|
40
42
|
|
|
41
43
|
```ts
|
|
42
44
|
// src/components/user-card.ts
|
|
43
|
-
import { component, html, css } from
|
|
44
|
-
|
|
45
|
-
component(
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
45
|
+
import { component, html, css } from "@madojs/mado";
|
|
46
|
+
|
|
47
|
+
component(
|
|
48
|
+
"x-user-card",
|
|
49
|
+
() => {
|
|
50
|
+
return () => html`<div class="card"><slot /></div>`;
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
styles: css`
|
|
54
|
+
.card {
|
|
55
|
+
padding: 1rem;
|
|
56
|
+
}
|
|
57
|
+
`,
|
|
58
|
+
},
|
|
59
|
+
);
|
|
50
60
|
```
|
|
51
61
|
|
|
52
62
|
`import './components/user-card.js'` **registers** the component via
|
|
@@ -61,7 +71,7 @@ component('x-user-card', () => {
|
|
|
61
71
|
const user = resource(() => `/api/users/${id()}`, jsonFetcher());
|
|
62
72
|
|
|
63
73
|
// writing → mutation
|
|
64
|
-
const save = mutation(api.save, { invalidates: [
|
|
74
|
+
const save = mutation(api.save, { invalidates: ["/api/users*"] });
|
|
65
75
|
```
|
|
66
76
|
|
|
67
77
|
This provides caching, cancellation, error handling, and auto-invalidation.
|
|
@@ -70,11 +80,11 @@ This provides caching, cancellation, error handling, and auto-invalidation.
|
|
|
70
80
|
|
|
71
81
|
```ts
|
|
72
82
|
// src/pages/user-profile.ts
|
|
73
|
-
import { page, html, resource, jsonFetcher } from
|
|
83
|
+
import { page, html, resource, jsonFetcher } from "@madojs/mado";
|
|
74
84
|
|
|
75
85
|
export default page({
|
|
76
86
|
title: ({ id }) => `User #${id}`,
|
|
77
|
-
view:
|
|
87
|
+
view: ({ params }) => html`...`,
|
|
78
88
|
});
|
|
79
89
|
```
|
|
80
90
|
|
|
@@ -99,6 +109,7 @@ See [`01-routing.md`](./01-routing.md).
|
|
|
99
109
|
## When in doubt
|
|
100
110
|
|
|
101
111
|
If you are asking "what's the best way here?" — that is a signal that:
|
|
112
|
+
|
|
102
113
|
1. Either there is a built-in helper you don't know about (check `docs/`).
|
|
103
114
|
2. Or this is a new situation — discuss it and **record** it in this document
|
|
104
115
|
as one more convention.
|
|
@@ -129,7 +129,6 @@ out/
|
|
|
129
129
|
{"@context":"https://schema.org","@type":"Product","..."}
|
|
130
130
|
</script>
|
|
131
131
|
<meta name="bake-revalidate" content="3600" data-mado-head="baked">
|
|
132
|
-
<meta name="bake-stamp" content="1234567890" data-mado-head="baked">
|
|
133
132
|
</head>
|
|
134
133
|
<body>
|
|
135
134
|
<div id="app">
|
|
@@ -212,7 +211,7 @@ export default page<{ slug: string }>({
|
|
|
212
211
|
|
|
213
212
|
## Revalidate / CDN
|
|
214
213
|
|
|
215
|
-
`bake.revalidate: 3600` writes `<meta name="bake-revalidate" content="3600">`
|
|
214
|
+
`bake.revalidate: 3600` writes `<meta name="bake-revalidate" content="3600">` to the HTML. This is **metadata** — the framework does not re-bake anything itself. Strategies:
|
|
216
215
|
|
|
217
216
|
1. **Simplest option**: cron in CI — `npm run bake && rsync out/ origin:/var/www/`.
|
|
218
217
|
2. **Via CDN** (Cloudflare/Fastly): serve HTML with `Cache-Control: max-age=3600`. CDN invalidates itself.
|
package/docs/en/05-why-mado.md
CHANGED
|
@@ -1,22 +1,27 @@
|
|
|
1
1
|
# Why Mado (and why not Lit / Solid / Alpine / htmx)
|
|
2
2
|
|
|
3
|
-
> If you are choosing a frontend stack for
|
|
4
|
-
>
|
|
3
|
+
> If you are choosing a frontend stack for an admin panel, internal tool or
|
|
4
|
+
> business SPA, this page is for you.
|
|
5
|
+
> If you already have something working — **don't migrate for the sake of
|
|
6
|
+
> migration**, it always costs more than it seems.
|
|
5
7
|
|
|
6
|
-
Mado is not a "killer" of React/Vue/Svelte. It is a
|
|
8
|
+
Mado is not a "killer" of React/Vue/Svelte. It is a focused tool for teams that
|
|
9
|
+
want a complete app stack (routing, forms, data, state, prerender) without
|
|
10
|
+
frontend infrastructure overhead. Here is an honest comparison of **when Mado is
|
|
11
|
+
genuinely better than the alternatives**, and when it is not.
|
|
7
12
|
|
|
8
13
|
---
|
|
9
14
|
|
|
10
15
|
## TL;DR — one table
|
|
11
16
|
|
|
12
|
-
| If you care about…
|
|
13
|
-
|
|
14
|
-
| Best learning infrastructure / huge ecosystem
|
|
15
|
-
| Component design system for embedding into any framework
|
|
16
|
-
| Top performance on large lists, "close to vanilla" with JSX
|
|
17
|
-
| Progressive enhancement of classic server-rendered apps
|
|
18
|
-
| "Sprinkling" reactivity onto a static site
|
|
19
|
-
| Minimal tooling, maximum platform, everything in one box (router + data + forms + SEO), readable in an evening | **Mado** ✓
|
|
17
|
+
| If you care about… | Choose |
|
|
18
|
+
| -------------------------------------------------------------------------------------------------------------- | ------------------------- |
|
|
19
|
+
| Best learning infrastructure / huge ecosystem | **React** or **Vue** |
|
|
20
|
+
| Component design system for embedding into any framework | **Lit** |
|
|
21
|
+
| Top performance on large lists, "close to vanilla" with JSX | **Solid** or **Svelte 5** |
|
|
22
|
+
| Progressive enhancement of classic server-rendered apps | **htmx** + your backend |
|
|
23
|
+
| "Sprinkling" reactivity onto a static site | **Alpine.js** |
|
|
24
|
+
| Minimal tooling, maximum platform, everything in one box (router + data + forms + SEO), readable in an evening | **Mado** ✓ |
|
|
20
25
|
|
|
21
26
|
If your case does not fall into the last point — Mado is most likely not the best choice. That's fine.
|
|
22
27
|
|
|
@@ -26,21 +31,21 @@ If your case does not fall into the last point — Mado is most likely not the b
|
|
|
26
31
|
|
|
27
32
|
**Lit** is the closest alternative in spirit. Same approach: Web Components + tagged templates + minimal magic.
|
|
28
33
|
|
|
29
|
-
|
|
|
30
|
-
|
|
31
|
-
| Size
|
|
32
|
-
| Age / support
|
|
33
|
-
| Reactivity
|
|
34
|
-
| Router
|
|
35
|
-
| Data fetching
|
|
36
|
-
| Forms
|
|
37
|
-
| SEO / static
|
|
38
|
-
| Build
|
|
39
|
-
| Code style
|
|
40
|
-
| Ecosystem
|
|
34
|
+
| | Lit | Mado |
|
|
35
|
+
| -------------- | -------------------------------------------------------------- | ------------------------------------------------------ |
|
|
36
|
+
| Size | ~6 KB | ~16 KB |
|
|
37
|
+
| Age / support | ~10 years, Google | 6 months, single author |
|
|
38
|
+
| Reactivity | `@property` decorators + manual `requestUpdate` | signals (`signal`/`computed`/`effect`) out of the box |
|
|
39
|
+
| Router | none, you need to find one (`@lit-labs/router`, etc) | included: `routes()` + nested + prefetch + sync-cache |
|
|
40
|
+
| Data fetching | none, you need to assemble it | `resource()` + `mutation()` + glob invalidation |
|
|
41
|
+
| Forms | none | `useForm()` with HTML-like constraints |
|
|
42
|
+
| SEO / static | complex (`@lit-labs/ssr`) | `bake` (linkedom) + edge-prerender |
|
|
43
|
+
| Build | needs esbuild/rollup/webpack | `tsc` is enough |
|
|
44
|
+
| Code style | classes + decorators | functions + tagged templates |
|
|
45
|
+
| Ecosystem | real (Shoelace, Material Web, etc.) | none |
|
|
41
46
|
| When to choose | writing a design system / Web Components library for embedding | writing a full application, want everything in one box |
|
|
42
47
|
|
|
43
|
-
**Honest pitch:**
|
|
48
|
+
**Honest pitch:** _"Lit is better if you're writing a component design system. Mado is better if you're writing an application and want batteries included without assembling 8 packages."_
|
|
44
49
|
|
|
45
50
|
---
|
|
46
51
|
|
|
@@ -48,21 +53,21 @@ If your case does not fall into the last point — Mado is most likely not the b
|
|
|
48
53
|
|
|
49
54
|
**Solid** is a top-tier reactive library built on signals. Technically very impressive.
|
|
50
55
|
|
|
51
|
-
|
|
|
52
|
-
|
|
53
|
-
| Size
|
|
54
|
-
| Performance
|
|
55
|
-
| Reactivity
|
|
56
|
-
| Templates
|
|
57
|
-
| Component model | functions, Solid virtual nodes
|
|
58
|
-
| Build
|
|
59
|
-
| Router
|
|
60
|
-
| Data
|
|
61
|
-
| SSR
|
|
62
|
-
| Ecosystem
|
|
63
|
-
| When to choose
|
|
64
|
-
|
|
65
|
-
**Honest pitch:**
|
|
56
|
+
| | Solid | Mado |
|
|
57
|
+
| --------------- | ----------------------------------------------------------- | ---------------------------------------------------- |
|
|
58
|
+
| Size | ~7 KB | ~16 KB |
|
|
59
|
+
| Performance | top-3 on js-framework-benchmark | good, but not top |
|
|
60
|
+
| Reactivity | signals (same class of ideas) | signals |
|
|
61
|
+
| Templates | JSX (compiled to reactive expressions) | tagged template `html\`\`` |
|
|
62
|
+
| Component model | functions, Solid virtual nodes | Web Components |
|
|
63
|
+
| Build | Vite + babel-plugin-solid required | `tsc` only |
|
|
64
|
+
| Router | `@solidjs/router` | included |
|
|
65
|
+
| Data | `createResource` | `resource()` |
|
|
66
|
+
| SSR | seriously supported (SolidStart) | intentionally none |
|
|
67
|
+
| Ecosystem | growing, ~50 packages | none |
|
|
68
|
+
| When to choose | need top performance + JSX + willing to configure the build | want to run without a build / minimal infrastructure |
|
|
69
|
+
|
|
70
|
+
**Honest pitch:** _"Solid is technically faster and more mature. But Solid requires Vite + a babel plugin. Mado requires nothing but `tsc` — it's 'open VS Code, F5, and work'. If that difference isn't critical — go with Solid."_
|
|
66
71
|
|
|
67
72
|
---
|
|
68
73
|
|
|
@@ -70,17 +75,17 @@ If your case does not fall into the last point — Mado is most likely not the b
|
|
|
70
75
|
|
|
71
76
|
**Svelte 5** with runes — also a signal model, also minimalist.
|
|
72
77
|
|
|
73
|
-
|
|
|
74
|
-
|
|
75
|
-
| Runtime size
|
|
76
|
-
| Compiler
|
|
77
|
-
| Syntax
|
|
78
|
-
| Reactivity
|
|
79
|
-
| SSR / SvelteKit | full-featured, mature
|
|
80
|
-
| Ecosystem
|
|
81
|
-
| When to choose
|
|
78
|
+
| | Svelte 5 | Mado |
|
|
79
|
+
| --------------- | ---------------------------------- | -------------------------------------- |
|
|
80
|
+
| Runtime size | ~3 KB | ~16 KB |
|
|
81
|
+
| Compiler | required (.svelte → JS) | none |
|
|
82
|
+
| Syntax | custom .svelte format | TS + tagged templates |
|
|
83
|
+
| Reactivity | `$state`/`$derived` (runes) | `signal`/`computed` |
|
|
84
|
+
| SSR / SvelteKit | full-featured, mature | intentionally none |
|
|
85
|
+
| Ecosystem | large, excellent dev-tools | none |
|
|
86
|
+
| When to choose | new production project with a team | private/internal tool, need simplicity |
|
|
82
87
|
|
|
83
|
-
**Honest pitch:**
|
|
88
|
+
**Honest pitch:** _"Svelte is a product choice. Mado is an engineering one. If you have a team and a production app — Svelte. If you're alone and want control — Mado."_
|
|
84
89
|
|
|
85
90
|
---
|
|
86
91
|
|
|
@@ -88,17 +93,17 @@ If your case does not fall into the last point — Mado is most likely not the b
|
|
|
88
93
|
|
|
89
94
|
**htmx** is a different school: HTML-fragments over the wire.
|
|
90
95
|
|
|
91
|
-
|
|
|
92
|
-
|
|
93
|
-
| Architecture
|
|
94
|
-
| Backend dependency | strong (backend must be able to serve HTML)
|
|
95
|
-
| Client state
|
|
96
|
-
| Optimistic updates | difficult
|
|
97
|
-
| Offline / PWA
|
|
98
|
-
| Size
|
|
99
|
-
| When to choose
|
|
96
|
+
| | htmx | Mado |
|
|
97
|
+
| ------------------ | ------------------------------------------------------------------------ | --------------------------------------------------- |
|
|
98
|
+
| Architecture | HTML from server, updated via fragments | SPA: JS loads data, renders itself |
|
|
99
|
+
| Backend dependency | strong (backend must be able to serve HTML) | weak (backend is a JSON API) |
|
|
100
|
+
| Client state | minimal (cookies, localStorage) | full (signal, persisted) |
|
|
101
|
+
| Optimistic updates | difficult | easy (mutation + invalidates) |
|
|
102
|
+
| Offline / PWA | poor | decent |
|
|
103
|
+
| Size | ~14 KB | ~16 KB |
|
|
104
|
+
| When to choose | classic server-rendered app (Rails, Django, Phoenix), need to "liven up" | SPA experience is required, backend is REST/GraphQL |
|
|
100
105
|
|
|
101
|
-
**Honest pitch:**
|
|
106
|
+
**Honest pitch:** _"htmx — if the backend is solid and can serve HTML. Mado — if the backend serves JSON and you need a full SPA experience."_
|
|
102
107
|
|
|
103
108
|
---
|
|
104
109
|
|
|
@@ -106,16 +111,16 @@ If your case does not fall into the last point — Mado is most likely not the b
|
|
|
106
111
|
|
|
107
112
|
**Alpine** — reactive attributes directly in HTML.
|
|
108
113
|
|
|
109
|
-
|
|
|
110
|
-
|
|
111
|
-
| Purpose
|
|
112
|
-
| Size
|
|
113
|
-
| State management | `x-data` locally
|
|
114
|
-
| Routing
|
|
115
|
-
| TypeScript
|
|
116
|
-
| When to choose
|
|
114
|
+
| | Alpine | Mado |
|
|
115
|
+
| ---------------- | ------------------------------------------------------- | ---------------------------------------- |
|
|
116
|
+
| Purpose | enhancing static HTML | full SPA |
|
|
117
|
+
| Size | ~7 KB | ~16 KB |
|
|
118
|
+
| State management | `x-data` locally | signals + context + persisted |
|
|
119
|
+
| Routing | none | included |
|
|
120
|
+
| TypeScript | poor | first-class |
|
|
121
|
+
| When to choose | static sites, landing pages, need 5 interactive buttons | full app: pages, navigation, forms, data |
|
|
117
122
|
|
|
118
|
-
**Honest pitch:**
|
|
123
|
+
**Honest pitch:** _"Alpine — for interactivity on static sites. Mado — for a full application."_
|
|
119
124
|
|
|
120
125
|
---
|
|
121
126
|
|
|
@@ -124,12 +129,14 @@ If your case does not fall into the last point — Mado is most likely not the b
|
|
|
124
129
|
I won't dwell on this for long, because React is in a **different weight class** in terms of ecosystem and maturity. But if you're seriously comparing:
|
|
125
130
|
|
|
126
131
|
**React wins:**
|
|
132
|
+
|
|
127
133
|
- massive ecosystem: thousands of UI kits, thousands of articles, endless tutorials;
|
|
128
134
|
- AI assistants (ChatGPT, Copilot) know React better than anything;
|
|
129
135
|
- better job market;
|
|
130
136
|
- better SSR support (Next.js).
|
|
131
137
|
|
|
132
138
|
**Mado wins:**
|
|
139
|
+
|
|
133
140
|
- bundle size dozens of times smaller;
|
|
134
141
|
- zero infrastructure (no Vite, no Babel, no 200 packages);
|
|
135
142
|
- readable in an evening — if something breaks, open `src/`;
|
|
@@ -137,11 +144,13 @@ I won't dwell on this for long, because React is in a **different weight class**
|
|
|
137
144
|
- no need to migrate between major versions.
|
|
138
145
|
|
|
139
146
|
**When to choose Mado over React:**
|
|
147
|
+
|
|
140
148
|
- 1–3 person project, for years to come;
|
|
141
149
|
- bundle size is critical;
|
|
142
150
|
- you're tired of React fatigue and are ready to sacrifice the ecosystem for simplicity.
|
|
143
151
|
|
|
144
152
|
**When to choose React:**
|
|
153
|
+
|
|
145
154
|
- team of 5 or more people;
|
|
146
155
|
- you need UI kits, you need the ecosystem;
|
|
147
156
|
- a project that will be hiring new people from the market;
|
|
@@ -166,6 +175,7 @@ For backend developers who are used to small, understandable libraries (chi in G
|
|
|
166
175
|
Honestly: **Mado is not the fastest**. The top-3 on js-framework-benchmark are Solid, Inferno, and Svelte. Mado is closer to Lit / Preact in characteristics.
|
|
167
176
|
|
|
168
177
|
What Mado does for performance out of the box:
|
|
178
|
+
|
|
169
179
|
- **lazy `computed`** (dirty-flag, not eager);
|
|
170
180
|
- **batch microtask scheduler** for `signal.set`;
|
|
171
181
|
- **keyed reconciliation** in `each()` with real DOM reuse;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# Mado for Backend Developers
|
|
2
2
|
|
|
3
|
-
> You write in Go / Rust / .NET / Java / Python and you need to build a web UI
|
|
3
|
+
> You write in Go / Rust / .NET / Java / Python and you need to build a web UI
|
|
4
|
+
> for an admin panel, internal tool or dashboard.
|
|
4
5
|
> This page is the mental model of Mado in 10 minutes, in your language.
|
|
5
6
|
|
|
6
7
|
---
|
|
@@ -9,17 +10,17 @@
|
|
|
9
10
|
|
|
10
11
|
Mado is structured **like an HTTP server**. Seriously:
|
|
11
12
|
|
|
12
|
-
| Server world
|
|
13
|
-
|
|
14
|
-
| HTTP router (chi, axum, mux)
|
|
15
|
-
| Handler `func(req, resp)`
|
|
16
|
-
| Middleware
|
|
17
|
-
| Template engine (Jinja, Handlebars) | `html\`\`` tagged template
|
|
18
|
-
| HTTP client with cache
|
|
19
|
-
| Reactive variable / atom
|
|
20
|
-
| Background goroutine / task
|
|
21
|
-
| `defer cleanup()`
|
|
22
|
-
| ENV variables
|
|
13
|
+
| Server world | Mado |
|
|
14
|
+
| ----------------------------------- | ---------------------------------------------- |
|
|
15
|
+
| HTTP router (chi, axum, mux) | `routes()` — path manifest |
|
|
16
|
+
| Handler `func(req, resp)` | `page({ view: (ctx) => html\`...\` })` |
|
|
17
|
+
| Middleware | `layout` in `nested()` (wraps the handler) |
|
|
18
|
+
| Template engine (Jinja, Handlebars) | `html\`\`` tagged template |
|
|
19
|
+
| HTTP client with cache | `resource()` — fetch + cache + invalidation |
|
|
20
|
+
| Reactive variable / atom | `signal()` — reactive getter |
|
|
21
|
+
| Background goroutine / task | `effect()` — auto-reruns when a signal changes |
|
|
22
|
+
| `defer cleanup()` | `ctx.onDispose(fn)` in component setup |
|
|
23
|
+
| ENV variables | `createContext()` + `provide()`/`inject()` |
|
|
23
24
|
|
|
24
25
|
If you understand an HTTP server, you understand Mado.
|
|
25
26
|
|
|
@@ -130,26 +131,34 @@ import { resource, mutation, jsonFetcher, invalidate } from "@madojs/mado";
|
|
|
130
131
|
const userId = signal(1);
|
|
131
132
|
|
|
132
133
|
const user = resource(
|
|
133
|
-
() => `/api/users/${userId()}`,
|
|
134
|
-
jsonFetcher<User>(),
|
|
135
|
-
{ staleTime: 60_000 },
|
|
134
|
+
() => `/api/users/${userId()}`, // cache key (reactive!)
|
|
135
|
+
jsonFetcher<User>(), // how to load
|
|
136
|
+
{ staleTime: 60_000 }, // 60-second cache
|
|
136
137
|
);
|
|
137
138
|
|
|
138
139
|
// in the component:
|
|
139
|
-
user.data();
|
|
140
|
-
user.error();
|
|
141
|
-
user.loading();
|
|
140
|
+
user.data(); // User | undefined
|
|
141
|
+
user.error(); // Error | null
|
|
142
|
+
user.loading(); // boolean
|
|
142
143
|
|
|
143
144
|
// mutation (like POST/PUT)
|
|
144
145
|
const save = mutation<User, User>(
|
|
145
|
-
(u) =>
|
|
146
|
-
|
|
146
|
+
(u) =>
|
|
147
|
+
fetch("/api/users", { method: "POST", body: JSON.stringify(u) }).then((r) =>
|
|
148
|
+
r.json(),
|
|
149
|
+
),
|
|
150
|
+
{ invalidates: ["/api/users*"] }, // glob invalidation — like `cache.Drop("users:*")`
|
|
147
151
|
);
|
|
148
152
|
|
|
149
153
|
await save.run(newUser);
|
|
150
154
|
// automatically: user.data() will update if glob matches
|
|
151
155
|
```
|
|
152
156
|
|
|
157
|
+
Resource keys are cache identities. Include the endpoint, query params and data
|
|
158
|
+
shape in the key: two live `resource()` calls with the same key share cached
|
|
159
|
+
data and any in-flight request. If two different fetchers use the same in-flight
|
|
160
|
+
key, Mado warns because that usually means the cache key is too broad.
|
|
161
|
+
|
|
153
162
|
If such an abstraction existed in the Go world for server-side caches — we'd all be crying with joy.
|
|
154
163
|
|
|
155
164
|
---
|
|
@@ -169,9 +178,7 @@ component("x-counter", () => {
|
|
|
169
178
|
const count = signal(0);
|
|
170
179
|
|
|
171
180
|
return () => html`
|
|
172
|
-
<button @click=${() => count.update(n => n + 1)}>
|
|
173
|
-
Clicks: ${count}
|
|
174
|
-
</button>
|
|
181
|
+
<button @click=${() => count.update((n) => n + 1)}>Clicks: ${count}</button>
|
|
175
182
|
`;
|
|
176
183
|
});
|
|
177
184
|
```
|
|
@@ -179,7 +186,7 @@ component("x-counter", () => {
|
|
|
179
186
|
Usage:
|
|
180
187
|
|
|
181
188
|
```ts
|
|
182
|
-
html`<x-counter></x-counter
|
|
189
|
+
html`<x-counter></x-counter>`;
|
|
183
190
|
```
|
|
184
191
|
|
|
185
192
|
We register the `<x-counter>` tag in the browser — it becomes a "function" that can be inserted into HTML. This is a **native** browser mechanism (Web Components), Mado only glues it together with signals.
|
|
@@ -195,22 +202,29 @@ import { useForm } from "@madojs/mado";
|
|
|
195
202
|
|
|
196
203
|
const f = useForm({
|
|
197
204
|
email: { required: true, type: "email" },
|
|
198
|
-
age:
|
|
205
|
+
age: { required: true, type: "number", min: 18 },
|
|
199
206
|
});
|
|
200
207
|
|
|
201
208
|
// in the template:
|
|
202
209
|
html`
|
|
203
|
-
<form
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
210
|
+
<form
|
|
211
|
+
@submit=${f.onSubmit(async (v) => {
|
|
212
|
+
await api.save(v);
|
|
213
|
+
f.reset();
|
|
214
|
+
})}
|
|
215
|
+
>
|
|
216
|
+
<input
|
|
217
|
+
name="email"
|
|
218
|
+
.value=${() => f.values().email ?? ""}
|
|
219
|
+
@input=${f.onInput}
|
|
220
|
+
@blur=${f.onBlur}
|
|
221
|
+
/>
|
|
222
|
+
|
|
223
|
+
${() =>
|
|
224
|
+
f.errors().email && f.touched().email
|
|
225
|
+
? html`<small>${f.errors().email}</small>`
|
|
226
|
+
: null}
|
|
227
|
+
|
|
214
228
|
<button ?disabled=${() => !f.isValid() || f.submitting()}>Save</button>
|
|
215
229
|
</form>
|
|
216
230
|
`;
|
|
@@ -233,12 +247,12 @@ const ApiCtx = createContext<ApiClient>(defaultApiClient);
|
|
|
233
247
|
// in the root component — provide
|
|
234
248
|
component("x-app", ({ host }) => {
|
|
235
249
|
provide(host, ApiCtx, new ApiClient("https://api.example.com"));
|
|
236
|
-
return () => html`<x-page/>`;
|
|
250
|
+
return () => html`<x-page />`;
|
|
237
251
|
});
|
|
238
252
|
|
|
239
253
|
// in any child — consume
|
|
240
254
|
component("x-page", ({ host }) => {
|
|
241
|
-
const api = inject(host, ApiCtx);
|
|
255
|
+
const api = inject(host, ApiCtx); // signal<ApiClient>
|
|
242
256
|
return () => html`<div>API version: ${() => api().version}</div>`;
|
|
243
257
|
});
|
|
244
258
|
```
|
|
@@ -255,7 +269,7 @@ If you're used to server-side rendering for SEO, in Mado this is solved differen
|
|
|
255
269
|
// src/pages/product.ts
|
|
256
270
|
export default page({
|
|
257
271
|
bake: {
|
|
258
|
-
paths: () => api.allProductSlugs(),
|
|
272
|
+
paths: () => api.allProductSlugs(), // build-time fetch
|
|
259
273
|
data: ({ slug }) => api.getProduct(slug),
|
|
260
274
|
revalidate: 3600,
|
|
261
275
|
},
|
|
@@ -264,7 +278,7 @@ export default page({
|
|
|
264
278
|
canonical: `/product/${slug}`,
|
|
265
279
|
og: { title: data.name, image: data.image },
|
|
266
280
|
}),
|
|
267
|
-
view: ({ params }) => html`<x-product data-slug=${params.slug}/>`,
|
|
281
|
+
view: ({ params }) => html`<x-product data-slug=${params.slug} />`,
|
|
268
282
|
});
|
|
269
283
|
```
|
|
270
284
|
|
|
@@ -288,14 +302,20 @@ import { page, html, resource, each, signal } from "@madojs/mado";
|
|
|
288
302
|
export default page({
|
|
289
303
|
view: () => {
|
|
290
304
|
const users = resource(() => "/api/users", jsonFetcher<User[]>());
|
|
291
|
-
|
|
305
|
+
|
|
292
306
|
return html`
|
|
293
|
-
${() => users.loading() ? html`<p>Loading…</p>` : null}
|
|
294
|
-
${() =>
|
|
307
|
+
${() => (users.loading() ? html`<p>Loading…</p>` : null)}
|
|
308
|
+
${() =>
|
|
309
|
+
users.error() ? html`<p>Error: ${users.error()!.message}</p>` : null}
|
|
295
310
|
<ul>
|
|
296
|
-
${() =>
|
|
297
|
-
|
|
298
|
-
|
|
311
|
+
${() =>
|
|
312
|
+
each(
|
|
313
|
+
users.data() ?? [],
|
|
314
|
+
(u) => u.id,
|
|
315
|
+
(u) => html`
|
|
316
|
+
<li><a href="/users/${u.id}" data-link>${u.name}</a></li>
|
|
317
|
+
`,
|
|
318
|
+
)}
|
|
299
319
|
</ul>
|
|
300
320
|
`;
|
|
301
321
|
},
|
|
@@ -308,7 +328,10 @@ export default page({
|
|
|
308
328
|
import { useForm, mutation } from "@madojs/mado";
|
|
309
329
|
|
|
310
330
|
const createUser = mutation<NewUser, User>(
|
|
311
|
-
(u) =>
|
|
331
|
+
(u) =>
|
|
332
|
+
fetch("/api/users", { method: "POST", body: JSON.stringify(u) }).then((r) =>
|
|
333
|
+
r.json(),
|
|
334
|
+
),
|
|
312
335
|
{ invalidates: ["/api/users*"] },
|
|
313
336
|
);
|
|
314
337
|
|
|
@@ -316,11 +339,13 @@ const createUser = mutation<NewUser, User>(
|
|
|
316
339
|
const f = useForm({ name: { required: true } });
|
|
317
340
|
|
|
318
341
|
html`
|
|
319
|
-
<form
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
342
|
+
<form
|
|
343
|
+
@submit=${f.onSubmit(async (v) => {
|
|
344
|
+
await createUser.run(v);
|
|
345
|
+
navigate("/users");
|
|
346
|
+
})}
|
|
347
|
+
>
|
|
348
|
+
<input name="name" @input=${f.onInput} />
|
|
324
349
|
<button>Create</button>
|
|
325
350
|
</form>
|
|
326
351
|
`;
|
|
@@ -353,8 +378,8 @@ export default routes({
|
|
|
353
378
|
"/app/*": nested({
|
|
354
379
|
layout: () => import("./layouts/auth-layout.js"),
|
|
355
380
|
routes: {
|
|
356
|
-
|
|
357
|
-
|
|
381
|
+
dashboard: () => import("./pages/dashboard.js"),
|
|
382
|
+
users: () => import("./pages/users.js"),
|
|
358
383
|
},
|
|
359
384
|
}),
|
|
360
385
|
});
|
|
@@ -367,7 +392,7 @@ export default routes({
|
|
|
367
392
|
export class ApiClient {
|
|
368
393
|
constructor(private base: string) {}
|
|
369
394
|
get<T>(path: string): Promise<T> {
|
|
370
|
-
return fetch(this.base + path).then(r => r.json());
|
|
395
|
+
return fetch(this.base + path).then((r) => r.json());
|
|
371
396
|
}
|
|
372
397
|
}
|
|
373
398
|
|