@madojs/mado 0.11.1 → 0.13.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 +116 -32
- package/CHANGELOG.md +216 -1
- package/README.md +158 -173
- package/dist/src/component.d.ts +6 -3
- package/dist/src/component.d.ts.map +1 -0
- package/dist/src/component.js +163 -36
- package/dist/src/component.js.map +1 -1
- package/dist/src/context.d.ts +13 -32
- package/dist/src/context.d.ts.map +1 -0
- package/dist/src/context.js +73 -48
- package/dist/src/context.js.map +1 -1
- package/dist/src/css.d.ts +1 -0
- package/dist/src/css.d.ts.map +1 -0
- package/dist/src/css.js +34 -5
- package/dist/src/css.js.map +1 -1
- package/dist/src/devtools-hook.d.ts +14 -0
- package/dist/src/devtools-hook.d.ts.map +1 -0
- package/dist/src/devtools-hook.js +27 -0
- package/dist/src/devtools-hook.js.map +1 -0
- package/dist/src/devtools.d.ts +24 -22
- package/dist/src/devtools.d.ts.map +1 -0
- package/dist/src/devtools.js +230 -58
- package/dist/src/devtools.js.map +1 -1
- package/dist/src/diagnostics.d.ts +12 -0
- package/dist/src/diagnostics.d.ts.map +1 -0
- package/dist/src/diagnostics.js +68 -8
- package/dist/src/diagnostics.js.map +1 -1
- package/dist/src/each.d.ts +1 -5
- package/dist/src/each.d.ts.map +1 -0
- package/dist/src/each.js +0 -8
- package/dist/src/each.js.map +1 -1
- package/dist/src/forms.d.ts +27 -96
- package/dist/src/forms.d.ts.map +1 -0
- package/dist/src/forms.js +184 -394
- package/dist/src/forms.js.map +1 -1
- package/dist/src/head.d.ts +7 -6
- package/dist/src/head.d.ts.map +1 -0
- package/dist/src/head.js +8 -7
- package/dist/src/head.js.map +1 -1
- package/dist/src/html/bindings.d.ts +1 -0
- package/dist/src/html/bindings.d.ts.map +1 -0
- package/dist/src/html/bindings.js.map +1 -1
- package/dist/src/html/parser.d.ts +4 -5
- package/dist/src/html/parser.d.ts.map +1 -0
- package/dist/src/html/parser.js +64 -15
- package/dist/src/html/parser.js.map +1 -1
- package/dist/src/html/template-types.d.ts +1 -0
- package/dist/src/html/template-types.d.ts.map +1 -0
- package/dist/src/html/template-types.js.map +1 -1
- package/dist/src/html/template.d.ts +5 -1
- package/dist/src/html/template.d.ts.map +1 -0
- package/dist/src/html/template.js +102 -13
- package/dist/src/html/template.js.map +1 -1
- package/dist/src/index.d.ts +15 -12
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +18 -10
- package/dist/src/index.js.map +1 -1
- package/dist/src/json.d.ts +3 -0
- package/dist/src/json.d.ts.map +1 -0
- package/dist/src/json.js +14 -0
- package/dist/src/json.js.map +1 -0
- package/dist/src/lifecycle.d.ts +1 -0
- package/dist/src/lifecycle.d.ts.map +1 -0
- package/dist/src/lifecycle.js +3 -2
- package/dist/src/lifecycle.js.map +1 -1
- package/dist/src/page.d.ts +63 -28
- package/dist/src/page.d.ts.map +1 -0
- package/dist/src/page.js +5 -0
- package/dist/src/page.js.map +1 -1
- package/dist/src/persisted.d.ts +6 -1
- package/dist/src/persisted.d.ts.map +1 -0
- package/dist/src/persisted.js +17 -11
- package/dist/src/persisted.js.map +1 -1
- package/dist/src/resource.d.ts +2 -1
- package/dist/src/resource.d.ts.map +1 -0
- package/dist/src/resource.js +128 -50
- package/dist/src/resource.js.map +1 -1
- package/dist/src/router/base.d.ts +66 -0
- package/dist/src/router/base.d.ts.map +1 -0
- package/dist/src/router/base.js +168 -0
- package/dist/src/router/base.js.map +1 -0
- package/dist/src/router/manifest.d.ts +1 -0
- package/dist/src/router/manifest.d.ts.map +1 -0
- package/dist/src/router/manifest.js +156 -171
- package/dist/src/router/manifest.js.map +1 -1
- package/dist/src/router/match.d.ts +17 -2
- package/dist/src/router/match.d.ts.map +1 -0
- package/dist/src/router/match.js +60 -6
- package/dist/src/router/match.js.map +1 -1
- package/dist/src/router/navigation.d.ts +1 -0
- package/dist/src/router/navigation.d.ts.map +1 -0
- package/dist/src/router/navigation.js +58 -9
- package/dist/src/router/navigation.js.map +1 -1
- package/dist/src/signal.d.ts +1 -0
- package/dist/src/signal.d.ts.map +1 -0
- package/dist/src/signal.js +46 -17
- package/dist/src/signal.js.map +1 -1
- package/dist/src/static-runtime.d.ts +82 -0
- package/dist/src/static-runtime.d.ts.map +1 -0
- package/dist/src/static-runtime.js +209 -0
- package/dist/src/static-runtime.js.map +1 -0
- package/dist/src/vite/index.d.ts +32 -3
- package/dist/src/vite/index.d.ts.map +1 -0
- package/dist/src/vite/index.js +49 -17
- package/dist/src/vite/index.js.map +1 -1
- package/docs/README.md +5 -8
- package/docs/architecture/adr/0001-browser-static-snapshots.md +132 -0
- package/docs/architecture/v1-roadmap.md +19 -0
- package/docs/en/00-the-mado-way.md +1 -1
- package/docs/en/01-quickstart.md +183 -0
- package/docs/en/10-pages-and-components.md +279 -0
- package/docs/en/11-templates-and-signals.md +211 -0
- package/docs/en/12-routing.md +229 -0
- package/docs/en/13-data.md +227 -0
- package/docs/en/14-forms.md +91 -0
- package/docs/en/15-static-snapshots.md +181 -0
- package/docs/en/{10-app-architecture.md → 16-app-architecture.md} +56 -4
- package/docs/en/{13-deployment.md → 20-deployment.md} +79 -17
- package/docs/en/{15-error-handling.md → 21-error-handling.md} +9 -6
- package/docs/en/{14-testing.md → 22-testing.md} +6 -5
- package/docs/en/23-cookbook.md +292 -0
- package/docs/en/24-devtools-and-diagnostics.md +45 -0
- package/docs/en/{18-api-freeze-map.md → 30-api-freeze-map.md} +17 -9
- package/docs/en/{20-v1-stability.md → 32-v1-stability.md} +5 -5
- package/docs/en/33-migration-0.12-0.13.md +56 -0
- package/docs/en/40-llm-guide.md +774 -0
- package/docs/en/{06-for-backenders.md → 41-for-backenders.md} +28 -25
- package/docs/en/{05-why-mado.md → 42-why-mado.md} +3 -3
- package/docs/en/README.md +99 -27
- package/docs/recipes/nginx/Containerfile +26 -0
- package/docs/recipes/nginx/nginx.conf +42 -0
- package/llms.txt +263 -210
- package/package.json +51 -23
- package/scripts/clean.mjs +8 -0
- package/scripts/cli/generate.mjs +57 -10
- package/scripts/cli/help.mjs +13 -5
- package/scripts/cli/index.mjs +31 -7
- package/scripts/cli/init.mjs +19 -19
- package/scripts/cli/release.mjs +38 -83
- package/scripts/cli/run.mjs +5 -3
- package/scripts/logger.mjs +117 -0
- package/scripts/output-guard.mjs +128 -0
- package/scripts/preview.mjs +105 -24
- package/scripts/static/browser.mjs +695 -0
- package/scripts/static/discover.mjs +284 -0
- package/scripts/static/output.mjs +141 -0
- package/scripts/static/serialize.mjs +212 -0
- package/scripts/static/server.mjs +167 -0
- package/scripts/static.mjs +205 -0
- package/starters/default/README.md +57 -55
- package/starters/default/package.json +6 -12
- package/starters/default/src/app.routes.ts +20 -33
- package/starters/default/src/components/feature-card.component.ts +40 -0
- package/starters/default/src/components/live-counter.component.ts +57 -0
- package/starters/default/src/content/guides.ts +55 -0
- package/starters/default/src/main.ts +9 -13
- package/starters/default/src/pages/app.page.ts +41 -0
- package/starters/default/src/pages/guide.page.ts +40 -0
- package/starters/default/src/pages/home.page.ts +49 -0
- package/starters/default/src/pages/not-found.page.ts +18 -0
- package/starters/default/src/styles/document.css +39 -0
- package/starters/default/src/styles/reset.css +37 -0
- package/starters/default/src/styles/tokens.css +30 -0
- package/starters/default/src/styles.d.ts +3 -1
- package/starters/default/src/vite-env.d.ts +1 -1
- package/starters/default/tsconfig.node.json +13 -0
- package/starters/default/vite.config.ts +14 -4
- package/starters/modular/README.md +142 -0
- package/starters/modular/index.html +13 -0
- package/starters/modular/package.json +30 -0
- package/starters/modular/src/app.routes.ts +39 -0
- package/starters/{default → modular}/src/layouts/app-shell.layout.ts +9 -7
- package/starters/{default → modular}/src/layouts/auth-shell.layout.ts +3 -3
- package/starters/modular/src/main.ts +16 -0
- package/starters/{default → modular}/src/modules/auth/login.page.ts +5 -12
- package/starters/{default → modular}/src/modules/billing/pages/invoice-detail.page.ts +3 -4
- package/starters/{default → modular}/src/modules/billing/pages/invoices-list.page.ts +4 -4
- package/starters/{default → modular}/src/modules/home/home.page.ts +5 -7
- package/starters/modular/src/modules/home/not-found.page.ts +14 -0
- package/starters/{default → modular}/src/shared/styles/reset.css +21 -1
- package/starters/modular/src/styles.d.ts +1 -0
- package/starters/modular/src/vite-env.d.ts +1 -0
- package/starters/modular/tsconfig.json +33 -0
- package/starters/modular/tsconfig.node.json +13 -0
- package/starters/modular/vite.config.ts +132 -0
- package/TODO.md +0 -79
- package/dist/src/lazy.d.ts +0 -38
- package/dist/src/lazy.js +0 -73
- package/dist/src/lazy.js.map +0 -1
- package/docs/en/01-routing.md +0 -152
- package/docs/en/02-project-layout.md +0 -124
- package/docs/en/03-static-bake.md +0 -249
- package/docs/en/04-ide-setup.md +0 -162
- package/docs/en/07-llm-pitfalls.md +0 -724
- package/docs/en/08-llm-zero-history-test.md +0 -56
- package/docs/en/09-shadow-vs-light-dom.md +0 -174
- package/docs/en/11-layouts.md +0 -113
- package/docs/en/12-auth-and-api.md +0 -124
- package/docs/en/16-bake-cookbook.md +0 -100
- package/docs/en/17-shadow-dom-forms.md +0 -192
- package/docs/fr/00-the-mado-way.md +0 -85
- package/docs/fr/01-routing.md +0 -120
- package/docs/fr/02-project-layout.md +0 -84
- package/docs/fr/03-static-bake.md +0 -289
- package/docs/fr/04-ide-setup.md +0 -162
- package/docs/fr/05-why-mado.md +0 -193
- package/docs/fr/06-for-backenders.md +0 -438
- package/docs/fr/07-llm-pitfalls.md +0 -625
- package/docs/fr/08-llm-zero-history-test.md +0 -38
- package/docs/fr/09-shadow-vs-light-dom.md +0 -65
- package/docs/fr/10-app-architecture.md +0 -138
- package/docs/fr/11-layouts.md +0 -47
- package/docs/fr/12-auth-and-api.md +0 -76
- package/docs/fr/13-deployment.md +0 -57
- package/docs/fr/14-testing.md +0 -41
- package/docs/fr/15-error-handling.md +0 -50
- package/docs/fr/16-bake-cookbook.md +0 -88
- package/docs/fr/17-shadow-dom-forms.md +0 -196
- package/docs/fr/18-api-freeze-map.md +0 -63
- package/docs/fr/19-reactivity-ordering.md +0 -97
- package/docs/fr/20-v1-stability.md +0 -88
- package/docs/fr/README.md +0 -27
- package/docs/ru/00-the-mado-way.md +0 -84
- package/docs/ru/01-routing.md +0 -119
- package/docs/ru/02-project-layout.md +0 -84
- package/docs/ru/03-static-bake.md +0 -250
- package/docs/ru/04-ide-setup.md +0 -144
- package/docs/ru/05-why-mado.md +0 -193
- package/docs/ru/06-for-backenders.md +0 -428
- package/docs/ru/07-llm-pitfalls.md +0 -624
- package/docs/ru/08-llm-zero-history-test.md +0 -57
- package/docs/ru/09-shadow-vs-light-dom.md +0 -63
- package/docs/ru/10-app-architecture.md +0 -152
- package/docs/ru/11-layouts.md +0 -47
- package/docs/ru/12-auth-and-api.md +0 -75
- package/docs/ru/13-deployment.md +0 -66
- package/docs/ru/14-testing.md +0 -50
- package/docs/ru/15-error-handling.md +0 -56
- package/docs/ru/16-bake-cookbook.md +0 -95
- package/docs/ru/17-shadow-dom-forms.md +0 -193
- package/docs/ru/18-api-freeze-map.md +0 -64
- package/docs/ru/19-reactivity-ordering.md +0 -95
- package/docs/ru/20-v1-stability.md +0 -82
- package/docs/ru/README.md +0 -25
- package/docs/uk/00-the-mado-way.md +0 -82
- package/docs/uk/01-routing.md +0 -76
- package/docs/uk/02-project-layout.md +0 -73
- package/docs/uk/03-static-bake.md +0 -48
- package/docs/uk/04-ide-setup.md +0 -26
- package/docs/uk/05-why-mado.md +0 -34
- package/docs/uk/06-for-backenders.md +0 -55
- package/docs/uk/07-llm-pitfalls.md +0 -145
- package/docs/uk/08-llm-zero-history-test.md +0 -34
- package/docs/uk/09-shadow-vs-light-dom.md +0 -58
- package/docs/uk/10-app-architecture.md +0 -97
- package/docs/uk/11-layouts.md +0 -47
- package/docs/uk/12-auth-and-api.md +0 -70
- package/docs/uk/13-deployment.md +0 -40
- package/docs/uk/14-testing.md +0 -34
- package/docs/uk/15-error-handling.md +0 -32
- package/docs/uk/16-bake-cookbook.md +0 -36
- package/docs/uk/17-shadow-dom-forms.md +0 -193
- package/docs/uk/18-api-freeze-map.md +0 -61
- package/docs/uk/19-reactivity-ordering.md +0 -95
- package/docs/uk/20-v1-stability.md +0 -83
- package/docs/uk/README.md +0 -27
- package/scripts/bake.mjs +0 -570
- package/scripts/package-smoke.mjs +0 -77
- package/scripts/release-notes.mjs +0 -66
- package/scripts/size-budget.mjs +0 -91
- package/starters/default/src/modules/home/not-found.page.ts +0 -11
- package/starters/default/src/shared/ui/x-button.component.ts +0 -49
- /package/docs/en/{19-reactivity-ordering.md → 31-reactivity-ordering.md} +0 -0
- /package/starters/{default → modular}/.editorconfig +0 -0
- /package/starters/{default → modular}/eslint.config.mjs +0 -0
- /package/starters/{default → modular}/public/favicon.svg +0 -0
- /package/starters/{default → modular}/src/modules/auth/_contracts/auth-api.types.ts +0 -0
- /package/starters/{default → modular}/src/modules/auth/auth.connector.ts +0 -0
- /package/starters/{default → modular}/src/modules/auth/auth.guard.ts +0 -0
- /package/starters/{default → modular}/src/modules/auth/auth.public.ts +0 -0
- /package/starters/{default → modular}/src/modules/auth/auth.routes.ts +0 -0
- /package/starters/{default → modular}/src/modules/auth/auth.service.ts +0 -0
- /package/starters/{default → modular}/src/modules/auth/auth.types.ts +0 -0
- /package/starters/{default → modular}/src/modules/billing/_contracts/stripe.types.ts +0 -0
- /package/starters/{default → modular}/src/modules/billing/api/stripe.connector.ts +0 -0
- /package/starters/{default → modular}/src/modules/billing/billing.public.ts +0 -0
- /package/starters/{default → modular}/src/modules/billing/billing.routes.ts +0 -0
- /package/starters/{default → modular}/src/modules/billing/billing.types.ts +0 -0
- /package/starters/{default → modular}/src/modules/billing/components/invoice-status-badge.component.ts +0 -0
- /package/starters/{default → modular}/src/modules/billing/data/invoices.resource.ts +0 -0
- /package/starters/{default → modular}/src/shared/http/http-client.ts +0 -0
- /package/starters/{default → modular}/src/shared/http/http-error.ts +0 -0
- /package/starters/{default → modular}/src/shared/http/interceptors.ts +0 -0
- /package/starters/{default → modular}/src/shared/lib/format-date.ts +0 -0
- /package/starters/{default → modular}/src/shared/styles/content.css +0 -0
- /package/starters/{default → modular}/src/shared/styles/shell.css +0 -0
- /package/starters/{default → modular}/src/shared/styles/tokens.css +0 -0
- /package/starters/{default → modular}/src/shared/ui/x-spinner.component.ts +0 -0
|
@@ -9,27 +9,29 @@
|
|
|
9
9
|
//
|
|
10
10
|
// Layouts live in src/layouts/ because they describe APP ZONES, not domains.
|
|
11
11
|
|
|
12
|
-
import { html, page } from "@madojs/mado";
|
|
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",
|
|
18
|
+
// Every internal anchor uses `data-link` + `routeUrl()` so SPA
|
|
19
|
+
// navigation intercepts the click and the href stays correct under
|
|
20
|
+
// any Vite `base` (e.g. deploying the app under `/admin/`).
|
|
19
21
|
view: ({ child }) => html`
|
|
20
22
|
<div class="layout layout--app">
|
|
21
23
|
<header class="app-header">
|
|
22
|
-
<a href
|
|
24
|
+
<a data-link href=${routeUrl("/")} class="brand">Mado App</a>
|
|
23
25
|
<nav>
|
|
24
|
-
<a href
|
|
26
|
+
<a data-link href=${routeUrl("/billing/invoices")}>Invoices</a>
|
|
25
27
|
${() =>
|
|
26
28
|
isAuthed()
|
|
27
29
|
? html`<span class="who">${() => user()?.email ?? ""}</span>
|
|
28
|
-
<
|
|
29
|
-
: html`<a href
|
|
30
|
+
<button class="button button--ghost" type="button" @click=${logout}>Sign out</button>`
|
|
31
|
+
: html`<a data-link href=${routeUrl("/login")}>Sign in</a>`}
|
|
30
32
|
</nav>
|
|
31
33
|
</header>
|
|
32
34
|
<main class="app-main">${child}</main>
|
|
33
35
|
</div>
|
|
34
36
|
`,
|
|
35
|
-
});
|
|
37
|
+
});
|
|
@@ -4,14 +4,14 @@
|
|
|
4
4
|
// auth ZONE. If tomorrow a `password-reset` module appears, it will reuse
|
|
5
5
|
// this same shell.
|
|
6
6
|
|
|
7
|
-
import { html, page } from "@madojs/mado";
|
|
7
|
+
import { html, page, routeUrl } from "@madojs/mado";
|
|
8
8
|
|
|
9
9
|
export default page({
|
|
10
10
|
title: "Sign in",
|
|
11
11
|
view: ({ child }) => html`
|
|
12
12
|
<div class="layout layout--auth">
|
|
13
|
-
<a href
|
|
13
|
+
<a data-link href=${routeUrl("/")} class="brand">Mado App</a>
|
|
14
14
|
<main class="auth-main">${child}</main>
|
|
15
15
|
</div>
|
|
16
16
|
`,
|
|
17
|
-
});
|
|
17
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// App entry point. The ONLY file allowed to import CSS at runtime.
|
|
2
|
+
import "./shared/styles/tokens.css";
|
|
3
|
+
import "./shared/styles/reset.css";
|
|
4
|
+
import "./shared/styles/shell.css";
|
|
5
|
+
import "./shared/styles/content.css";
|
|
6
|
+
|
|
7
|
+
import { html, render } from "@madojs/mado";
|
|
8
|
+
|
|
9
|
+
import { init as initAuth } from "./modules/auth/auth.service";
|
|
10
|
+
import appRoutes from "./app.routes";
|
|
11
|
+
|
|
12
|
+
// One-off boot order: init cross-cutting modules first, then render the
|
|
13
|
+
// router view into #app.
|
|
14
|
+
await initAuth();
|
|
15
|
+
|
|
16
|
+
render(html`${appRoutes.view}`, document.getElementById("app")!);
|
|
@@ -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
|
-
|
|
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
|
-
<
|
|
56
|
-
${() => (submitting() ? "Signing in…" : "Sign in")}
|
|
57
|
-
</
|
|
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 "
|
|
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`<
|
|
60
|
+
? html`<button class="button" type="button" ?disabled=${paying} @click=${onPay}>
|
|
62
61
|
${() => (paying() ? "Paying…" : "Pay now")}
|
|
63
|
-
</
|
|
62
|
+
</button>`
|
|
64
63
|
: null}
|
|
65
64
|
`;
|
|
66
65
|
})()}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { each, html, page, signal, untracked } from "@madojs/mado";
|
|
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 "
|
|
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";
|
|
@@ -50,7 +50,7 @@ export default page({
|
|
|
50
50
|
(i) => html`
|
|
51
51
|
<tr>
|
|
52
52
|
<td>
|
|
53
|
-
<a href=${`/billing/invoices/${i.id}`}>${i.number}</a>
|
|
53
|
+
<a data-link href=${routeUrl(`/billing/invoices/${i.id}`)}>${i.number}</a>
|
|
54
54
|
</td>
|
|
55
55
|
<td>${i.customerEmail}</td>
|
|
56
56
|
<td>${formatMoney(i.amount, i.currency)}</td>
|
|
@@ -59,7 +59,7 @@ export default page({
|
|
|
59
59
|
</td>
|
|
60
60
|
<td>${formatDate(i.issuedAt)}</td>
|
|
61
61
|
<td>
|
|
62
|
-
<a href=${`/billing/invoices/${i.id}`}>Open</a>
|
|
62
|
+
<a data-link href=${routeUrl(`/billing/invoices/${i.id}`)}>Open</a>
|
|
63
63
|
</td>
|
|
64
64
|
</tr>
|
|
65
65
|
`,
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
//
|
|
7
7
|
// A page should be read top-to-bottom and understood without jumping files.
|
|
8
8
|
|
|
9
|
-
import { html, page } from "@madojs/mado";
|
|
9
|
+
import { html, page, routeUrl } from "@madojs/mado";
|
|
10
10
|
|
|
11
11
|
// 1. LOCAL STATE — none
|
|
12
12
|
// 2. DATA — none
|
|
@@ -14,20 +14,18 @@ import { html, page } from "@madojs/mado";
|
|
|
14
14
|
|
|
15
15
|
// 4. VIEW
|
|
16
16
|
export default page({
|
|
17
|
+
static: true,
|
|
17
18
|
title: "Home",
|
|
18
19
|
head: () => ({
|
|
19
20
|
description: "A modular Mado application.",
|
|
20
21
|
}),
|
|
21
|
-
bake: {
|
|
22
|
-
paths: () => [{}],
|
|
23
|
-
data: () => ({}),
|
|
24
|
-
},
|
|
25
22
|
view: () => html`
|
|
26
23
|
<section>
|
|
27
24
|
<h1>Mado App</h1>
|
|
28
25
|
<p>
|
|
29
|
-
Welcome. Try
|
|
30
|
-
<a href
|
|
26
|
+
Welcome. Try
|
|
27
|
+
<a data-link href=${routeUrl("/billing/invoices")}>billing</a>
|
|
28
|
+
or <a data-link href=${routeUrl("/login")}>sign in</a>.
|
|
31
29
|
</p>
|
|
32
30
|
</section>
|
|
33
31
|
`,
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { html, page, routeUrl } from "@madojs/mado";
|
|
2
|
+
|
|
3
|
+
export default page({
|
|
4
|
+
title: "Not Found",
|
|
5
|
+
view: () => html`
|
|
6
|
+
<section>
|
|
7
|
+
<h1>404</h1>
|
|
8
|
+
<p>
|
|
9
|
+
This route does not exist.
|
|
10
|
+
<a data-link href=${routeUrl("/")}>Go home</a>.
|
|
11
|
+
</p>
|
|
12
|
+
</section>
|
|
13
|
+
`,
|
|
14
|
+
});
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
declare module "*.css";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/// <reference types="vite/client" />
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"paths": {
|
|
4
|
+
"@/*": [
|
|
5
|
+
"./src/*"
|
|
6
|
+
]
|
|
7
|
+
},
|
|
8
|
+
"target": "ES2022",
|
|
9
|
+
"module": "ES2022",
|
|
10
|
+
"moduleResolution": "Bundler",
|
|
11
|
+
"lib": [
|
|
12
|
+
"ES2022",
|
|
13
|
+
"DOM",
|
|
14
|
+
"DOM.Iterable"
|
|
15
|
+
],
|
|
16
|
+
"strict": true,
|
|
17
|
+
"noImplicitOverride": true,
|
|
18
|
+
"noUncheckedIndexedAccess": true,
|
|
19
|
+
"exactOptionalPropertyTypes": true,
|
|
20
|
+
"noFallthroughCasesInSwitch": true,
|
|
21
|
+
"forceConsistentCasingInFileNames": true,
|
|
22
|
+
"esModuleInterop": true,
|
|
23
|
+
"resolveJsonModule": true,
|
|
24
|
+
"skipLibCheck": true,
|
|
25
|
+
"useDefineForClassFields": true,
|
|
26
|
+
"experimentalDecorators": false,
|
|
27
|
+
"isolatedModules": true,
|
|
28
|
+
"verbatimModuleSyntax": true,
|
|
29
|
+
"noEmit": true
|
|
30
|
+
},
|
|
31
|
+
"include": ["src/**/*.ts", "src/**/*.d.ts"],
|
|
32
|
+
"exclude": ["node_modules", "out"]
|
|
33
|
+
}
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { defineConfig, type Plugin } from "vite";
|
|
2
|
+
import { mado } from "@madojs/mado/vite";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Tiny in-memory mock for the auth + billing API used by this starter.
|
|
6
|
+
*
|
|
7
|
+
* Mounted only in `dev` so `mado release` ships a clean bundle that
|
|
8
|
+
* talks to your real backend. The endpoint shape mirrors what
|
|
9
|
+
* `auth.connector.ts` and `stripe.connector.ts` expect: any change in
|
|
10
|
+
* one must be mirrored in the other.
|
|
11
|
+
*
|
|
12
|
+
* Disable by removing this plugin or setting `MADO_MOCK_API=0`.
|
|
13
|
+
*/
|
|
14
|
+
function devApiMock(): Plugin {
|
|
15
|
+
const users = new Map<string, { password: string; user: unknown }>([
|
|
16
|
+
[
|
|
17
|
+
"demo@mado.dev",
|
|
18
|
+
{
|
|
19
|
+
password: "demo123",
|
|
20
|
+
user: {
|
|
21
|
+
id: "u_demo",
|
|
22
|
+
email: "demo@mado.dev",
|
|
23
|
+
roles: ["user"],
|
|
24
|
+
permissions: ["billing.read", "billing.invoices.pay"],
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
]);
|
|
29
|
+
const invoices = Array.from({ length: 6 }, (_, i) => ({
|
|
30
|
+
id: `in_${1000 + i}`,
|
|
31
|
+
number: `INV-${1000 + i}`,
|
|
32
|
+
customer_email: "demo@mado.dev",
|
|
33
|
+
amount_due: 1000 * (i + 1),
|
|
34
|
+
currency: "usd",
|
|
35
|
+
status: i % 3 === 0 ? "paid" : i % 3 === 1 ? "pending" : "draft",
|
|
36
|
+
created: Math.floor(Date.now() / 1000) - i * 86_400,
|
|
37
|
+
}));
|
|
38
|
+
let token: string | null = null;
|
|
39
|
+
const json = (res: import("http").ServerResponse, status: number, body: unknown) => {
|
|
40
|
+
res.statusCode = status;
|
|
41
|
+
res.setHeader("content-type", "application/json");
|
|
42
|
+
res.end(JSON.stringify(body));
|
|
43
|
+
};
|
|
44
|
+
const readJson = (req: import("http").IncomingMessage) =>
|
|
45
|
+
new Promise<Record<string, unknown>>((resolve) => {
|
|
46
|
+
let buf = "";
|
|
47
|
+
req.on("data", (chunk) => (buf += chunk));
|
|
48
|
+
req.on("end", () => {
|
|
49
|
+
try {
|
|
50
|
+
resolve(buf ? JSON.parse(buf) : {});
|
|
51
|
+
} catch {
|
|
52
|
+
resolve({});
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
name: "starter:dev-api-mock",
|
|
59
|
+
apply: "serve",
|
|
60
|
+
configureServer(server) {
|
|
61
|
+
if (process.env.MADO_MOCK_API === "0") return;
|
|
62
|
+
server.middlewares.use(async (req, res, next) => {
|
|
63
|
+
const url = req.url ?? "";
|
|
64
|
+
if (!url.startsWith("/api/")) return next();
|
|
65
|
+
|
|
66
|
+
// ---- auth -----------------------------------------------------
|
|
67
|
+
if (url === "/api/auth/login" && req.method === "POST") {
|
|
68
|
+
const body = await readJson(req);
|
|
69
|
+
const entry = users.get(String(body.email ?? ""));
|
|
70
|
+
if (!entry || entry.password !== body.password) {
|
|
71
|
+
return json(res, 401, { error: "invalid_credentials" });
|
|
72
|
+
}
|
|
73
|
+
token = `tok_${Math.random().toString(36).slice(2)}`;
|
|
74
|
+
return json(res, 200, { token, user: entry.user });
|
|
75
|
+
}
|
|
76
|
+
if (url === "/api/auth/me" && req.method === "GET") {
|
|
77
|
+
if (!token) return json(res, 401, { error: "unauthenticated" });
|
|
78
|
+
const entry = users.values().next().value;
|
|
79
|
+
if (!entry) return json(res, 500, { error: "mock_user_missing" });
|
|
80
|
+
return json(res, 200, entry.user);
|
|
81
|
+
}
|
|
82
|
+
if (url === "/api/auth/logout" && req.method === "POST") {
|
|
83
|
+
token = null;
|
|
84
|
+
return json(res, 204, null);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// ---- billing --------------------------------------------------
|
|
88
|
+
if (url.startsWith("/api/billing/stripe/invoices")) {
|
|
89
|
+
const id = url.match(/\/invoices\/([^/?]+)/)?.[1];
|
|
90
|
+
if (req.method === "POST" && url.endsWith("/pay") && id) {
|
|
91
|
+
const inv = invoices.find((x) => x.id === id);
|
|
92
|
+
if (!inv) return json(res, 404, { error: "not_found" });
|
|
93
|
+
inv.status = "paid";
|
|
94
|
+
return json(res, 200, inv);
|
|
95
|
+
}
|
|
96
|
+
if (id && req.method === "GET") {
|
|
97
|
+
const inv = invoices.find((x) => x.id === id);
|
|
98
|
+
return inv
|
|
99
|
+
? json(res, 200, inv)
|
|
100
|
+
: json(res, 404, { error: "not_found" });
|
|
101
|
+
}
|
|
102
|
+
if (req.method === "GET") {
|
|
103
|
+
return json(res, 200, { data: invoices, has_more: false });
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return next();
|
|
108
|
+
});
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export default defineConfig({
|
|
114
|
+
resolve: {
|
|
115
|
+
tsconfigPaths: true,
|
|
116
|
+
},
|
|
117
|
+
plugins: [
|
|
118
|
+
devApiMock(),
|
|
119
|
+
mado({
|
|
120
|
+
// Public origin used to build absolute URLs for static snapshots
|
|
121
|
+
// (sitemap, canonical, OpenGraph). Combined with Vite's `base`,
|
|
122
|
+
// the canonical for a route is `site + base + pathname`.
|
|
123
|
+
//
|
|
124
|
+
// REQUIRED when any page declares `static`. Set to your deployment
|
|
125
|
+
// origin (e.g. https://your-app.example) before running
|
|
126
|
+
// `mado release`. Override per environment via:
|
|
127
|
+
// mado release --base-url https://staging.example
|
|
128
|
+
// MADO_SITE=https://staging.example mado release
|
|
129
|
+
// site: "https://your-app.example",
|
|
130
|
+
}),
|
|
131
|
+
],
|
|
132
|
+
});
|
package/TODO.md
DELETED
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
# TODO
|
|
2
|
-
|
|
3
|
-
Backlog, not a roadmap. Keep this file small and honest: ideas stay here only
|
|
4
|
-
while they are plausible next work. Promote concrete work to issues or release
|
|
5
|
-
notes when it becomes real.
|
|
6
|
-
|
|
7
|
-
## Before Public Demo / Release
|
|
8
|
-
|
|
9
|
-
- [ ] Run the default starter as a real demo app and feed every friction point
|
|
10
|
-
back into docs, CLI generators, or starter structure.
|
|
11
|
-
- [ ] Add focused tests for every `mado new` generator template.
|
|
12
|
-
- [ ] Audit EN/RU/FR docs after each tooling/runtime change; no old `dist/`,
|
|
13
|
-
`tsc-only`, custom-bundler, or legacy example wording should remain.
|
|
14
|
-
- [ ] Keep `mado preview` boring and reliable inside generated apps.
|
|
15
|
-
- [ ] Add a short maintainer-only release checklist if manual npm publish stays
|
|
16
|
-
in use.
|
|
17
|
-
|
|
18
|
-
## CLI / DX
|
|
19
|
-
|
|
20
|
-
- [ ] Decide whether `npx mado create my-app` is worth adding after package
|
|
21
|
-
naming and install friction are clear.
|
|
22
|
-
- [ ] Consider short aliases over `mado new` only after real usage shows they
|
|
23
|
-
save enough typing (`mado page`, `mado component`, etc.).
|
|
24
|
-
- [ ] Split preview/static-server internals only if `scripts/preview.mjs` grows.
|
|
25
|
-
- [ ] Typed navigation inferred from route patterns.
|
|
26
|
-
- [ ] Editor highlighting docs should prefer existing tooling first; build a
|
|
27
|
-
Mado-specific VS Code extension only if current HTML/CSS template support is
|
|
28
|
-
not enough.
|
|
29
|
-
|
|
30
|
-
## Runtime / App API
|
|
31
|
-
|
|
32
|
-
- [ ] Mutation rollback helper on top of `resource().mutate()` and `mutation()`.
|
|
33
|
-
- [ ] Document loading/error shell patterns before adding an `<x-async>` API.
|
|
34
|
-
- [ ] `liveResource()` over SSE / WebSocket after a real app needs live data.
|
|
35
|
-
- [ ] i18n helper once browser-native message formatting is realistic.
|
|
36
|
-
- [ ] Accessibility helpers: focus trap, live region, click outside.
|
|
37
|
-
|
|
38
|
-
## Styling
|
|
39
|
-
|
|
40
|
-
- [ ] Docs for `::part`, `::slotted()` and `:host-context()`.
|
|
41
|
-
- [ ] Document Vite CSS options, including `css.transformer: "lightningcss"`,
|
|
42
|
-
for apps that want Lightning CSS.
|
|
43
|
-
- [ ] Optional tiny utilities stylesheet only if the starter/demo repeats the
|
|
44
|
-
same small classes often enough to justify it.
|
|
45
|
-
- [ ] CSP-friendly style mode.
|
|
46
|
-
|
|
47
|
-
## Bake / Smart Static
|
|
48
|
-
|
|
49
|
-
- [ ] Add bake regression coverage for layout groups, dynamic params, sitemap,
|
|
50
|
-
and hard-refresh deploy paths.
|
|
51
|
-
- [ ] Incremental bake cache after a real content-heavy app needs it.
|
|
52
|
-
- [ ] Webhook re-bake endpoint only after incremental bake has a concrete use.
|
|
53
|
-
- [ ] RSS / Atom feed generator for docs/blog/demo content.
|
|
54
|
-
|
|
55
|
-
## Operations
|
|
56
|
-
|
|
57
|
-
- [ ] Benchmark against Lit / Solid / Preact.
|
|
58
|
-
- [ ] Source-map notes and a minimal dev error overlay.
|
|
59
|
-
- [ ] `eslint-plugin-mado` rules only if docs/starter conventions are not enough
|
|
60
|
-
to prevent recurring mistakes.
|
|
61
|
-
- [ ] Timeout / retry options for resource fetchers after real API friction.
|
|
62
|
-
- [ ] Schema validation hook for resources after choosing a dependency-free
|
|
63
|
-
story or a clearly optional integration.
|
|
64
|
-
- [ ] PWA scaffold only if the public demo or a real app needs offline behavior.
|
|
65
|
-
|
|
66
|
-
## Community / GitHub
|
|
67
|
-
|
|
68
|
-
- [ ] Minimal landing site built with Mado.
|
|
69
|
-
- [ ] Public live demo: CRUD starter and showcase hosted from Mado itself.
|
|
70
|
-
- [ ] Case study: real React page -> Mado + bake.
|
|
71
|
-
- [ ] Convert contribution-ready TODO items into GitHub issues with labels
|
|
72
|
-
(`good first issue`, `help wanted`, `docs`, `cli`, `ci`) after the first
|
|
73
|
-
public feedback round.
|
|
74
|
-
- [ ] Configure npm Trusted Publishing for GitHub Actions after verifying the
|
|
75
|
-
first manual publishes.
|
|
76
|
-
- [ ] Decide the release flow: continue manual npm publish for patch releases or
|
|
77
|
-
publish from signed `v*` tags through `.github/workflows/release.yml`.
|
|
78
|
-
- [ ] Improve generated release notes with PR links/authors when releases start
|
|
79
|
-
going through pull requests.
|
package/dist/src/lazy.d.ts
DELETED
|
@@ -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
|
package/dist/src/lazy.js.map
DELETED
|
@@ -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"}
|