@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
package/dist/src/component.js
CHANGED
|
@@ -12,14 +12,88 @@
|
|
|
12
12
|
* The returned render function is called via an effect, so any signals
|
|
13
13
|
* read inside it automatically re-render the template.
|
|
14
14
|
*
|
|
15
|
-
* Shadow DOM (open) is used by default. It can be disabled
|
|
16
|
-
* styles will be scoped via @scope
|
|
15
|
+
* Shadow DOM (open, serializable) is used by default. It can be disabled for
|
|
16
|
+
* advanced integration cases, and styles will be scoped via @scope
|
|
17
|
+
* (or a tag-prefix fallback).
|
|
17
18
|
*/
|
|
18
19
|
import { signal, effect } from "./signal.js";
|
|
19
|
-
import { html, render } from "./html/template.js";
|
|
20
|
-
import { adopt, scopeStyles } from "./css.js";
|
|
20
|
+
import { html, render, unmount } from "./html/template.js";
|
|
21
|
+
import { adopt, createStylesheet, scopeStyles, } from "./css.js";
|
|
21
22
|
import { createLifecycle, runInLifecycle, } from "./lifecycle.js";
|
|
22
23
|
import { warnOnce } from "./diagnostics.js";
|
|
24
|
+
import { emitDevtools } from "./devtools-hook.js";
|
|
25
|
+
/**
|
|
26
|
+
* Components upgraded inside a server-side or build-time snapshot live
|
|
27
|
+
* temporarily under `<#app data-mado-static>`. Their setup() is intentionally
|
|
28
|
+
* NOT run yet: Mado replaces the snapshot tree atomically when the SPA
|
|
29
|
+
* boots, and any work performed by setup() before takeover would leak.
|
|
30
|
+
*
|
|
31
|
+
* The set lets the runtime double-check, after takeover, that no deferred
|
|
32
|
+
* element survives — see _flushDeferredStaticElements().
|
|
33
|
+
*/
|
|
34
|
+
const deferredStaticElements = new Set();
|
|
35
|
+
/**
|
|
36
|
+
* Walk composed ancestry (Light DOM parentNode plus ShadowRoot.host) and
|
|
37
|
+
* return true when the element lives inside a tree marked with
|
|
38
|
+
* `data-mado-static`. Used to defer component activation until the SPA
|
|
39
|
+
* takeover atomically removes that tree.
|
|
40
|
+
*/
|
|
41
|
+
function isInsideStaticTree(node) {
|
|
42
|
+
let n = node;
|
|
43
|
+
while (n) {
|
|
44
|
+
if (n instanceof Element &&
|
|
45
|
+
n.hasAttribute &&
|
|
46
|
+
n.hasAttribute("data-mado-static")) {
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
const parent = typeof ShadowRoot !== "undefined" && n instanceof ShadowRoot
|
|
50
|
+
? n.host
|
|
51
|
+
: n.parentNode;
|
|
52
|
+
n = parent;
|
|
53
|
+
}
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
/** @internal */
|
|
57
|
+
export function _markDeferredForStatic(host) {
|
|
58
|
+
deferredStaticElements.add(host);
|
|
59
|
+
}
|
|
60
|
+
/** @internal */
|
|
61
|
+
export function _isDeferredForStatic(host) {
|
|
62
|
+
return deferredStaticElements.has(host);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Called by render() after a root takeover has atomically replaced the
|
|
66
|
+
* `data-mado-static` tree. By that time every component that was
|
|
67
|
+
* mid-snapshot must either be:
|
|
68
|
+
* 1. removed with the static tree (the typical case), or
|
|
69
|
+
* 2. still connected but no longer under a static ancestor — in which
|
|
70
|
+
* case the SPA tree placed it live and we activate it now.
|
|
71
|
+
* Any element that fails both checks is a stuck snapshot leaf; emit a
|
|
72
|
+
* single diagnostic so the developer notices the orphan.
|
|
73
|
+
*
|
|
74
|
+
* @internal
|
|
75
|
+
*/
|
|
76
|
+
export function _flushDeferredStaticElements() {
|
|
77
|
+
for (const el of [...deferredStaticElements]) {
|
|
78
|
+
if (!el.isConnected) {
|
|
79
|
+
deferredStaticElements.delete(el);
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
if (!isInsideStaticTree(el)) {
|
|
83
|
+
// Reset the guard so the live connectedCallback runs setup().
|
|
84
|
+
deferredStaticElements.delete(el);
|
|
85
|
+
// Force a clean re-enter of connectedCallback. Browsers fire that
|
|
86
|
+
// callback exactly once per insertion, so we toggle the element out
|
|
87
|
+
// and back in. Avoid this if the element is the document root.
|
|
88
|
+
const parent = el.parentNode;
|
|
89
|
+
const next = el.nextSibling;
|
|
90
|
+
if (parent) {
|
|
91
|
+
parent.removeChild(el);
|
|
92
|
+
parent.insertBefore(el, next);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
23
97
|
export function component(tagName, setup, options = {}) {
|
|
24
98
|
if (!tagName.includes("-")) {
|
|
25
99
|
warnOnce(`component-invalid-tag-${tagName}`, `component("${tagName}") skipped: Custom Element names must contain a hyphen.`);
|
|
@@ -52,7 +126,13 @@ export function component(tagName, setup, options = {}) {
|
|
|
52
126
|
#attrDefaults = new Map();
|
|
53
127
|
constructor() {
|
|
54
128
|
super();
|
|
55
|
-
this.#root = useShadow
|
|
129
|
+
this.#root = useShadow
|
|
130
|
+
? this.shadowRoot ??
|
|
131
|
+
this.attachShadow({
|
|
132
|
+
mode: "open",
|
|
133
|
+
serializable: true,
|
|
134
|
+
})
|
|
135
|
+
: this;
|
|
56
136
|
}
|
|
57
137
|
connectedCallback() {
|
|
58
138
|
// A keyed move (each() relocating via insertBefore) fires
|
|
@@ -62,7 +142,18 @@ export function component(tagName, setup, options = {}) {
|
|
|
62
142
|
this.#teardownQueued = false;
|
|
63
143
|
if (this.#connected)
|
|
64
144
|
return;
|
|
65
|
-
|
|
145
|
+
// Deferred activation inside a static snapshot tree. The runtime keeps
|
|
146
|
+
// setup()/effect() asleep until the SPA performs root takeover; the
|
|
147
|
+
// existing DSD inside shadowRoot stays as inert first-paint markup.
|
|
148
|
+
// When the SPA replaces the static root, this element is either
|
|
149
|
+
// removed with that tree (the common case) or re-inserted live —
|
|
150
|
+
// in which case connectedCallback() runs again and reaches setup().
|
|
151
|
+
if (isInsideStaticTree(this)) {
|
|
152
|
+
_markDeferredForStatic(this);
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
if (typeof __MADO_DEVTOOLS__ === "undefined" || __MADO_DEVTOOLS__)
|
|
156
|
+
emitDevtools("component:connect", this, { tagName });
|
|
66
157
|
if (stylesheets.length > 0) {
|
|
67
158
|
if (useShadow) {
|
|
68
159
|
adopt(this.#root, ...stylesheets);
|
|
@@ -75,45 +166,76 @@ export function component(tagName, setup, options = {}) {
|
|
|
75
166
|
// called from setup() (resource, ...) will see it via
|
|
76
167
|
// getCurrentLifecycle() and register its own cleanup.
|
|
77
168
|
const lifecycle = createLifecycle();
|
|
78
|
-
this.#lifecycle = lifecycle;
|
|
79
169
|
const host = this;
|
|
170
|
+
const attr = ((name, defaultValue) => {
|
|
171
|
+
let s = host.#attrSignals.get(name);
|
|
172
|
+
if (!s) {
|
|
173
|
+
const fallback = defaultValue ?? null;
|
|
174
|
+
host.#attrDefaults.set(name, fallback);
|
|
175
|
+
s = signal(host.getAttribute(name) ?? fallback);
|
|
176
|
+
host.#attrSignals.set(name, s);
|
|
177
|
+
}
|
|
178
|
+
return s;
|
|
179
|
+
});
|
|
80
180
|
const ctx = {
|
|
81
181
|
host: this,
|
|
82
182
|
// ctx.onDispose proxies to lifecycle — the single source of truth
|
|
83
183
|
// for component cleanups (including auto-cleanup from
|
|
84
184
|
// resource(), navigator listeners, etc.).
|
|
85
185
|
onDispose: (fn) => lifecycle.onDispose(fn),
|
|
86
|
-
attr
|
|
87
|
-
let s = host.#attrSignals.get(name);
|
|
88
|
-
if (!s) {
|
|
89
|
-
host.#attrDefaults.set(name, defaultValue);
|
|
90
|
-
s = signal(host.getAttribute(name) ?? defaultValue);
|
|
91
|
-
host.#attrSignals.set(name, s);
|
|
92
|
-
}
|
|
93
|
-
return s;
|
|
94
|
-
},
|
|
186
|
+
attr,
|
|
95
187
|
};
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
188
|
+
try {
|
|
189
|
+
const renderer = runInLifecycle(lifecycle, () => setup(ctx));
|
|
190
|
+
if (typeof renderer !== "function") {
|
|
191
|
+
throw new TypeError(`component("${tagName}") setup must return a renderer function`);
|
|
192
|
+
}
|
|
193
|
+
// After setup(), install a single MutationObserver for all attrs
|
|
194
|
+
// registered via ctx.attr().
|
|
195
|
+
if (this.#attrSignals.size > 0) {
|
|
196
|
+
const attrNames = [...this.#attrSignals.keys()];
|
|
197
|
+
const obs = new MutationObserver((mutations) => {
|
|
198
|
+
for (const m of mutations) {
|
|
199
|
+
const s = this.#attrSignals.get(m.attributeName);
|
|
200
|
+
const fallback = this.#attrDefaults.get(m.attributeName) ?? null;
|
|
201
|
+
if (s)
|
|
202
|
+
s.set(this.getAttribute(m.attributeName) ?? fallback);
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
obs.observe(this, { attributes: true, attributeFilter: attrNames });
|
|
206
|
+
lifecycle.onDispose(() => obs.disconnect());
|
|
207
|
+
}
|
|
208
|
+
const effectDispose = runInLifecycle(lifecycle, () => effect(() => {
|
|
209
|
+
if (typeof __MADO_DEVTOOLS__ === "undefined" || __MADO_DEVTOOLS__)
|
|
210
|
+
emitDevtools("component:render", this, { tagName });
|
|
211
|
+
render(renderer(), this.#root);
|
|
212
|
+
}));
|
|
213
|
+
this.#renderer = renderer;
|
|
214
|
+
this.#effectDispose = effectDispose;
|
|
215
|
+
this.#lifecycle = lifecycle;
|
|
216
|
+
this.#connected = true;
|
|
217
|
+
if (typeof __MADO_DEVTOOLS__ === "undefined" || __MADO_DEVTOOLS__)
|
|
218
|
+
emitDevtools("component:ready", this, { tagName });
|
|
219
|
+
}
|
|
220
|
+
catch (err) {
|
|
221
|
+
unmount(this.#root);
|
|
222
|
+
lifecycle.dispose();
|
|
223
|
+
this.#renderer = null;
|
|
224
|
+
this.#effectDispose = null;
|
|
225
|
+
this.#lifecycle = null;
|
|
226
|
+
this.#attrSignals.clear();
|
|
227
|
+
this.#attrDefaults.clear();
|
|
228
|
+
this.#connected = false;
|
|
229
|
+
if (typeof __MADO_DEVTOOLS__ === "undefined" || __MADO_DEVTOOLS__)
|
|
230
|
+
emitDevtools("component:error", this, { tagName, error: err });
|
|
231
|
+
throw err;
|
|
111
232
|
}
|
|
112
|
-
this.#effectDispose = effect(() => {
|
|
113
|
-
render(this.#renderer(), this.#root);
|
|
114
|
-
});
|
|
115
233
|
}
|
|
116
234
|
disconnectedCallback() {
|
|
235
|
+
// A deferred snapshot leaf was removed by takeover before setup() ever
|
|
236
|
+
// ran. Clear the deferred marker so the registry does not accumulate
|
|
237
|
+
// dead references.
|
|
238
|
+
deferredStaticElements.delete(this);
|
|
117
239
|
// Defer teardown to a microtask. A keyed move (each() relocating a node
|
|
118
240
|
// via insertBefore) fires disconnectedCallback → connectedCallback in the
|
|
119
241
|
// same tick; connectedCallback clears #teardownQueued so the teardown is
|
|
@@ -133,10 +255,16 @@ export function component(tagName, setup, options = {}) {
|
|
|
133
255
|
}
|
|
134
256
|
/** Synchronously dispose effects and lifecycle. */
|
|
135
257
|
#teardown() {
|
|
258
|
+
if (typeof __MADO_DEVTOOLS__ === "undefined" || __MADO_DEVTOOLS__)
|
|
259
|
+
emitDevtools("component:dispose", this, { tagName });
|
|
136
260
|
this.#effectDispose?.();
|
|
137
261
|
this.#effectDispose = null;
|
|
262
|
+
unmount(this.#root);
|
|
138
263
|
this.#lifecycle?.dispose();
|
|
139
264
|
this.#lifecycle = null;
|
|
265
|
+
this.#renderer = null;
|
|
266
|
+
this.#attrSignals.clear();
|
|
267
|
+
this.#attrDefaults.clear();
|
|
140
268
|
this.#connected = false;
|
|
141
269
|
}
|
|
142
270
|
}
|
|
@@ -151,8 +279,7 @@ function normalizeStyles(input, tagName, useShadow) {
|
|
|
151
279
|
return arr.map((s) => {
|
|
152
280
|
let sheet;
|
|
153
281
|
if (typeof s === "string") {
|
|
154
|
-
sheet =
|
|
155
|
-
sheet.replaceSync(s);
|
|
282
|
+
sheet = createStylesheet(s);
|
|
156
283
|
}
|
|
157
284
|
else {
|
|
158
285
|
sheet = s;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"component.js","sourceRoot":"","sources":["../../src/component.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,EAA8B,MAAM,aAAa,CAAC;AACzE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAElD,OAAO,EAAE,KAAK,EAAE,WAAW,EAAkB,MAAM,UAAU,CAAC;AAC9D,OAAO,EACL,eAAe,EACf,cAAc,GAEf,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAmC5C,MAAM,UAAU,SAAS,CACvB,OAAe,EACf,KAAc,EACd,UAA4B,EAAE;IAE9B,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QAC3B,QAAQ,CACN,yBAAyB,OAAO,EAAE,EAClC,cAAc,OAAO,yDAAyD,CAC/E,CAAC;QACF,OAAO;IACT,CAAC;IAED,MAAM,YAAY,GAAG,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAC7C,IAAI,cAAc,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;QAChC,IACE,CAAC,YAAY;YACb,YAAY,CAAC,KAAK,KAAK,KAAK;YAC5B,CAAC,oBAAoB,CAAC,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,EACpD,CAAC;YACD,QAAQ,CACN,uBAAuB,OAAO,EAAE,EAChC,cAAc,OAAO,oFAAoF,CAC1G,CAAC;QACJ,CAAC;QACD,OAAO,CAAC,wBAAwB;IAClC,CAAC;IAED,MAAM,SAAS,GAAG,OAAO,CAAC,MAAM,KAAK,KAAK,CAAC;IAE3C,sDAAsD;IACtD,qEAAqE;IACrE,MAAM,WAAW,GAAgB,eAAe,CAC9C,OAAO,CAAC,MAAM,EACd,OAAO,EACP,SAAS,CACV,CAAC;IAEF,MAAM,WAAY,SAAQ,WAAW;QACnC,KAAK,CAAuB;QAC5B,SAAS,GAAkC,IAAI,CAAC;QAChD,cAAc,GAAoB,IAAI,CAAC;QACvC,UAAU,GAA2B,IAAI,CAAC;QAC1C,UAAU,GAAG,KAAK,CAAC;QACnB,2DAA2D;QAC3D,oEAAoE;QACpE,gEAAgE;QAChE,eAAe,GAAG,KAAK,CAAC;QACxB,YAAY,GAAG,IAAI,GAAG,EAA0B,CAAC;QACjD,aAAa,GAAG,IAAI,GAAG,EAAkB,CAAC;QAE1C;YACE,KAAK,EAAE,CAAC;YACR,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QACtE,CAAC;QAED,iBAAiB;YACf,0DAA0D;YAC1D,uEAAuE;YACvE,yEAAyE;YACzE,kCAAkC;YAClC,IAAI,CAAC,eAAe,GAAG,KAAK,CAAC;YAC7B,IAAI,IAAI,CAAC,UAAU;gBAAE,OAAO;YAC5B,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;YAEvB,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC3B,IAAI,SAAS,EAAE,CAAC;oBACd,KAAK,CAAC,IAAI,CAAC,KAAmB,EAAE,GAAG,WAAW,CAAC,CAAC;gBAClD,CAAC;qBAAM,CAAC;oBACN,mBAAmB,CAAC,WAAW,CAAC,CAAC;gBACnC,CAAC;YACH,CAAC;YAED,sDAAsD;YACtD,sDAAsD;YACtD,sDAAsD;YACtD,MAAM,SAAS,GAAG,eAAe,EAAE,CAAC;YACpC,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;YAE5B,MAAM,IAAI,GAAG,IAAI,CAAC;YAElB,MAAM,GAAG,GAAqB;gBAC5B,IAAI,EAAE,IAAI;gBACV,kEAAkE;gBAClE,sDAAsD;gBACtD,0CAA0C;gBAC1C,SAAS,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,SAAS,CAAC,SAAS,CAAC,EAAE,CAAC;gBAC1C,IAAI,CAAC,IAAY,EAAE,YAAY,GAAG,EAAE;oBAClC,IAAI,CAAC,GAAG,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;oBACpC,IAAI,CAAC,CAAC,EAAE,CAAC;wBACP,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC;wBAC3C,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,YAAY,CAAC,CAAC;wBACpD,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;oBACjC,CAAC;oBACD,OAAO,CAAC,CAAC;gBACX,CAAC;aACF,CAAC;YAEF,IAAI,CAAC,SAAS,GAAG,cAAc,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;YAE7D,iEAAiE;YACjE,6BAA6B;YAC7B,IAAI,IAAI,CAAC,YAAY,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;gBAC/B,MAAM,SAAS,GAAG,CAAC,GAAG,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE,CAAC,CAAC;gBAChD,MAAM,GAAG,GAAG,IAAI,gBAAgB,CAAC,CAAC,SAAS,EAAE,EAAE;oBAC7C,KAAK,MAAM,CAAC,IAAI,SAAS,EAAE,CAAC;wBAC1B,MAAM,CAAC,GAAG,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,aAAc,CAAC,CAAC;wBAClD,MAAM,QAAQ,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,aAAc,CAAC,IAAI,EAAE,CAAC;wBAChE,IAAI,CAAC;4BAAE,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,aAAc,CAAC,IAAI,QAAQ,CAAC,CAAC;oBAChE,CAAC;gBACH,CAAC,CAAC,CAAC;gBACH,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,eAAe,EAAE,SAAS,EAAE,CAAC,CAAC;gBACpE,SAAS,CAAC,SAAS,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC;YAC9C,CAAC;YAED,IAAI,CAAC,cAAc,GAAG,MAAM,CAAC,GAAG,EAAE;gBAChC,MAAM,CAAC,IAAI,CAAC,SAAU,EAAE,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;YACxC,CAAC,CAAC,CAAC;QACL,CAAC;QAED,oBAAoB;YAClB,wEAAwE;YACxE,0EAA0E;YAC1E,yEAAyE;YACzE,0EAA0E;YAC1E,4DAA4D;YAC5D,IAAI,IAAI,CAAC,eAAe;gBAAE,OAAO;YACjC,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC;YAC5B,cAAc,CAAC,GAAG,EAAE;gBAClB,IAAI,CAAC,IAAI,CAAC,eAAe;oBAAE,OAAO,CAAC,+BAA+B;gBAClE,IAAI,CAAC,eAAe,GAAG,KAAK,CAAC;gBAC7B,IAAI,IAAI,CAAC,WAAW;oBAAE,OAAO,CAAC,+BAA+B;gBAC7D,IAAI,CAAC,SAAS,EAAE,CAAC;YACnB,CAAC,CAAC,CAAC;QACL,CAAC;QAED,mDAAmD;QACnD,SAAS;YACP,IAAI,CAAC,cAAc,EAAE,EAAE,CAAC;YACxB,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;YAC3B,IAAI,CAAC,UAAU,EAAE,OAAO,EAAE,CAAC;YAC3B,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;YACvB,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC;QAC1B,CAAC;KAEF;IAED,cAAc,CAAC,MAAM,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;IAC5C,UAAU,CAAC,GAAG,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;AAC9C,CAAC;AAED,gCAAgC;AAEhC,SAAS,eAAe,CACtB,KAA6B,EAC7B,OAAe,EACf,SAAkB;IAElB,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,CAAC;IACtB,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;IACnD,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QACnB,IAAI,KAAgB,CAAC;QACrB,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;YAC1B,KAAK,GAAG,IAAI,aAAa,EAAE,CAAC;YAC5B,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;QACvB,CAAC;aAAM,CAAC;YACN,KAAK,GAAG,CAAC,CAAC;QACZ,CAAC;QACD,+BAA+B;QAC/B,OAAO,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,WAAW,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,eAAe,GAAG,IAAI,OAAO,EAAa,CAAC;AACjD,MAAM,UAAU,GAAG,IAAI,GAAG,EAGvB,CAAC;AAEJ,SAAS,oBAAoB,CAC3B,CAAmB,EACnB,CAAmB;IAEnB,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IACxC,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IACxC,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,mBAAmB,CAAC,MAAmB;IAC9C,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5D,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO;IAC/B,KAAK,MAAM,CAAC,IAAI,KAAK;QAAE,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IAC9C,QAAQ,CAAC,kBAAkB,GAAG,CAAC,GAAG,QAAQ,CAAC,kBAAkB,EAAE,GAAG,KAAK,CAAC,CAAC;AAC3E,CAAC;AAED,yBAAyB;AACzB,OAAO,EAAE,IAAI,EAAE,CAAC"}
|
|
1
|
+
{"version":3,"file":"component.js","sourceRoot":"","sources":["../../src/component.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Wrapper around Custom Elements.\n *\n * component('x-counter', () => {\n * const count = signal(0);\n * return () => html`<button @click=${() => count.update(n=>n+1)}>${count}</button>`;\n * }, {\n * styles: css`button { padding: .5rem }`,\n * });\n *\n * The setup function is called once on the first connectedCallback.\n * The returned render function is called via an effect, so any signals\n * read inside it automatically re-render the template.\n *\n * Shadow DOM (open, serializable) is used by default. It can be disabled for\n * advanced integration cases, and styles will be scoped via @scope\n * (or a tag-prefix fallback).\n */\n\nimport { signal, effect, type Signal, type Disposer } from \"./signal.js\";\nimport { html, render, unmount } from \"./html/template.js\";\nimport type { TemplateResult } from \"./html/template-types.js\";\nimport {\n adopt,\n createStylesheet,\n scopeStyles,\n type CSSResult,\n} from \"./css.js\";\nimport {\n createLifecycle,\n runInLifecycle,\n type LifecycleHandle,\n} from \"./lifecycle.js\";\nimport { warnOnce } from \"./diagnostics.js\";\nimport { emitDevtools } from \"./devtools-hook.js\";\n\n/**\n * Components upgraded inside a server-side or build-time snapshot live\n * temporarily under `<#app data-mado-static>`. Their setup() is intentionally\n * NOT run yet: Mado replaces the snapshot tree atomically when the SPA\n * boots, and any work performed by setup() before takeover would leak.\n *\n * The set lets the runtime double-check, after takeover, that no deferred\n * element survives — see _flushDeferredStaticElements().\n */\nconst deferredStaticElements = new Set<HTMLElement>();\n\n/**\n * Walk composed ancestry (Light DOM parentNode plus ShadowRoot.host) and\n * return true when the element lives inside a tree marked with\n * `data-mado-static`. Used to defer component activation until the SPA\n * takeover atomically removes that tree.\n */\nfunction isInsideStaticTree(node: Node | null): boolean {\n let n: Node | null = node;\n while (n) {\n if (\n n instanceof Element &&\n (n as Element).hasAttribute &&\n (n as Element).hasAttribute(\"data-mado-static\")\n ) {\n return true;\n }\n const parent: Node | null =\n typeof ShadowRoot !== \"undefined\" && n instanceof ShadowRoot\n ? n.host\n : n.parentNode;\n n = parent;\n }\n return false;\n}\n\n/** @internal */\nexport function _markDeferredForStatic(host: HTMLElement): void {\n deferredStaticElements.add(host);\n}\n\n/** @internal */\nexport function _isDeferredForStatic(host: HTMLElement): boolean {\n return deferredStaticElements.has(host);\n}\n\n/**\n * Called by render() after a root takeover has atomically replaced the\n * `data-mado-static` tree. By that time every component that was\n * mid-snapshot must either be:\n * 1. removed with the static tree (the typical case), or\n * 2. still connected but no longer under a static ancestor — in which\n * case the SPA tree placed it live and we activate it now.\n * Any element that fails both checks is a stuck snapshot leaf; emit a\n * single diagnostic so the developer notices the orphan.\n *\n * @internal\n */\nexport function _flushDeferredStaticElements(): void {\n for (const el of [...deferredStaticElements]) {\n if (!el.isConnected) {\n deferredStaticElements.delete(el);\n continue;\n }\n if (!isInsideStaticTree(el)) {\n // Reset the guard so the live connectedCallback runs setup().\n deferredStaticElements.delete(el);\n // Force a clean re-enter of connectedCallback. Browsers fire that\n // callback exactly once per insertion, so we toggle the element out\n // and back in. Avoid this if the element is the document root.\n const parent = el.parentNode;\n const next = el.nextSibling;\n if (parent) {\n parent.removeChild(el);\n parent.insertBefore(el, next);\n }\n }\n }\n}\n\nexport interface ComponentContext {\n host: HTMLElement;\n /** Run cleanup when the component is removed. */\n onDispose(fn: Disposer): void;\n /**\n * Reactive attribute accessor. Returns a Signal<string> that updates\n * automatically whenever the attribute changes on the host element.\n *\n * const variant = ctx.attr(\"variant\", \"primary\");\n * return () => html`<div class=${variant()}>…</div>`;\n *\n * No MutationObserver boilerplate needed. The signal updates via a\n * per-instance MutationObserver registered during setup().\n */\n attr(name: string): Signal<string | null>;\n attr(name: string, defaultValue: string): Signal<string>;\n}\n\nexport type SetupFn = (ctx: ComponentContext) => () => TemplateResult;\n\nexport type StyleInput = string | CSSResult | Array<string | CSSResult>;\n\nexport interface ComponentOptions {\n /** Enable Shadow DOM (default: true). */\n shadow?: boolean;\n /**\n * Component styles. Accepts:\n * - a CSS string (quick start)\n * - a CSSStyleSheet via `css\\`...\\`` (recommended — one copy in memory)\n * - an array of the above\n */\n styles?: StyleInput;\n}\n\nexport function component(\n tagName: string,\n setup: SetupFn,\n options: ComponentOptions = {},\n): void {\n if (!tagName.includes(\"-\")) {\n warnOnce(\n `component-invalid-tag-${tagName}`,\n `component(\"${tagName}\") skipped: Custom Element names must contain a hyphen.`,\n );\n return;\n }\n\n const existingMeta = registered.get(tagName);\n if (customElements.get(tagName)) {\n if (\n !existingMeta ||\n existingMeta.setup !== setup ||\n !sameComponentOptions(existingMeta.options, options)\n ) {\n warnOnce(\n `component-duplicate-${tagName}`,\n `component(\"${tagName}\") is already registered. Re-registration with different setup/options is ignored.`,\n );\n }\n return; // idempotent hot-reload\n }\n\n const useShadow = options.shadow !== false;\n\n // Normalize styles to an array of CSSStyleSheet once.\n // Sheets are shared across all instances — memory is not duplicated.\n const stylesheets: CSSResult[] = normalizeStyles(\n options.styles,\n tagName,\n useShadow,\n );\n\n class MadoElement extends HTMLElement {\n #root: Element | ShadowRoot;\n #renderer: (() => TemplateResult) | null = null;\n #effectDispose: Disposer | null = null;\n #lifecycle: LifecycleHandle | null = null;\n #connected = false;\n // True while a teardown is queued on the microtask after a\n // disconnectedCallback. Used to cancel teardown when the element is\n // re-inserted in the same tick (a keyed move via insertBefore).\n #teardownQueued = false;\n #attrSignals = new Map<string, Signal<string | null>>();\n #attrDefaults = new Map<string, string | null>();\n\n constructor() {\n super();\n this.#root = useShadow\n ? this.shadowRoot ??\n this.attachShadow({\n mode: \"open\",\n serializable: true,\n } as ShadowRootInit)\n : this;\n }\n\n connectedCallback() {\n // A keyed move (each() relocating via insertBefore) fires\n // disconnectedCallback → connectedCallback synchronously. The teardown\n // queued by disconnectedCallback is cancelled here so state survives the\n // move and setup() is NOT re-run.\n this.#teardownQueued = false;\n if (this.#connected) return;\n\n // Deferred activation inside a static snapshot tree. The runtime keeps\n // setup()/effect() asleep until the SPA performs root takeover; the\n // existing DSD inside shadowRoot stays as inert first-paint markup.\n // When the SPA replaces the static root, this element is either\n // removed with that tree (the common case) or re-inserted live —\n // in which case connectedCallback() runs again and reaches setup().\n if (isInsideStaticTree(this)) {\n _markDeferredForStatic(this);\n return;\n }\n if (typeof __MADO_DEVTOOLS__ === \"undefined\" || __MADO_DEVTOOLS__) emitDevtools(\"component:connect\", this, { tagName });\n\n if (stylesheets.length > 0) {\n if (useShadow) {\n adopt(this.#root as ShadowRoot, ...stylesheets);\n } else {\n installGlobalSheets(stylesheets);\n }\n }\n\n // create a lifecycle for this component. Any function\n // called from setup() (resource, ...) will see it via\n // getCurrentLifecycle() and register its own cleanup.\n const lifecycle = createLifecycle();\n const host = this;\n\n const attr = ((name: string, defaultValue?: string) => {\n let s = host.#attrSignals.get(name);\n if (!s) {\n const fallback = defaultValue ?? null;\n host.#attrDefaults.set(name, fallback);\n s = signal(host.getAttribute(name) ?? fallback);\n host.#attrSignals.set(name, s);\n }\n return s;\n }) as ComponentContext[\"attr\"];\n\n const ctx: ComponentContext = {\n host: this,\n // ctx.onDispose proxies to lifecycle — the single source of truth\n // for component cleanups (including auto-cleanup from\n // resource(), navigator listeners, etc.).\n onDispose: (fn) => lifecycle.onDispose(fn),\n attr,\n };\n\n try {\n const renderer = runInLifecycle(lifecycle, () => setup(ctx));\n if (typeof renderer !== \"function\") {\n throw new TypeError(`component(\"${tagName}\") setup must return a renderer function`);\n }\n\n // After setup(), install a single MutationObserver for all attrs\n // registered via ctx.attr().\n if (this.#attrSignals.size > 0) {\n const attrNames = [...this.#attrSignals.keys()];\n const obs = new MutationObserver((mutations) => {\n for (const m of mutations) {\n const s = this.#attrSignals.get(m.attributeName!);\n const fallback = this.#attrDefaults.get(m.attributeName!) ?? null;\n if (s) s.set(this.getAttribute(m.attributeName!) ?? fallback);\n }\n });\n obs.observe(this, { attributes: true, attributeFilter: attrNames });\n lifecycle.onDispose(() => obs.disconnect());\n }\n\n const effectDispose = runInLifecycle(lifecycle, () => effect(() => {\n if (typeof __MADO_DEVTOOLS__ === \"undefined\" || __MADO_DEVTOOLS__) emitDevtools(\"component:render\", this, { tagName });\n render(renderer(), this.#root);\n }));\n this.#renderer = renderer;\n this.#effectDispose = effectDispose;\n this.#lifecycle = lifecycle;\n this.#connected = true;\n if (typeof __MADO_DEVTOOLS__ === \"undefined\" || __MADO_DEVTOOLS__) emitDevtools(\"component:ready\", this, { tagName });\n } catch (err) {\n unmount(this.#root);\n lifecycle.dispose();\n this.#renderer = null;\n this.#effectDispose = null;\n this.#lifecycle = null;\n this.#attrSignals.clear();\n this.#attrDefaults.clear();\n this.#connected = false;\n if (typeof __MADO_DEVTOOLS__ === \"undefined\" || __MADO_DEVTOOLS__) emitDevtools(\"component:error\", this, { tagName, error: err });\n throw err;\n }\n }\n\n disconnectedCallback() {\n // A deferred snapshot leaf was removed by takeover before setup() ever\n // ran. Clear the deferred marker so the registry does not accumulate\n // dead references.\n deferredStaticElements.delete(this);\n\n // Defer teardown to a microtask. A keyed move (each() relocating a node\n // via insertBefore) fires disconnectedCallback → connectedCallback in the\n // same tick; connectedCallback clears #teardownQueued so the teardown is\n // skipped and component state survives the move. A genuine removal leaves\n // the element disconnected, so the microtask tears it down.\n if (this.#teardownQueued) return;\n this.#teardownQueued = true;\n queueMicrotask(() => {\n if (!this.#teardownQueued) return; // re-inserted in the same tick\n this.#teardownQueued = false;\n if (this.isConnected) return; // moved back into the document\n this.#teardown();\n });\n }\n\n /** Synchronously dispose effects and lifecycle. */\n #teardown() {\n if (typeof __MADO_DEVTOOLS__ === \"undefined\" || __MADO_DEVTOOLS__) emitDevtools(\"component:dispose\", this, { tagName });\n this.#effectDispose?.();\n this.#effectDispose = null;\n unmount(this.#root);\n this.#lifecycle?.dispose();\n this.#lifecycle = null;\n this.#renderer = null;\n this.#attrSignals.clear();\n this.#attrDefaults.clear();\n this.#connected = false;\n }\n\n }\n\n customElements.define(tagName, MadoElement);\n registered.set(tagName, { setup, options });\n}\n\n// ---------- helpers ----------\n\nfunction normalizeStyles(\n input: StyleInput | undefined,\n tagName: string,\n useShadow: boolean,\n): CSSResult[] {\n if (!input) return [];\n const arr = Array.isArray(input) ? input : [input];\n return arr.map((s) => {\n let sheet: CSSResult;\n if (typeof s === \"string\") {\n sheet = createStylesheet(s);\n } else {\n sheet = s;\n }\n // light DOM: scope by tag name\n return useShadow ? sheet : scopeStyles(tagName, sheet);\n });\n}\n\nconst installedGlobal = new WeakSet<CSSResult>();\nconst registered = new Map<\n string,\n { setup: SetupFn; options: ComponentOptions }\n>();\n\nfunction sameComponentOptions(\n a: ComponentOptions,\n b: ComponentOptions,\n): boolean {\n if (a.shadow !== b.shadow) return false;\n if (a.styles !== b.styles) return false;\n return true;\n}\n\nfunction installGlobalSheets(sheets: CSSResult[]): void {\n const toAdd = sheets.filter((s) => !installedGlobal.has(s));\n if (toAdd.length === 0) return;\n for (const s of toAdd) installedGlobal.add(s);\n document.adoptedStyleSheets = [...document.adoptedStyleSheets, ...toAdd];\n}\n\n// Convenience re-export.\nexport { html };\n"]}
|
package/dist/src/context.d.ts
CHANGED
|
@@ -1,40 +1,21 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
*
|
|
4
|
-
* const ThemeCtx = createContext<'light'|'dark'>('light');
|
|
5
|
-
*
|
|
6
|
-
* component('x-app', ({ host }) => {
|
|
7
|
-
* provide(host, ThemeCtx, signal('dark'));
|
|
8
|
-
* return () => html`<x-child/>`;
|
|
9
|
-
* });
|
|
10
|
-
*
|
|
11
|
-
* component('x-child', ({ host }) => {
|
|
12
|
-
* const theme = inject(host, ThemeCtx); // signal: () => 'dark' | 'light'
|
|
13
|
-
* return () => html`<div data-theme=${theme}>...</div>`;
|
|
14
|
-
* });
|
|
15
|
-
*
|
|
16
|
-
* How it works:
|
|
17
|
-
* provide() listens for the 'mado:context' event on the host — when a child
|
|
18
|
-
* component dispatches it (bubbles), the parent writes the current signal
|
|
19
|
-
* into the event detail and calls preventDefault.
|
|
20
|
-
* inject() dispatches the event and reads the result.
|
|
21
|
-
* This fully conforms to the Web Components Community Context Protocol.
|
|
22
|
-
*/
|
|
23
|
-
import { type Signal } from "./signal.js";
|
|
1
|
+
/** Context DI implemented through the Web Components Context Protocol. */
|
|
2
|
+
import { type Disposer, type Signal } from "./signal.js";
|
|
24
3
|
export interface Context<T> {
|
|
25
4
|
readonly _ctx: true;
|
|
26
5
|
readonly key: symbol;
|
|
27
6
|
readonly defaultValue: T;
|
|
28
7
|
}
|
|
8
|
+
export interface ContextRequestEventDetail<T = unknown> {
|
|
9
|
+
context: unknown;
|
|
10
|
+
callback(value: T, unsubscribe?: Disposer): void;
|
|
11
|
+
subscribe?: boolean;
|
|
12
|
+
}
|
|
29
13
|
export declare function createContext<T>(defaultValue: T): Context<T>;
|
|
30
14
|
/**
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*/
|
|
34
|
-
export declare function provide<T>(host: HTMLElement, ctx: Context<T>, initial: T | Signal<T>): Signal<T>;
|
|
35
|
-
/**
|
|
36
|
-
* Request a context value. Walks up the DOM tree (including
|
|
37
|
-
* Shadow DOM via composed) to the first provider. If not found —
|
|
38
|
-
* returns the defaultValue wrapped in a signal.
|
|
15
|
+
* Provide a context value from a host. The returned signal updates protocol
|
|
16
|
+
* subscribers. Listener/effect cleanup follows the current Mado lifecycle.
|
|
39
17
|
*/
|
|
40
|
-
export declare function
|
|
18
|
+
export declare function provide<T>(host: HTMLElement, context: Context<T>, initial: T | Signal<T>): Signal<T>;
|
|
19
|
+
/** Request the nearest provider and expose its current value as a Signal. */
|
|
20
|
+
export declare function inject<T>(host: HTMLElement, context: Context<T>): Signal<T>;
|
|
21
|
+
//# sourceMappingURL=context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/context.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAE1E,OAAO,EAAkB,KAAK,QAAQ,EAAE,KAAK,MAAM,EAAE,MAAM,aAAa,CAAC;AAMzE,MAAM,WAAW,OAAO,CAAC,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;IACpB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,YAAY,EAAE,CAAC,CAAC;CAC1B;AAED,MAAM,WAAW,yBAAyB,CAAC,CAAC,GAAG,OAAO;IACpD,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,KAAK,EAAE,CAAC,EAAE,WAAW,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAC;IACjD,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,wBAAgB,aAAa,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAM5D;AAED;;;GAGG;AACH,wBAAgB,OAAO,CAAC,CAAC,EACvB,IAAI,EAAE,WAAW,EACjB,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EACnB,OAAO,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,GACrB,MAAM,CAAC,CAAC,CAAC,CAiDX;AAED,6EAA6E;AAC7E,wBAAgB,MAAM,CAAC,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAyB3E"}
|
package/dist/src/context.js
CHANGED
|
@@ -1,67 +1,92 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
* component('x-app', ({ host }) => {
|
|
7
|
-
* provide(host, ThemeCtx, signal('dark'));
|
|
8
|
-
* return () => html`<x-child/>`;
|
|
9
|
-
* });
|
|
10
|
-
*
|
|
11
|
-
* component('x-child', ({ host }) => {
|
|
12
|
-
* const theme = inject(host, ThemeCtx); // signal: () => 'dark' | 'light'
|
|
13
|
-
* return () => html`<div data-theme=${theme}>...</div>`;
|
|
14
|
-
* });
|
|
15
|
-
*
|
|
16
|
-
* How it works:
|
|
17
|
-
* provide() listens for the 'mado:context' event on the host — when a child
|
|
18
|
-
* component dispatches it (bubbles), the parent writes the current signal
|
|
19
|
-
* into the event detail and calls preventDefault.
|
|
20
|
-
* inject() dispatches the event and reads the result.
|
|
21
|
-
* This fully conforms to the Web Components Community Context Protocol.
|
|
22
|
-
*/
|
|
23
|
-
import { signal } from "./signal.js";
|
|
24
|
-
const CONTEXT_EVENT = "mado:context";
|
|
1
|
+
/** Context DI implemented through the Web Components Context Protocol. */
|
|
2
|
+
import { effect, signal } from "./signal.js";
|
|
3
|
+
import { getCurrentLifecycle } from "./lifecycle.js";
|
|
4
|
+
import { warnOnce } from "./diagnostics.js";
|
|
5
|
+
const CONTEXT_EVENT = "context-request";
|
|
25
6
|
export function createContext(defaultValue) {
|
|
26
7
|
return {
|
|
27
8
|
_ctx: true,
|
|
28
|
-
key: Symbol("
|
|
9
|
+
key: Symbol("mado-context"),
|
|
29
10
|
defaultValue,
|
|
30
11
|
};
|
|
31
12
|
}
|
|
32
13
|
/**
|
|
33
|
-
*
|
|
34
|
-
*
|
|
14
|
+
* Provide a context value from a host. The returned signal updates protocol
|
|
15
|
+
* subscribers. Listener/effect cleanup follows the current Mado lifecycle.
|
|
35
16
|
*/
|
|
36
|
-
export function provide(host,
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
const handler = (
|
|
41
|
-
const
|
|
42
|
-
if (
|
|
17
|
+
export function provide(host, context, initial) {
|
|
18
|
+
const source = isSignal(initial) ? initial : signal(initial);
|
|
19
|
+
const subscribers = new Set();
|
|
20
|
+
let disposed = false;
|
|
21
|
+
const handler = (event) => {
|
|
22
|
+
const request = event;
|
|
23
|
+
if (request.detail?.context !== context.key)
|
|
24
|
+
return;
|
|
25
|
+
event.stopPropagation();
|
|
26
|
+
if (!request.detail.subscribe) {
|
|
27
|
+
request.detail.callback(source.peek());
|
|
43
28
|
return;
|
|
44
|
-
|
|
45
|
-
|
|
29
|
+
}
|
|
30
|
+
const callback = request.detail.callback;
|
|
31
|
+
let active = true;
|
|
32
|
+
const unsubscribe = () => {
|
|
33
|
+
if (!active)
|
|
34
|
+
return;
|
|
35
|
+
active = false;
|
|
36
|
+
subscribers.delete(callback);
|
|
37
|
+
};
|
|
38
|
+
subscribers.add(callback);
|
|
39
|
+
callback(source.peek(), unsubscribe);
|
|
46
40
|
};
|
|
47
41
|
host.addEventListener(CONTEXT_EVENT, handler);
|
|
48
|
-
|
|
42
|
+
const stopPublish = effect(() => {
|
|
43
|
+
const value = source();
|
|
44
|
+
for (const callback of [...subscribers])
|
|
45
|
+
callback(value);
|
|
46
|
+
});
|
|
47
|
+
const dispose = () => {
|
|
48
|
+
if (disposed)
|
|
49
|
+
return;
|
|
50
|
+
disposed = true;
|
|
51
|
+
host.removeEventListener(CONTEXT_EVENT, handler);
|
|
52
|
+
subscribers.clear();
|
|
53
|
+
stopPublish();
|
|
54
|
+
};
|
|
55
|
+
const lifecycle = getCurrentLifecycle();
|
|
56
|
+
if (lifecycle)
|
|
57
|
+
lifecycle.onDispose(dispose);
|
|
58
|
+
else {
|
|
59
|
+
warnOnce("context-provider-outside-lifecycle", "provide() called outside a component/page lifecycle; its event listener cannot be cleaned up automatically.");
|
|
60
|
+
}
|
|
61
|
+
return source;
|
|
49
62
|
}
|
|
50
|
-
/**
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
63
|
+
/** Request the nearest provider and expose its current value as a Signal. */
|
|
64
|
+
export function inject(host, context) {
|
|
65
|
+
const value = signal(context.defaultValue);
|
|
66
|
+
let unsubscribe;
|
|
67
|
+
const detail = {
|
|
68
|
+
context: context.key,
|
|
69
|
+
subscribe: true,
|
|
70
|
+
callback(next, dispose) {
|
|
71
|
+
value.set(next);
|
|
72
|
+
if (dispose)
|
|
73
|
+
unsubscribe = dispose;
|
|
74
|
+
},
|
|
75
|
+
};
|
|
57
76
|
host.dispatchEvent(new CustomEvent(CONTEXT_EVENT, {
|
|
58
77
|
detail,
|
|
59
78
|
bubbles: true,
|
|
60
79
|
composed: true,
|
|
61
|
-
cancelable: true,
|
|
62
80
|
}));
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
81
|
+
getCurrentLifecycle()?.onDispose(() => {
|
|
82
|
+
unsubscribe?.();
|
|
83
|
+
unsubscribe = undefined;
|
|
84
|
+
});
|
|
85
|
+
return value;
|
|
86
|
+
}
|
|
87
|
+
function isSignal(value) {
|
|
88
|
+
return typeof value === "function" &&
|
|
89
|
+
typeof value.set === "function" &&
|
|
90
|
+
typeof value.peek === "function";
|
|
66
91
|
}
|
|
67
92
|
//# sourceMappingURL=context.js.map
|
package/dist/src/context.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/context.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/context.ts"],"names":[],"mappings":"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","sourcesContent":["/** Context DI implemented through the Web Components Context Protocol. */\n\nimport { effect, signal, type Disposer, type Signal } from \"./signal.js\";\nimport { getCurrentLifecycle } from \"./lifecycle.js\";\nimport { warnOnce } from \"./diagnostics.js\";\n\nconst CONTEXT_EVENT = \"context-request\";\n\nexport interface Context<T> {\n readonly _ctx: true;\n readonly key: symbol;\n readonly defaultValue: T;\n}\n\nexport interface ContextRequestEventDetail<T = unknown> {\n context: unknown;\n callback(value: T, unsubscribe?: Disposer): void;\n subscribe?: boolean;\n}\n\nexport function createContext<T>(defaultValue: T): Context<T> {\n return {\n _ctx: true,\n key: Symbol(\"mado-context\"),\n defaultValue,\n };\n}\n\n/**\n * Provide a context value from a host. The returned signal updates protocol\n * subscribers. Listener/effect cleanup follows the current Mado lifecycle.\n */\nexport function provide<T>(\n host: HTMLElement,\n context: Context<T>,\n initial: T | Signal<T>,\n): Signal<T> {\n const source = isSignal<T>(initial) ? initial : signal(initial as T);\n const subscribers = new Set<(value: T, unsubscribe?: Disposer) => void>();\n let disposed = false;\n\n const handler = (event: Event): void => {\n const request = event as CustomEvent<ContextRequestEventDetail<T>>;\n if (request.detail?.context !== context.key) return;\n event.stopPropagation();\n\n if (!request.detail.subscribe) {\n request.detail.callback(source.peek());\n return;\n }\n\n const callback = request.detail.callback;\n let active = true;\n const unsubscribe = (): void => {\n if (!active) return;\n active = false;\n subscribers.delete(callback);\n };\n subscribers.add(callback);\n callback(source.peek(), unsubscribe);\n };\n\n host.addEventListener(CONTEXT_EVENT, handler);\n const stopPublish = effect(() => {\n const value = source();\n for (const callback of [...subscribers]) callback(value);\n });\n\n const dispose = (): void => {\n if (disposed) return;\n disposed = true;\n host.removeEventListener(CONTEXT_EVENT, handler);\n subscribers.clear();\n stopPublish();\n };\n const lifecycle = getCurrentLifecycle();\n if (lifecycle) lifecycle.onDispose(dispose);\n else {\n warnOnce(\n \"context-provider-outside-lifecycle\",\n \"provide() called outside a component/page lifecycle; its event listener cannot be cleaned up automatically.\",\n );\n }\n\n return source;\n}\n\n/** Request the nearest provider and expose its current value as a Signal. */\nexport function inject<T>(host: HTMLElement, context: Context<T>): Signal<T> {\n const value = signal(context.defaultValue);\n let unsubscribe: Disposer | undefined;\n const detail: ContextRequestEventDetail<T> = {\n context: context.key,\n subscribe: true,\n callback(next, dispose) {\n value.set(next);\n if (dispose) unsubscribe = dispose;\n },\n };\n\n host.dispatchEvent(\n new CustomEvent<ContextRequestEventDetail<T>>(CONTEXT_EVENT, {\n detail,\n bubbles: true,\n composed: true,\n }),\n );\n\n getCurrentLifecycle()?.onDispose(() => {\n unsubscribe?.();\n unsubscribe = undefined;\n });\n return value;\n}\n\nfunction isSignal<T>(value: T | Signal<T>): value is Signal<T> {\n return typeof value === \"function\" &&\n typeof (value as Partial<Signal<T>>).set === \"function\" &&\n typeof (value as Partial<Signal<T>>).peek === \"function\";\n}\n"]}
|
package/dist/src/css.d.ts
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"css.d.ts","sourceRoot":"","sources":["../../src/css.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,MAAM,MAAM,SAAS,GAAG,aAAa,CAAC;AAItC;;;;;;;GAOG;AACH,wBAAgB,GAAG,CACjB,OAAO,EAAE,oBAAoB,EAC7B,GAAG,MAAM,EAAE,OAAO,EAAE,GACnB,SAAS,CAwBX;AAoBD;;;;;;;;;GASG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,GAAG,MAAM,CASrE;AAED;;;GAGG;AACH,wBAAgB,KAAK,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,MAAM,EAAE,SAAS,EAAE,GAAG,IAAI,CAMpE;AAgBD;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,GAAG,SAAS,CA4BxE"}
|
package/dist/src/css.js
CHANGED
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
* - if the browser has @scope (Chrome 118+, Safari 17.4+), we wrap styles.
|
|
17
17
|
* - fallback: prefix selectors with the tag — a simple regex at string level.
|
|
18
18
|
*/
|
|
19
|
+
const stylesheetText = new WeakMap();
|
|
19
20
|
/**
|
|
20
21
|
* Tagged literal for CSS. Returns a CSSStyleSheet ready for
|
|
21
22
|
* adoptedStyleSheets. Value interpolation — only primitives or
|
|
@@ -34,8 +35,7 @@ export function css(strings, ...values) {
|
|
|
34
35
|
continue;
|
|
35
36
|
if (v instanceof CSSStyleSheet) {
|
|
36
37
|
// composition — insert all rules
|
|
37
|
-
|
|
38
|
-
text += rule.cssText;
|
|
38
|
+
text += getStylesheetText(v) ?? rulesText(v);
|
|
39
39
|
continue;
|
|
40
40
|
}
|
|
41
41
|
const s = String(v);
|
|
@@ -47,8 +47,24 @@ export function css(strings, ...values) {
|
|
|
47
47
|
}
|
|
48
48
|
const sheet = new CSSStyleSheet();
|
|
49
49
|
sheet.replaceSync(text);
|
|
50
|
+
stylesheetText.set(sheet, text);
|
|
50
51
|
return sheet;
|
|
51
52
|
}
|
|
53
|
+
/** @internal */
|
|
54
|
+
export function createStylesheet(text) {
|
|
55
|
+
const sheet = new CSSStyleSheet();
|
|
56
|
+
sheet.replaceSync(text);
|
|
57
|
+
stylesheetText.set(sheet, text);
|
|
58
|
+
return sheet;
|
|
59
|
+
}
|
|
60
|
+
/** @internal */
|
|
61
|
+
export function getStylesheetText(sheet) {
|
|
62
|
+
return stylesheetText.get(sheet);
|
|
63
|
+
}
|
|
64
|
+
/** @internal */
|
|
65
|
+
export function getStylesheetHash(sheet) {
|
|
66
|
+
return stableHash(getStylesheetText(sheet) ?? rulesText(sheet));
|
|
67
|
+
}
|
|
52
68
|
/**
|
|
53
69
|
* Build an inline-style string from a CSS variables object.
|
|
54
70
|
*
|
|
@@ -104,9 +120,7 @@ const hasScope = (() => {
|
|
|
104
120
|
* → 'x-button button { color: red }'
|
|
105
121
|
*/
|
|
106
122
|
export function scopeStyles(tagName, sheet) {
|
|
107
|
-
|
|
108
|
-
for (const rule of sheet.cssRules)
|
|
109
|
-
text += rule.cssText;
|
|
123
|
+
const text = getStylesheetText(sheet) ?? rulesText(sheet);
|
|
110
124
|
let scoped;
|
|
111
125
|
if (hasScope) {
|
|
112
126
|
scoped = `@scope (${tagName}) { ${text} }`;
|
|
@@ -132,6 +146,21 @@ export function scopeStyles(tagName, sheet) {
|
|
|
132
146
|
}
|
|
133
147
|
const out = new CSSStyleSheet();
|
|
134
148
|
out.replaceSync(scoped);
|
|
149
|
+
stylesheetText.set(out, scoped);
|
|
135
150
|
return out;
|
|
136
151
|
}
|
|
152
|
+
function rulesText(sheet) {
|
|
153
|
+
let text = "";
|
|
154
|
+
for (const rule of sheet.cssRules)
|
|
155
|
+
text += rule.cssText;
|
|
156
|
+
return text;
|
|
157
|
+
}
|
|
158
|
+
function stableHash(text) {
|
|
159
|
+
let hash = 0x811c9dc5;
|
|
160
|
+
for (let i = 0; i < text.length; i++) {
|
|
161
|
+
hash ^= text.charCodeAt(i);
|
|
162
|
+
hash = Math.imul(hash, 0x01000193);
|
|
163
|
+
}
|
|
164
|
+
return (hash >>> 0).toString(36);
|
|
165
|
+
}
|
|
137
166
|
//# sourceMappingURL=css.js.map
|