@vielzeug/ore 26.11.8 → 26.11.10
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/dist/base-element.cjs +1 -1
- package/dist/base-element.cjs.map +1 -1
- package/dist/base-element.d.ts +12 -26
- package/dist/base-element.d.ts.map +1 -1
- package/dist/base-element.js +1 -1
- package/dist/base-element.js.map +1 -1
- package/dist/context.cjs.map +1 -1
- package/dist/context.js.map +1 -1
- package/dist/define.cjs +1 -1
- package/dist/define.cjs.map +1 -1
- package/dist/define.d.ts.map +1 -1
- package/dist/define.js +1 -1
- package/dist/define.js.map +1 -1
- package/dist/directives/each.cjs +1 -1
- package/dist/directives/each.cjs.map +1 -1
- package/dist/directives/each.d.ts +5 -0
- package/dist/directives/each.d.ts.map +1 -1
- package/dist/directives/each.js +1 -1
- package/dist/directives/each.js.map +1 -1
- package/dist/directives/model.cjs.map +1 -1
- package/dist/directives/model.d.ts +2 -3
- package/dist/directives/model.d.ts.map +1 -1
- package/dist/directives/model.js.map +1 -1
- package/dist/directives/unsafe-html.cjs +1 -1
- package/dist/directives/unsafe-html.cjs.map +1 -1
- package/dist/directives/unsafe-html.d.ts.map +1 -1
- package/dist/directives/unsafe-html.js +1 -1
- package/dist/directives/unsafe-html.js.map +1 -1
- package/dist/directives/when.cjs +1 -1
- package/dist/directives/when.cjs.map +1 -1
- package/dist/directives/when.d.ts.map +1 -1
- package/dist/directives/when.js +1 -1
- package/dist/directives/when.js.map +1 -1
- package/dist/errors.cjs +1 -1
- package/dist/errors.cjs.map +1 -1
- package/dist/errors.d.ts +2 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +1 -1
- package/dist/errors.js.map +1 -1
- package/dist/host-bind.cjs +1 -1
- package/dist/host-bind.cjs.map +1 -1
- package/dist/host-bind.d.ts +2 -2
- package/dist/host-bind.js +1 -1
- package/dist/host-bind.js.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/ore.cjs +2 -2
- package/dist/ore.cjs.map +1 -1
- package/dist/ore.iife.js +2 -2
- package/dist/ore.iife.js.map +1 -1
- package/dist/ore.js +423 -373
- package/dist/ore.js.map +1 -1
- package/dist/props.cjs +1 -1
- package/dist/props.cjs.map +1 -1
- package/dist/props.d.ts +6 -10
- package/dist/props.d.ts.map +1 -1
- package/dist/props.js +1 -1
- package/dist/props.js.map +1 -1
- package/dist/runtime.cjs +1 -1
- package/dist/runtime.cjs.map +1 -1
- package/dist/runtime.d.ts +22 -9
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +1 -1
- package/dist/runtime.js.map +1 -1
- package/dist/slots.cjs +1 -1
- package/dist/slots.cjs.map +1 -1
- package/dist/slots.d.ts.map +1 -1
- package/dist/slots.js +1 -1
- package/dist/slots.js.map +1 -1
- package/dist/template/binding-types.d.ts +3 -9
- package/dist/template/binding-types.d.ts.map +1 -1
- package/dist/template/bindings.cjs +1 -1
- package/dist/template/bindings.cjs.map +1 -1
- package/dist/template/bindings.d.ts +8 -9
- package/dist/template/bindings.d.ts.map +1 -1
- package/dist/template/bindings.js +1 -1
- package/dist/template/bindings.js.map +1 -1
- package/dist/template/instantiator.cjs +1 -1
- package/dist/template/instantiator.cjs.map +1 -1
- package/dist/template/instantiator.d.ts.map +1 -1
- package/dist/template/instantiator.js +1 -1
- package/dist/template/instantiator.js.map +1 -1
- package/dist/template/result.cjs +1 -1
- package/dist/template/result.cjs.map +1 -1
- package/dist/template/result.d.ts +10 -16
- package/dist/template/result.d.ts.map +1 -1
- package/dist/template/result.js +1 -1
- package/dist/template/result.js.map +1 -1
- package/dist/testing/index.d.ts +1 -1
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/mount.cjs +1 -1
- package/dist/testing/mount.cjs.map +1 -1
- package/dist/testing/mount.d.ts.map +1 -1
- package/dist/testing/mount.js +1 -1
- package/dist/testing/mount.js.map +1 -1
- package/dist/testing/render-hook.cjs +1 -1
- package/dist/testing/render-hook.cjs.map +1 -1
- package/dist/testing/render-hook.d.ts +16 -13
- package/dist/testing/render-hook.d.ts.map +1 -1
- package/dist/testing/render-hook.js +1 -1
- package/dist/testing/render-hook.js.map +1 -1
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.js.map +1 -1
- package/dist/utils/brand.cjs.map +1 -1
- package/dist/utils/brand.d.ts +1 -1
- package/dist/utils/brand.js.map +1 -1
- package/dist/utils/dom.cjs +1 -1
- package/dist/utils/dom.cjs.map +1 -1
- package/dist/utils/dom.d.ts +12 -10
- package/dist/utils/dom.d.ts.map +1 -1
- package/dist/utils/dom.js +1 -1
- package/dist/utils/dom.js.map +1 -1
- package/package.json +5 -5
- package/dist/directives/live.cjs +0 -2
- package/dist/directives/live.cjs.map +0 -1
- package/dist/directives/live.d.ts +0 -23
- package/dist/directives/live.d.ts.map +0 -1
- package/dist/directives/live.js +0 -2
- package/dist/directives/live.js.map +0 -1
package/dist/slots.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slots.js","names":["SLOT_DEFAULT","normalizeSlotName","slotName","createSlots","slotSignals","slotNodesByName","slotCleanupMap","ensureSlotEntry","entry","normalizedName","signal","areElementsEqual","prev","next","i","recomputeSlot","normalized","name","slotsForName","assigned","slotEl","hasElements","bindSlot","setForName","onChange","unbindSlot","cleanup","bindAllSlots","host","recomputeAllSlots","observer","onMounted","mutation","mutations","node","onCleanup","slotsByElement","useSlots","ctx","requireSetupContext"],"sources":["../src/slots.ts"],"sourcesContent":["/**\n * Slot observation and reactive slot signals.\n *\n * `slots.has(name?)`: Signal<boolean>: whether a named slot has assigned elements.\n * `slots.elements(name?)`: Signal<Element[]>: assigned elements for a slot (flattened).\n */\n\nimport { type Readable, type Signal, signal } from '@vielzeug/ripple';\n\nimport { onCleanup, onMounted, requireSetupContext } from './runtime';\n\nexport type ComponentSlots<SlotNames extends string = string> = {\n elements: (name?: SlotNames) => Readable<Element[]>;\n has: (name?: SlotNames) => Readable<boolean>;\n};\n\nconst SLOT_DEFAULT = 'default';\nconst normalizeSlotName = (slotName: string | null | undefined): string => slotName || SLOT_DEFAULT;\n\nconst createSlots = (host: HTMLElement): ComponentSlots<string> => {\n type SlotEntry = {\n elements: Signal<Element[]>;\n presence: Signal<boolean>;\n };\n\n const slotSignals = new Map<string, SlotEntry>();\n const slotNodesByName = new Map<string, Set<HTMLSlotElement>>();\n const slotCleanupMap = new Map<HTMLSlotElement, () => void>();\n\n const ensureSlotEntry = (normalizedName: string): SlotEntry => {\n let entry = slotSignals.get(normalizedName);\n\n if (!entry) {\n entry = {\n elements: signal<Element[]>([]),\n presence: signal(false),\n };\n slotSignals.set(normalizedName, entry);\n }\n\n return entry;\n };\n\n const areElementsEqual = (prev: Element[], next: Element[]): boolean => {\n if (prev.length !== next.length) return false;\n\n for (let i = 0; i < prev.length; i++) {\n if (prev[i] !== next[i]) return false;\n }\n\n return true;\n };\n\n const recomputeSlot = (name: string): void => {\n const normalized = normalizeSlotName(name);\n const slotsForName = slotNodesByName.get(normalized);\n const assigned: Element[] = [];\n\n if (slotsForName) {\n for (const slotEl of slotsForName) {\n assigned.push(...slotEl.assignedElements({ flatten: true }));\n }\n }\n\n const entry = ensureSlotEntry(normalized);\n\n if (!areElementsEqual(entry.elements.value, assigned)) entry.elements.value = assigned;\n\n const hasElements = assigned.length > 0;\n\n if (entry.presence.value !== hasElements) entry.presence.value = hasElements;\n };\n\n const bindSlot = (slotEl: HTMLSlotElement): void => {\n if (slotCleanupMap.has(slotEl)) return;\n\n const name = normalizeSlotName(slotEl.getAttribute('name'));\n const setForName = slotNodesByName.get(name) ?? new Set<HTMLSlotElement>();\n\n setForName.add(slotEl);\n slotNodesByName.set(name, setForName);\n\n const onChange = () => recomputeSlot(name);\n\n slotEl.addEventListener('slotchange', onChange);\n\n slotCleanupMap.set(slotEl, () => {\n slotEl.removeEventListener('slotchange', onChange);\n });\n\n recomputeSlot(name);\n };\n\n const unbindSlot = (slotEl: HTMLSlotElement): void => {\n const cleanup = slotCleanupMap.get(slotEl);\n\n if (!cleanup) return;\n\n cleanup();\n slotCleanupMap.delete(slotEl);\n\n const name = normalizeSlotName(slotEl.getAttribute('name'));\n const setForName = slotNodesByName.get(name);\n\n if (setForName) {\n setForName.delete(slotEl);\n\n if (setForName.size === 0) slotNodesByName.delete(name);\n }\n\n recomputeSlot(name);\n };\n\n const bindAllSlots = (): void => {\n host.shadowRoot?.querySelectorAll('slot').forEach((slotEl) => {\n bindSlot(slotEl);\n });\n };\n\n const recomputeAllSlots = (): void => {\n for (const name of slotNodesByName.keys()) {\n recomputeSlot(name);\n }\n };\n\n // Watch for dynamically-inserted <slot> elements (e.g. inside when(), each()).\n let observer: MutationObserver | null = null;\n\n // Single init pass, run after the first render: binds slots already present\n // (pre-upgrade markup and template-rendered ones alike) and starts observation\n // for slots inserted later. The observer must stay connected for the component's\n // lifetime: it is the only way to detect a *first* <slot> appearing dynamically\n // (e.g. a when() branch that renders a slot), so it cannot be disconnected when\n // the bound-slot count drops to zero.\n const initSlots = (): undefined => {\n bindAllSlots();\n recomputeAllSlots();\n\n if (!observer && host.shadowRoot) {\n observer = new MutationObserver((mutations) => {\n for (const mutation of mutations) {\n for (const node of mutation.removedNodes) {\n if (node instanceof HTMLSlotElement) unbindSlot(node);\n }\n }\n\n bindAllSlots();\n\n if (slotCleanupMap.size > 0) recomputeAllSlots();\n });\n observer.observe(host.shadowRoot, { childList: true, subtree: true });\n }\n\n return undefined;\n };\n\n onMounted(initSlots);\n\n onCleanup(() => {\n observer?.disconnect();\n observer = null;\n\n for (const cleanup of slotCleanupMap.values()) cleanup();\n\n slotCleanupMap.clear();\n slotNodesByName.clear();\n slotSignals.clear();\n\n // The element instance survives disconnect/reconnect (custom elements aren't recreated),\n // but this registry's observer/listeners are torn down above: drop the cache entry so a\n // subsequent reconnect's setup() rebuilds a live registry instead of reusing a dead one.\n slotsByElement.delete(host);\n });\n\n return {\n elements: (name?: string) => ensureSlotEntry(normalizeSlotName(name)).elements,\n has: (name?: string) => ensureSlotEntry(normalizeSlotName(name)).presence,\n };\n};\n\n// Keyed by the host element, not the ephemeral `RuntimeContext`: `onMounted()` callbacks each\n// run with their own freshly-created context object (see base-element.ts's\n// `_scheduleMountCallbacks`), so keying this on `RuntimeContext` would silently create a second,\n// independent registry (a second `MutationObserver`, a second signal set) every time `useSlots()`\n// was called from inside `onMounted()` rather than directly in `setup()`: a real bug the\n// \"one registry per instance\" doc comment below never actually held for that (common) case.\n/** One slot registry per component instance: reused across repeated `useSlots()` calls. */\nconst slotsByElement = new WeakMap<HTMLElement, ComponentSlots<string>>();\n\n/**\n * Returns reactive slot presence / element signals for the current component.\n * Safe to call multiple times during `setup()`: the underlying slot registry\n * (MutationObserver + `slotchange` listeners) is created once per instance.\n *\n * Pass a `SlotNames` type parameter for typed slot names:\n * ```ts\n * const slots = useSlots<'header' | 'footer'>();\n * slots.has('header'); // typed ✓\n * ```\n */\nexport const useSlots = <SlotNames extends string = string>(): ComponentSlots<SlotNames> => {\n const ctx = requireSetupContext('useSlots');\n let entry = slotsByElement.get(ctx.element);\n\n if (!entry) {\n entry = createSlots(ctx.element);\n slotsByElement.set(ctx.element, entry);\n }\n\n return entry as ComponentSlots<SlotNames>;\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"slots.js","names":["onCleanup$1","SLOT_DEFAULT","normalizeSlotName","slotName","createSlots","slotSignals","slotNodesByName","slotCleanupMap","ensureSlotEntry","entry","normalizedName","signal","areElementsEqual","prev","next","i","recomputeSlot","normalized","name","slotsForName","assigned","slotEl","hasElements","bindSlot","setForName","onChange","unbindSlot","cleanup","bindAllSlots","host","recomputeAllSlots","isOrContainsSlot","node","unbindSlotsIn","inner","observer","onMounted","slotsTouched","mutation","mutations","slotsByElement","useSlots","ctx","requireSetupContext"],"sources":["../src/slots.ts"],"sourcesContent":["/**\n * Slot observation and reactive slot signals.\n *\n * `slots.has(name?)`: Signal<boolean>: whether a named slot has assigned elements.\n * `slots.elements(name?)`: Signal<Element[]>: assigned elements for a slot (flattened).\n */\n\nimport { type Readable, type Signal, signal } from '@vielzeug/ripple';\n\nimport { onCleanup, onMounted, requireSetupContext } from './runtime';\n\nexport type ComponentSlots<SlotNames extends string = string> = {\n elements: (name?: SlotNames) => Readable<Element[]>;\n has: (name?: SlotNames) => Readable<boolean>;\n};\n\nconst SLOT_DEFAULT = 'default';\nconst normalizeSlotName = (slotName: string | null | undefined): string => slotName || SLOT_DEFAULT;\n\nconst createSlots = (host: HTMLElement): ComponentSlots<string> => {\n type SlotEntry = {\n elements: Signal<Element[]>;\n presence: Signal<boolean>;\n };\n\n const slotSignals = new Map<string, SlotEntry>();\n const slotNodesByName = new Map<string, Set<HTMLSlotElement>>();\n const slotCleanupMap = new Map<HTMLSlotElement, () => void>();\n\n const ensureSlotEntry = (normalizedName: string): SlotEntry => {\n let entry = slotSignals.get(normalizedName);\n\n if (!entry) {\n entry = {\n elements: signal<Element[]>([]),\n presence: signal(false),\n };\n slotSignals.set(normalizedName, entry);\n }\n\n return entry;\n };\n\n const areElementsEqual = (prev: Element[], next: Element[]): boolean => {\n if (prev.length !== next.length) return false;\n\n for (let i = 0; i < prev.length; i++) {\n if (prev[i] !== next[i]) return false;\n }\n\n return true;\n };\n\n const recomputeSlot = (name: string): void => {\n const normalized = normalizeSlotName(name);\n const slotsForName = slotNodesByName.get(normalized);\n const assigned: Element[] = [];\n\n if (slotsForName) {\n for (const slotEl of slotsForName) {\n assigned.push(...slotEl.assignedElements({ flatten: true }));\n }\n }\n\n const entry = ensureSlotEntry(normalized);\n\n if (!areElementsEqual(entry.elements.value, assigned)) entry.elements.value = assigned;\n\n const hasElements = assigned.length > 0;\n\n if (entry.presence.value !== hasElements) entry.presence.value = hasElements;\n };\n\n const bindSlot = (slotEl: HTMLSlotElement): void => {\n if (slotCleanupMap.has(slotEl)) return;\n\n const name = normalizeSlotName(slotEl.getAttribute('name'));\n const setForName = slotNodesByName.get(name) ?? new Set<HTMLSlotElement>();\n\n setForName.add(slotEl);\n slotNodesByName.set(name, setForName);\n\n const onChange = () => recomputeSlot(name);\n\n slotEl.addEventListener('slotchange', onChange);\n\n slotCleanupMap.set(slotEl, () => {\n slotEl.removeEventListener('slotchange', onChange);\n });\n\n recomputeSlot(name);\n };\n\n const unbindSlot = (slotEl: HTMLSlotElement): void => {\n const cleanup = slotCleanupMap.get(slotEl);\n\n if (!cleanup) return;\n\n cleanup();\n slotCleanupMap.delete(slotEl);\n\n const name = normalizeSlotName(slotEl.getAttribute('name'));\n const setForName = slotNodesByName.get(name);\n\n if (setForName) {\n setForName.delete(slotEl);\n\n if (setForName.size === 0) slotNodesByName.delete(name);\n }\n\n recomputeSlot(name);\n };\n\n const bindAllSlots = (): void => {\n host.shadowRoot?.querySelectorAll('slot').forEach((slotEl) => {\n bindSlot(slotEl);\n });\n };\n\n const recomputeAllSlots = (): void => {\n for (const name of slotNodesByName.keys()) {\n recomputeSlot(name);\n }\n };\n\n const isOrContainsSlot = (node: Node): boolean =>\n node instanceof HTMLSlotElement ||\n (node.nodeType === Node.ELEMENT_NODE && (node as Element).querySelector('slot') !== null);\n\n // Detached subtrees still support `querySelector`, so a slot removed inside\n // a moved/removed wrapper is unbound from the whole removed subtree, not\n // just its top node: the old direct-children-only check leaked bound slots.\n const unbindSlotsIn = (node: Node): void => {\n if (node instanceof HTMLSlotElement) {\n unbindSlot(node);\n\n return;\n }\n\n if (node.nodeType === Node.ELEMENT_NODE) {\n (node as Element).querySelectorAll('slot').forEach((inner) => {\n unbindSlot(inner);\n });\n }\n };\n\n // Watch for dynamically-inserted <slot> elements (e.g. inside when(), each()).\n let observer: MutationObserver | null = null;\n\n // Single init pass, run after the first render: binds slots already present\n // (pre-upgrade markup and template-rendered ones alike) and starts observation\n // for slots inserted later. The observer must stay connected for the component's\n // lifetime: it is the only way to detect a *first* <slot> appearing dynamically\n // (e.g. a when() branch that renders a slot), so it cannot be disconnected when\n // the bound-slot count drops to zero.\n const initSlots = (): undefined => {\n bindAllSlots();\n recomputeAllSlots();\n\n if (!observer && host.shadowRoot) {\n observer = new MutationObserver((mutations) => {\n // List UIs mutate shadow DOM constantly; only mutations that actually\n // add or remove a slot (directly or inside a moved subtree) can change\n // slot bindings. Nothing slot-related: skip the full\n // `querySelectorAll('slot')` rescan and `assignedElements()` recompute.\n let slotsTouched = false;\n\n for (const mutation of mutations) {\n for (const node of mutation.removedNodes) {\n if (!isOrContainsSlot(node)) continue;\n\n slotsTouched = true;\n unbindSlotsIn(node);\n }\n\n if (slotsTouched) continue;\n\n for (const node of mutation.addedNodes) {\n if (isOrContainsSlot(node)) {\n slotsTouched = true;\n break;\n }\n }\n }\n\n if (!slotsTouched) return;\n\n bindAllSlots();\n\n if (slotCleanupMap.size > 0) recomputeAllSlots();\n });\n observer.observe(host.shadowRoot, { childList: true, subtree: true });\n }\n\n return undefined;\n };\n\n onMounted(initSlots);\n\n onCleanup(() => {\n observer?.disconnect();\n observer = null;\n\n for (const cleanup of slotCleanupMap.values()) cleanup();\n\n slotCleanupMap.clear();\n slotNodesByName.clear();\n slotSignals.clear();\n\n // The element instance survives disconnect/reconnect (custom elements aren't recreated),\n // but this registry's observer/listeners are torn down above: drop the cache entry so a\n // subsequent reconnect's setup() rebuilds a live registry instead of reusing a dead one.\n slotsByElement.delete(host);\n });\n\n return {\n elements: (name?: string) => ensureSlotEntry(normalizeSlotName(name)).elements,\n has: (name?: string) => ensureSlotEntry(normalizeSlotName(name)).presence,\n };\n};\n\n/**\n * One slot registry per component instance: reused across repeated `useSlots()` calls in\n * `setup()` and its `onMounted` callbacks, and dropped on cleanup so a remount builds a live one.\n */\nconst slotsByElement = new WeakMap<HTMLElement, ComponentSlots<string>>();\n\n/**\n * Returns reactive slot presence / element signals for the current component.\n * Safe to call multiple times during `setup()`: the underlying slot registry\n * (MutationObserver + `slotchange` listeners) is created once per instance.\n *\n * Pass a `SlotNames` type parameter for typed slot names:\n * ```ts\n * const slots = useSlots<'header' | 'footer'>();\n * slots.has('header'); // typed ✓\n * ```\n */\nexport const useSlots = <SlotNames extends string = string>(): ComponentSlots<SlotNames> => {\n const ctx = requireSetupContext('useSlots');\n let entry = slotsByElement.get(ctx.element);\n\n if (!entry) {\n entry = createSlots(ctx.element);\n slotsByElement.set(ctx.element, entry);\n }\n\n return entry as ComponentSlots<SlotNames>;\n};\n"],"mappings":"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"}
|
|
@@ -10,12 +10,6 @@ import type { EventModifiers } from './compiler';
|
|
|
10
10
|
import type { DirectiveResult, HTMLResult, Ref, RefCallback } from './result';
|
|
11
11
|
type AttrBaseFields = {
|
|
12
12
|
el: HTMLElement;
|
|
13
|
-
/**
|
|
14
|
-
* When true the binding uses live-write semantics: stale app-state writes are
|
|
15
|
-
* skipped if the DOM value has diverged from the last programmatic write.
|
|
16
|
-
* Set when the bound value came through `live()` (see directives/live.ts).
|
|
17
|
-
*/
|
|
18
|
-
live?: true;
|
|
19
13
|
mode: 'attr' | 'bool';
|
|
20
14
|
name: string;
|
|
21
15
|
type: 'attr';
|
|
@@ -72,9 +66,9 @@ export type HtmlBinding = {
|
|
|
72
66
|
* memo computed: a reactive value is usually a function that builds nested
|
|
73
67
|
* `html` results, and building them allocates their per-node binding computeds.
|
|
74
68
|
* Ripple attaches a computed to the scope active when it is created, so a memo
|
|
75
|
-
* computed would let
|
|
76
|
-
*
|
|
77
|
-
*
|
|
69
|
+
* computed would let whichever effect's read triggers its re-derive become the
|
|
70
|
+
* owner - and dispose those bindings on its next run while this slot still shows
|
|
71
|
+
* the nodes they drive.
|
|
78
72
|
*/
|
|
79
73
|
source: (() => unknown) | Readable<unknown>;
|
|
80
74
|
type: 'html';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"binding-types.d.ts","sourceRoot":"","sources":["../../src/template/binding-types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAEzD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,KAAK,EAAE,eAAe,EAAE,UAAU,EAAE,GAAG,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAI9E,KAAK,cAAc,GAAG;IACpB,EAAE,EAAE,WAAW,CAAC;IAChB
|
|
1
|
+
{"version":3,"file":"binding-types.d.ts","sourceRoot":"","sources":["../../src/template/binding-types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAEzD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,KAAK,EAAE,eAAe,EAAE,UAAU,EAAE,GAAG,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAI9E,KAAK,cAAc,GAAG;IACpB,EAAE,EAAE,WAAW,CAAC;IAChB,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,yDAAyD;AACzD,MAAM,MAAM,iBAAiB,GAAG,cAAc,GAAG;IAAE,KAAK,EAAE,OAAO,CAAA;CAAE,CAAC;AAEpE,qDAAqD;AACrD,MAAM,MAAM,mBAAmB,GAAG,cAAc,GAAG;IAAE,MAAM,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAA;CAAE,CAAC;AAEjF,MAAM,MAAM,WAAW,GAAG,iBAAiB,GAAG,mBAAmB,CAAC;AAElE,MAAM,MAAM,YAAY,GAAG;IACzB,EAAE,EAAE,WAAW,CAAC;IAChB,OAAO,EAAE,CAAC,CAAC,EAAE,KAAK,KAAK,IAAI,CAAC;IAC5B,SAAS,CAAC,EAAE,cAAc,CAAC;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,OAAO,CAAC;CACf,CAAC;AAEF,kGAAkG;AAClG,MAAM,MAAM,WAAW,GAAG;IACxB,EAAE,EAAE,WAAW,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd,GAAG,CAAC;IAAE,MAAM,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,OAAO,CAAA;CAAE,CAAC,CAAC;AAEzD;;;;GAIG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,EAAE,EAAE,WAAW,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,CAAC,EAAE,EAAE,WAAW,KAAK,OAAO,CAAC;IAClC,MAAM,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;IACxB,IAAI,EAAE,OAAO,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,EAAE,EAAE,WAAW,CAAC;IAChB,GAAG,EAAE,GAAG,CAAC,OAAO,CAAC,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;IACzC,IAAI,EAAE,KAAK,CAAC;CACb,CAAC;AAEF,oDAAoD;AACpD,MAAM,MAAM,gBAAgB,GAAG,UAAU,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC;AAEzF,MAAM,MAAM,WAAW,GAAG;IACxB,MAAM,EAAE,OAAO,CAAC;IAChB;;;;;;;;OAQG;IACH,MAAM,EAAE,CAAC,MAAM,OAAO,CAAC,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC5C,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,MAAM,EAAE,OAAO,CAAC;IAChB,SAAS,EAAE,eAAe,CAAC;IAC3B,IAAI,EAAE,WAAW,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,OAAO,GACf,WAAW,GACX,gBAAgB,GAChB,YAAY,GACZ,WAAW,GACX,YAAY,GACZ,WAAW,GACX,UAAU,CAAC"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("../
|
|
1
|
+
const e=require("../errors.cjs"),t=require("../props.cjs"),n=require("../utils/dom.cjs"),r=require("./result.cjs");let i=require("@vielzeug/ripple");var a=(e,t)=>{(0,i.effect)(()=>{t(e.value)})},o=e=>e instanceof HTMLInputElement||e instanceof HTMLTextAreaElement||e instanceof HTMLSelectElement,s=e=>e instanceof HTMLInputElement&&(e.type===`checkbox`||e.type===`radio`),c=(e,t,n)=>{let r=s(e),i=r?!!t:t==null?``:String(t),a=r?e.checked:e.value;(!n||n.last===void 0||Object.is(a,n.last)||Object.is(a,i))&&(r?e.checked=i:e.value=i,n&&(n.last=i))},l=e=>typeof e==`string`||typeof e==`number`||typeof e==`boolean`,u=(e,t,n)=>t===`bool`?e.parse(n?``:null):n==null||n===!1?e.parse(null):typeof n==`string`?e.parse(n):n,d=(e,t,r,a)=>{let o=u(t,r.mode,a);Object.is((0,i.untrack)(()=>t.signal.value),o)||(t.signal.value=o),!t.reflect&&(a==null||l(a))&&(r.mode===`bool`?e.toggleAttribute(r.name,!!a):n.setAttr(e,r.name,a))},f=e=>{let{el:r,mode:i,name:s}=e,l=a=>{let l=t.getPropMeta(r,s);l?d(r,l,e,a):s===`value`&&o(r)||s===`checked`&&r instanceof HTMLInputElement?c(r,a):s===`indeterminate`&&r instanceof HTMLInputElement?r.indeterminate=!!a:i===`bool`?r.toggleAttribute(s,!!a):n.setAttr(r,s,a)};`signal`in e?a(e.signal,l):l(e.value)},p=e=>{let{modifiers:t}=e,r=t?.stop||t?.prevent?n=>{t.stop&&n.stopPropagation(),t.prevent&&n.preventDefault(),e.handler(n)}:e.handler,a=t?.once||t?.capture?{capture:t.capture,once:t.once}:void 0;(0,i.onCleanup)(n.listen(e.el,e.name,r,a))},m=e=>{let t=t=>{e.el[e.name]=t};`signal`in e?a(e.signal,t):t(e.value)},h=e=>{let{el:t,get:r,signal:a}=e,s={last:void 0},l=e=>{o(t)?c(t,e,s):t.value=e};(0,i.effect)(()=>{l(a.value)}),(0,i.onCleanup)(n.listen(t,e.event,()=>{let e=r(t);Object.is((0,i.untrack)(()=>a.value),e)||(a.value=e)}))},g=e=>{let{el:t,ref:n}=e;typeof n==`function`?(n(t),(0,i.onCleanup)(()=>n(null))):(n.value=t,(0,i.onCleanup)(()=>{n.value=null}))},_=(t,n,i=[])=>{let a=n.parentNode;e.invariant(a,`html binding anchor has no parent node`);for(let e of t)if(r.isHtmlResult(e)){e.claim();let t=Array.from(e.fragment.childNodes);a.insertBefore(e.fragment,n),e.apply(),i.push(...t)}else if(e!=null&&e!==!1){let t=document.createTextNode(String(e));a.insertBefore(t,n),i.push(t)}return i},v=e=>Array.isArray(e)?e:[e],y=e=>{let{anchor:t,source:r}=e,a=n.createReplaceableSlot(),o=(0,i.effect)(()=>{let e=v(typeof r==`function`?r():r.value);e==null||e.length===0?a.clear():(0,i.untrack)(()=>{a.render(()=>{let n=[];try{_(e,t,n)}finally{a.setNodes(n)}})})});(0,i.onCleanup)(()=>{o.dispose(),a.clear()})},b=e=>{e.directive.mount(e.anchor)},x=e=>{switch(e.type){case`attr`:f(e);break;case`directive`:b(e);break;case`event`:p(e);break;case`html`:y(e);break;case`model`:h(e);break;case`prop`:m(e);break;case`ref`:g(e)}},S=(e,t,n,r)=>typeof r==`function`?{el:e,mode:t,name:n,signal:(0,i.computed)(r),type:`attr`}:(0,i.isReactive)(r)?{el:e,mode:t,name:n,signal:r,type:`attr`}:{el:e,mode:t,name:n,type:`attr`,value:r},C=e=>e==null?``:String(e);exports.applyAttrBinding=f,exports.applyBinding=x,exports.applyHtmlBinding=y,exports.createAttrBindingFromValue=S,exports.resolveStaticText=C,exports.syncFormControl=c;
|
|
2
2
|
//# sourceMappingURL=bindings.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bindings.cjs","names":["rawEffect","isStructuredValue","untrack","getPropMeta","listen","isHtmlResult","createReplaceableSlot","isLiveBinding","computed","isReactive","require_live","require_errors","require_props","require_dom","require_result","_vielzeug_ripple","signalEffect","sub","update","signal","registerCleanup","isNativeFormInput","el","isCheckableInput","syncFormControl","checkable","next","value","current","state","isLive","syncRegisteredProp","parsed","meta","binding","applyAttrBinding","liveState","propMeta","name","mode","applyEventBinding","handler","modifiers","e","options","applyPropBinding","applyModelBinding","write","get","applyRefBinding","ref","insertHtmlValues","nodes","parent","insertBefore","v","values","captured","text","toHtmlValues","raw","applyHtmlBinding","slot","stop","source","anchor","applyDirectiveBinding","applyBinding","createAttrBindingFromValue","resolveStaticText"],"sources":["../../src/template/bindings.ts"],"sourcesContent":["/**\n * template/bindings.ts: Runtime binding appliers.\n *\n * Responsibilities:\n * - Apply each Binding variant to the live DOM (attr, event, html, model,\n * prop, ref, directive).\n * - Manage reactive effects and cleanup registration.\n * - Expose `applyBinding()` as the single dispatch entry point.\n * - Own the signal-to-form-control write path (`syncFormControl`) for regular\n * attribute bindings, and the two-way `model()` write-back loop.\n */\n\nimport { computed, isReactive, type Readable, effect as rawEffect, type Signal, untrack } from '@vielzeug/ripple';\n\nimport { isLiveBinding } from '../directives/live';\nimport { invariant } from '../errors';\nimport { getPropMeta, type PropMeta } from '../props';\nimport { createReplaceableSlot, isStructuredValue, listen, setAttr } from '../utils/dom';\nimport type {\n AttrBinding,\n Binding,\n DirectiveBinding,\n EventBinding,\n HtmlBinding,\n HtmlBindingValue,\n ModelBinding,\n PropBinding,\n RefBinding,\n} from './binding-types';\nimport { isHtmlResult } from './result';\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type RegisterCleanup = (fn: () => void) => void;\n\n// ─── Signal helpers ───────────────────────────────────────────────────────────\n\nconst signalEffect = (\n signal: Readable<unknown>,\n update: (v: unknown) => void,\n registerCleanup: RegisterCleanup,\n): void => {\n const sub = rawEffect(() => {\n update(signal.value);\n return undefined;\n });\n\n registerCleanup(() => sub.dispose());\n};\n\n// ─── Form control value sync ──────────────────────────────────────────────────\n\nconst isNativeFormInput = (el: HTMLElement): el is HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement =>\n el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement || el instanceof HTMLSelectElement;\n\nconst isCheckableInput = (el: HTMLElement): el is HTMLInputElement =>\n el instanceof HTMLInputElement && (el.type === 'checkbox' || el.type === 'radio');\n\ntype LiveWriteState = { last: unknown };\n\n/**\n * The single write path from a reactive value to a form control's `value`/`checked`\n * property for regular attribute bindings.\n *\n * Live-write: when the binding was created from `live(source)`, a write is skipped\n * if the DOM value has diverged from this binding's last write (in-progress user\n * input): unless the incoming value already matches the DOM (write would be a no-op).\n */\nexport const syncFormControl = (\n el: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement,\n value: unknown,\n isLive?: boolean,\n state: LiveWriteState = { last: undefined },\n): void => {\n const checkable = isCheckableInput(el);\n const next: boolean | string = checkable ? Boolean(value) : value == null ? '' : String(value);\n const current: boolean | string = checkable ? (el as HTMLInputElement).checked : el.value;\n\n if (isLive && state.last !== undefined && !Object.is(current, state.last) && !Object.is(current, next)) return;\n\n if (checkable) (el as HTMLInputElement).checked = next as boolean;\n else el.value = next as string;\n\n if (isLive) state.last = next;\n};\n\n// ─── Attributes ───────────────────────────────────────────────────────────────\n\nconst syncRegisteredProp = (el: HTMLElement, meta: PropMeta, binding: AttrBinding, value: unknown): void => {\n const parsed = isStructuredValue(value)\n ? value\n : meta.parse(\n binding.mode === 'bool' ? (value ? '' : null) : value == null || value === false ? null : String(value),\n );\n\n if (\n !Object.is(\n untrack(() => meta.signal.value),\n parsed,\n )\n ) {\n meta.signal.value = parsed;\n }\n\n if (!meta.reflect) {\n if (isStructuredValue(value)) return;\n\n if (binding.mode === 'bool') el.toggleAttribute(binding.name, Boolean(value));\n else setAttr(el, binding.name, value);\n }\n};\n\nexport const applyAttrBinding = (binding: AttrBinding, registerCleanup: RegisterCleanup): void => {\n const { el, mode, name } = binding;\n const liveState: LiveWriteState = { last: undefined };\n\n const update = (value: unknown): void => {\n // Resolved per update, not at instantiation: a nested custom element only\n // registers its props when it is connected, which happens when this result\n // mounts: later than binding construction. By update time the registry entry\n // exists, so a structured value (array/object/callback) reaches a registered\n // prop through its signal instead of an attribute round-trip that could not\n // represent it. Unregistered targets get no such pass: structured data to\n // them is an explicit `.prop=` binding.\n const propMeta = getPropMeta(el, name);\n\n if (propMeta) {\n syncRegisteredProp(el, propMeta, binding, value);\n\n return;\n }\n\n if ((name === 'value' && isNativeFormInput(el)) || (name === 'checked' && el instanceof HTMLInputElement)) {\n syncFormControl(el as HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement, value, binding.live, liveState);\n\n return;\n }\n\n // `indeterminate` is a property-only concept on checkboxes (no attribute exists):\n // bind it like any other element property so reactive indeterminate state works\n // through the same `indeterminate=\"${sig}\"` syntax as `checked`.\n if (name === 'indeterminate' && el instanceof HTMLInputElement) {\n el.indeterminate = Boolean(value);\n\n return;\n }\n\n if (mode === 'bool') el.toggleAttribute(name, Boolean(value));\n else setAttr(el, name, value);\n };\n\n if ('signal' in binding) {\n signalEffect(binding.signal, update, registerCleanup);\n } else {\n update(binding.value);\n }\n};\n\n// ─── Events ───────────────────────────────────────────────────────────────────\n\nconst applyEventBinding = (binding: EventBinding, registerCleanup: RegisterCleanup): void => {\n const { modifiers } = binding;\n\n const handler =\n modifiers?.stop || modifiers?.prevent\n ? (e: Event): void => {\n if (modifiers.stop) e.stopPropagation();\n if (modifiers.prevent) e.preventDefault();\n binding.handler(e);\n }\n : binding.handler;\n\n const options =\n modifiers?.once || modifiers?.capture ? { capture: modifiers.capture, once: modifiers.once } : undefined;\n\n registerCleanup(listen(binding.el, binding.name, handler, options));\n};\n\n// ─── Explicit property bindings ───────────────────────────────────────────────\n\n/**\n * `.prop=${value}` binding: always writes the element property (never an\n * attribute). This is the explicit channel for structured values (arrays,\n * objects, callbacks): the old implicit \"value happens to be an object, so set\n * a property\" fallback in the attr path is gone, so intent lives in the markup.\n * Prototype-polluting names are rejected at compile time (see compiler.ts\n * PROP_RE), not silently dropped here.\n */\nconst applyPropBinding = (binding: PropBinding, registerCleanup: RegisterCleanup): void => {\n const update = (value: unknown): void => {\n (binding.el as unknown as Record<string, unknown>)[binding.name] = value;\n };\n\n if ('signal' in binding) {\n signalEffect(binding.signal, update, registerCleanup);\n } else {\n update(binding.value);\n }\n};\n\n// ─── Two-way model bindings ──────────────────────────────────────────────────\n\n/**\n * `model()` binding: signal → control property (through the same live-aware\n * `syncFormControl` write path as `live()`, so an in-progress user edit is never\n * clobbered) and control event → signal (the write-back that makes it two-way).\n */\nconst applyModelBinding = (binding: ModelBinding, registerCleanup: RegisterCleanup): void => {\n const { el, get, signal } = binding;\n const liveState: LiveWriteState = { last: undefined };\n\n const write = (value: unknown): void => {\n if (isNativeFormInput(el)) {\n syncFormControl(el, value, true, liveState);\n } else {\n // Non-native target: plain property write (custom controls expose `value`).\n (el as unknown as { value: unknown }).value = value;\n }\n };\n\n const sub = rawEffect(() => {\n write(signal.value);\n return undefined;\n });\n\n const onEvent: EventListener = () => {\n const next = get(el);\n\n if (\n !Object.is(\n untrack(() => signal.value),\n next,\n )\n ) {\n (signal as Signal<unknown>).value = next;\n }\n };\n\n registerCleanup(() => sub.dispose());\n registerCleanup(listen(el, binding.event, onEvent));\n};\n\n// ─── Refs ─────────────────────────────────────────────────────────────────────\n\nconst applyRefBinding = (binding: RefBinding, registerCleanup: RegisterCleanup): void => {\n const { el, ref } = binding;\n\n if (typeof ref === 'function') {\n ref(el as never);\n registerCleanup(() => ref(null as never));\n\n return;\n }\n\n ref.value = el as never;\n registerCleanup(() => {\n ref.value = null as never;\n });\n};\n\n// ─── HTML (reactive) ─────────────────────────────────────────────────────────\n\nconst insertHtmlValues = (\n values: HtmlBindingValue[],\n insertBefore: ChildNode,\n registerCleanup: RegisterCleanup,\n): Node[] => {\n const nodes: Node[] = [];\n const parent = insertBefore.parentNode;\n\n invariant(parent, 'html binding anchor has no parent node');\n\n for (const v of values) {\n if (isHtmlResult(v)) {\n const captured = Array.from(v.fragment.childNodes);\n\n parent.insertBefore(v.fragment, insertBefore);\n v.apply(registerCleanup);\n nodes.push(...captured);\n } else if (v != null && v !== false) {\n const text = document.createTextNode(String(v));\n\n parent.insertBefore(text, insertBefore);\n nodes.push(text);\n }\n }\n\n return nodes;\n};\n\n/** Normalize a reactive node-slot value to the render effect's array shape. */\nconst toHtmlValues = (raw: unknown): HtmlBindingValue[] =>\n Array.isArray(raw) ? (raw as HtmlBindingValue[]) : [raw as HtmlBindingValue];\n\nexport const applyHtmlBinding = (binding: HtmlBinding, registerCleanup: RegisterCleanup): void => {\n const { anchor, source } = binding;\n\n const slot = createReplaceableSlot();\n\n // The source is read here, not through a memo computed, so this effect is the\n // observer of its dependencies and the owner of every computed the value builds:\n // nested bindings are disposed exactly when this slot's nodes are replaced.\n const stop = rawEffect(() => {\n const raw = toHtmlValues(typeof source === 'function' ? source() : source.value);\n\n slot.clear();\n\n if (raw == null || raw.length === 0) return;\n\n untrack(() => {\n slot.setNodes(insertHtmlValues(raw, anchor, slot.registerCleanup));\n });\n });\n\n registerCleanup(() => {\n stop.dispose();\n slot.clear();\n });\n};\n\n// ─── Directives ───────────────────────────────────────────────────────────────\n\nconst applyDirectiveBinding = (binding: DirectiveBinding, registerCleanup: RegisterCleanup): void => {\n binding.directive.mount(binding.anchor, registerCleanup);\n};\n\n// ─── Binding dispatch ─────────────────────────────────────────────────────────\n\nexport const applyBinding = (binding: Binding, registerCleanup: RegisterCleanup): void => {\n switch (binding.type) {\n case 'attr':\n applyAttrBinding(binding, registerCleanup);\n break;\n case 'directive':\n applyDirectiveBinding(binding, registerCleanup);\n break;\n case 'event':\n applyEventBinding(binding, registerCleanup);\n break;\n case 'html':\n applyHtmlBinding(binding, registerCleanup);\n break;\n case 'model':\n applyModelBinding(binding, registerCleanup);\n break;\n case 'prop':\n applyPropBinding(binding, registerCleanup);\n break;\n case 'ref':\n applyRefBinding(binding, registerCleanup);\n break;\n }\n};\n\n// ─── Attr binding factory (used by instantiator) ──────────────────────────────\n\nexport const createAttrBindingFromValue = (\n el: HTMLElement,\n mode: 'attr' | 'bool',\n name: string,\n value: unknown,\n): AttrBinding => {\n if (isLiveBinding(value)) {\n return { el, live: true, mode, name, signal: value.source, type: 'attr' };\n }\n\n if (typeof value === 'function') {\n return { el, mode, name, signal: computed(value as () => unknown), type: 'attr' };\n }\n\n if (isReactive(value)) {\n return { el, mode, name, signal: value as Readable<unknown>, type: 'attr' };\n }\n\n return { el, mode, name, type: 'attr', value };\n};\n\nexport const resolveStaticText = (value: unknown): string => {\n if (value == null) return '';\n\n return String(value);\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"bindings.cjs","names":["untrack","getPropMeta","listen","isHtmlResult","createReplaceableSlot","rawEffect","computed","isReactive","require_errors","require_props","require_dom","require_result","_vielzeug_ripple","signalEffect","update","signal","isNativeFormInput","el","isCheckableInput","syncFormControl","checkable","next","value","current","liveState","isAttributeValue","toRegisteredPropValue","mode","meta","syncRegisteredProp","binding","applyAttrBinding","propMeta","name","applyEventBinding","handler","modifiers","e","options","applyPropBinding","applyModelBinding","write","get","applyRefBinding","ref","insertHtmlValues","parent","insertBefore","v","values","captured","nodes","text","toHtmlValues","raw","applyHtmlBinding","slot","stop","source","inserted","anchor","applyDirectiveBinding","applyBinding","createAttrBindingFromValue","resolveStaticText"],"sources":["../../src/template/bindings.ts"],"sourcesContent":["/**\n * template/bindings.ts: Runtime binding appliers.\n *\n * Responsibilities:\n * - Apply each Binding variant to the live DOM (attr, event, html, model,\n * prop, ref, directive).\n * - Manage reactive effects and cleanup registration.\n * - Expose `applyBinding()` as the single dispatch entry point.\n * - Own the signal-to-form-control write path (`syncFormControl`) for regular\n * attribute bindings, and the two-way `model()` write-back loop.\n */\n\nimport {\n computed,\n isReactive,\n onCleanup,\n type Readable,\n effect as rawEffect,\n type Signal,\n untrack,\n} from '@vielzeug/ripple';\n\nimport { invariant } from '../errors';\nimport { getPropMeta, type PropMeta } from '../props';\nimport { createReplaceableSlot, listen, setAttr } from '../utils/dom';\nimport type {\n AttrBinding,\n Binding,\n DirectiveBinding,\n EventBinding,\n HtmlBinding,\n HtmlBindingValue,\n ModelBinding,\n PropBinding,\n RefBinding,\n} from './binding-types';\nimport { isHtmlResult } from './result';\n\n// ─── Signal helpers ───────────────────────────────────────────────────────────\n\n// Every mount runs inside a reactive owner (component scope, each() item/fallback\n// scope, or a replaceable slot's scope) whose lifetime matches the nodes'.\n// Binding effects and `onCleanup` registrations are disposed with that owner.\nconst signalEffect = (signal: Readable<unknown>, update: (v: unknown) => void): void => {\n rawEffect(() => {\n update(signal.value);\n return undefined;\n });\n};\n\n// ─── Form control value sync ──────────────────────────────────────────────────\n\nconst isNativeFormInput = (el: HTMLElement): el is HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement =>\n el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement || el instanceof HTMLSelectElement;\n\nconst isCheckableInput = (el: HTMLElement): el is HTMLInputElement =>\n el instanceof HTMLInputElement && (el.type === 'checkbox' || el.type === 'radio');\n\ntype LiveWriteState = { last: unknown };\n\n/**\n * The single write path from a reactive value to a form control's `value`/`checked`\n * property.\n *\n * Stale-write guard (`model()` bindings pass `liveState`): a write is skipped when the\n * DOM value has diverged from this binding's last write (in-progress user input),\n * unless the incoming value already matches the DOM (the write would be a no-op).\n */\nexport const syncFormControl = (\n el: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement,\n value: unknown,\n liveState?: LiveWriteState,\n): void => {\n const checkable = isCheckableInput(el);\n const next: boolean | string = checkable ? Boolean(value) : value == null ? '' : String(value);\n const current: boolean | string = checkable ? (el as HTMLInputElement).checked : el.value;\n\n if (liveState && liveState.last !== undefined && !Object.is(current, liveState.last) && !Object.is(current, next)) {\n return;\n }\n\n if (checkable) (el as HTMLInputElement).checked = next as boolean;\n else el.value = next as string;\n\n if (liveState) liveState.last = next;\n};\n\n// ─── Attributes ───────────────────────────────────────────────────────────────\n\nconst isAttributeValue = (value: unknown): value is string | number | boolean =>\n typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean';\n\n/**\n * The one write path from an attribute binding into a registered child prop. Absent\n * values (`null`/`undefined`/`false`) behave like a removed attribute and `?name=`\n * like attribute presence; strings parse like attribute text; every other value\n * (numbers, booleans, objects, arrays, functions) reaches the prop signal as-is,\n * the same as assigning the element property.\n */\nconst toRegisteredPropValue = (meta: PropMeta, mode: AttrBinding['mode'], value: unknown): unknown => {\n if (mode === 'bool') return meta.parse(value ? '' : null);\n if (value == null || value === false) return meta.parse(null);\n if (typeof value === 'string') return meta.parse(value);\n\n return value;\n};\n\nconst syncRegisteredProp = (el: HTMLElement, meta: PropMeta, binding: AttrBinding, value: unknown): void => {\n const next = toRegisteredPropValue(meta, binding.mode, value);\n\n if (\n !Object.is(\n untrack(() => meta.signal.value),\n next,\n )\n )\n meta.signal.value = next;\n\n // A non-reflecting prop still mirrors plain attribute values, so attribute\n // selectors on the host keep matching what the parent template bound.\n if (meta.reflect || !(value == null || isAttributeValue(value))) return;\n\n if (binding.mode === 'bool') el.toggleAttribute(binding.name, Boolean(value));\n else setAttr(el, binding.name, value);\n};\n\nexport const applyAttrBinding = (binding: AttrBinding): void => {\n const { el, mode, name } = binding;\n\n const update = (value: unknown): void => {\n // Resolved per update, not at instantiation: a nested custom element only\n // registers its props when it is connected, which happens when this result\n // mounts: later than binding construction. For a child that is already\n // connected (or connects before this write), a structured value\n // (array/object/callback) reaches the registered prop through its signal\n // instead of an attribute round-trip that could not represent it. Note the\n // staging window: `each()` mounts items into a detached fragment, so a\n // STATIC structured binding on a staged child misses the registry and falls\n // through to the attribute path — deliver such values through an explicitly\n // reactive binding (applied again after the child connects) or `.prop=`.\n // Unregistered targets get no signal pass at all: structured data to them\n // is an explicit `.prop=` binding.\n const propMeta = getPropMeta(el, name);\n\n if (propMeta) {\n syncRegisteredProp(el, propMeta, binding, value);\n\n return;\n }\n\n if ((name === 'value' && isNativeFormInput(el)) || (name === 'checked' && el instanceof HTMLInputElement)) {\n syncFormControl(el as HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement, value);\n\n return;\n }\n\n // `indeterminate` is a property-only concept on checkboxes (no attribute exists):\n // bind it like any other element property so reactive indeterminate state works\n // through the same `indeterminate=\"${sig}\"` syntax as `checked`.\n if (name === 'indeterminate' && el instanceof HTMLInputElement) {\n el.indeterminate = Boolean(value);\n\n return;\n }\n\n if (mode === 'bool') el.toggleAttribute(name, Boolean(value));\n else setAttr(el, name, value);\n };\n\n if ('signal' in binding) {\n signalEffect(binding.signal, update);\n } else {\n update(binding.value);\n }\n};\n\n// ─── Events ───────────────────────────────────────────────────────────────────\n\nconst applyEventBinding = (binding: EventBinding): void => {\n const { modifiers } = binding;\n\n const handler =\n modifiers?.stop || modifiers?.prevent\n ? (e: Event): void => {\n if (modifiers.stop) e.stopPropagation();\n if (modifiers.prevent) e.preventDefault();\n binding.handler(e);\n }\n : binding.handler;\n\n const options =\n modifiers?.once || modifiers?.capture ? { capture: modifiers.capture, once: modifiers.once } : undefined;\n\n onCleanup(listen(binding.el, binding.name, handler, options));\n};\n\n// ─── Explicit property bindings ───────────────────────────────────────────────\n\n/**\n * `.prop=${value}` binding: always writes the element property (never an\n * attribute). This is the explicit channel for structured values (arrays,\n * objects, callbacks): the old implicit \"value happens to be an object, so set\n * a property\" fallback in the attr path is gone, so intent lives in the markup.\n * Prototype-polluting names are rejected at compile time (see compiler.ts\n * PROP_RE), not silently dropped here.\n */\nconst applyPropBinding = (binding: PropBinding): void => {\n const update = (value: unknown): void => {\n (binding.el as unknown as Record<string, unknown>)[binding.name] = value;\n };\n\n if ('signal' in binding) {\n signalEffect(binding.signal, update);\n } else {\n update(binding.value);\n }\n};\n\n// ─── Two-way model bindings ──────────────────────────────────────────────────\n\n/**\n * `model()` binding: signal → control property (through `syncFormControl` with its\n * stale-write guard, so an in-progress user edit is never clobbered) and control\n * event → signal (the write-back that makes it two-way).\n */\nconst applyModelBinding = (binding: ModelBinding): void => {\n const { el, get, signal } = binding;\n const liveState: LiveWriteState = { last: undefined };\n\n const write = (value: unknown): void => {\n if (isNativeFormInput(el)) {\n syncFormControl(el, value, liveState);\n } else {\n // Non-native target: plain property write (custom controls expose `value`).\n (el as unknown as { value: unknown }).value = value;\n }\n };\n\n rawEffect(() => {\n write(signal.value);\n return undefined;\n });\n\n const onEvent: EventListener = () => {\n const next = get(el);\n\n if (\n !Object.is(\n untrack(() => signal.value),\n next,\n )\n ) {\n (signal as Signal<unknown>).value = next;\n }\n };\n\n onCleanup(listen(el, binding.event, onEvent));\n};\n\n// ─── Refs ─────────────────────────────────────────────────────────────────────\n\nconst applyRefBinding = (binding: RefBinding): void => {\n const { el, ref } = binding;\n\n if (typeof ref === 'function') {\n ref(el as never);\n onCleanup(() => ref(null as never));\n\n return;\n }\n\n ref.value = el as never;\n onCleanup(() => {\n ref.value = null as never;\n });\n};\n\n// ─── HTML (reactive) ─────────────────────────────────────────────────────────\n\nconst insertHtmlValues = (values: HtmlBindingValue[], insertBefore: ChildNode, nodes: Node[] = []): Node[] => {\n const parent = insertBefore.parentNode;\n\n invariant(parent, 'html binding anchor has no parent node');\n\n for (const v of values) {\n if (isHtmlResult(v)) {\n v.claim();\n\n const captured = Array.from(v.fragment.childNodes);\n\n parent.insertBefore(v.fragment, insertBefore);\n v.apply();\n nodes.push(...captured);\n } else if (v != null && v !== false) {\n const text = document.createTextNode(String(v));\n\n parent.insertBefore(text, insertBefore);\n nodes.push(text);\n }\n }\n\n return nodes;\n};\n\n/** Normalize a reactive node-slot value to the render effect's array shape. */\nconst toHtmlValues = (raw: unknown): HtmlBindingValue[] =>\n Array.isArray(raw) ? (raw as HtmlBindingValue[]) : [raw as HtmlBindingValue];\n\nexport const applyHtmlBinding = (binding: HtmlBinding): void => {\n const { anchor, source } = binding;\n\n const slot = createReplaceableSlot();\n\n // The source is read here, not through a memo computed, so this effect is the\n // observer of its dependencies. Nested results mount inside the slot's own\n // scope (`slot.render`), which `clear()` disposes together with the nodes.\n const stop = rawEffect(() => {\n const raw = toHtmlValues(typeof source === 'function' ? source() : source.value);\n\n if (raw == null || raw.length === 0) {\n slot.clear();\n\n return;\n }\n\n untrack(() => {\n slot.render(() => {\n // Register items incrementally: if a later item throws (a reused,\n // already-claimed result, or user code in `apply()`), the earlier nodes\n // are already inserted in the DOM, and only a tracked list lets the\n // next `clear()` remove them.\n const inserted: Node[] = [];\n\n try {\n insertHtmlValues(raw, anchor, inserted);\n } finally {\n slot.setNodes(inserted);\n }\n });\n });\n });\n\n onCleanup(() => {\n stop.dispose();\n slot.clear();\n });\n};\n\n// ─── Directives ───────────────────────────────────────────────────────────────\n\nconst applyDirectiveBinding = (binding: DirectiveBinding): void => {\n binding.directive.mount(binding.anchor);\n};\n\n// ─── Binding dispatch ─────────────────────────────────────────────────────────\n\nexport const applyBinding = (binding: Binding): void => {\n switch (binding.type) {\n case 'attr':\n applyAttrBinding(binding);\n break;\n case 'directive':\n applyDirectiveBinding(binding);\n break;\n case 'event':\n applyEventBinding(binding);\n break;\n case 'html':\n applyHtmlBinding(binding);\n break;\n case 'model':\n applyModelBinding(binding);\n break;\n case 'prop':\n applyPropBinding(binding);\n break;\n case 'ref':\n applyRefBinding(binding);\n break;\n }\n};\n\n// ─── Attr binding factory (used by instantiator) ──────────────────────────────\n\nexport const createAttrBindingFromValue = (\n el: HTMLElement,\n mode: 'attr' | 'bool',\n name: string,\n value: unknown,\n): AttrBinding => {\n if (typeof value === 'function') {\n return { el, mode, name, signal: computed(value as () => unknown), type: 'attr' };\n }\n\n if (isReactive(value)) {\n return { el, mode, name, signal: value as Readable<unknown>, type: 'attr' };\n }\n\n return { el, mode, name, type: 'attr', value };\n};\n\nexport const resolveStaticText = (value: unknown): string => {\n if (value == null) return '';\n\n return String(value);\n};\n"],"mappings":"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"}
|
|
@@ -10,22 +10,21 @@
|
|
|
10
10
|
* attribute bindings, and the two-way `model()` write-back loop.
|
|
11
11
|
*/
|
|
12
12
|
import type { AttrBinding, Binding, HtmlBinding } from './binding-types';
|
|
13
|
-
export type RegisterCleanup = (fn: () => void) => void;
|
|
14
13
|
type LiveWriteState = {
|
|
15
14
|
last: unknown;
|
|
16
15
|
};
|
|
17
16
|
/**
|
|
18
17
|
* The single write path from a reactive value to a form control's `value`/`checked`
|
|
19
|
-
* property
|
|
18
|
+
* property.
|
|
20
19
|
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
20
|
+
* Stale-write guard (`model()` bindings pass `liveState`): a write is skipped when the
|
|
21
|
+
* DOM value has diverged from this binding's last write (in-progress user input),
|
|
22
|
+
* unless the incoming value already matches the DOM (the write would be a no-op).
|
|
24
23
|
*/
|
|
25
|
-
export declare const syncFormControl: (el: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement, value: unknown,
|
|
26
|
-
export declare const applyAttrBinding: (binding: AttrBinding
|
|
27
|
-
export declare const applyHtmlBinding: (binding: HtmlBinding
|
|
28
|
-
export declare const applyBinding: (binding: Binding
|
|
24
|
+
export declare const syncFormControl: (el: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement, value: unknown, liveState?: LiveWriteState) => void;
|
|
25
|
+
export declare const applyAttrBinding: (binding: AttrBinding) => void;
|
|
26
|
+
export declare const applyHtmlBinding: (binding: HtmlBinding) => void;
|
|
27
|
+
export declare const applyBinding: (binding: Binding) => void;
|
|
29
28
|
export declare const createAttrBindingFromValue: (el: HTMLElement, mode: "attr" | "bool", name: string, value: unknown) => AttrBinding;
|
|
30
29
|
export declare const resolveStaticText: (value: unknown) => string;
|
|
31
30
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bindings.d.ts","sourceRoot":"","sources":["../../src/template/bindings.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;
|
|
1
|
+
{"version":3,"file":"bindings.d.ts","sourceRoot":"","sources":["../../src/template/bindings.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAeH,OAAO,KAAK,EACV,WAAW,EACX,OAAO,EAGP,WAAW,EAKZ,MAAM,iBAAiB,CAAC;AAuBzB,KAAK,cAAc,GAAG;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,CAAC;AAExC;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,GAC1B,IAAI,gBAAgB,GAAG,mBAAmB,GAAG,iBAAiB,EAC9D,OAAO,OAAO,EACd,YAAY,cAAc,KACzB,IAaF,CAAC;AAyCF,eAAO,MAAM,gBAAgB,GAAI,SAAS,WAAW,KAAG,IAgDvD,CAAC;AAsIF,eAAO,MAAM,gBAAgB,GAAI,SAAS,WAAW,KAAG,IAsCvD,CAAC;AAUF,eAAO,MAAM,YAAY,GAAI,SAAS,OAAO,KAAG,IAwB/C,CAAC;AAIF,eAAO,MAAM,0BAA0B,GACrC,IAAI,WAAW,EACf,MAAM,MAAM,GAAG,MAAM,EACrB,MAAM,MAAM,EACZ,OAAO,OAAO,KACb,WAUF,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAI,OAAO,OAAO,KAAG,MAIlD,CAAC"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{invariant as e}from"../errors.js";import{getPropMeta as t}from"../props.js";import{createReplaceableSlot as n,listen as r,setAttr as i}from"../utils/dom.js";import{isHtmlResult as a}from"./result.js";import{computed as o,effect as s,isReactive as c,onCleanup as l,untrack as u}from"@vielzeug/ripple";var d=(e,t)=>{s(()=>{t(e.value)})},f=e=>e instanceof HTMLInputElement||e instanceof HTMLTextAreaElement||e instanceof HTMLSelectElement,p=e=>e instanceof HTMLInputElement&&(e.type===`checkbox`||e.type===`radio`),m=(e,t,n)=>{let r=p(e),i=r?!!t:t==null?``:String(t),a=r?e.checked:e.value;(!n||n.last===void 0||Object.is(a,n.last)||Object.is(a,i))&&(r?e.checked=i:e.value=i,n&&(n.last=i))},h=e=>typeof e==`string`||typeof e==`number`||typeof e==`boolean`,g=(e,t,n)=>t===`bool`?e.parse(n?``:null):n==null||n===!1?e.parse(null):typeof n==`string`?e.parse(n):n,_=(e,t,n,r)=>{let a=g(t,n.mode,r);Object.is(u(()=>t.signal.value),a)||(t.signal.value=a),!t.reflect&&(r==null||h(r))&&(n.mode===`bool`?e.toggleAttribute(n.name,!!r):i(e,n.name,r))},v=e=>{let{el:n,mode:r,name:a}=e,o=o=>{let s=t(n,a);s?_(n,s,e,o):a===`value`&&f(n)||a===`checked`&&n instanceof HTMLInputElement?m(n,o):a===`indeterminate`&&n instanceof HTMLInputElement?n.indeterminate=!!o:r===`bool`?n.toggleAttribute(a,!!o):i(n,a,o)};`signal`in e?d(e.signal,o):o(e.value)},y=e=>{let{modifiers:t}=e,n=t?.stop||t?.prevent?n=>{t.stop&&n.stopPropagation(),t.prevent&&n.preventDefault(),e.handler(n)}:e.handler,i=t?.once||t?.capture?{capture:t.capture,once:t.once}:void 0;l(r(e.el,e.name,n,i))},b=e=>{let t=t=>{e.el[e.name]=t};`signal`in e?d(e.signal,t):t(e.value)},x=e=>{let{el:t,get:n,signal:i}=e,a={last:void 0},o=e=>{f(t)?m(t,e,a):t.value=e};s(()=>{o(i.value)}),l(r(t,e.event,()=>{let e=n(t);Object.is(u(()=>i.value),e)||(i.value=e)}))},S=e=>{let{el:t,ref:n}=e;typeof n==`function`?(n(t),l(()=>n(null))):(n.value=t,l(()=>{n.value=null}))},C=(t,n,r=[])=>{let i=n.parentNode;e(i,`html binding anchor has no parent node`);for(let e of t)if(a(e)){e.claim();let t=Array.from(e.fragment.childNodes);i.insertBefore(e.fragment,n),e.apply(),r.push(...t)}else if(e!=null&&e!==!1){let t=document.createTextNode(String(e));i.insertBefore(t,n),r.push(t)}return r},w=e=>Array.isArray(e)?e:[e],T=e=>{let{anchor:t,source:r}=e,i=n(),a=s(()=>{let e=w(typeof r==`function`?r():r.value);e==null||e.length===0?i.clear():u(()=>{i.render(()=>{let n=[];try{C(e,t,n)}finally{i.setNodes(n)}})})});l(()=>{a.dispose(),i.clear()})},E=e=>{e.directive.mount(e.anchor)},D=e=>{switch(e.type){case`attr`:v(e);break;case`directive`:E(e);break;case`event`:y(e);break;case`html`:T(e);break;case`model`:x(e);break;case`prop`:b(e);break;case`ref`:S(e)}},O=(e,t,n,r)=>typeof r==`function`?{el:e,mode:t,name:n,signal:o(r),type:`attr`}:c(r)?{el:e,mode:t,name:n,signal:r,type:`attr`}:{el:e,mode:t,name:n,type:`attr`,value:r},k=e=>e==null?``:String(e);export{v as applyAttrBinding,D as applyBinding,T as applyHtmlBinding,O as createAttrBindingFromValue,k as resolveStaticText,m as syncFormControl};
|
|
2
2
|
//# sourceMappingURL=bindings.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bindings.js","names":["rawEffect","signalEffect","sub","effect","update","signal","registerCleanup","isNativeFormInput","el","isCheckableInput","syncFormControl","checkable","next","value","current","state","isLive","syncRegisteredProp","parsed","isStructuredValue","meta","binding","untrack","setAttr","applyAttrBinding","liveState","propMeta","getPropMeta","name","mode","applyEventBinding","handler","modifiers","e","options","listen","applyPropBinding","applyModelBinding","write","get","applyRefBinding","ref","insertHtmlValues","nodes","parent","insertBefore","invariant","v","values","isHtmlResult","captured","text","toHtmlValues","raw","applyHtmlBinding","slot","createReplaceableSlot","stop","source","anchor","applyDirectiveBinding","applyBinding","createAttrBindingFromValue","isLiveBinding","computed","isReactive","resolveStaticText"],"sources":["../../src/template/bindings.ts"],"sourcesContent":["/**\n * template/bindings.ts: Runtime binding appliers.\n *\n * Responsibilities:\n * - Apply each Binding variant to the live DOM (attr, event, html, model,\n * prop, ref, directive).\n * - Manage reactive effects and cleanup registration.\n * - Expose `applyBinding()` as the single dispatch entry point.\n * - Own the signal-to-form-control write path (`syncFormControl`) for regular\n * attribute bindings, and the two-way `model()` write-back loop.\n */\n\nimport { computed, isReactive, type Readable, effect as rawEffect, type Signal, untrack } from '@vielzeug/ripple';\n\nimport { isLiveBinding } from '../directives/live';\nimport { invariant } from '../errors';\nimport { getPropMeta, type PropMeta } from '../props';\nimport { createReplaceableSlot, isStructuredValue, listen, setAttr } from '../utils/dom';\nimport type {\n AttrBinding,\n Binding,\n DirectiveBinding,\n EventBinding,\n HtmlBinding,\n HtmlBindingValue,\n ModelBinding,\n PropBinding,\n RefBinding,\n} from './binding-types';\nimport { isHtmlResult } from './result';\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type RegisterCleanup = (fn: () => void) => void;\n\n// ─── Signal helpers ───────────────────────────────────────────────────────────\n\nconst signalEffect = (\n signal: Readable<unknown>,\n update: (v: unknown) => void,\n registerCleanup: RegisterCleanup,\n): void => {\n const sub = rawEffect(() => {\n update(signal.value);\n return undefined;\n });\n\n registerCleanup(() => sub.dispose());\n};\n\n// ─── Form control value sync ──────────────────────────────────────────────────\n\nconst isNativeFormInput = (el: HTMLElement): el is HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement =>\n el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement || el instanceof HTMLSelectElement;\n\nconst isCheckableInput = (el: HTMLElement): el is HTMLInputElement =>\n el instanceof HTMLInputElement && (el.type === 'checkbox' || el.type === 'radio');\n\ntype LiveWriteState = { last: unknown };\n\n/**\n * The single write path from a reactive value to a form control's `value`/`checked`\n * property for regular attribute bindings.\n *\n * Live-write: when the binding was created from `live(source)`, a write is skipped\n * if the DOM value has diverged from this binding's last write (in-progress user\n * input): unless the incoming value already matches the DOM (write would be a no-op).\n */\nexport const syncFormControl = (\n el: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement,\n value: unknown,\n isLive?: boolean,\n state: LiveWriteState = { last: undefined },\n): void => {\n const checkable = isCheckableInput(el);\n const next: boolean | string = checkable ? Boolean(value) : value == null ? '' : String(value);\n const current: boolean | string = checkable ? (el as HTMLInputElement).checked : el.value;\n\n if (isLive && state.last !== undefined && !Object.is(current, state.last) && !Object.is(current, next)) return;\n\n if (checkable) (el as HTMLInputElement).checked = next as boolean;\n else el.value = next as string;\n\n if (isLive) state.last = next;\n};\n\n// ─── Attributes ───────────────────────────────────────────────────────────────\n\nconst syncRegisteredProp = (el: HTMLElement, meta: PropMeta, binding: AttrBinding, value: unknown): void => {\n const parsed = isStructuredValue(value)\n ? value\n : meta.parse(\n binding.mode === 'bool' ? (value ? '' : null) : value == null || value === false ? null : String(value),\n );\n\n if (\n !Object.is(\n untrack(() => meta.signal.value),\n parsed,\n )\n ) {\n meta.signal.value = parsed;\n }\n\n if (!meta.reflect) {\n if (isStructuredValue(value)) return;\n\n if (binding.mode === 'bool') el.toggleAttribute(binding.name, Boolean(value));\n else setAttr(el, binding.name, value);\n }\n};\n\nexport const applyAttrBinding = (binding: AttrBinding, registerCleanup: RegisterCleanup): void => {\n const { el, mode, name } = binding;\n const liveState: LiveWriteState = { last: undefined };\n\n const update = (value: unknown): void => {\n // Resolved per update, not at instantiation: a nested custom element only\n // registers its props when it is connected, which happens when this result\n // mounts: later than binding construction. By update time the registry entry\n // exists, so a structured value (array/object/callback) reaches a registered\n // prop through its signal instead of an attribute round-trip that could not\n // represent it. Unregistered targets get no such pass: structured data to\n // them is an explicit `.prop=` binding.\n const propMeta = getPropMeta(el, name);\n\n if (propMeta) {\n syncRegisteredProp(el, propMeta, binding, value);\n\n return;\n }\n\n if ((name === 'value' && isNativeFormInput(el)) || (name === 'checked' && el instanceof HTMLInputElement)) {\n syncFormControl(el as HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement, value, binding.live, liveState);\n\n return;\n }\n\n // `indeterminate` is a property-only concept on checkboxes (no attribute exists):\n // bind it like any other element property so reactive indeterminate state works\n // through the same `indeterminate=\"${sig}\"` syntax as `checked`.\n if (name === 'indeterminate' && el instanceof HTMLInputElement) {\n el.indeterminate = Boolean(value);\n\n return;\n }\n\n if (mode === 'bool') el.toggleAttribute(name, Boolean(value));\n else setAttr(el, name, value);\n };\n\n if ('signal' in binding) {\n signalEffect(binding.signal, update, registerCleanup);\n } else {\n update(binding.value);\n }\n};\n\n// ─── Events ───────────────────────────────────────────────────────────────────\n\nconst applyEventBinding = (binding: EventBinding, registerCleanup: RegisterCleanup): void => {\n const { modifiers } = binding;\n\n const handler =\n modifiers?.stop || modifiers?.prevent\n ? (e: Event): void => {\n if (modifiers.stop) e.stopPropagation();\n if (modifiers.prevent) e.preventDefault();\n binding.handler(e);\n }\n : binding.handler;\n\n const options =\n modifiers?.once || modifiers?.capture ? { capture: modifiers.capture, once: modifiers.once } : undefined;\n\n registerCleanup(listen(binding.el, binding.name, handler, options));\n};\n\n// ─── Explicit property bindings ───────────────────────────────────────────────\n\n/**\n * `.prop=${value}` binding: always writes the element property (never an\n * attribute). This is the explicit channel for structured values (arrays,\n * objects, callbacks): the old implicit \"value happens to be an object, so set\n * a property\" fallback in the attr path is gone, so intent lives in the markup.\n * Prototype-polluting names are rejected at compile time (see compiler.ts\n * PROP_RE), not silently dropped here.\n */\nconst applyPropBinding = (binding: PropBinding, registerCleanup: RegisterCleanup): void => {\n const update = (value: unknown): void => {\n (binding.el as unknown as Record<string, unknown>)[binding.name] = value;\n };\n\n if ('signal' in binding) {\n signalEffect(binding.signal, update, registerCleanup);\n } else {\n update(binding.value);\n }\n};\n\n// ─── Two-way model bindings ──────────────────────────────────────────────────\n\n/**\n * `model()` binding: signal → control property (through the same live-aware\n * `syncFormControl` write path as `live()`, so an in-progress user edit is never\n * clobbered) and control event → signal (the write-back that makes it two-way).\n */\nconst applyModelBinding = (binding: ModelBinding, registerCleanup: RegisterCleanup): void => {\n const { el, get, signal } = binding;\n const liveState: LiveWriteState = { last: undefined };\n\n const write = (value: unknown): void => {\n if (isNativeFormInput(el)) {\n syncFormControl(el, value, true, liveState);\n } else {\n // Non-native target: plain property write (custom controls expose `value`).\n (el as unknown as { value: unknown }).value = value;\n }\n };\n\n const sub = rawEffect(() => {\n write(signal.value);\n return undefined;\n });\n\n const onEvent: EventListener = () => {\n const next = get(el);\n\n if (\n !Object.is(\n untrack(() => signal.value),\n next,\n )\n ) {\n (signal as Signal<unknown>).value = next;\n }\n };\n\n registerCleanup(() => sub.dispose());\n registerCleanup(listen(el, binding.event, onEvent));\n};\n\n// ─── Refs ─────────────────────────────────────────────────────────────────────\n\nconst applyRefBinding = (binding: RefBinding, registerCleanup: RegisterCleanup): void => {\n const { el, ref } = binding;\n\n if (typeof ref === 'function') {\n ref(el as never);\n registerCleanup(() => ref(null as never));\n\n return;\n }\n\n ref.value = el as never;\n registerCleanup(() => {\n ref.value = null as never;\n });\n};\n\n// ─── HTML (reactive) ─────────────────────────────────────────────────────────\n\nconst insertHtmlValues = (\n values: HtmlBindingValue[],\n insertBefore: ChildNode,\n registerCleanup: RegisterCleanup,\n): Node[] => {\n const nodes: Node[] = [];\n const parent = insertBefore.parentNode;\n\n invariant(parent, 'html binding anchor has no parent node');\n\n for (const v of values) {\n if (isHtmlResult(v)) {\n const captured = Array.from(v.fragment.childNodes);\n\n parent.insertBefore(v.fragment, insertBefore);\n v.apply(registerCleanup);\n nodes.push(...captured);\n } else if (v != null && v !== false) {\n const text = document.createTextNode(String(v));\n\n parent.insertBefore(text, insertBefore);\n nodes.push(text);\n }\n }\n\n return nodes;\n};\n\n/** Normalize a reactive node-slot value to the render effect's array shape. */\nconst toHtmlValues = (raw: unknown): HtmlBindingValue[] =>\n Array.isArray(raw) ? (raw as HtmlBindingValue[]) : [raw as HtmlBindingValue];\n\nexport const applyHtmlBinding = (binding: HtmlBinding, registerCleanup: RegisterCleanup): void => {\n const { anchor, source } = binding;\n\n const slot = createReplaceableSlot();\n\n // The source is read here, not through a memo computed, so this effect is the\n // observer of its dependencies and the owner of every computed the value builds:\n // nested bindings are disposed exactly when this slot's nodes are replaced.\n const stop = rawEffect(() => {\n const raw = toHtmlValues(typeof source === 'function' ? source() : source.value);\n\n slot.clear();\n\n if (raw == null || raw.length === 0) return;\n\n untrack(() => {\n slot.setNodes(insertHtmlValues(raw, anchor, slot.registerCleanup));\n });\n });\n\n registerCleanup(() => {\n stop.dispose();\n slot.clear();\n });\n};\n\n// ─── Directives ───────────────────────────────────────────────────────────────\n\nconst applyDirectiveBinding = (binding: DirectiveBinding, registerCleanup: RegisterCleanup): void => {\n binding.directive.mount(binding.anchor, registerCleanup);\n};\n\n// ─── Binding dispatch ─────────────────────────────────────────────────────────\n\nexport const applyBinding = (binding: Binding, registerCleanup: RegisterCleanup): void => {\n switch (binding.type) {\n case 'attr':\n applyAttrBinding(binding, registerCleanup);\n break;\n case 'directive':\n applyDirectiveBinding(binding, registerCleanup);\n break;\n case 'event':\n applyEventBinding(binding, registerCleanup);\n break;\n case 'html':\n applyHtmlBinding(binding, registerCleanup);\n break;\n case 'model':\n applyModelBinding(binding, registerCleanup);\n break;\n case 'prop':\n applyPropBinding(binding, registerCleanup);\n break;\n case 'ref':\n applyRefBinding(binding, registerCleanup);\n break;\n }\n};\n\n// ─── Attr binding factory (used by instantiator) ──────────────────────────────\n\nexport const createAttrBindingFromValue = (\n el: HTMLElement,\n mode: 'attr' | 'bool',\n name: string,\n value: unknown,\n): AttrBinding => {\n if (isLiveBinding(value)) {\n return { el, live: true, mode, name, signal: value.source, type: 'attr' };\n }\n\n if (typeof value === 'function') {\n return { el, mode, name, signal: computed(value as () => unknown), type: 'attr' };\n }\n\n if (isReactive(value)) {\n return { el, mode, name, signal: value as Readable<unknown>, type: 'attr' };\n }\n\n return { el, mode, name, type: 'attr', value };\n};\n\nexport const resolveStaticText = (value: unknown): string => {\n if (value == null) return '';\n\n return String(value);\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"bindings.js","names":["rawEffect","signalEffect","effect","update","signal","isNativeFormInput","el","isCheckableInput","syncFormControl","checkable","next","value","current","liveState","isAttributeValue","toRegisteredPropValue","mode","meta","syncRegisteredProp","binding","untrack","setAttr","applyAttrBinding","propMeta","getPropMeta","name","applyEventBinding","handler","modifiers","e","options","onCleanup","listen","applyPropBinding","applyModelBinding","write","get","applyRefBinding","ref","insertHtmlValues","parent","insertBefore","invariant","v","values","isHtmlResult","captured","nodes","text","toHtmlValues","raw","applyHtmlBinding","slot","createReplaceableSlot","stop","source","inserted","anchor","applyDirectiveBinding","applyBinding","createAttrBindingFromValue","computed","isReactive","resolveStaticText"],"sources":["../../src/template/bindings.ts"],"sourcesContent":["/**\n * template/bindings.ts: Runtime binding appliers.\n *\n * Responsibilities:\n * - Apply each Binding variant to the live DOM (attr, event, html, model,\n * prop, ref, directive).\n * - Manage reactive effects and cleanup registration.\n * - Expose `applyBinding()` as the single dispatch entry point.\n * - Own the signal-to-form-control write path (`syncFormControl`) for regular\n * attribute bindings, and the two-way `model()` write-back loop.\n */\n\nimport {\n computed,\n isReactive,\n onCleanup,\n type Readable,\n effect as rawEffect,\n type Signal,\n untrack,\n} from '@vielzeug/ripple';\n\nimport { invariant } from '../errors';\nimport { getPropMeta, type PropMeta } from '../props';\nimport { createReplaceableSlot, listen, setAttr } from '../utils/dom';\nimport type {\n AttrBinding,\n Binding,\n DirectiveBinding,\n EventBinding,\n HtmlBinding,\n HtmlBindingValue,\n ModelBinding,\n PropBinding,\n RefBinding,\n} from './binding-types';\nimport { isHtmlResult } from './result';\n\n// ─── Signal helpers ───────────────────────────────────────────────────────────\n\n// Every mount runs inside a reactive owner (component scope, each() item/fallback\n// scope, or a replaceable slot's scope) whose lifetime matches the nodes'.\n// Binding effects and `onCleanup` registrations are disposed with that owner.\nconst signalEffect = (signal: Readable<unknown>, update: (v: unknown) => void): void => {\n rawEffect(() => {\n update(signal.value);\n return undefined;\n });\n};\n\n// ─── Form control value sync ──────────────────────────────────────────────────\n\nconst isNativeFormInput = (el: HTMLElement): el is HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement =>\n el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement || el instanceof HTMLSelectElement;\n\nconst isCheckableInput = (el: HTMLElement): el is HTMLInputElement =>\n el instanceof HTMLInputElement && (el.type === 'checkbox' || el.type === 'radio');\n\ntype LiveWriteState = { last: unknown };\n\n/**\n * The single write path from a reactive value to a form control's `value`/`checked`\n * property.\n *\n * Stale-write guard (`model()` bindings pass `liveState`): a write is skipped when the\n * DOM value has diverged from this binding's last write (in-progress user input),\n * unless the incoming value already matches the DOM (the write would be a no-op).\n */\nexport const syncFormControl = (\n el: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement,\n value: unknown,\n liveState?: LiveWriteState,\n): void => {\n const checkable = isCheckableInput(el);\n const next: boolean | string = checkable ? Boolean(value) : value == null ? '' : String(value);\n const current: boolean | string = checkable ? (el as HTMLInputElement).checked : el.value;\n\n if (liveState && liveState.last !== undefined && !Object.is(current, liveState.last) && !Object.is(current, next)) {\n return;\n }\n\n if (checkable) (el as HTMLInputElement).checked = next as boolean;\n else el.value = next as string;\n\n if (liveState) liveState.last = next;\n};\n\n// ─── Attributes ───────────────────────────────────────────────────────────────\n\nconst isAttributeValue = (value: unknown): value is string | number | boolean =>\n typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean';\n\n/**\n * The one write path from an attribute binding into a registered child prop. Absent\n * values (`null`/`undefined`/`false`) behave like a removed attribute and `?name=`\n * like attribute presence; strings parse like attribute text; every other value\n * (numbers, booleans, objects, arrays, functions) reaches the prop signal as-is,\n * the same as assigning the element property.\n */\nconst toRegisteredPropValue = (meta: PropMeta, mode: AttrBinding['mode'], value: unknown): unknown => {\n if (mode === 'bool') return meta.parse(value ? '' : null);\n if (value == null || value === false) return meta.parse(null);\n if (typeof value === 'string') return meta.parse(value);\n\n return value;\n};\n\nconst syncRegisteredProp = (el: HTMLElement, meta: PropMeta, binding: AttrBinding, value: unknown): void => {\n const next = toRegisteredPropValue(meta, binding.mode, value);\n\n if (\n !Object.is(\n untrack(() => meta.signal.value),\n next,\n )\n )\n meta.signal.value = next;\n\n // A non-reflecting prop still mirrors plain attribute values, so attribute\n // selectors on the host keep matching what the parent template bound.\n if (meta.reflect || !(value == null || isAttributeValue(value))) return;\n\n if (binding.mode === 'bool') el.toggleAttribute(binding.name, Boolean(value));\n else setAttr(el, binding.name, value);\n};\n\nexport const applyAttrBinding = (binding: AttrBinding): void => {\n const { el, mode, name } = binding;\n\n const update = (value: unknown): void => {\n // Resolved per update, not at instantiation: a nested custom element only\n // registers its props when it is connected, which happens when this result\n // mounts: later than binding construction. For a child that is already\n // connected (or connects before this write), a structured value\n // (array/object/callback) reaches the registered prop through its signal\n // instead of an attribute round-trip that could not represent it. Note the\n // staging window: `each()` mounts items into a detached fragment, so a\n // STATIC structured binding on a staged child misses the registry and falls\n // through to the attribute path — deliver such values through an explicitly\n // reactive binding (applied again after the child connects) or `.prop=`.\n // Unregistered targets get no signal pass at all: structured data to them\n // is an explicit `.prop=` binding.\n const propMeta = getPropMeta(el, name);\n\n if (propMeta) {\n syncRegisteredProp(el, propMeta, binding, value);\n\n return;\n }\n\n if ((name === 'value' && isNativeFormInput(el)) || (name === 'checked' && el instanceof HTMLInputElement)) {\n syncFormControl(el as HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement, value);\n\n return;\n }\n\n // `indeterminate` is a property-only concept on checkboxes (no attribute exists):\n // bind it like any other element property so reactive indeterminate state works\n // through the same `indeterminate=\"${sig}\"` syntax as `checked`.\n if (name === 'indeterminate' && el instanceof HTMLInputElement) {\n el.indeterminate = Boolean(value);\n\n return;\n }\n\n if (mode === 'bool') el.toggleAttribute(name, Boolean(value));\n else setAttr(el, name, value);\n };\n\n if ('signal' in binding) {\n signalEffect(binding.signal, update);\n } else {\n update(binding.value);\n }\n};\n\n// ─── Events ───────────────────────────────────────────────────────────────────\n\nconst applyEventBinding = (binding: EventBinding): void => {\n const { modifiers } = binding;\n\n const handler =\n modifiers?.stop || modifiers?.prevent\n ? (e: Event): void => {\n if (modifiers.stop) e.stopPropagation();\n if (modifiers.prevent) e.preventDefault();\n binding.handler(e);\n }\n : binding.handler;\n\n const options =\n modifiers?.once || modifiers?.capture ? { capture: modifiers.capture, once: modifiers.once } : undefined;\n\n onCleanup(listen(binding.el, binding.name, handler, options));\n};\n\n// ─── Explicit property bindings ───────────────────────────────────────────────\n\n/**\n * `.prop=${value}` binding: always writes the element property (never an\n * attribute). This is the explicit channel for structured values (arrays,\n * objects, callbacks): the old implicit \"value happens to be an object, so set\n * a property\" fallback in the attr path is gone, so intent lives in the markup.\n * Prototype-polluting names are rejected at compile time (see compiler.ts\n * PROP_RE), not silently dropped here.\n */\nconst applyPropBinding = (binding: PropBinding): void => {\n const update = (value: unknown): void => {\n (binding.el as unknown as Record<string, unknown>)[binding.name] = value;\n };\n\n if ('signal' in binding) {\n signalEffect(binding.signal, update);\n } else {\n update(binding.value);\n }\n};\n\n// ─── Two-way model bindings ──────────────────────────────────────────────────\n\n/**\n * `model()` binding: signal → control property (through `syncFormControl` with its\n * stale-write guard, so an in-progress user edit is never clobbered) and control\n * event → signal (the write-back that makes it two-way).\n */\nconst applyModelBinding = (binding: ModelBinding): void => {\n const { el, get, signal } = binding;\n const liveState: LiveWriteState = { last: undefined };\n\n const write = (value: unknown): void => {\n if (isNativeFormInput(el)) {\n syncFormControl(el, value, liveState);\n } else {\n // Non-native target: plain property write (custom controls expose `value`).\n (el as unknown as { value: unknown }).value = value;\n }\n };\n\n rawEffect(() => {\n write(signal.value);\n return undefined;\n });\n\n const onEvent: EventListener = () => {\n const next = get(el);\n\n if (\n !Object.is(\n untrack(() => signal.value),\n next,\n )\n ) {\n (signal as Signal<unknown>).value = next;\n }\n };\n\n onCleanup(listen(el, binding.event, onEvent));\n};\n\n// ─── Refs ─────────────────────────────────────────────────────────────────────\n\nconst applyRefBinding = (binding: RefBinding): void => {\n const { el, ref } = binding;\n\n if (typeof ref === 'function') {\n ref(el as never);\n onCleanup(() => ref(null as never));\n\n return;\n }\n\n ref.value = el as never;\n onCleanup(() => {\n ref.value = null as never;\n });\n};\n\n// ─── HTML (reactive) ─────────────────────────────────────────────────────────\n\nconst insertHtmlValues = (values: HtmlBindingValue[], insertBefore: ChildNode, nodes: Node[] = []): Node[] => {\n const parent = insertBefore.parentNode;\n\n invariant(parent, 'html binding anchor has no parent node');\n\n for (const v of values) {\n if (isHtmlResult(v)) {\n v.claim();\n\n const captured = Array.from(v.fragment.childNodes);\n\n parent.insertBefore(v.fragment, insertBefore);\n v.apply();\n nodes.push(...captured);\n } else if (v != null && v !== false) {\n const text = document.createTextNode(String(v));\n\n parent.insertBefore(text, insertBefore);\n nodes.push(text);\n }\n }\n\n return nodes;\n};\n\n/** Normalize a reactive node-slot value to the render effect's array shape. */\nconst toHtmlValues = (raw: unknown): HtmlBindingValue[] =>\n Array.isArray(raw) ? (raw as HtmlBindingValue[]) : [raw as HtmlBindingValue];\n\nexport const applyHtmlBinding = (binding: HtmlBinding): void => {\n const { anchor, source } = binding;\n\n const slot = createReplaceableSlot();\n\n // The source is read here, not through a memo computed, so this effect is the\n // observer of its dependencies. Nested results mount inside the slot's own\n // scope (`slot.render`), which `clear()` disposes together with the nodes.\n const stop = rawEffect(() => {\n const raw = toHtmlValues(typeof source === 'function' ? source() : source.value);\n\n if (raw == null || raw.length === 0) {\n slot.clear();\n\n return;\n }\n\n untrack(() => {\n slot.render(() => {\n // Register items incrementally: if a later item throws (a reused,\n // already-claimed result, or user code in `apply()`), the earlier nodes\n // are already inserted in the DOM, and only a tracked list lets the\n // next `clear()` remove them.\n const inserted: Node[] = [];\n\n try {\n insertHtmlValues(raw, anchor, inserted);\n } finally {\n slot.setNodes(inserted);\n }\n });\n });\n });\n\n onCleanup(() => {\n stop.dispose();\n slot.clear();\n });\n};\n\n// ─── Directives ───────────────────────────────────────────────────────────────\n\nconst applyDirectiveBinding = (binding: DirectiveBinding): void => {\n binding.directive.mount(binding.anchor);\n};\n\n// ─── Binding dispatch ─────────────────────────────────────────────────────────\n\nexport const applyBinding = (binding: Binding): void => {\n switch (binding.type) {\n case 'attr':\n applyAttrBinding(binding);\n break;\n case 'directive':\n applyDirectiveBinding(binding);\n break;\n case 'event':\n applyEventBinding(binding);\n break;\n case 'html':\n applyHtmlBinding(binding);\n break;\n case 'model':\n applyModelBinding(binding);\n break;\n case 'prop':\n applyPropBinding(binding);\n break;\n case 'ref':\n applyRefBinding(binding);\n break;\n }\n};\n\n// ─── Attr binding factory (used by instantiator) ──────────────────────────────\n\nexport const createAttrBindingFromValue = (\n el: HTMLElement,\n mode: 'attr' | 'bool',\n name: string,\n value: unknown,\n): AttrBinding => {\n if (typeof value === 'function') {\n return { el, mode, name, signal: computed(value as () => unknown), type: 'attr' };\n }\n\n if (isReactive(value)) {\n return { el, mode, name, signal: value as Readable<unknown>, type: 'attr' };\n }\n\n return { el, mode, name, type: 'attr', value };\n};\n\nexport const resolveStaticText = (value: unknown): string => {\n if (value == null) return '';\n\n return String(value);\n};\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("../directives/model.cjs"),t=require("../errors.cjs"),n=require("./bindings.cjs"),r=require("./compiler.cjs"),i=require("./result.cjs");let a=require("@vielzeug/ripple");var o="html`...`: node-slot comment anchor has no parent node",s=(e,n,r)=>{let i=n.parentNode;for(t.invariant(i,o);e.fragment.firstChild;)i.insertBefore(e.fragment.firstChild,n);r.push(e.apply.bind(e))},c=(c,l)=>{let u=r.getStaticTemplate(c),d=u.element.content.cloneNode(!0),f=[],p=[],m=u.slots.map((e,n)=>{let i=l[n];if(e.kind===r.SlotKind.NODE){let n=e.commentId===void 0?void 0:u.commentPaths.get(e.commentId);return t.invariant(n,`compiled template is missing a comment path for node slot ${e.commentId}`),{comment:r.followPath(d,n),slot:e,value:i}}let a=e.elementId===void 0?void 0:u.elementPaths.get(e.elementId);return t.invariant(a,`compiled template is missing an element path for slot ${e.elementId}`),{el:r.followPath(d,a),slot:e,value:i}});for(let{comment:c,el:l,slot:u,value:d}of m)if(u.kind===r.SlotKind.NODE){let e=c;if(t.invariant(e,`compiled template produced a node slot without a comment anchor`),i.isDirectiveResult(d)){f.push({anchor:e,directive:d,type:`directive`});continue}if(i.isHtmlResult(d)){s(d,e,p),e.remove();continue}if(typeof d==`function`||(0,a.isReactive)(d)){f.push({anchor:e,source:d,type:`html`});continue}if(Array.isArray(d)){for(let r of d)if(i.isHtmlResult(r))s(r,e,p);else{let i=e.parentNode;t.invariant(i,o),i.insertBefore(document.createTextNode(n.resolveStaticText(r)),e)}e.remove();continue}e.replaceWith(document.createTextNode(n.resolveStaticText(d)))}else if(t.invariant(l,`compiled template produced an element slot without an element`),u.kind===r.SlotKind.EVENT){let e=u.name;if(t.invariant(e,`compiled template produced an event slot without an event name`),typeof d==`function`)f.push({el:l,handler:d,modifiers:u.eventModifiers,name:e,type:`event`});else if((0,a.isReactive)(d)){let t=d;f.push({el:l,handler:e=>{let n=t.value;typeof n==`function`&&n(e)},modifiers:u.eventModifiers,name:e,type:`event`})}}else if(u.kind===r.SlotKind.PROP){let e=u.name;t.invariant(e,`compiled template produced a prop slot without a property name`),typeof d==`function`?f.push({el:l,name:e,signal:(0,a.computed)(d),type:`prop`}):(0,a.isReactive)(d)?f.push({el:l,name:e,signal:d,type:`prop`}):f.push({el:l,name:e,type:`prop`,value:d})}else if(u.kind===r.SlotKind.REF)d&&f.push({el:l,ref:d,type:`ref`});else if(t.invariant(u.name,`compiled template produced an attr slot without an attribute name`),e.isModelSource(d)){if(u.mode!==`attr`)throw new t.OreApiError(t.ORE_ERRORS.modelRequiresValueBinding(u.name));f.push({el:l,event:d.event??e.modelDefaultEvent(l),get:d.get??e.modelDefaultGet,signal:d.source,type:`model`})}else f.push(n.createAttrBindingFromValue(l,u.mode??`attr`,u.name,d));return i.createHtmlResult(d,
|
|
1
|
+
const e=require("../directives/model.cjs"),t=require("../errors.cjs"),n=require("./bindings.cjs"),r=require("./compiler.cjs"),i=require("./result.cjs");let a=require("@vielzeug/ripple");var o="html`...`: node-slot comment anchor has no parent node",s=(e,n,r)=>{e.claim();let i=n.parentNode;for(t.invariant(i,o);e.fragment.firstChild;)i.insertBefore(e.fragment.firstChild,n);r.push(e.apply.bind(e))},c=(c,l)=>{let u=r.getStaticTemplate(c),d=u.element.content.cloneNode(!0),f=[],p=[],m=u.slots.map((e,n)=>{let i=l[n];if(e.kind===r.SlotKind.NODE){let n=e.commentId===void 0?void 0:u.commentPaths.get(e.commentId);return t.invariant(n,`compiled template is missing a comment path for node slot ${e.commentId}`),{comment:r.followPath(d,n),slot:e,value:i}}let a=e.elementId===void 0?void 0:u.elementPaths.get(e.elementId);return t.invariant(a,`compiled template is missing an element path for slot ${e.elementId}`),{el:r.followPath(d,a),slot:e,value:i}});for(let{comment:c,el:l,slot:u,value:d}of m)if(u.kind===r.SlotKind.NODE){let e=c;if(t.invariant(e,`compiled template produced a node slot without a comment anchor`),i.isDirectiveResult(d)){f.push({anchor:e,directive:d,type:`directive`});continue}if(i.isHtmlResult(d)){s(d,e,p),e.remove();continue}if(typeof d==`function`||(0,a.isReactive)(d)){f.push({anchor:e,source:d,type:`html`});continue}if(Array.isArray(d)){for(let r of d)if(i.isHtmlResult(r))s(r,e,p);else{let i=e.parentNode;t.invariant(i,o),i.insertBefore(document.createTextNode(n.resolveStaticText(r)),e)}e.remove();continue}e.replaceWith(document.createTextNode(n.resolveStaticText(d)))}else if(t.invariant(l,`compiled template produced an element slot without an element`),u.kind===r.SlotKind.EVENT){let e=u.name;if(t.invariant(e,`compiled template produced an event slot without an event name`),typeof d==`function`)f.push({el:l,handler:d,modifiers:u.eventModifiers,name:e,type:`event`});else if((0,a.isReactive)(d)){let t=d;f.push({el:l,handler:e=>{let n=t.value;typeof n==`function`&&n(e)},modifiers:u.eventModifiers,name:e,type:`event`})}}else if(u.kind===r.SlotKind.PROP){let e=u.name;t.invariant(e,`compiled template produced a prop slot without a property name`),typeof d==`function`?f.push({el:l,name:e,signal:(0,a.computed)(d),type:`prop`}):(0,a.isReactive)(d)?f.push({el:l,name:e,signal:d,type:`prop`}):f.push({el:l,name:e,type:`prop`,value:d})}else if(u.kind===r.SlotKind.REF)d&&f.push({el:l,ref:d,type:`ref`});else if(t.invariant(u.name,`compiled template produced an attr slot without an attribute name`),e.isModelSource(d)){if(u.mode!==`attr`)throw new t.OreApiError(t.ORE_ERRORS.modelRequiresValueBinding(u.name));f.push({el:l,event:d.event??e.modelDefaultEvent(l),get:d.get??e.modelDefaultGet,signal:d.source,type:`model`})}else f.push(n.createAttrBindingFromValue(l,u.mode??`attr`,u.name,d));return i.createHtmlResult(d,()=>{for(let e of f)n.applyBinding(e);for(let e of p)e()})},l=(e,...t)=>c(e,t);exports.compileTemplate=c,exports.html=l;
|
|
2
2
|
//# sourceMappingURL=instantiator.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instantiator.cjs","names":["getStaticTemplate","SlotKind","followPath","isDirectiveResult","isHtmlResult","isReactive","resolveStaticText","computed","isModelSource","OreApiError","ORE_ERRORS","modelDefaultEvent","modelDefaultGet","createAttrBindingFromValue","createHtmlResult","require_model","require_errors","require_bindings","require_compiler","require_result","_vielzeug_ripple","NODE_SLOT_NO_PARENT_MSG","embedStaticResult","parent","anchor","result","chainedApplies","compileTemplate","compiled","strings","fragment","bindings","boundSlots","value","values","i","slot","commentPath","elementPath","comment","item","el","name","signalValue","h","binding","registerCleanup","chainedApply","html"],"sources":["../../src/template/instantiator.ts"],"sourcesContent":["/**\n * template/instantiator.ts: Template instantiation and the `html` tagged literal.\n *\n * Responsibilities:\n * - Clone a compiled static template and wire up live bindings.\n * - Expose `compileTemplate()` and `html` as the public authoring API.\n */\n\nimport { computed, isReactive, type Readable, type Signal } from '@vielzeug/ripple';\n\nimport { isModelSource, modelDefaultEvent, modelDefaultGet } from '../directives/model';\nimport { invariant, ORE_ERRORS, OreApiError } from '../errors';\nimport type { Binding } from './binding-types';\nimport { applyBinding, createAttrBindingFromValue, resolveStaticText } from './bindings';\nimport { followPath, getStaticTemplate, SlotKind } from './compiler';\nimport {\n type CompiledHTMLResult,\n createHtmlResult,\n type HTMLResult,\n isDirectiveResult,\n isHtmlResult,\n type Ref,\n type RefCallback,\n} from './result';\n\n// ─── Template instantiation ──────────────────────────────────────────────────\n\nconst NODE_SLOT_NO_PARENT_MSG = 'html`...`: node-slot comment anchor has no parent node';\n\n/**\n * Static-embed an already-created HTMLResult at a node-slot anchor: move its\n * fragment children into place and chain its apply into the outer apply phase\n * (so embedded reactive wiring starts when the host template mounts, not now).\n */\nconst embedStaticResult = (\n result: CompiledHTMLResult,\n anchor: Comment,\n chainedApplies: Array<(rc: (fn: () => void) => void) => void>,\n): void => {\n const parent = anchor.parentNode;\n\n invariant(parent, NODE_SLOT_NO_PARENT_MSG);\n\n while (result.fragment.firstChild) parent.insertBefore(result.fragment.firstChild, anchor);\n\n chainedApplies.push(result.apply.bind(result));\n};\n\n/**\n * Instantiate a compiled template: clone the cached DOM template, navigate\n * to each binding target using pre-recorded paths, and build bindings with\n * direct node references. Returns an HTMLResult ready to mount.\n */\nexport const compileTemplate = (strings: TemplateStringsArray, values: unknown[]): HTMLResult => {\n const compiled = getStaticTemplate(strings);\n const fragment = compiled.element.content.cloneNode(true) as DocumentFragment;\n const bindings: Binding[] = [];\n // For static HTMLResult embeds: chain their apply calls\n const chainedApplies: Array<(rc: (fn: () => void) => void) => void> = [];\n\n // Phase 1: Resolve all binding targets BEFORE any DOM modifications\n type BoundSlot = { comment?: Comment; el?: HTMLElement; slot: (typeof compiled.slots)[number]; value: unknown };\n\n const boundSlots: BoundSlot[] = compiled.slots.map((slot, i) => {\n const value = values[i];\n\n if (slot.kind === SlotKind.NODE) {\n const commentPath = slot.commentId !== undefined ? compiled.commentPaths.get(slot.commentId) : undefined;\n\n invariant(commentPath, `compiled template is missing a comment path for node slot ${slot.commentId}`);\n\n return { comment: followPath(fragment, commentPath) as Comment, slot, value };\n }\n\n const elementPath = slot.elementId !== undefined ? compiled.elementPaths.get(slot.elementId) : undefined;\n\n invariant(elementPath, `compiled template is missing an element path for slot ${slot.elementId}`);\n\n return { el: followPath(fragment, elementPath) as HTMLElement, slot, value };\n });\n\n // Phase 2: Build bindings (may modify DOM for static content).\n for (const { comment, el, slot, value } of boundSlots) {\n if (slot.kind === SlotKind.NODE) {\n const anchor = comment;\n\n invariant(anchor, 'compiled template produced a node slot without a comment anchor');\n\n if (isDirectiveResult(value)) {\n bindings.push({ anchor, directive: value, type: 'directive' });\n continue;\n }\n\n if (isHtmlResult(value)) {\n // Static embed: move fragment children into place, chain apply\n embedStaticResult(value, anchor, chainedApplies);\n anchor.remove();\n continue;\n }\n\n if (typeof value === 'function' || isReactive(value)) {\n // Always use the html binding for reactive values: it handles both text\n // values and HTMLResult values, preventing silent \"[object Object]\"\n // corruption when a signal's runtime type changes from null/string to HTMLResult.\n // The source is handed over raw, without a memo computed, so the render effect\n // itself is the observer and the owner of anything the value builds (see HtmlBinding).\n bindings.push({ anchor, source: value as (() => unknown) | Readable<unknown>, type: 'html' });\n continue;\n }\n\n if (Array.isArray(value)) {\n for (const item of value) {\n if (isHtmlResult(item)) {\n embedStaticResult(item, anchor, chainedApplies);\n } else {\n const parent = anchor.parentNode;\n\n invariant(parent, NODE_SLOT_NO_PARENT_MSG);\n parent.insertBefore(document.createTextNode(resolveStaticText(item)), anchor);\n }\n }\n anchor.remove();\n continue;\n }\n\n // Static primitive: replace with text node, no binding\n anchor.replaceWith(document.createTextNode(resolveStaticText(value)));\n continue;\n }\n\n // Element slot\n invariant(el, 'compiled template produced an element slot without an element');\n\n if (slot.kind === SlotKind.EVENT) {\n const name = slot.name;\n\n invariant(name, 'compiled template produced an event slot without an event name');\n\n if (typeof value === 'function') {\n bindings.push({\n el,\n handler: value as (e: Event) => void,\n modifiers: slot.eventModifiers,\n name,\n type: 'event',\n });\n } else if (isReactive(value)) {\n const signalValue = value as Readable<unknown>;\n const handler = (e: Event) => {\n const h = signalValue.value;\n\n if (typeof h === 'function') (h as (e: Event) => void)(e);\n };\n\n bindings.push({ el, handler, modifiers: slot.eventModifiers, name, type: 'event' });\n }\n\n continue;\n }\n\n if (slot.kind === SlotKind.PROP) {\n const name = slot.name;\n\n invariant(name, 'compiled template produced a prop slot without a property name');\n\n if (typeof value === 'function') {\n bindings.push({ el, name, signal: computed(value as () => unknown), type: 'prop' });\n } else if (isReactive(value)) {\n bindings.push({ el, name, signal: value as Readable<unknown>, type: 'prop' });\n } else {\n bindings.push({ el, name, type: 'prop', value });\n }\n\n continue;\n }\n\n if (slot.kind === SlotKind.REF) {\n if (value) {\n bindings.push({ el, ref: value as Ref<Element> | RefCallback<Element>, type: 'ref' });\n }\n\n continue;\n }\n\n // attr / boolAttr\n invariant(slot.name, 'compiled template produced an attr slot without an attribute name');\n\n if (isModelSource(value)) {\n if (slot.mode !== 'attr') throw new OreApiError(ORE_ERRORS.modelRequiresValueBinding(slot.name));\n\n bindings.push({\n el,\n event: value.event ?? modelDefaultEvent(el),\n get: (value.get ?? modelDefaultGet) as (el: HTMLElement) => unknown,\n signal: value.source as Signal<unknown>,\n type: 'model',\n });\n continue;\n }\n\n bindings.push(createAttrBindingFromValue(el, slot.mode ?? 'attr', slot.name, value));\n }\n\n return createHtmlResult(fragment, (registerCleanup) => {\n for (const binding of bindings) applyBinding(binding, registerCleanup);\n for (const chainedApply of chainedApplies) chainedApply(registerCleanup);\n });\n};\n\nexport const html = (strings: TemplateStringsArray, ...values: unknown[]): HTMLResult =>\n compileTemplate(strings, values);\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"instantiator.cjs","names":["getStaticTemplate","SlotKind","followPath","isDirectiveResult","isHtmlResult","isReactive","resolveStaticText","computed","isModelSource","OreApiError","ORE_ERRORS","modelDefaultEvent","modelDefaultGet","createAttrBindingFromValue","createHtmlResult","require_model","require_errors","require_bindings","require_compiler","require_result","_vielzeug_ripple","NODE_SLOT_NO_PARENT_MSG","embedStaticResult","result","parent","anchor","chainedApplies","compileTemplate","compiled","strings","fragment","bindings","boundSlots","value","values","i","slot","commentPath","elementPath","comment","item","el","name","signalValue","h","binding","chainedApply","html"],"sources":["../../src/template/instantiator.ts"],"sourcesContent":["/**\n * template/instantiator.ts: Template instantiation and the `html` tagged literal.\n *\n * Responsibilities:\n * - Clone a compiled static template and wire up live bindings.\n * - Expose `compileTemplate()` and `html` as the public authoring API.\n */\n\nimport { computed, isReactive, type Readable, type Signal } from '@vielzeug/ripple';\n\nimport { isModelSource, modelDefaultEvent, modelDefaultGet } from '../directives/model';\nimport { invariant, ORE_ERRORS, OreApiError } from '../errors';\nimport type { Binding } from './binding-types';\nimport { applyBinding, createAttrBindingFromValue, resolveStaticText } from './bindings';\nimport { followPath, getStaticTemplate, SlotKind } from './compiler';\nimport {\n type CompiledHTMLResult,\n createHtmlResult,\n type HTMLResult,\n isDirectiveResult,\n isHtmlResult,\n type Ref,\n type RefCallback,\n} from './result';\n\n// ─── Template instantiation ──────────────────────────────────────────────────\n\nconst NODE_SLOT_NO_PARENT_MSG = 'html`...`: node-slot comment anchor has no parent node';\n\n/**\n * Static-embed an already-created HTMLResult at a node-slot anchor: move its\n * fragment children into place and chain its apply into the outer apply phase\n * (so embedded reactive wiring starts when the host template mounts, not now).\n */\nconst embedStaticResult = (result: CompiledHTMLResult, anchor: Comment, chainedApplies: Array<() => void>): void => {\n result.claim();\n\n const parent = anchor.parentNode;\n\n invariant(parent, NODE_SLOT_NO_PARENT_MSG);\n\n while (result.fragment.firstChild) parent.insertBefore(result.fragment.firstChild, anchor);\n\n chainedApplies.push(result.apply.bind(result));\n};\n\n/**\n * Instantiate a compiled template: clone the cached DOM template, navigate\n * to each binding target using pre-recorded paths, and build bindings with\n * direct node references. Returns an HTMLResult ready to mount.\n */\nexport const compileTemplate = (strings: TemplateStringsArray, values: unknown[]): HTMLResult => {\n const compiled = getStaticTemplate(strings);\n const fragment = compiled.element.content.cloneNode(true) as DocumentFragment;\n const bindings: Binding[] = [];\n // For static HTMLResult embeds: chain their apply calls\n const chainedApplies: Array<() => void> = [];\n\n // Phase 1: Resolve all binding targets BEFORE any DOM modifications\n type BoundSlot = { comment?: Comment; el?: HTMLElement; slot: (typeof compiled.slots)[number]; value: unknown };\n\n const boundSlots: BoundSlot[] = compiled.slots.map((slot, i) => {\n const value = values[i];\n\n if (slot.kind === SlotKind.NODE) {\n const commentPath = slot.commentId !== undefined ? compiled.commentPaths.get(slot.commentId) : undefined;\n\n invariant(commentPath, `compiled template is missing a comment path for node slot ${slot.commentId}`);\n\n return { comment: followPath(fragment, commentPath) as Comment, slot, value };\n }\n\n const elementPath = slot.elementId !== undefined ? compiled.elementPaths.get(slot.elementId) : undefined;\n\n invariant(elementPath, `compiled template is missing an element path for slot ${slot.elementId}`);\n\n return { el: followPath(fragment, elementPath) as HTMLElement, slot, value };\n });\n\n // Phase 2: Build bindings (may modify DOM for static content).\n for (const { comment, el, slot, value } of boundSlots) {\n if (slot.kind === SlotKind.NODE) {\n const anchor = comment;\n\n invariant(anchor, 'compiled template produced a node slot without a comment anchor');\n\n if (isDirectiveResult(value)) {\n bindings.push({ anchor, directive: value, type: 'directive' });\n continue;\n }\n\n if (isHtmlResult(value)) {\n // Static embed: move fragment children into place, chain apply\n embedStaticResult(value, anchor, chainedApplies);\n anchor.remove();\n continue;\n }\n\n if (typeof value === 'function' || isReactive(value)) {\n // Always use the html binding for reactive values: it handles both text\n // values and HTMLResult values, preventing silent \"[object Object]\"\n // corruption when a signal's runtime type changes from null/string to HTMLResult.\n // The source is handed over raw, without a memo computed, so the render effect\n // itself is the observer and the owner of anything the value builds (see HtmlBinding).\n bindings.push({ anchor, source: value as (() => unknown) | Readable<unknown>, type: 'html' });\n continue;\n }\n\n if (Array.isArray(value)) {\n for (const item of value) {\n if (isHtmlResult(item)) {\n embedStaticResult(item, anchor, chainedApplies);\n } else {\n const parent = anchor.parentNode;\n\n invariant(parent, NODE_SLOT_NO_PARENT_MSG);\n parent.insertBefore(document.createTextNode(resolveStaticText(item)), anchor);\n }\n }\n anchor.remove();\n continue;\n }\n\n // Static primitive: replace with text node, no binding\n anchor.replaceWith(document.createTextNode(resolveStaticText(value)));\n continue;\n }\n\n // Element slot\n invariant(el, 'compiled template produced an element slot without an element');\n\n if (slot.kind === SlotKind.EVENT) {\n const name = slot.name;\n\n invariant(name, 'compiled template produced an event slot without an event name');\n\n if (typeof value === 'function') {\n bindings.push({\n el,\n handler: value as (e: Event) => void,\n modifiers: slot.eventModifiers,\n name,\n type: 'event',\n });\n } else if (isReactive(value)) {\n const signalValue = value as Readable<unknown>;\n const handler = (e: Event) => {\n const h = signalValue.value;\n\n if (typeof h === 'function') (h as (e: Event) => void)(e);\n };\n\n bindings.push({ el, handler, modifiers: slot.eventModifiers, name, type: 'event' });\n }\n\n continue;\n }\n\n if (slot.kind === SlotKind.PROP) {\n const name = slot.name;\n\n invariant(name, 'compiled template produced a prop slot without a property name');\n\n if (typeof value === 'function') {\n bindings.push({ el, name, signal: computed(value as () => unknown), type: 'prop' });\n } else if (isReactive(value)) {\n bindings.push({ el, name, signal: value as Readable<unknown>, type: 'prop' });\n } else {\n bindings.push({ el, name, type: 'prop', value });\n }\n\n continue;\n }\n\n if (slot.kind === SlotKind.REF) {\n if (value) {\n bindings.push({ el, ref: value as Ref<Element> | RefCallback<Element>, type: 'ref' });\n }\n\n continue;\n }\n\n // attr / boolAttr\n invariant(slot.name, 'compiled template produced an attr slot without an attribute name');\n\n if (isModelSource(value)) {\n if (slot.mode !== 'attr') throw new OreApiError(ORE_ERRORS.modelRequiresValueBinding(slot.name));\n\n bindings.push({\n el,\n event: value.event ?? modelDefaultEvent(el),\n get: (value.get ?? modelDefaultGet) as (el: HTMLElement) => unknown,\n signal: value.source as Signal<unknown>,\n type: 'model',\n });\n continue;\n }\n\n bindings.push(createAttrBindingFromValue(el, slot.mode ?? 'attr', slot.name, value));\n }\n\n return createHtmlResult(fragment, () => {\n for (const binding of bindings) applyBinding(binding);\n for (const chainedApply of chainedApplies) chainedApply();\n });\n};\n\nexport const html = (strings: TemplateStringsArray, ...values: unknown[]): HTMLResult =>\n compileTemplate(strings, values);\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instantiator.d.ts","sourceRoot":"","sources":["../../src/template/instantiator.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AASH,OAAO,EAGL,KAAK,UAAU,EAKhB,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"instantiator.d.ts","sourceRoot":"","sources":["../../src/template/instantiator.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AASH,OAAO,EAGL,KAAK,UAAU,EAKhB,MAAM,UAAU,CAAC;AAuBlB;;;;GAIG;AACH,eAAO,MAAM,eAAe,GAAI,SAAS,oBAAoB,EAAE,QAAQ,OAAO,EAAE,KAAG,UA0JlF,CAAC;AAEF,eAAO,MAAM,IAAI,GAAI,SAAS,oBAAoB,EAAE,GAAG,QAAQ,OAAO,EAAE,KAAG,UACzC,CAAC"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{isModelSource as e,modelDefaultEvent as t,modelDefaultGet as n}from"../directives/model.js";import{ORE_ERRORS as r,OreApiError as i,invariant as a}from"../errors.js";import{applyBinding as o,createAttrBindingFromValue as s,resolveStaticText as c}from"./bindings.js";import{SlotKind as l,followPath as u,getStaticTemplate as d}from"./compiler.js";import{createHtmlResult as f,isDirectiveResult as p,isHtmlResult as m}from"./result.js";import{computed as h,isReactive as g}from"@vielzeug/ripple";var _="html`...`: node-slot comment anchor has no parent node",v=(e,t,n)=>{let r=t.parentNode;for(a(r,_);e.fragment.firstChild;)r.insertBefore(e.fragment.firstChild,t);n.push(e.apply.bind(e))},y=(y,b)=>{let x=d(y),S=x.element.content.cloneNode(!0),C=[],w=[],T=x.slots.map((e,t)=>{let n=b[t];if(e.kind===l.NODE){let t=e.commentId===void 0?void 0:x.commentPaths.get(e.commentId);return a(t,`compiled template is missing a comment path for node slot ${e.commentId}`),{comment:u(S,t),slot:e,value:n}}let r=e.elementId===void 0?void 0:x.elementPaths.get(e.elementId);return a(r,`compiled template is missing an element path for slot ${e.elementId}`),{el:u(S,r),slot:e,value:n}});for(let{comment:o,el:u,slot:d,value:f}of T)if(d.kind===l.NODE){let e=o;if(a(e,`compiled template produced a node slot without a comment anchor`),p(f)){C.push({anchor:e,directive:f,type:`directive`});continue}if(m(f)){v(f,e,w),e.remove();continue}if(typeof f==`function`||g(f)){C.push({anchor:e,source:f,type:`html`});continue}if(Array.isArray(f)){for(let t of f)if(m(t))v(t,e,w);else{let n=e.parentNode;a(n,_),n.insertBefore(document.createTextNode(c(t)),e)}e.remove();continue}e.replaceWith(document.createTextNode(c(f)))}else if(a(u,`compiled template produced an element slot without an element`),d.kind===l.EVENT){let e=d.name;if(a(e,`compiled template produced an event slot without an event name`),typeof f==`function`)C.push({el:u,handler:f,modifiers:d.eventModifiers,name:e,type:`event`});else if(g(f)){let t=f;C.push({el:u,handler:e=>{let n=t.value;typeof n==`function`&&n(e)},modifiers:d.eventModifiers,name:e,type:`event`})}}else if(d.kind===l.PROP){let e=d.name;a(e,`compiled template produced a prop slot without a property name`),typeof f==`function`?C.push({el:u,name:e,signal:h(f),type:`prop`}):g(f)?C.push({el:u,name:e,signal:f,type:`prop`}):C.push({el:u,name:e,type:`prop`,value:f})}else if(d.kind===l.REF)f&&C.push({el:u,ref:f,type:`ref`});else if(a(d.name,`compiled template produced an attr slot without an attribute name`),e(f)){if(d.mode!==`attr`)throw new i(r.modelRequiresValueBinding(d.name));C.push({el:u,event:f.event??t(u),get:f.get??n,signal:f.source,type:`model`})}else C.push(s(u,d.mode??`attr`,d.name,f));return f(S,
|
|
1
|
+
import{isModelSource as e,modelDefaultEvent as t,modelDefaultGet as n}from"../directives/model.js";import{ORE_ERRORS as r,OreApiError as i,invariant as a}from"../errors.js";import{applyBinding as o,createAttrBindingFromValue as s,resolveStaticText as c}from"./bindings.js";import{SlotKind as l,followPath as u,getStaticTemplate as d}from"./compiler.js";import{createHtmlResult as f,isDirectiveResult as p,isHtmlResult as m}from"./result.js";import{computed as h,isReactive as g}from"@vielzeug/ripple";var _="html`...`: node-slot comment anchor has no parent node",v=(e,t,n)=>{e.claim();let r=t.parentNode;for(a(r,_);e.fragment.firstChild;)r.insertBefore(e.fragment.firstChild,t);n.push(e.apply.bind(e))},y=(y,b)=>{let x=d(y),S=x.element.content.cloneNode(!0),C=[],w=[],T=x.slots.map((e,t)=>{let n=b[t];if(e.kind===l.NODE){let t=e.commentId===void 0?void 0:x.commentPaths.get(e.commentId);return a(t,`compiled template is missing a comment path for node slot ${e.commentId}`),{comment:u(S,t),slot:e,value:n}}let r=e.elementId===void 0?void 0:x.elementPaths.get(e.elementId);return a(r,`compiled template is missing an element path for slot ${e.elementId}`),{el:u(S,r),slot:e,value:n}});for(let{comment:o,el:u,slot:d,value:f}of T)if(d.kind===l.NODE){let e=o;if(a(e,`compiled template produced a node slot without a comment anchor`),p(f)){C.push({anchor:e,directive:f,type:`directive`});continue}if(m(f)){v(f,e,w),e.remove();continue}if(typeof f==`function`||g(f)){C.push({anchor:e,source:f,type:`html`});continue}if(Array.isArray(f)){for(let t of f)if(m(t))v(t,e,w);else{let n=e.parentNode;a(n,_),n.insertBefore(document.createTextNode(c(t)),e)}e.remove();continue}e.replaceWith(document.createTextNode(c(f)))}else if(a(u,`compiled template produced an element slot without an element`),d.kind===l.EVENT){let e=d.name;if(a(e,`compiled template produced an event slot without an event name`),typeof f==`function`)C.push({el:u,handler:f,modifiers:d.eventModifiers,name:e,type:`event`});else if(g(f)){let t=f;C.push({el:u,handler:e=>{let n=t.value;typeof n==`function`&&n(e)},modifiers:d.eventModifiers,name:e,type:`event`})}}else if(d.kind===l.PROP){let e=d.name;a(e,`compiled template produced a prop slot without a property name`),typeof f==`function`?C.push({el:u,name:e,signal:h(f),type:`prop`}):g(f)?C.push({el:u,name:e,signal:f,type:`prop`}):C.push({el:u,name:e,type:`prop`,value:f})}else if(d.kind===l.REF)f&&C.push({el:u,ref:f,type:`ref`});else if(a(d.name,`compiled template produced an attr slot without an attribute name`),e(f)){if(d.mode!==`attr`)throw new i(r.modelRequiresValueBinding(d.name));C.push({el:u,event:f.event??t(u),get:f.get??n,signal:f.source,type:`model`})}else C.push(s(u,d.mode??`attr`,d.name,f));return f(S,()=>{for(let e of C)o(e);for(let e of w)e()})},b=(e,...t)=>y(e,t);export{y as compileTemplate,b as html};
|
|
2
2
|
//# sourceMappingURL=instantiator.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instantiator.js","names":["NODE_SLOT_NO_PARENT_MSG","embedStaticResult","parent","anchor","invariant","result","chainedApplies","compileTemplate","compiled","getStaticTemplate","strings","fragment","bindings","boundSlots","value","values","i","slot","SlotKind","commentPath","followPath","elementPath","comment","isDirectiveResult","isHtmlResult","isReactive","item","resolveStaticText","el","name","signalValue","h","computed","isModelSource","OreApiError","ORE_ERRORS","modelDefaultEvent","modelDefaultGet","createAttrBindingFromValue","createHtmlResult","binding","applyBinding","registerCleanup","chainedApply","html"],"sources":["../../src/template/instantiator.ts"],"sourcesContent":["/**\n * template/instantiator.ts: Template instantiation and the `html` tagged literal.\n *\n * Responsibilities:\n * - Clone a compiled static template and wire up live bindings.\n * - Expose `compileTemplate()` and `html` as the public authoring API.\n */\n\nimport { computed, isReactive, type Readable, type Signal } from '@vielzeug/ripple';\n\nimport { isModelSource, modelDefaultEvent, modelDefaultGet } from '../directives/model';\nimport { invariant, ORE_ERRORS, OreApiError } from '../errors';\nimport type { Binding } from './binding-types';\nimport { applyBinding, createAttrBindingFromValue, resolveStaticText } from './bindings';\nimport { followPath, getStaticTemplate, SlotKind } from './compiler';\nimport {\n type CompiledHTMLResult,\n createHtmlResult,\n type HTMLResult,\n isDirectiveResult,\n isHtmlResult,\n type Ref,\n type RefCallback,\n} from './result';\n\n// ─── Template instantiation ──────────────────────────────────────────────────\n\nconst NODE_SLOT_NO_PARENT_MSG = 'html`...`: node-slot comment anchor has no parent node';\n\n/**\n * Static-embed an already-created HTMLResult at a node-slot anchor: move its\n * fragment children into place and chain its apply into the outer apply phase\n * (so embedded reactive wiring starts when the host template mounts, not now).\n */\nconst embedStaticResult = (\n result: CompiledHTMLResult,\n anchor: Comment,\n chainedApplies: Array<(rc: (fn: () => void) => void) => void>,\n): void => {\n const parent = anchor.parentNode;\n\n invariant(parent, NODE_SLOT_NO_PARENT_MSG);\n\n while (result.fragment.firstChild) parent.insertBefore(result.fragment.firstChild, anchor);\n\n chainedApplies.push(result.apply.bind(result));\n};\n\n/**\n * Instantiate a compiled template: clone the cached DOM template, navigate\n * to each binding target using pre-recorded paths, and build bindings with\n * direct node references. Returns an HTMLResult ready to mount.\n */\nexport const compileTemplate = (strings: TemplateStringsArray, values: unknown[]): HTMLResult => {\n const compiled = getStaticTemplate(strings);\n const fragment = compiled.element.content.cloneNode(true) as DocumentFragment;\n const bindings: Binding[] = [];\n // For static HTMLResult embeds: chain their apply calls\n const chainedApplies: Array<(rc: (fn: () => void) => void) => void> = [];\n\n // Phase 1: Resolve all binding targets BEFORE any DOM modifications\n type BoundSlot = { comment?: Comment; el?: HTMLElement; slot: (typeof compiled.slots)[number]; value: unknown };\n\n const boundSlots: BoundSlot[] = compiled.slots.map((slot, i) => {\n const value = values[i];\n\n if (slot.kind === SlotKind.NODE) {\n const commentPath = slot.commentId !== undefined ? compiled.commentPaths.get(slot.commentId) : undefined;\n\n invariant(commentPath, `compiled template is missing a comment path for node slot ${slot.commentId}`);\n\n return { comment: followPath(fragment, commentPath) as Comment, slot, value };\n }\n\n const elementPath = slot.elementId !== undefined ? compiled.elementPaths.get(slot.elementId) : undefined;\n\n invariant(elementPath, `compiled template is missing an element path for slot ${slot.elementId}`);\n\n return { el: followPath(fragment, elementPath) as HTMLElement, slot, value };\n });\n\n // Phase 2: Build bindings (may modify DOM for static content).\n for (const { comment, el, slot, value } of boundSlots) {\n if (slot.kind === SlotKind.NODE) {\n const anchor = comment;\n\n invariant(anchor, 'compiled template produced a node slot without a comment anchor');\n\n if (isDirectiveResult(value)) {\n bindings.push({ anchor, directive: value, type: 'directive' });\n continue;\n }\n\n if (isHtmlResult(value)) {\n // Static embed: move fragment children into place, chain apply\n embedStaticResult(value, anchor, chainedApplies);\n anchor.remove();\n continue;\n }\n\n if (typeof value === 'function' || isReactive(value)) {\n // Always use the html binding for reactive values: it handles both text\n // values and HTMLResult values, preventing silent \"[object Object]\"\n // corruption when a signal's runtime type changes from null/string to HTMLResult.\n // The source is handed over raw, without a memo computed, so the render effect\n // itself is the observer and the owner of anything the value builds (see HtmlBinding).\n bindings.push({ anchor, source: value as (() => unknown) | Readable<unknown>, type: 'html' });\n continue;\n }\n\n if (Array.isArray(value)) {\n for (const item of value) {\n if (isHtmlResult(item)) {\n embedStaticResult(item, anchor, chainedApplies);\n } else {\n const parent = anchor.parentNode;\n\n invariant(parent, NODE_SLOT_NO_PARENT_MSG);\n parent.insertBefore(document.createTextNode(resolveStaticText(item)), anchor);\n }\n }\n anchor.remove();\n continue;\n }\n\n // Static primitive: replace with text node, no binding\n anchor.replaceWith(document.createTextNode(resolveStaticText(value)));\n continue;\n }\n\n // Element slot\n invariant(el, 'compiled template produced an element slot without an element');\n\n if (slot.kind === SlotKind.EVENT) {\n const name = slot.name;\n\n invariant(name, 'compiled template produced an event slot without an event name');\n\n if (typeof value === 'function') {\n bindings.push({\n el,\n handler: value as (e: Event) => void,\n modifiers: slot.eventModifiers,\n name,\n type: 'event',\n });\n } else if (isReactive(value)) {\n const signalValue = value as Readable<unknown>;\n const handler = (e: Event) => {\n const h = signalValue.value;\n\n if (typeof h === 'function') (h as (e: Event) => void)(e);\n };\n\n bindings.push({ el, handler, modifiers: slot.eventModifiers, name, type: 'event' });\n }\n\n continue;\n }\n\n if (slot.kind === SlotKind.PROP) {\n const name = slot.name;\n\n invariant(name, 'compiled template produced a prop slot without a property name');\n\n if (typeof value === 'function') {\n bindings.push({ el, name, signal: computed(value as () => unknown), type: 'prop' });\n } else if (isReactive(value)) {\n bindings.push({ el, name, signal: value as Readable<unknown>, type: 'prop' });\n } else {\n bindings.push({ el, name, type: 'prop', value });\n }\n\n continue;\n }\n\n if (slot.kind === SlotKind.REF) {\n if (value) {\n bindings.push({ el, ref: value as Ref<Element> | RefCallback<Element>, type: 'ref' });\n }\n\n continue;\n }\n\n // attr / boolAttr\n invariant(slot.name, 'compiled template produced an attr slot without an attribute name');\n\n if (isModelSource(value)) {\n if (slot.mode !== 'attr') throw new OreApiError(ORE_ERRORS.modelRequiresValueBinding(slot.name));\n\n bindings.push({\n el,\n event: value.event ?? modelDefaultEvent(el),\n get: (value.get ?? modelDefaultGet) as (el: HTMLElement) => unknown,\n signal: value.source as Signal<unknown>,\n type: 'model',\n });\n continue;\n }\n\n bindings.push(createAttrBindingFromValue(el, slot.mode ?? 'attr', slot.name, value));\n }\n\n return createHtmlResult(fragment, (registerCleanup) => {\n for (const binding of bindings) applyBinding(binding, registerCleanup);\n for (const chainedApply of chainedApplies) chainedApply(registerCleanup);\n });\n};\n\nexport const html = (strings: TemplateStringsArray, ...values: unknown[]): HTMLResult =>\n compileTemplate(strings, values);\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"instantiator.js","names":["NODE_SLOT_NO_PARENT_MSG","embedStaticResult","result","parent","anchor","invariant","chainedApplies","compileTemplate","compiled","getStaticTemplate","strings","fragment","bindings","boundSlots","value","values","i","slot","SlotKind","commentPath","followPath","elementPath","comment","isDirectiveResult","isHtmlResult","isReactive","item","resolveStaticText","el","name","signalValue","h","computed","isModelSource","OreApiError","ORE_ERRORS","modelDefaultEvent","modelDefaultGet","createAttrBindingFromValue","createHtmlResult","binding","applyBinding","chainedApply","html"],"sources":["../../src/template/instantiator.ts"],"sourcesContent":["/**\n * template/instantiator.ts: Template instantiation and the `html` tagged literal.\n *\n * Responsibilities:\n * - Clone a compiled static template and wire up live bindings.\n * - Expose `compileTemplate()` and `html` as the public authoring API.\n */\n\nimport { computed, isReactive, type Readable, type Signal } from '@vielzeug/ripple';\n\nimport { isModelSource, modelDefaultEvent, modelDefaultGet } from '../directives/model';\nimport { invariant, ORE_ERRORS, OreApiError } from '../errors';\nimport type { Binding } from './binding-types';\nimport { applyBinding, createAttrBindingFromValue, resolveStaticText } from './bindings';\nimport { followPath, getStaticTemplate, SlotKind } from './compiler';\nimport {\n type CompiledHTMLResult,\n createHtmlResult,\n type HTMLResult,\n isDirectiveResult,\n isHtmlResult,\n type Ref,\n type RefCallback,\n} from './result';\n\n// ─── Template instantiation ──────────────────────────────────────────────────\n\nconst NODE_SLOT_NO_PARENT_MSG = 'html`...`: node-slot comment anchor has no parent node';\n\n/**\n * Static-embed an already-created HTMLResult at a node-slot anchor: move its\n * fragment children into place and chain its apply into the outer apply phase\n * (so embedded reactive wiring starts when the host template mounts, not now).\n */\nconst embedStaticResult = (result: CompiledHTMLResult, anchor: Comment, chainedApplies: Array<() => void>): void => {\n result.claim();\n\n const parent = anchor.parentNode;\n\n invariant(parent, NODE_SLOT_NO_PARENT_MSG);\n\n while (result.fragment.firstChild) parent.insertBefore(result.fragment.firstChild, anchor);\n\n chainedApplies.push(result.apply.bind(result));\n};\n\n/**\n * Instantiate a compiled template: clone the cached DOM template, navigate\n * to each binding target using pre-recorded paths, and build bindings with\n * direct node references. Returns an HTMLResult ready to mount.\n */\nexport const compileTemplate = (strings: TemplateStringsArray, values: unknown[]): HTMLResult => {\n const compiled = getStaticTemplate(strings);\n const fragment = compiled.element.content.cloneNode(true) as DocumentFragment;\n const bindings: Binding[] = [];\n // For static HTMLResult embeds: chain their apply calls\n const chainedApplies: Array<() => void> = [];\n\n // Phase 1: Resolve all binding targets BEFORE any DOM modifications\n type BoundSlot = { comment?: Comment; el?: HTMLElement; slot: (typeof compiled.slots)[number]; value: unknown };\n\n const boundSlots: BoundSlot[] = compiled.slots.map((slot, i) => {\n const value = values[i];\n\n if (slot.kind === SlotKind.NODE) {\n const commentPath = slot.commentId !== undefined ? compiled.commentPaths.get(slot.commentId) : undefined;\n\n invariant(commentPath, `compiled template is missing a comment path for node slot ${slot.commentId}`);\n\n return { comment: followPath(fragment, commentPath) as Comment, slot, value };\n }\n\n const elementPath = slot.elementId !== undefined ? compiled.elementPaths.get(slot.elementId) : undefined;\n\n invariant(elementPath, `compiled template is missing an element path for slot ${slot.elementId}`);\n\n return { el: followPath(fragment, elementPath) as HTMLElement, slot, value };\n });\n\n // Phase 2: Build bindings (may modify DOM for static content).\n for (const { comment, el, slot, value } of boundSlots) {\n if (slot.kind === SlotKind.NODE) {\n const anchor = comment;\n\n invariant(anchor, 'compiled template produced a node slot without a comment anchor');\n\n if (isDirectiveResult(value)) {\n bindings.push({ anchor, directive: value, type: 'directive' });\n continue;\n }\n\n if (isHtmlResult(value)) {\n // Static embed: move fragment children into place, chain apply\n embedStaticResult(value, anchor, chainedApplies);\n anchor.remove();\n continue;\n }\n\n if (typeof value === 'function' || isReactive(value)) {\n // Always use the html binding for reactive values: it handles both text\n // values and HTMLResult values, preventing silent \"[object Object]\"\n // corruption when a signal's runtime type changes from null/string to HTMLResult.\n // The source is handed over raw, without a memo computed, so the render effect\n // itself is the observer and the owner of anything the value builds (see HtmlBinding).\n bindings.push({ anchor, source: value as (() => unknown) | Readable<unknown>, type: 'html' });\n continue;\n }\n\n if (Array.isArray(value)) {\n for (const item of value) {\n if (isHtmlResult(item)) {\n embedStaticResult(item, anchor, chainedApplies);\n } else {\n const parent = anchor.parentNode;\n\n invariant(parent, NODE_SLOT_NO_PARENT_MSG);\n parent.insertBefore(document.createTextNode(resolveStaticText(item)), anchor);\n }\n }\n anchor.remove();\n continue;\n }\n\n // Static primitive: replace with text node, no binding\n anchor.replaceWith(document.createTextNode(resolveStaticText(value)));\n continue;\n }\n\n // Element slot\n invariant(el, 'compiled template produced an element slot without an element');\n\n if (slot.kind === SlotKind.EVENT) {\n const name = slot.name;\n\n invariant(name, 'compiled template produced an event slot without an event name');\n\n if (typeof value === 'function') {\n bindings.push({\n el,\n handler: value as (e: Event) => void,\n modifiers: slot.eventModifiers,\n name,\n type: 'event',\n });\n } else if (isReactive(value)) {\n const signalValue = value as Readable<unknown>;\n const handler = (e: Event) => {\n const h = signalValue.value;\n\n if (typeof h === 'function') (h as (e: Event) => void)(e);\n };\n\n bindings.push({ el, handler, modifiers: slot.eventModifiers, name, type: 'event' });\n }\n\n continue;\n }\n\n if (slot.kind === SlotKind.PROP) {\n const name = slot.name;\n\n invariant(name, 'compiled template produced a prop slot without a property name');\n\n if (typeof value === 'function') {\n bindings.push({ el, name, signal: computed(value as () => unknown), type: 'prop' });\n } else if (isReactive(value)) {\n bindings.push({ el, name, signal: value as Readable<unknown>, type: 'prop' });\n } else {\n bindings.push({ el, name, type: 'prop', value });\n }\n\n continue;\n }\n\n if (slot.kind === SlotKind.REF) {\n if (value) {\n bindings.push({ el, ref: value as Ref<Element> | RefCallback<Element>, type: 'ref' });\n }\n\n continue;\n }\n\n // attr / boolAttr\n invariant(slot.name, 'compiled template produced an attr slot without an attribute name');\n\n if (isModelSource(value)) {\n if (slot.mode !== 'attr') throw new OreApiError(ORE_ERRORS.modelRequiresValueBinding(slot.name));\n\n bindings.push({\n el,\n event: value.event ?? modelDefaultEvent(el),\n get: (value.get ?? modelDefaultGet) as (el: HTMLElement) => unknown,\n signal: value.source as Signal<unknown>,\n type: 'model',\n });\n continue;\n }\n\n bindings.push(createAttrBindingFromValue(el, slot.mode ?? 'attr', slot.name, value));\n }\n\n return createHtmlResult(fragment, () => {\n for (const binding of bindings) applyBinding(binding);\n for (const chainedApply of chainedApplies) chainedApply();\n });\n};\n\nexport const html = (strings: TemplateStringsArray, ...values: unknown[]): HTMLResult =>\n compileTemplate(strings, values);\n"],"mappings":"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"}
|
package/dist/template/result.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("../utils/brand.cjs");let
|
|
1
|
+
const e=require("../errors.cjs"),t=require("../utils/brand.cjs");let n=require("@vielzeug/ripple");function r(){return(0,n.signal)(null)}var i=t.makeBrand(`ore:directive`),a=e=>i.stamp({mount:e}),o=i.is,s=t.makeBrand(`ore:html-result`),c=s.is,l=t=>{if(!c(t))throw new e.OreApiError(e.ORE_ERRORS.notAnHtmlResult);return t};function u(t,n){let r=!1,i=()=>{if(r)throw new e.OreApiError(e.ORE_ERRORS.htmlResultReused);r=!0};return s.stamp({apply:n,claim:i,fragment:t,mount:(e,r)=>{i();let a=Array.from(t.childNodes);return e.insertBefore(t,r),n(),a}})}exports.asCompiled=l,exports.createDirectiveResult=a,exports.createHtmlResult=u,exports.isDirectiveResult=o,exports.isHtmlResult=c,exports.ref=r;
|
|
2
2
|
//# sourceMappingURL=result.cjs.map
|