@madojs/mado 0.12.0 → 0.13.1

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.
Files changed (154) hide show
  1. package/AGENTS.md +15 -8
  2. package/CHANGELOG.md +66 -0
  3. package/README.md +25 -3
  4. package/dist/src/component.d.ts +3 -1
  5. package/dist/src/component.d.ts.map +1 -0
  6. package/dist/src/component.js +65 -30
  7. package/dist/src/component.js.map +1 -1
  8. package/dist/src/context.d.ts +13 -32
  9. package/dist/src/context.d.ts.map +1 -0
  10. package/dist/src/context.js +73 -48
  11. package/dist/src/context.js.map +1 -1
  12. package/dist/src/css.d.ts +1 -0
  13. package/dist/src/css.d.ts.map +1 -0
  14. package/dist/src/css.js.map +1 -1
  15. package/dist/src/devtools-hook.d.ts +14 -0
  16. package/dist/src/devtools-hook.d.ts.map +1 -0
  17. package/dist/src/devtools-hook.js +27 -0
  18. package/dist/src/devtools-hook.js.map +1 -0
  19. package/dist/src/devtools.d.ts +24 -22
  20. package/dist/src/devtools.d.ts.map +1 -0
  21. package/dist/src/devtools.js +230 -58
  22. package/dist/src/devtools.js.map +1 -1
  23. package/dist/src/diagnostics.d.ts +12 -0
  24. package/dist/src/diagnostics.d.ts.map +1 -0
  25. package/dist/src/diagnostics.js +68 -8
  26. package/dist/src/diagnostics.js.map +1 -1
  27. package/dist/src/each.d.ts +1 -5
  28. package/dist/src/each.d.ts.map +1 -0
  29. package/dist/src/each.js +0 -8
  30. package/dist/src/each.js.map +1 -1
  31. package/dist/src/forms.d.ts +27 -96
  32. package/dist/src/forms.d.ts.map +1 -0
  33. package/dist/src/forms.js +184 -394
  34. package/dist/src/forms.js.map +1 -1
  35. package/dist/src/head.d.ts +1 -0
  36. package/dist/src/head.d.ts.map +1 -0
  37. package/dist/src/head.js +2 -1
  38. package/dist/src/head.js.map +1 -1
  39. package/dist/src/html/bindings.d.ts +1 -0
  40. package/dist/src/html/bindings.d.ts.map +1 -0
  41. package/dist/src/html/bindings.js.map +1 -1
  42. package/dist/src/html/parser.d.ts +3 -4
  43. package/dist/src/html/parser.d.ts.map +1 -0
  44. package/dist/src/html/parser.js +63 -14
  45. package/dist/src/html/parser.js.map +1 -1
  46. package/dist/src/html/template-types.d.ts +1 -0
  47. package/dist/src/html/template-types.d.ts.map +1 -0
  48. package/dist/src/html/template-types.js.map +1 -1
  49. package/dist/src/html/template.d.ts +5 -1
  50. package/dist/src/html/template.d.ts.map +1 -0
  51. package/dist/src/html/template.js +78 -3
  52. package/dist/src/html/template.js.map +1 -1
  53. package/dist/src/index.d.ts +7 -8
  54. package/dist/src/index.d.ts.map +1 -0
  55. package/dist/src/index.js +5 -7
  56. package/dist/src/index.js.map +1 -1
  57. package/dist/src/json.d.ts +3 -0
  58. package/dist/src/json.d.ts.map +1 -0
  59. package/dist/src/json.js +14 -0
  60. package/dist/src/json.js.map +1 -0
  61. package/dist/src/lifecycle.d.ts +1 -0
  62. package/dist/src/lifecycle.d.ts.map +1 -0
  63. package/dist/src/lifecycle.js +3 -2
  64. package/dist/src/lifecycle.js.map +1 -1
  65. package/dist/src/page.d.ts +1 -0
  66. package/dist/src/page.d.ts.map +1 -0
  67. package/dist/src/page.js.map +1 -1
  68. package/dist/src/persisted.d.ts +6 -1
  69. package/dist/src/persisted.d.ts.map +1 -0
  70. package/dist/src/persisted.js +17 -11
  71. package/dist/src/persisted.js.map +1 -1
  72. package/dist/src/resource.d.ts +2 -1
  73. package/dist/src/resource.d.ts.map +1 -0
  74. package/dist/src/resource.js +126 -49
  75. package/dist/src/resource.js.map +1 -1
  76. package/dist/src/router/base.d.ts +1 -0
  77. package/dist/src/router/base.d.ts.map +1 -0
  78. package/dist/src/router/base.js.map +1 -1
  79. package/dist/src/router/manifest.d.ts +1 -0
  80. package/dist/src/router/manifest.d.ts.map +1 -0
  81. package/dist/src/router/manifest.js +89 -148
  82. package/dist/src/router/manifest.js.map +1 -1
  83. package/dist/src/router/match.d.ts +1 -0
  84. package/dist/src/router/match.d.ts.map +1 -0
  85. package/dist/src/router/match.js +19 -5
  86. package/dist/src/router/match.js.map +1 -1
  87. package/dist/src/router/navigation.d.ts +1 -0
  88. package/dist/src/router/navigation.d.ts.map +1 -0
  89. package/dist/src/router/navigation.js.map +1 -1
  90. package/dist/src/signal.d.ts +1 -0
  91. package/dist/src/signal.d.ts.map +1 -0
  92. package/dist/src/signal.js +46 -17
  93. package/dist/src/signal.js.map +1 -1
  94. package/dist/src/static-runtime.d.ts +1 -0
  95. package/dist/src/static-runtime.d.ts.map +1 -0
  96. package/dist/src/static-runtime.js.map +1 -1
  97. package/dist/src/vite/index.d.ts +1 -0
  98. package/dist/src/vite/index.d.ts.map +1 -0
  99. package/dist/src/vite/index.js +4 -3
  100. package/dist/src/vite/index.js.map +1 -1
  101. package/docs/architecture/v1-roadmap.md +19 -0
  102. package/docs/en/10-pages-and-components.md +9 -1
  103. package/docs/en/13-data.md +8 -6
  104. package/docs/en/14-forms.md +66 -219
  105. package/docs/en/15-static-snapshots.md +4 -4
  106. package/docs/en/16-app-architecture.md +3 -2
  107. package/docs/en/20-deployment.md +21 -8
  108. package/docs/en/21-error-handling.md +9 -6
  109. package/docs/en/24-devtools-and-diagnostics.md +45 -0
  110. package/docs/en/30-api-freeze-map.md +8 -9
  111. package/docs/en/32-v1-stability.md +2 -2
  112. package/docs/en/33-migration-0.12-0.13.md +56 -0
  113. package/docs/en/40-llm-guide.md +7 -9
  114. package/docs/en/41-for-backenders.md +13 -9
  115. package/docs/en/42-why-mado.md +2 -2
  116. package/docs/en/README.md +2 -0
  117. package/docs/recipes/nginx/nginx.conf +2 -2
  118. package/llms.txt +25 -7
  119. package/package.json +37 -16
  120. package/scripts/clean.mjs +8 -0
  121. package/scripts/cli/generate.mjs +11 -14
  122. package/scripts/cli/help.mjs +2 -0
  123. package/scripts/cli/index.mjs +14 -6
  124. package/scripts/cli/init.mjs +13 -18
  125. package/scripts/cli/release.mjs +22 -19
  126. package/scripts/cli/run.mjs +5 -3
  127. package/scripts/logger.mjs +117 -0
  128. package/scripts/output-guard.mjs +128 -0
  129. package/scripts/preview.mjs +11 -19
  130. package/scripts/static/browser.mjs +58 -8
  131. package/scripts/static/discover.mjs +4 -1
  132. package/scripts/static.mjs +21 -7
  133. package/starters/default/package.json +4 -4
  134. package/starters/default/tsconfig.node.json +13 -0
  135. package/starters/default/vite.config.ts +0 -3
  136. package/starters/modular/README.md +4 -4
  137. package/starters/modular/package.json +4 -4
  138. package/starters/modular/src/layouts/app-shell.layout.ts +1 -2
  139. package/starters/modular/src/modules/auth/login.page.ts +5 -12
  140. package/starters/modular/src/modules/billing/pages/invoice-detail.page.ts +3 -4
  141. package/starters/modular/src/modules/billing/pages/invoices-list.page.ts +1 -1
  142. package/starters/modular/src/shared/styles/reset.css +21 -1
  143. package/starters/modular/tsconfig.json +10 -1
  144. package/starters/modular/tsconfig.node.json +13 -0
  145. package/starters/modular/vite.config.ts +6 -5
  146. package/dist/src/lazy.d.ts +0 -38
  147. package/dist/src/lazy.js +0 -73
  148. package/dist/src/lazy.js.map +0 -1
  149. package/scripts/bake.mjs +0 -5
  150. package/scripts/docs-lint.mjs +0 -170
  151. package/scripts/package-smoke.mjs +0 -135
  152. package/scripts/release-notes.mjs +0 -66
  153. package/scripts/size-budget.mjs +0 -95
  154. package/starters/modular/src/shared/ui/x-button.component.ts +0 -49
@@ -12,7 +12,6 @@
12
12
  import { html, page, routeUrl } from "@madojs/mado";
13
13
 
14
14
  import { isAuthed, logout, user } from "../modules/auth/auth.public";
15
- import "../shared/ui/x-button.component";
16
15
 
17
16
  export default page({
18
17
  title: "Mado App",
@@ -28,7 +27,7 @@ export default page({
28
27
  ${() =>
29
28
  isAuthed()
30
29
  ? html`<span class="who">${() => user()?.email ?? ""}</span>
31
- <x-button variant="ghost" @click=${logout}>Sign out</x-button>`
30
+ <button class="button button--ghost" type="button" @click=${logout}>Sign out</button>`
32
31
  : html`<a data-link href=${routeUrl("/login")}>Sign in</a>`}
33
32
  </nav>
34
33
  </header>
@@ -1,7 +1,5 @@
1
1
  import { html, navigate, page, signal, useForm } from "@madojs/mado";
2
2
 
3
- import "../../shared/ui/x-button.component";
4
-
5
3
  import { login } from "./auth.service";
6
4
 
7
5
  // 1. LOCAL STATE
@@ -16,15 +14,12 @@ import { login } from "./auth.service";
16
14
  export default page({
17
15
  title: "Sign in",
18
16
  view: () => {
19
- const submitting = signal(false);
20
17
  const error = signal<string | null>(null);
21
18
  const form = useForm({
22
- email: { required: true, type: "email" },
23
- password: { required: true, minLength: 6 },
19
+ initial: { email: "", password: "" },
24
20
  });
25
21
 
26
22
  const onSubmit = form.onSubmit(async (values) => {
27
- submitting.set(true);
28
23
  error.set(null);
29
24
  try {
30
25
  await login({
@@ -34,8 +29,6 @@ export default page({
34
29
  navigate("/");
35
30
  } catch (err) {
36
31
  error.set(err instanceof Error ? err.message : "Login failed");
37
- } finally {
38
- submitting.set(false);
39
32
  }
40
33
  });
41
34
 
@@ -49,12 +42,12 @@ export default page({
49
42
  </label>
50
43
  <label>
51
44
  Password
52
- <input name="password" type="password" required @input=${form.onInput} />
45
+ <input name="password" type="password" required minlength="6" @input=${form.onInput} />
53
46
  </label>
54
47
  ${() => (error() ? html`<p class="error">${error()}</p>` : null)}
55
- <x-button ?disabled=${() => submitting() || !form.isValid()}>
56
- ${() => (submitting() ? "Signing in…" : "Sign in")}
57
- </x-button>
48
+ <button class="button" type="submit" ?disabled=${() => form.submitting() || !form.isValid()}>
49
+ ${() => (form.submitting() ? "Signing in…" : "Sign in")}
50
+ </button>
58
51
  </form>
59
52
  </section>
60
53
  `;
@@ -1,8 +1,7 @@
1
1
  import { html, page, signal } from "@madojs/mado";
2
2
 
3
3
  import { formatDate, formatMoney } from "../../../shared/lib/format-date";
4
- import "../../../shared/ui/x-button.component";
5
- import "../../../shared/ui/x-spinner.component";
4
+ import "@/shared/ui/x-spinner.component";
6
5
 
7
6
  import { hasPermission } from "../../auth/auth.public";
8
7
 
@@ -58,9 +57,9 @@ export default page({
58
57
  <dt>Issued</dt><dd>${formatDate(i.issuedAt)}</dd>
59
58
  </dl>
60
59
  ${i.status === "pending" && hasPermission("billing.invoices.pay")
61
- ? html`<x-button ?disabled=${paying} @click=${onPay}>
60
+ ? html`<button class="button" type="button" ?disabled=${paying} @click=${onPay}>
62
61
  ${() => (paying() ? "Paying…" : "Pay now")}
63
- </x-button>`
62
+ </button>`
64
63
  : null}
65
64
  `;
66
65
  })()}
@@ -1,7 +1,7 @@
1
1
  import { each, html, page, routeUrl, signal, untracked } from "@madojs/mado";
2
2
 
3
3
  import { formatDate, formatMoney } from "../../../shared/lib/format-date";
4
- import "../../../shared/ui/x-spinner.component";
4
+ import "@/shared/ui/x-spinner.component";
5
5
 
6
6
  import "../components/invoice-status-badge.component";
7
7
  import { useInvoices } from "../data/invoices.resource";
@@ -29,4 +29,24 @@ a:hover {
29
29
  button {
30
30
  font-family: inherit;
31
31
  font-size: inherit;
32
- }
32
+ }
33
+
34
+ .button {
35
+ padding: var(--space-2) var(--space-4);
36
+ border: 1px solid transparent;
37
+ border-radius: var(--radius-md);
38
+ background: var(--color-primary);
39
+ color: var(--color-primary-contrast);
40
+ cursor: pointer;
41
+ }
42
+
43
+ .button--ghost {
44
+ border-color: var(--color-border);
45
+ background: transparent;
46
+ color: var(--color-text);
47
+ }
48
+
49
+ .button:disabled {
50
+ cursor: not-allowed;
51
+ opacity: 0.55;
52
+ }
@@ -1,9 +1,18 @@
1
1
  {
2
2
  "compilerOptions": {
3
+ "paths": {
4
+ "@/*": [
5
+ "./src/*"
6
+ ]
7
+ },
3
8
  "target": "ES2022",
4
9
  "module": "ES2022",
5
10
  "moduleResolution": "Bundler",
6
- "lib": ["ES2022", "DOM", "DOM.Iterable"],
11
+ "lib": [
12
+ "ES2022",
13
+ "DOM",
14
+ "DOM.Iterable"
15
+ ],
7
16
  "strict": true,
8
17
  "noImplicitOverride": true,
9
18
  "noUncheckedIndexedAccess": true,
@@ -0,0 +1,13 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "NodeNext",
5
+ "moduleResolution": "NodeNext",
6
+ "types": ["node"],
7
+ "strict": true,
8
+ "noUncheckedIndexedAccess": true,
9
+ "skipLibCheck": true,
10
+ "noEmit": true
11
+ },
12
+ "include": ["vite.config.ts"]
13
+ }
@@ -16,12 +16,12 @@ function devApiMock(): Plugin {
16
16
  [
17
17
  "demo@mado.dev",
18
18
  {
19
- password: "demo",
19
+ password: "demo123",
20
20
  user: {
21
21
  id: "u_demo",
22
22
  email: "demo@mado.dev",
23
23
  roles: ["user"],
24
- permissions: ["billing.read"],
24
+ permissions: ["billing.read", "billing.invoices.pay"],
25
25
  },
26
26
  },
27
27
  ],
@@ -76,6 +76,7 @@ function devApiMock(): Plugin {
76
76
  if (url === "/api/auth/me" && req.method === "GET") {
77
77
  if (!token) return json(res, 401, { error: "unauthenticated" });
78
78
  const entry = users.values().next().value;
79
+ if (!entry) return json(res, 500, { error: "mock_user_missing" });
79
80
  return json(res, 200, entry.user);
80
81
  }
81
82
  if (url === "/api/auth/logout" && req.method === "POST") {
@@ -110,6 +111,9 @@ function devApiMock(): Plugin {
110
111
  }
111
112
 
112
113
  export default defineConfig({
114
+ resolve: {
115
+ tsconfigPaths: true,
116
+ },
113
117
  plugins: [
114
118
  devApiMock(),
115
119
  mado({
@@ -125,7 +129,4 @@ export default defineConfig({
125
129
  // site: "https://your-app.example",
126
130
  }),
127
131
  ],
128
- css: {
129
- transformer: "lightningcss",
130
- },
131
132
  });
@@ -1,38 +0,0 @@
1
- /**
2
- * lazy() — deferred module loading with a reactive placeholder.
3
- *
4
- * Why: heavy components (charts, code editors, modals)
5
- * don't need to be loaded on startup. lazy() gives a TemplateResult function
6
- * that returns a fallback until loaded, then the real template.
7
- *
8
- * const Chart = lazy(
9
- * () => import('./components/chart.js'),
10
- * { fallback: () => html`<x-spinner/>` },
11
- * );
12
- *
13
- * html`<div>${Chart({ data })}</div>`
14
- *
15
- * Module contract: must default-export a function
16
- * (props) => TemplateResult.
17
- *
18
- * Loading is cached (one import() per loader reference).
19
- */
20
- import type { TemplateResult } from "./html/template-types.js";
21
- export interface LazyOptions<P> {
22
- /** What to show while the module is loading. */
23
- fallback?: (props: P) => TemplateResult;
24
- /** What to show if the import failed. Default — empty string. */
25
- error?: (err: Error, props: P) => TemplateResult;
26
- }
27
- type Renderer<P> = (props: P) => TemplateResult;
28
- /**
29
- * Create a lazy component.
30
- * Takes a dynamic import, returns a render function.
31
- *
32
- * const Modal = lazy(() => import('./modal.js'));
33
- * html`${open() ? Modal({ onClose }) : null}`
34
- */
35
- export declare function lazy<P = unknown>(loader: () => Promise<{
36
- default: Renderer<P>;
37
- }>, options?: LazyOptions<P>): (props: P) => TemplateResult;
38
- export {};
package/dist/src/lazy.js DELETED
@@ -1,73 +0,0 @@
1
- /**
2
- * lazy() — deferred module loading with a reactive placeholder.
3
- *
4
- * Why: heavy components (charts, code editors, modals)
5
- * don't need to be loaded on startup. lazy() gives a TemplateResult function
6
- * that returns a fallback until loaded, then the real template.
7
- *
8
- * const Chart = lazy(
9
- * () => import('./components/chart.js'),
10
- * { fallback: () => html`<x-spinner/>` },
11
- * );
12
- *
13
- * html`<div>${Chart({ data })}</div>`
14
- *
15
- * Module contract: must default-export a function
16
- * (props) => TemplateResult.
17
- *
18
- * Loading is cached (one import() per loader reference).
19
- */
20
- import { signal } from "./signal.js";
21
- import { html } from "./html/template.js";
22
- const cache = new WeakMap();
23
- /**
24
- * Create a lazy component.
25
- * Takes a dynamic import, returns a render function.
26
- *
27
- * const Modal = lazy(() => import('./modal.js'));
28
- * html`${open() ? Modal({ onClose }) : null}`
29
- */
30
- export function lazy(loader, options = {}) {
31
- return (props) => {
32
- // One signal trigger per call instance; the html binder will resubscribe
33
- // when the state changes.
34
- const trig = signal(0);
35
- const state = ensureLoaded(loader);
36
- if (state.state === "loading") {
37
- // subscribe: when state transitions to ready/error, trigger trig
38
- const wait = waitFor(loader);
39
- wait.then(() => trig.update((n) => n + 1), () => trig.update((n) => n + 1));
40
- }
41
- return html `${() => {
42
- trig(); // subscription
43
- const s = ensureLoaded(loader);
44
- if (s.state === "ready" && s.renderer)
45
- return s.renderer(props);
46
- if (s.state === "error" && s.err) {
47
- return options.error?.(s.err, props) ?? "";
48
- }
49
- return options.fallback?.(props) ?? "";
50
- }}`;
51
- };
52
- }
53
- function ensureLoaded(loader) {
54
- const existing = cache.get(loader);
55
- if (existing)
56
- return existing;
57
- const fresh = { state: "loading" };
58
- cache.set(loader, fresh);
59
- loader().then((mod) => {
60
- fresh.state = "ready";
61
- fresh.renderer = mod.default;
62
- }, (err) => {
63
- fresh.state = "error";
64
- fresh.err = err instanceof Error ? err : new Error(String(err));
65
- });
66
- return fresh;
67
- }
68
- function waitFor(loader) {
69
- // return the same Promise as ensureLoaded — otherwise we'd create a new one.
70
- // The browser actually caches the same module, so the second call returns instantly.
71
- return loader();
72
- }
73
- //# sourceMappingURL=lazy.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"lazy.js","sourceRoot":"","sources":["../../src/lazy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAkB1C,MAAM,KAAK,GAAG,IAAI,OAAO,EAA8B,CAAC;AAExD;;;;;;GAMG;AACH,MAAM,UAAU,IAAI,CAClB,MAA+C,EAC/C,UAA0B,EAAE;IAE5B,OAAO,CAAC,KAAQ,EAAE,EAAE;QAClB,yEAAyE;QACzE,0BAA0B;QAC1B,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;QACvB,MAAM,KAAK,GAAG,YAAY,CAAI,MAAM,CAAC,CAAC;QAEtC,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;YAC9B,iEAAiE;YACjE,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;YAC7B,IAAI,CAAC,IAAI,CACP,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,EAC/B,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAChC,CAAC;QACJ,CAAC;QAED,OAAO,IAAI,CAAA,GAAG,GAAG,EAAE;YACjB,IAAI,EAAE,CAAC,CAAC,eAAe;YACvB,MAAM,CAAC,GAAG,YAAY,CAAI,MAAM,CAAC,CAAC;YAClC,IAAI,CAAC,CAAC,KAAK,KAAK,OAAO,IAAI,CAAC,CAAC,QAAQ;gBAAE,OAAO,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;YAChE,IAAI,CAAC,CAAC,KAAK,KAAK,OAAO,IAAI,CAAC,CAAC,GAAG,EAAE,CAAC;gBACjC,OAAO,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC;YAC7C,CAAC;YACD,OAAO,OAAO,CAAC,QAAQ,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;QACzC,CAAC,EAAE,CAAC;IACN,CAAC,CAAC;AACJ,CAAC;AAED,SAAS,YAAY,CACnB,MAA+C;IAE/C,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,MAA2B,CAEzC,CAAC;IACd,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAC9B,MAAM,KAAK,GAAiB,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;IACjD,KAAK,CAAC,GAAG,CAAC,MAA2B,EAAE,KAA2B,CAAC,CAAC;IACpE,MAAM,EAAE,CAAC,IAAI,CACX,CAAC,GAAG,EAAE,EAAE;QACN,KAAK,CAAC,KAAK,GAAG,OAAO,CAAC;QACtB,KAAK,CAAC,QAAQ,GAAG,GAAG,CAAC,OAAO,CAAC;IAC/B,CAAC,EACD,CAAC,GAAY,EAAE,EAAE;QACf,KAAK,CAAC,KAAK,GAAG,OAAO,CAAC;QACtB,KAAK,CAAC,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;IAClE,CAAC,CACF,CAAC;IACF,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,OAAO,CACd,MAA+C;IAE/C,6EAA6E;IAC7E,qFAAqF;IACrF,OAAO,MAAM,EAAE,CAAC;AAClB,CAAC"}
package/scripts/bake.mjs DELETED
@@ -1,5 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- console.error("[mado] `mado bake` was removed.");
4
- console.error("Use `mado static`, or run the complete pipeline with `mado release`.");
5
- process.exit(1);
@@ -1,170 +0,0 @@
1
- #!/usr/bin/env node
2
- // Docs lint: refuses to ship documentation that still uses the legacy
3
- // vocabulary the 0.12 release replaced. Active reference docs, README,
4
- // llms.txt, AGENTS.md, CONTRIBUTING.md, TODO.md and the starter READMEs
5
- // must use the new terms; migration guides, CHANGELOG, release notes
6
- // and ADRs are allowed to mention the old names by name so users can
7
- // find them.
8
-
9
- import { readdirSync, readFileSync, statSync } from "node:fs";
10
- import { dirname, join, resolve } from "node:path";
11
- import { fileURLToPath } from "node:url";
12
-
13
- const __dirname = dirname(fileURLToPath(import.meta.url));
14
- const REPO_ROOT = resolve(__dirname, "..");
15
-
16
- // Phrases that MUST disappear from current docs/README/llms/AGENTS. They
17
- // remain legal inside the allow-listed contexts below.
18
- const FORBIDDEN = [
19
- // Page API renamed from `bake` to `static`.
20
- { pattern: /\bpage\.bake\b/, replacement: "page.static" },
21
- { pattern: /\bbake\.paths\b/, replacement: "static.paths" },
22
- { pattern: /\bbake\.data\b/, replacement: "static.initialData" },
23
- { pattern: /\bbake\.revalidate\b/, replacement: "drop; no revalidate API" },
24
-
25
- // CLI renamed from `bake` to `static`.
26
- { pattern: /\bmado bake\b/, replacement: "mado static" },
27
- { pattern: /\bnpm run bake\b/, replacement: "npm run release" },
28
-
29
- // Output / marker shape changed.
30
- { pattern: /\bout\/baked\b/, replacement: "out/<route>/index.html" },
31
- { pattern: /#bake\b/, replacement: "data-mado-static-data" },
32
-
33
- // Old "Smart Static (`bake`)" headline.
34
- { pattern: /Smart Static \(`bake`\)/, replacement: "Static snapshots (`mado static`)" },
35
-
36
- // Old transport / renderer claims.
37
- { pattern: /No Vite required/i, replacement: "drop the phrase; Vite is the canonical transport" },
38
- { pattern: /\bNo Chromium needed\b/i, replacement: "drop the phrase; mado static REQUIRES Chromium" },
39
- // `linkedom` is also a legitimate Node-side test helper (the framework
40
- // uses it in test/router/* for DOM unit tests). We only flag the
41
- // phrases that mis-described the snapshot pipeline as a linkedom
42
- // renderer.
43
- { pattern: /linkedom (?:as|is) (?:the |a )?(?:static|snapshot|production) renderer/i,
44
- replacement: "drop the claim; capture uses Playwright/Chromium" },
45
- { pattern: /linkedom-?based renderer/i,
46
- replacement: "drop the claim; capture uses Playwright/Chromium" },
47
- { pattern: /server-rendered snapshot/i, replacement: "browser-rendered snapshot" },
48
- { pattern: /\bmeta[- ]shell\b/i, replacement: "static snapshot" },
49
-
50
- // Old positioning.
51
- {
52
- pattern: /SEO-heavy public sites are not supported/i,
53
- replacement: "drop the phrase — Mado now snapshots public sites",
54
- },
55
- {
56
- pattern: /\binternal tools only\b/i,
57
- replacement: "drop the phrase — Mado is for sites and apps",
58
- },
59
- {
60
- pattern: /SPA framework for internal tools/i,
61
- replacement: "drop the phrase — Mado is for sites and apps",
62
- },
63
- {
64
- pattern: /shadow:\s*false[^.\n]*SEO/i,
65
- replacement: "drop the recommendation; SEO is handled by snapshots",
66
- },
67
-
68
- // Old pre-Vite transport language.
69
- { pattern: /\btsc[- ]only\b/i, replacement: "drop the phrase — Vite is the canonical transport" },
70
- { pattern: /\bno bundler\b/i, replacement: "drop the phrase — Vite is the canonical bundler" },
71
- { pattern: /\btsc\s*(?:→|->)+\s*browser\b/i,
72
- replacement: "drop the phrase — generated apps go through Vite" },
73
- { pattern: /\bimport[\s-]?map(?:s)?\b/i, replacement: "drop the phrase — apps use Vite, not import maps" },
74
-
75
- // Old dev / static / edge claims.
76
- { pattern: /SSE[- ]?reload/i, replacement: "drop the phrase — dev uses Vite HMR" },
77
- { pattern: /\bedge[- ]prerender\b/i, replacement: "drop the phrase — capture is `mado static` only" },
78
-
79
- // Old doc filenames in active docs (the migration table in
80
- // docs/en/README.md is allow-listed via docs-lint:allow-legacy-mention
81
- // markers around it if needed).
82
- { pattern: /\b03-static-bake\.md\b/, replacement: "15-static-snapshots.md" },
83
- { pattern: /\b09-shadow-vs-light-dom\.md\b/, replacement: "10-pages-and-components.md" },
84
- { pattern: /\b10-app-architecture\.md\b/, replacement: "16-app-architecture.md" },
85
- { pattern: /\b13-deployment\.md\b/, replacement: "20-deployment.md" },
86
- { pattern: /\b16-bake-cookbook\.md\b/, replacement: "23-cookbook.md" },
87
- { pattern: /\b18-api-freeze-map\.md\b/, replacement: "30-api-freeze-map.md" },
88
- { pattern: /\b19-reactivity-ordering\.md\b/, replacement: "31-reactivity-ordering.md" },
89
- { pattern: /\b20-v1-stability\.md\b/, replacement: "32-v1-stability.md" },
90
- { pattern: /\b07-llm-pitfalls\.md\b/, replacement: "40-llm-guide.md" },
91
- { pattern: /\b08-llm-zero-history-test\.md\b/, replacement: "40-llm-guide.md" },
92
- ];
93
-
94
- const ALLOW_LISTED = [
95
- /(^|\/)CHANGELOG\.md$/,
96
- /(^|\/)docs\/architecture\/adr\//,
97
- /(^|\/)docs\/.+migration/i,
98
- /(^|\/)docs\/.+v1-stability/i,
99
- /(^|\/)scripts\/docs-lint\.mjs$/,
100
- ];
101
-
102
- // Files / trees that must stay on-message about the post-0.12 API. New
103
- // roots must be added here AND covered by the CI step.
104
- const ROOTS = [
105
- "README.md",
106
- "AGENTS.md",
107
- "llms.txt",
108
- "CONTRIBUTING.md",
109
- "TODO.md",
110
- "docs/README.md",
111
- "docs/en",
112
- "starters/default/README.md",
113
- "starters/modular/README.md",
114
- ];
115
-
116
- let errors = 0;
117
-
118
- for (const root of ROOTS) {
119
- const full = join(REPO_ROOT, root);
120
- try {
121
- statSync(full);
122
- } catch {
123
- continue;
124
- }
125
- scan(full);
126
- }
127
-
128
- if (errors > 0) {
129
- console.error(`\n[docs-lint] ${errors} forbidden term(s) in current docs.`);
130
- process.exit(1);
131
- }
132
- console.log("[docs-lint] OK");
133
-
134
- function scan(path) {
135
- const stat = statSync(path);
136
- if (stat.isDirectory()) {
137
- for (const entry of readdirSync(path)) scan(join(path, entry));
138
- return;
139
- }
140
- if (!path.endsWith(".md") && !path.endsWith(".txt")) return;
141
- const rel = path.slice(REPO_ROOT.length + 1);
142
- if (ALLOW_LISTED.some((rx) => rx.test(rel))) return;
143
-
144
- const body = readFileSync(path, "utf8");
145
- const lines = body.split("\n");
146
- let inIgnoreBlock = false;
147
- for (let i = 0; i < lines.length; i++) {
148
- const line = lines[i];
149
- // Block-scoped allowance: paragraphs that teach LLMs which names
150
- // are obsolete must be free to mention those names verbatim.
151
- // <!-- docs-lint:allow-legacy-mention -->
152
- // ...legacy terms...
153
- // <!-- /docs-lint:allow-legacy-mention -->
154
- if (line.includes("docs-lint:allow-legacy-mention")) {
155
- inIgnoreBlock = !line.includes("/docs-lint:allow-legacy-mention");
156
- continue;
157
- }
158
- if (inIgnoreBlock) continue;
159
-
160
- for (const rule of FORBIDDEN) {
161
- if (rule.pattern.test(line)) {
162
- errors++;
163
- console.error(
164
- `${rel}:${i + 1}: forbidden term matches /${rule.pattern.source}/. ` +
165
- `Replace with: ${rule.replacement}`,
166
- );
167
- }
168
- }
169
- }
170
- }
@@ -1,135 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import { execFile } from "node:child_process";
4
- import { mkdir, mkdtemp, rm } from "node:fs/promises";
5
- import { tmpdir } from "node:os";
6
- import { basename, join, resolve } from "node:path";
7
- import { fileURLToPath } from "node:url";
8
- import { promisify } from "node:util";
9
-
10
- const exec = promisify(execFile);
11
- const repoRoot = resolve(fileURLToPath(new URL("..", import.meta.url)));
12
- const tempRoot = await mkdtemp(join(tmpdir(), "mado-package-smoke-"));
13
- let tarball = "";
14
-
15
- try {
16
- const packed = await exec("npm", ["pack", "--silent"], { cwd: repoRoot });
17
- tarball = resolve(repoRoot, packed.stdout.trim().split(/\s+/).at(-1) ?? "");
18
- if (!tarball) throw new Error("[package-smoke] npm pack did not return a tarball");
19
-
20
- const installRoot = join(tempRoot, "installed");
21
- await mkdir(installRoot, { recursive: true });
22
- await run("npm", ["install", tarball], { cwd: installRoot });
23
-
24
- // Public-API surface smoke: prove the published tarball exposes the
25
- // root entry, the devtools side-effect import and the Vite plugin,
26
- // while still hiding internal subpaths behind `exports`.
27
- await run(
28
- process.execPath,
29
- [
30
- "--input-type=module",
31
- "--eval",
32
- `
33
- import { html, signal, routeUrl, appBase } from "@madojs/mado";
34
- import "@madojs/mado/devtools.js";
35
- import { mado } from "@madojs/mado/vite";
36
- if (typeof html !== "function" || typeof signal !== "function") {
37
- throw new Error("public root import failed");
38
- }
39
- if (typeof routeUrl !== "function" || typeof appBase !== "string") {
40
- throw new Error("routing helpers missing from public root");
41
- }
42
- if (typeof mado !== "function") throw new Error("vite plugin import failed");
43
- try {
44
- await import("@madojs/mado/lifecycle.js");
45
- throw new Error("internal lifecycle subpath unexpectedly resolved");
46
- } catch (err) {
47
- if (err?.code !== "ERR_PACKAGE_PATH_NOT_EXPORTED") throw err;
48
- }
49
- `,
50
- ],
51
- { cwd: installRoot },
52
- );
53
-
54
- // We exercise BOTH published starters end-to-end:
55
- //
56
- // smoke-universal — `mado init` (default), npm install, typecheck,
57
- // release. This is the path the README quick-start
58
- // points new users to; if it ever breaks for a
59
- // published tarball the first-run experience is
60
- // broken.
61
- //
62
- // smoke-modular — `mado init --starter modular`, npm install,
63
- // `mado new module`, release. Long-lived apps
64
- // deploy this shape; the `mado new` generator
65
- // also lives here.
66
- //
67
- // Each run uses MADO_SITE so static routes resolve canonical URLs,
68
- // and points the starter at the freshly packed tarball through
69
- // MADO_PACKAGE_SPEC so package.json doesn't reference an unpublished
70
- // version.
71
-
72
- await smokeStarter({
73
- label: "universal",
74
- appName: "smoke-universal",
75
- initArgs: ["mado", "init", "smoke-universal"],
76
- after: async (appRoot) => {
77
- await run("npm", ["run", "typecheck"], { cwd: appRoot });
78
- await run("npm", ["run", "release"], {
79
- cwd: appRoot,
80
- env: { ...process.env, MADO_SITE: "https://package-smoke.test" },
81
- });
82
- },
83
- installRoot,
84
- tarball,
85
- });
86
-
87
- await smokeStarter({
88
- label: "modular",
89
- appName: "smoke-modular",
90
- initArgs: ["mado", "init", "smoke-modular", "--starter", "modular"],
91
- after: async (appRoot) => {
92
- await run("npm", ["run", "new", "--", "module", "smoke"], {
93
- cwd: appRoot,
94
- });
95
- await run("npm", ["run", "typecheck"], { cwd: appRoot });
96
- await run("npm", ["run", "release"], {
97
- cwd: appRoot,
98
- env: { ...process.env, MADO_SITE: "https://package-smoke.test" },
99
- });
100
- },
101
- installRoot,
102
- tarball,
103
- });
104
-
105
- console.log(`[package-smoke] ok ${basename(tarball)}`);
106
- } finally {
107
- await rm(tempRoot, { recursive: true, force: true });
108
- if (tarball) await rm(tarball, { force: true });
109
- }
110
-
111
- async function smokeStarter({ label, appName, initArgs, after, installRoot, tarball }) {
112
- console.log(`[package-smoke] === ${label} starter ===`);
113
- await run("npx", initArgs, {
114
- cwd: installRoot,
115
- env: { ...process.env, MADO_PACKAGE_SPEC: tarball },
116
- });
117
- const appRoot = join(installRoot, appName);
118
- await run("npm", ["install"], { cwd: appRoot });
119
- await after(appRoot);
120
- }
121
-
122
- async function run(cmd, args, options) {
123
- console.log(`[package-smoke] ${cmd} ${args.join(" ")}`);
124
- try {
125
- await exec(cmd, args, {
126
- cwd: options.cwd,
127
- env: options.env ?? process.env,
128
- maxBuffer: 20 * 1024 * 1024,
129
- });
130
- } catch (err) {
131
- if (err.stdout) process.stdout.write(err.stdout);
132
- if (err.stderr) process.stderr.write(err.stderr);
133
- throw err;
134
- }
135
- }