@vielzeug/ore 1.0.3
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/README.md +73 -0
- package/dist/_dev.cjs +2 -0
- package/dist/_dev.cjs.map +1 -0
- package/dist/_dev.d.ts +2 -0
- package/dist/_dev.d.ts.map +1 -0
- package/dist/_dev.js +2 -0
- package/dist/_dev.js.map +1 -0
- package/dist/aria.cjs +2 -0
- package/dist/aria.cjs.map +1 -0
- package/dist/aria.d.ts +12 -0
- package/dist/aria.d.ts.map +1 -0
- package/dist/aria.js +2 -0
- package/dist/aria.js.map +1 -0
- package/dist/base-element.cjs +2 -0
- package/dist/base-element.cjs.map +1 -0
- package/dist/base-element.d.ts +22 -0
- package/dist/base-element.d.ts.map +1 -0
- package/dist/base-element.js +2 -0
- package/dist/base-element.js.map +1 -0
- package/dist/component-types.d.ts +104 -0
- package/dist/component-types.d.ts.map +1 -0
- package/dist/context-bag.cjs +2 -0
- package/dist/context-bag.cjs.map +1 -0
- package/dist/context-bag.d.ts +12 -0
- package/dist/context-bag.d.ts.map +1 -0
- package/dist/context-bag.js +2 -0
- package/dist/context-bag.js.map +1 -0
- package/dist/context.cjs +2 -0
- package/dist/context.cjs.map +1 -0
- package/dist/context.d.ts +15 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +2 -0
- package/dist/context.js.map +1 -0
- package/dist/define.cjs +2 -0
- package/dist/define.cjs.map +1 -0
- package/dist/define.d.ts +33 -0
- package/dist/define.d.ts.map +1 -0
- package/dist/define.js +2 -0
- package/dist/define.js.map +1 -0
- package/dist/devtools.cjs +2 -0
- package/dist/devtools.cjs.map +1 -0
- package/dist/devtools.d.ts +32 -0
- package/dist/devtools.d.ts.map +1 -0
- package/dist/devtools.js +2 -0
- package/dist/devtools.js.map +1 -0
- package/dist/directives/classMap.cjs +2 -0
- package/dist/directives/classMap.cjs.map +1 -0
- package/dist/directives/classMap.d.ts +19 -0
- package/dist/directives/classMap.d.ts.map +1 -0
- package/dist/directives/classMap.js +2 -0
- package/dist/directives/classMap.js.map +1 -0
- package/dist/directives/each.cjs +2 -0
- package/dist/directives/each.cjs.map +1 -0
- package/dist/directives/each.d.ts +25 -0
- package/dist/directives/each.d.ts.map +1 -0
- package/dist/directives/each.js +2 -0
- package/dist/directives/each.js.map +1 -0
- package/dist/directives/index.d.ts +8 -0
- package/dist/directives/index.d.ts.map +1 -0
- package/dist/directives/live.cjs +2 -0
- package/dist/directives/live.cjs.map +1 -0
- package/dist/directives/live.d.ts +25 -0
- package/dist/directives/live.d.ts.map +1 -0
- package/dist/directives/live.js +2 -0
- package/dist/directives/live.js.map +1 -0
- package/dist/directives/model.cjs +2 -0
- package/dist/directives/model.cjs.map +1 -0
- package/dist/directives/model.d.ts +28 -0
- package/dist/directives/model.d.ts.map +1 -0
- package/dist/directives/model.js +2 -0
- package/dist/directives/model.js.map +1 -0
- package/dist/directives/raw.cjs +2 -0
- package/dist/directives/raw.cjs.map +1 -0
- package/dist/directives/raw.d.ts +16 -0
- package/dist/directives/raw.d.ts.map +1 -0
- package/dist/directives/raw.js +2 -0
- package/dist/directives/raw.js.map +1 -0
- package/dist/directives/styleMap.cjs +2 -0
- package/dist/directives/styleMap.cjs.map +1 -0
- package/dist/directives/styleMap.d.ts +25 -0
- package/dist/directives/styleMap.d.ts.map +1 -0
- package/dist/directives/styleMap.js +2 -0
- package/dist/directives/styleMap.js.map +1 -0
- package/dist/directives/when.cjs +2 -0
- package/dist/directives/when.cjs.map +1 -0
- package/dist/directives/when.d.ts +19 -0
- package/dist/directives/when.d.ts.map +1 -0
- package/dist/directives/when.js +2 -0
- package/dist/directives/when.js.map +1 -0
- package/dist/directives.cjs +1 -0
- package/dist/directives.js +1 -0
- package/dist/errors.cjs +3 -0
- package/dist/errors.cjs.map +1 -0
- package/dist/errors.d.ts +47 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +3 -0
- package/dist/errors.js.map +1 -0
- package/dist/form-context.cjs +2 -0
- package/dist/form-context.cjs.map +1 -0
- package/dist/form-context.d.ts +56 -0
- package/dist/form-context.d.ts.map +1 -0
- package/dist/form-context.js +2 -0
- package/dist/form-context.js.map +1 -0
- package/dist/form-field.cjs +2 -0
- package/dist/form-field.cjs.map +1 -0
- package/dist/form-field.d.ts +31 -0
- package/dist/form-field.d.ts.map +1 -0
- package/dist/form-field.js +2 -0
- package/dist/form-field.js.map +1 -0
- package/dist/host-bind.cjs +2 -0
- package/dist/host-bind.cjs.map +1 -0
- package/dist/host-bind.d.ts +37 -0
- package/dist/host-bind.d.ts.map +1 -0
- package/dist/host-bind.js +2 -0
- package/dist/host-bind.js.map +1 -0
- package/dist/index.cjs +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1 -0
- package/dist/observers/index.d.ts +5 -0
- package/dist/observers/index.d.ts.map +1 -0
- package/dist/observers/intersection-observe.cjs +2 -0
- package/dist/observers/intersection-observe.cjs.map +1 -0
- package/dist/observers/intersection-observe.d.ts +9 -0
- package/dist/observers/intersection-observe.d.ts.map +1 -0
- package/dist/observers/intersection-observe.js +2 -0
- package/dist/observers/intersection-observe.js.map +1 -0
- package/dist/observers/media-observe.cjs +2 -0
- package/dist/observers/media-observe.cjs.map +1 -0
- package/dist/observers/media-observe.d.ts +8 -0
- package/dist/observers/media-observe.d.ts.map +1 -0
- package/dist/observers/media-observe.js +2 -0
- package/dist/observers/media-observe.js.map +1 -0
- package/dist/observers/mutation-observe.cjs +2 -0
- package/dist/observers/mutation-observe.cjs.map +1 -0
- package/dist/observers/mutation-observe.d.ts +10 -0
- package/dist/observers/mutation-observe.d.ts.map +1 -0
- package/dist/observers/mutation-observe.js +2 -0
- package/dist/observers/mutation-observe.js.map +1 -0
- package/dist/observers/resize-observe.cjs +2 -0
- package/dist/observers/resize-observe.cjs.map +1 -0
- package/dist/observers/resize-observe.d.ts +11 -0
- package/dist/observers/resize-observe.d.ts.map +1 -0
- package/dist/observers/resize-observe.js +2 -0
- package/dist/observers/resize-observe.js.map +1 -0
- package/dist/observers.cjs +1 -0
- package/dist/observers.js +1 -0
- package/dist/ore.cjs +3 -0
- package/dist/ore.cjs.map +1 -0
- package/dist/ore.iife.js +3 -0
- package/dist/ore.iife.js.map +1 -0
- package/dist/ore.js +3 -0
- package/dist/ore.js.map +1 -0
- package/dist/props.cjs +2 -0
- package/dist/props.cjs.map +1 -0
- package/dist/props.d.ts +76 -0
- package/dist/props.d.ts.map +1 -0
- package/dist/props.js +2 -0
- package/dist/props.js.map +1 -0
- package/dist/runtime.cjs +2 -0
- package/dist/runtime.cjs.map +1 -0
- package/dist/runtime.d.ts +34 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +2 -0
- package/dist/runtime.js.map +1 -0
- package/dist/slots.cjs +2 -0
- package/dist/slots.cjs.map +1 -0
- package/dist/slots.d.ts +13 -0
- package/dist/slots.d.ts.map +1 -0
- package/dist/slots.js +2 -0
- package/dist/slots.js.map +1 -0
- package/dist/template/bindings.cjs +2 -0
- package/dist/template/bindings.cjs.map +1 -0
- package/dist/template/bindings.d.ts +17 -0
- package/dist/template/bindings.d.ts.map +1 -0
- package/dist/template/bindings.js +2 -0
- package/dist/template/bindings.js.map +1 -0
- package/dist/template/compiler.cjs +2 -0
- package/dist/template/compiler.cjs.map +1 -0
- package/dist/template/compiler.d.ts +27 -0
- package/dist/template/compiler.d.ts.map +1 -0
- package/dist/template/compiler.js +2 -0
- package/dist/template/compiler.js.map +1 -0
- package/dist/template/instantiator.cjs +2 -0
- package/dist/template/instantiator.cjs.map +1 -0
- package/dist/template/instantiator.d.ts +16 -0
- package/dist/template/instantiator.d.ts.map +1 -0
- package/dist/template/instantiator.js +2 -0
- package/dist/template/instantiator.js.map +1 -0
- package/dist/template.cjs +1 -0
- package/dist/template.d.ts +4 -0
- package/dist/template.d.ts.map +1 -0
- package/dist/template.js +1 -0
- package/dist/testing/events.cjs +2 -0
- package/dist/testing/events.cjs.map +1 -0
- package/dist/testing/events.d.ts +37 -0
- package/dist/testing/events.d.ts.map +1 -0
- package/dist/testing/events.js +2 -0
- package/dist/testing/events.js.map +1 -0
- package/dist/testing/flush.cjs +2 -0
- package/dist/testing/flush.cjs.map +1 -0
- package/dist/testing/flush.d.ts +55 -0
- package/dist/testing/flush.d.ts.map +1 -0
- package/dist/testing/flush.js +2 -0
- package/dist/testing/flush.js.map +1 -0
- package/dist/testing/index.d.ts +2 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/interactions.cjs +2 -0
- package/dist/testing/interactions.cjs.map +1 -0
- package/dist/testing/interactions.d.ts +27 -0
- package/dist/testing/interactions.d.ts.map +1 -0
- package/dist/testing/interactions.js +2 -0
- package/dist/testing/interactions.js.map +1 -0
- package/dist/testing/mount.cjs +2 -0
- package/dist/testing/mount.cjs.map +1 -0
- package/dist/testing/mount.d.ts +107 -0
- package/dist/testing/mount.d.ts.map +1 -0
- package/dist/testing/mount.js +2 -0
- package/dist/testing/mount.js.map +1 -0
- package/dist/testing/query.cjs +2 -0
- package/dist/testing/query.cjs.map +1 -0
- package/dist/testing/query.d.ts +24 -0
- package/dist/testing/query.d.ts.map +1 -0
- package/dist/testing/query.js +2 -0
- package/dist/testing/query.js.map +1 -0
- package/dist/testing/render-hook.cjs +2 -0
- package/dist/testing/render-hook.cjs.map +1 -0
- package/dist/testing/render-hook.d.ts +44 -0
- package/dist/testing/render-hook.d.ts.map +1 -0
- package/dist/testing/render-hook.js +2 -0
- package/dist/testing/render-hook.js.map +1 -0
- package/dist/testing/testing.cjs +2 -0
- package/dist/testing/testing.cjs.map +1 -0
- package/dist/testing/testing.d.ts +26 -0
- package/dist/testing/testing.d.ts.map +1 -0
- package/dist/testing/testing.js +2 -0
- package/dist/testing/testing.js.map +1 -0
- package/dist/testing/wait.cjs +2 -0
- package/dist/testing/wait.cjs.map +1 -0
- package/dist/testing/wait.d.ts +35 -0
- package/dist/testing/wait.d.ts.map +1 -0
- package/dist/testing/wait.js +2 -0
- package/dist/testing/wait.js.map +1 -0
- package/dist/testing.cjs +1 -0
- package/dist/testing.js +1 -0
- package/dist/types/bindings.cjs +2 -0
- package/dist/types/bindings.cjs.map +1 -0
- package/dist/types/bindings.d.ts +125 -0
- package/dist/types/bindings.d.ts.map +1 -0
- package/dist/types/bindings.js +2 -0
- package/dist/types/bindings.js.map +1 -0
- package/dist/types.cjs +2 -0
- package/dist/types.cjs.map +1 -0
- package/dist/types.d.ts +19 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/aria.cjs +2 -0
- package/dist/utils/aria.cjs.map +1 -0
- package/dist/utils/aria.d.ts +16 -0
- package/dist/utils/aria.d.ts.map +1 -0
- package/dist/utils/aria.js +2 -0
- package/dist/utils/aria.js.map +1 -0
- package/dist/utils/css.cjs +2 -0
- package/dist/utils/css.cjs.map +1 -0
- package/dist/utils/css.d.ts +11 -0
- package/dist/utils/css.d.ts.map +1 -0
- package/dist/utils/css.js +2 -0
- package/dist/utils/css.js.map +1 -0
- package/dist/utils/dom.cjs +2 -0
- package/dist/utils/dom.cjs.map +1 -0
- package/dist/utils/dom.d.ts +10 -0
- package/dist/utils/dom.d.ts.map +1 -0
- package/dist/utils/dom.js +2 -0
- package/dist/utils/dom.js.map +1 -0
- package/dist/utils/emit.cjs +2 -0
- package/dist/utils/emit.cjs.map +1 -0
- package/dist/utils/emit.d.ts +16 -0
- package/dist/utils/emit.d.ts.map +1 -0
- package/dist/utils/emit.js +2 -0
- package/dist/utils/emit.js.map +1 -0
- package/dist/utils/event-modifiers.cjs +2 -0
- package/dist/utils/event-modifiers.cjs.map +1 -0
- package/dist/utils/event-modifiers.d.ts +11 -0
- package/dist/utils/event-modifiers.d.ts.map +1 -0
- package/dist/utils/event-modifiers.js +2 -0
- package/dist/utils/event-modifiers.js.map +1 -0
- package/dist/utils/id.cjs +2 -0
- package/dist/utils/id.cjs.map +1 -0
- package/dist/utils/id.d.ts +18 -0
- package/dist/utils/id.d.ts.map +1 -0
- package/dist/utils/id.js +2 -0
- package/dist/utils/id.js.map +1 -0
- package/package.json +68 -0
package/dist/slots.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slot observation and reactive slot signals.
|
|
3
|
+
*
|
|
4
|
+
* `slots.has(name?)`: Signal<boolean> — whether a named slot has assigned elements.
|
|
5
|
+
* `slots.elements(name?)`: Signal<Element[]> — assigned elements for a slot (flattened).
|
|
6
|
+
*/
|
|
7
|
+
import { type Readable } from '@vielzeug/ripple';
|
|
8
|
+
export type ComponentSlots<SlotNames extends string = string> = {
|
|
9
|
+
elements: (name?: SlotNames) => Readable<Element[]>;
|
|
10
|
+
has: (name?: SlotNames) => Readable<boolean>;
|
|
11
|
+
};
|
|
12
|
+
export declare const createSlots: (host: HTMLElement) => ComponentSlots<string>;
|
|
13
|
+
//# sourceMappingURL=slots.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slots.d.ts","sourceRoot":"","sources":["../src/slots.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,KAAK,QAAQ,EAAuB,MAAM,kBAAkB,CAAC;AAItE,MAAM,MAAM,cAAc,CAAC,SAAS,SAAS,MAAM,GAAG,MAAM,IAAI;IAC9D,QAAQ,EAAE,CAAC,IAAI,CAAC,EAAE,SAAS,KAAK,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;IACpD,GAAG,EAAE,CAAC,IAAI,CAAC,EAAE,SAAS,KAAK,QAAQ,CAAC,OAAO,CAAC,CAAC;CAC9C,CAAC;AAKF,eAAO,MAAM,WAAW,GAAI,MAAM,WAAW,KAAG,cAAc,CAAC,MAAM,CAkJpE,CAAC"}
|
package/dist/slots.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{onCleanup as e,onMounted as t}from"./runtime.js";import{signal as n}from"@vielzeug/ripple";var r=`default`,i=e=>e||r,a=r=>{let a=new Map,o=new Map,s=new Map,c=e=>{let t=a.get(e);return t||(t={elements:n([]),presence:n(!1)},a.set(e,t)),t},l=(e,t)=>{if(e.length!==t.length)return!1;for(let n=0;n<e.length;n++)if(e[n]!==t[n])return!1;return!0},u=e=>{let t=i(e),n=o.get(t),r=[];if(n)for(let e of n)r.push(...e.assignedElements({flatten:!0}));let a=c(t);l(a.elements.value,r)||(a.elements.value=r);let s=r.length>0;a.presence.value!==s&&(a.presence.value=s)},d=e=>{if(s.has(e))return;let t=i(e.getAttribute(`name`)),n=o.get(t)??new Set;n.add(e),o.set(t,n);let r=()=>u(t);e.addEventListener(`slotchange`,r),s.set(e,()=>{e.removeEventListener(`slotchange`,r)}),u(t)},f=e=>{let t=s.get(e);if(!t)return;t(),s.delete(e);let n=i(e.getAttribute(`name`)),r=o.get(n);r&&(r.delete(e),r.size===0&&o.delete(n)),u(n)},p=()=>{r.shadowRoot?.querySelectorAll(`slot`).forEach(e=>d(e))},m=()=>{for(let e of o.keys())u(e)},h=null;return r.shadowRoot&&(h=new MutationObserver(e=>{for(let t of e)for(let e of t.removedNodes)e instanceof HTMLSlotElement&&f(e);p(),m()}),h.observe(r.shadowRoot,{childList:!0,subtree:!0})),p(),t(()=>{p(),m()}),e(()=>{h?.disconnect(),h=null;for(let e of s.values())e();s.clear(),o.clear(),a.clear()}),{elements:e=>c(i(e)).elements,has:e=>c(i(e)).presence}};export{a as createSlots};
|
|
2
|
+
//# sourceMappingURL=slots.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slots.js","names":[],"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, signal, type Signal } from '@vielzeug/ripple';\n\nimport { onCleanup, onMounted } 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\nexport const 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) => bindSlot(slotEl));\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 // Start MutationObserver immediately in setup so any slots inserted synchronously\n // before the first render are captured without a timing gap.\n if (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 recomputeAllSlots();\n });\n observer.observe(host.shadowRoot, { childList: true, subtree: true });\n }\n\n // Bind slots already present in the shadow root (pre-upgrade scenarios)\n // and schedule a post-render pass to pick up slots injected by the template.\n bindAllSlots();\n onMounted(() => {\n bindAllSlots();\n recomputeAllSlots();\n });\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\n return {\n elements: (name?: string) => ensureSlotEntry(normalizeSlotName(name)).elements,\n has: (name?: string) => ensureSlotEntry(normalizeSlotName(name)).presence,\n };\n};\n"],"mappings":"kGAgBA,IAAM,EAAe,UACf,EAAqB,GAAgD,GAAY,EAE1E,EAAe,GAA8C,CAMxE,IAAM,EAAc,IAAI,IAClB,EAAkB,IAAI,IACtB,EAAiB,IAAI,IAErB,EAAmB,GAAsC,CAC7D,IAAI,EAAQ,EAAY,IAAI,CAAc,EAU1C,OARK,IACH,EAAQ,CACN,SAAU,EAAkB,CAAC,CAAC,EAC9B,SAAU,EAAO,EAAK,CACxB,EACA,EAAY,IAAI,EAAgB,CAAK,GAGhC,CACT,EAEM,GAAoB,EAAiB,IAA6B,CACtE,GAAI,EAAK,SAAW,EAAK,OAAQ,MAAO,GAExC,IAAK,IAAI,EAAI,EAAG,EAAI,EAAK,OAAQ,IAC/B,GAAI,EAAK,KAAO,EAAK,GAAI,MAAO,GAGlC,MAAO,EACT,EAEM,EAAiB,GAAuB,CAC5C,IAAM,EAAa,EAAkB,CAAI,EACnC,EAAe,EAAgB,IAAI,CAAU,EAC7C,EAAsB,CAAC,EAE7B,GAAI,EACF,IAAK,IAAM,KAAU,EACnB,EAAS,KAAK,GAAG,EAAO,iBAAiB,CAAE,QAAS,EAAK,CAAC,CAAC,EAI/D,IAAM,EAAQ,EAAgB,CAAU,EAEnC,EAAiB,EAAM,SAAS,MAAO,CAAQ,IAAG,EAAM,SAAS,MAAQ,GAE9E,IAAM,EAAc,EAAS,OAAS,EAElC,EAAM,SAAS,QAAU,IAAa,EAAM,SAAS,MAAQ,EACnE,EAEM,EAAY,GAAkC,CAClD,GAAI,EAAe,IAAI,CAAM,EAAG,OAEhC,IAAM,EAAO,EAAkB,EAAO,aAAa,MAAM,CAAC,EACpD,EAAa,EAAgB,IAAI,CAAI,GAAK,IAAI,IAEpD,EAAW,IAAI,CAAM,EACrB,EAAgB,IAAI,EAAM,CAAU,EAEpC,IAAM,MAAiB,EAAc,CAAI,EAEzC,EAAO,iBAAiB,aAAc,CAAQ,EAE9C,EAAe,IAAI,MAAc,CAC/B,EAAO,oBAAoB,aAAc,CAAQ,CACnD,CAAC,EAED,EAAc,CAAI,CACpB,EAEM,EAAc,GAAkC,CACpD,IAAM,EAAU,EAAe,IAAI,CAAM,EAEzC,GAAI,CAAC,EAAS,OAEd,EAAQ,EACR,EAAe,OAAO,CAAM,EAE5B,IAAM,EAAO,EAAkB,EAAO,aAAa,MAAM,CAAC,EACpD,EAAa,EAAgB,IAAI,CAAI,EAEvC,IACF,EAAW,OAAO,CAAM,EAEpB,EAAW,OAAS,GAAG,EAAgB,OAAO,CAAI,GAGxD,EAAc,CAAI,CACpB,EAEM,MAA2B,CAC/B,EAAK,YAAY,iBAAiB,MAAM,CAAC,CAAC,QAAS,GAAW,EAAS,CAAM,CAAC,CAChF,EAEM,MAAgC,CACpC,IAAK,IAAM,KAAQ,EAAgB,KAAK,EACtC,EAAc,CAAI,CAEtB,EAGI,EAAoC,KAqCxC,OAjCI,EAAK,aACP,EAAW,IAAI,iBAAkB,GAAc,CAC7C,IAAK,IAAM,KAAY,EACrB,IAAK,IAAM,KAAQ,EAAS,aACtB,aAAgB,iBAAiB,EAAW,CAAI,EAIxD,EAAa,EACb,EAAkB,CACpB,CAAC,EACD,EAAS,QAAQ,EAAK,WAAY,CAAE,UAAW,GAAM,QAAS,EAAK,CAAC,GAKtE,EAAa,EACb,MAAgB,CACd,EAAa,EACb,EAAkB,CACpB,CAAC,EAED,MAAgB,CACd,GAAU,WAAW,EACrB,EAAW,KAEX,IAAK,IAAM,KAAW,EAAe,OAAO,EAAG,EAAQ,EAEvD,EAAe,MAAM,EACrB,EAAgB,MAAM,EACtB,EAAY,MAAM,CACpB,CAAC,EAEM,CACL,SAAW,GAAkB,EAAgB,EAAkB,CAAI,CAAC,CAAC,CAAC,SACtE,IAAM,GAAkB,EAAgB,EAAkB,CAAI,CAAC,CAAC,CAAC,QACnE,CACF"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require("../types/bindings.cjs"),t=require("../utils/dom.cjs"),n=require("../directives/live.cjs"),r=require("../props.cjs");let i=require("@vielzeug/ripple");var a=(e,t,n)=>{let r=(0,i.effect)(()=>t(e.value));n(()=>r.dispose())},o=e=>e.value,s=(e,t)=>{a(e.signal,t=>{e.node.textContent=String(t??``)},t)},c=e=>e instanceof HTMLInputElement||e instanceof HTMLTextAreaElement||e instanceof HTMLSelectElement,l=(e,t,n,r)=>{let i=t==null?``:String(t);n&&r.last!==void 0&&!Object.is(e.value,r.last)&&!Object.is(e.value,i)||(e.value=i,n&&(r.last=i))},u=(e,t,n,r)=>{let i=!!t;n&&r.last!==void 0&&e.checked!==!!r.last&&e.checked!==i||(e.checked=i,n&&(r.last=i))},d=(e,n,r,a)=>{let o=t.isStructuredValue(a)?a:n.parse(r.mode===`bool`?a?``:null:a==null||a===!1?null:String(a));if(Object.is((0,i.untrack)(()=>n.signal.value),o)||(n.signal.value=o),!n.reflect){if(t.isStructuredValue(a))return;r.mode===`bool`?e.toggleAttribute(r.name,!!a):t.setAttr(e,r.name,a)}},f=(e,n)=>{let{el:r,mode:o,name:s,propMeta:f}=e,p={last:void 0},m=n=>{if(f){d(r,f,e,n);return}if(!(0,i.isReactive)(n)&&t.isStructuredValue(n)){s!==`__proto__`&&s!==`constructor`&&s!==`prototype`&&(r[s]=n);return}if(s===`value`&&c(r)){l(r,n,e.live,p);return}if(s===`checked`&&r instanceof HTMLInputElement){u(r,n,e.live,p);return}o===`bool`?r.toggleAttribute(s,!!n):t.setAttr(r,s,n)};`signal`in e?a(e.signal,m,n):m(e.value)},p=(e,n)=>{n(t.listen(e.el,e.name,e.handler,e.options))},m=(e,t)=>{let{el:n,ref:r}=e;if(typeof r==`function`){r(n),t(()=>r(null));return}r.value=n,t(()=>{r.value=null})},h=(t,n,r)=>{let i=[],a=n.parentNode;for(let o of t)if(e.isHtmlResult(o)){let e=Array.from(o.fragment.childNodes);a.insertBefore(o.fragment,n),o.apply(r),i.push(...e)}else if(o!=null&&o!==!1){let e=document.createTextNode(String(o));a.insertBefore(e,n),i.push(e)}return i},g=(e,n)=>{let{anchor:r,signal:a}=e,s=[],c=()=>{t.runAll(s),s=[]},l=[],u=(0,i.effect)(()=>{let e=o(a);c(),t.removeNodes(l),l=[],!(e==null||e.length===0)&&(0,i.untrack)(()=>{l=h(e,r,e=>s.push(e))})});n(()=>{u.dispose(),c(),t.removeNodes(l)})},_=(e,t)=>{e.directive.mount(e.anchor,t)},v=(e,t)=>{e.spread.apply(e.el,t)},y=(e,t)=>{switch(e.type){case`attr`:f(e,t);break;case`directive`:_(e,t);break;case`event`:p(e,t);break;case`html`:g(e,t);break;case`ref`:m(e,t);break;case`spread`:v(e,t);break;case`text`:s(e,t);break}},b=(e,t,a,o)=>{let s=r.getPropMeta(e,a);return n.isLiveSignal(o)?{el:e,live:!0,mode:t,name:a,propMeta:s,signal:o,type:`attr`}:typeof o==`function`?{el:e,mode:t,name:a,propMeta:s,signal:(0,i.computed)(o),type:`attr`}:(0,i.isReactive)(o)?{el:e,mode:t,name:a,propMeta:s,signal:o,type:`attr`}:{el:e,mode:t,name:a,propMeta:s,type:`attr`,value:o}},x=e=>e==null?``:String(e);exports.applyAttrBinding=f,exports.applyBinding=y,exports.applyHtmlBinding=g,exports.createAttrBindingFromValue=b,exports.resolveStaticText=x;
|
|
2
|
+
//# sourceMappingURL=bindings.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bindings.cjs","names":[],"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 (text, attr, event, html, ref,\n * directive, spread).\n * - Manage reactive effects and cleanup registration.\n * - Expose `applyBinding()` as the single dispatch entry point.\n */\n\nimport { computed, effect as rawEffect, isReactive, type Readable, untrack } from '@vielzeug/ripple';\n\nimport { isLiveSignal } from '../directives/live';\nimport { getPropMeta } from '../props';\nimport {\n type AttrBinding,\n type AttrPropMeta,\n type Binding,\n type DirectiveBinding,\n type EventBinding,\n type HtmlBinding,\n type HtmlBindingValue,\n isHtmlResult,\n type RefBinding,\n type SpreadBinding,\n type TextBinding,\n} from '../types/bindings';\nimport { isStructuredValue, listen, removeNodes, runAll, setAttr } from '../utils/dom';\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(() => update(signal.value));\n\n registerCleanup(() => sub.dispose());\n};\n\n/**\n * Read a signal safely during scope teardown. Disposed signals and computeds\n * return their last known value (or undefined) without throwing — no try/catch needed.\n */\nconst readSignalSafe = <T>(sig: Readable<T>): T | undefined => sig.value;\n\n// ─── Text ─────────────────────────────────────────────────────────────────────\n\nconst applyTextBinding = (binding: TextBinding, registerCleanup: RegisterCleanup): void => {\n signalEffect(\n binding.signal,\n (v) => {\n binding.node.textContent = String(v ?? '');\n },\n registerCleanup,\n );\n};\n\n// ─── Attributes ───────────────────────────────────────────────────────────────\n\nconst isNativeFormInput = (el: HTMLElement): el is HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement =>\n el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement || el instanceof HTMLSelectElement;\n\ntype LiveWriteState = { last: unknown };\n\nconst applyFormValue = (\n el: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement,\n value: unknown,\n isLive: boolean | undefined,\n state: LiveWriteState,\n): void => {\n const next = value == null ? '' : String(value);\n\n if (isLive && state.last !== undefined && !Object.is(el.value, state.last) && !Object.is(el.value, next)) return;\n\n el.value = next;\n\n if (isLive) state.last = next;\n};\n\nconst applyCheckedValue = (\n el: HTMLInputElement,\n value: unknown,\n isLive: boolean | undefined,\n state: LiveWriteState,\n): void => {\n const next = Boolean(value);\n\n if (isLive && state.last !== undefined && el.checked !== Boolean(state.last) && el.checked !== next) return;\n\n el.checked = next;\n\n if (isLive) state.last = next;\n};\n\nconst syncRegisteredProp = (el: HTMLElement, meta: AttrPropMeta, 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 as never;\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, propMeta } = binding;\n const liveState: LiveWriteState = { last: undefined };\n\n const update = (value: unknown): void => {\n if (propMeta) {\n syncRegisteredProp(el, propMeta, binding, value);\n\n return;\n }\n\n if (!isReactive(value) && isStructuredValue(value)) {\n if (name !== '__proto__' && name !== 'constructor' && name !== 'prototype') {\n (el as unknown as Record<string, unknown>)[name] = value;\n }\n\n return;\n }\n\n if (name === 'value' && isNativeFormInput(el)) {\n applyFormValue(el, value, binding.live, liveState);\n\n return;\n }\n\n if (name === 'checked' && el instanceof HTMLInputElement) {\n applyCheckedValue(el, value, binding.live, liveState);\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 registerCleanup(listen(binding.el, binding.name, binding.handler, binding.options));\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 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\nexport const applyHtmlBinding = (binding: HtmlBinding, registerCleanup: RegisterCleanup): void => {\n const { anchor, signal } = binding;\n\n let currentCleanups: (() => void)[] = [];\n const clearCurrent = (): void => {\n runAll(currentCleanups);\n currentCleanups = [];\n };\n let currentNodes: Node[] = [];\n\n const stop = rawEffect(() => {\n const raw = readSignalSafe(signal);\n\n clearCurrent();\n removeNodes(currentNodes);\n currentNodes = [];\n\n if (raw == null || raw.length === 0) return;\n\n untrack(() => {\n currentNodes = insertHtmlValues(raw, anchor, (fn) => currentCleanups.push(fn));\n });\n });\n\n registerCleanup(() => {\n stop.dispose();\n clearCurrent();\n removeNodes(currentNodes);\n });\n};\n\n// ─── Directives ───────────────────────────────────────────────────────────────\n\nconst applyDirectiveBinding = (binding: DirectiveBinding, registerCleanup: RegisterCleanup): void => {\n binding.directive.mount(binding.anchor, registerCleanup);\n};\n\n// ─── Spread ───────────────────────────────────────────────────────────────────\n\nconst applySpreadBinding = (binding: SpreadBinding, registerCleanup: RegisterCleanup): void => {\n binding.spread.apply(binding.el, 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 'ref':\n applyRefBinding(binding, registerCleanup);\n break;\n case 'spread':\n applySpreadBinding(binding, registerCleanup);\n break;\n case 'text':\n applyTextBinding(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 const propMeta = getPropMeta(el, name) as AttrPropMeta | undefined;\n\n if (isLiveSignal(value)) {\n return { el, live: true, mode, name, propMeta, signal: value as Readable<unknown>, type: 'attr' };\n }\n\n if (typeof value === 'function') {\n return { el, mode, name, propMeta, signal: computed(value as () => unknown), type: 'attr' };\n }\n\n if (isReactive(value)) {\n return { el, mode, name, propMeta, signal: value as Readable<unknown>, type: 'attr' };\n }\n\n return { el, mode, name, propMeta, type: 'attr', value };\n};\n\nexport const resolveStaticText = (value: unknown): string => {\n if (value == null) return '';\n\n return String(value);\n};\n"],"mappings":"uKAmCA,IAAM,GACJ,EACA,EACA,IACS,CACT,IAAM,GAAA,EAAA,EAAA,OAAA,KAAsB,EAAO,EAAO,KAAK,CAAC,EAEhD,MAAsB,EAAI,QAAQ,CAAC,CACrC,EAMM,EAAqB,GAAoC,EAAI,MAI7D,GAAoB,EAAsB,IAA2C,CACzF,EACE,EAAQ,OACP,GAAM,CACL,EAAQ,KAAK,YAAc,OAAO,GAAK,EAAE,CAC3C,EACA,CACF,CACF,EAIM,EAAqB,GACzB,aAAc,kBAAoB,aAAc,qBAAuB,aAAc,kBAIjF,GACJ,EACA,EACA,EACA,IACS,CACT,IAAM,EAAO,GAAS,KAAO,GAAK,OAAO,CAAK,EAE1C,GAAU,EAAM,OAAS,IAAA,IAAa,CAAC,OAAO,GAAG,EAAG,MAAO,EAAM,IAAI,GAAK,CAAC,OAAO,GAAG,EAAG,MAAO,CAAI,IAEvG,EAAG,MAAQ,EAEP,IAAQ,EAAM,KAAO,GAC3B,EAEM,GACJ,EACA,EACA,EACA,IACS,CACT,IAAM,EAAO,EAAQ,EAEjB,GAAU,EAAM,OAAS,IAAA,IAAa,EAAG,UAAY,EAAQ,EAAM,MAAS,EAAG,UAAY,IAE/F,EAAG,QAAU,EAET,IAAQ,EAAM,KAAO,GAC3B,EAEM,GAAsB,EAAiB,EAAoB,EAAsB,IAAyB,CAC9G,IAAM,EAAS,EAAA,kBAAkB,CAAK,EAClC,EACA,EAAK,MACH,EAAQ,OAAS,OAAU,EAAQ,GAAK,KAAQ,GAAS,MAAQ,IAAU,GAAQ,KAAO,OAAO,CAAK,CACxG,EAWJ,GARG,OAAO,IAAA,EAAA,EAAA,QAAA,KACQ,EAAK,OAAO,KAAK,EAC/B,CACF,IAEA,EAAK,OAAO,MAAQ,GAGlB,CAAC,EAAK,QAAS,CACjB,GAAI,EAAA,kBAAkB,CAAK,EAAG,OAE1B,EAAQ,OAAS,OAAQ,EAAG,gBAAgB,EAAQ,KAAM,EAAQ,CAAM,EACvE,EAAA,QAAQ,EAAI,EAAQ,KAAM,CAAK,CACtC,CACF,EAEa,GAAoB,EAAsB,IAA2C,CAChG,GAAM,CAAE,KAAI,OAAM,OAAM,YAAa,EAC/B,EAA4B,CAAE,KAAM,IAAA,EAAU,EAE9C,EAAU,GAAyB,CACvC,GAAI,EAAU,CACZ,EAAmB,EAAI,EAAU,EAAS,CAAK,EAE/C,MACF,CAEA,GAAI,EAAA,EAAA,EAAA,WAAA,CAAY,CAAK,GAAK,EAAA,kBAAkB,CAAK,EAAG,CAC9C,IAAS,aAAe,IAAS,eAAiB,IAAS,cAC7D,EAA2C,GAAQ,GAGrD,MACF,CAEA,GAAI,IAAS,SAAW,EAAkB,CAAE,EAAG,CAC7C,EAAe,EAAI,EAAO,EAAQ,KAAM,CAAS,EAEjD,MACF,CAEA,GAAI,IAAS,WAAa,aAAc,iBAAkB,CACxD,EAAkB,EAAI,EAAO,EAAQ,KAAM,CAAS,EAEpD,MACF,CAEI,IAAS,OAAQ,EAAG,gBAAgB,EAAM,EAAQ,CAAM,EACvD,EAAA,QAAQ,EAAI,EAAM,CAAK,CAC9B,EAEI,WAAY,EACd,EAAa,EAAQ,OAAQ,EAAQ,CAAe,EAEpD,EAAO,EAAQ,KAAK,CAExB,EAIM,GAAqB,EAAuB,IAA2C,CAC3F,EAAgB,EAAA,OAAO,EAAQ,GAAI,EAAQ,KAAM,EAAQ,QAAS,EAAQ,OAAO,CAAC,CACpF,EAIM,GAAmB,EAAqB,IAA2C,CACvF,GAAM,CAAE,KAAI,OAAQ,EAEpB,GAAI,OAAO,GAAQ,WAAY,CAC7B,EAAI,CAAW,EACf,MAAsB,EAAI,IAAa,CAAC,EAExC,MACF,CAEA,EAAI,MAAQ,EACZ,MAAsB,CACpB,EAAI,MAAQ,IACd,CAAC,CACH,EAIM,GACJ,EACA,EACA,IACW,CACX,IAAM,EAAgB,CAAC,EACjB,EAAS,EAAa,WAE5B,IAAK,IAAM,KAAK,EACd,GAAI,EAAA,aAAa,CAAC,EAAG,CACnB,IAAM,EAAW,MAAM,KAAK,EAAE,SAAS,UAAU,EAEjD,EAAO,aAAa,EAAE,SAAU,CAAY,EAC5C,EAAE,MAAM,CAAe,EACvB,EAAM,KAAK,GAAG,CAAQ,CACxB,MAAO,GAAI,GAAK,MAAQ,IAAM,GAAO,CACnC,IAAM,EAAO,SAAS,eAAe,OAAO,CAAC,CAAC,EAE9C,EAAO,aAAa,EAAM,CAAY,EACtC,EAAM,KAAK,CAAI,CACjB,CAGF,OAAO,CACT,EAEa,GAAoB,EAAsB,IAA2C,CAChG,GAAM,CAAE,SAAQ,UAAW,EAEvB,EAAkC,CAAC,EACjC,MAA2B,CAC/B,EAAA,OAAO,CAAe,EACtB,EAAkB,CAAC,CACrB,EACI,EAAuB,CAAC,EAEtB,GAAA,EAAA,EAAA,OAAA,KAAuB,CAC3B,IAAM,EAAM,EAAe,CAAM,EAEjC,EAAa,EACb,EAAA,YAAY,CAAY,EACxB,EAAe,CAAC,EAEZ,KAAO,MAAQ,EAAI,SAAW,KAElC,EAAA,EAAA,QAAA,KAAc,CACZ,EAAe,EAAiB,EAAK,EAAS,GAAO,EAAgB,KAAK,CAAE,CAAC,CAC/E,CAAC,CACH,CAAC,EAED,MAAsB,CACpB,EAAK,QAAQ,EACb,EAAa,EACb,EAAA,YAAY,CAAY,CAC1B,CAAC,CACH,EAIM,GAAyB,EAA2B,IAA2C,CACnG,EAAQ,UAAU,MAAM,EAAQ,OAAQ,CAAe,CACzD,EAIM,GAAsB,EAAwB,IAA2C,CAC7F,EAAQ,OAAO,MAAM,EAAQ,GAAI,CAAe,CAClD,EAIa,GAAgB,EAAkB,IAA2C,CACxF,OAAQ,EAAQ,KAAhB,CACE,IAAK,OACH,EAAiB,EAAS,CAAe,EACzC,MACF,IAAK,YACH,EAAsB,EAAS,CAAe,EAC9C,MACF,IAAK,QACH,EAAkB,EAAS,CAAe,EAC1C,MACF,IAAK,OACH,EAAiB,EAAS,CAAe,EACzC,MACF,IAAK,MACH,EAAgB,EAAS,CAAe,EACxC,MACF,IAAK,SACH,EAAmB,EAAS,CAAe,EAC3C,MACF,IAAK,OACH,EAAiB,EAAS,CAAe,EACzC,KACJ,CACF,EAIa,GACX,EACA,EACA,EACA,IACgB,CAChB,IAAM,EAAW,EAAA,YAAY,EAAI,CAAI,EAcrC,OAZI,EAAA,aAAa,CAAK,EACb,CAAE,KAAI,KAAM,GAAM,OAAM,OAAM,WAAU,OAAQ,EAA4B,KAAM,MAAO,EAG9F,OAAO,GAAU,WACZ,CAAE,KAAI,OAAM,OAAM,WAAU,QAAA,EAAA,EAAA,SAAA,CAAiB,CAAsB,EAAG,KAAM,MAAO,GAG5F,EAAA,EAAA,WAAA,CAAe,CAAK,EACX,CAAE,KAAI,OAAM,OAAM,WAAU,OAAQ,EAA4B,KAAM,MAAO,EAG/E,CAAE,KAAI,OAAM,OAAM,WAAU,KAAM,OAAQ,OAAM,CACzD,EAEa,EAAqB,GAC5B,GAAS,KAAa,GAEnB,OAAO,CAAK"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* template/bindings.ts — Runtime binding appliers.
|
|
3
|
+
*
|
|
4
|
+
* Responsibilities:
|
|
5
|
+
* - Apply each Binding variant to the live DOM (text, attr, event, html, ref,
|
|
6
|
+
* directive, spread).
|
|
7
|
+
* - Manage reactive effects and cleanup registration.
|
|
8
|
+
* - Expose `applyBinding()` as the single dispatch entry point.
|
|
9
|
+
*/
|
|
10
|
+
import { type AttrBinding, type Binding, type HtmlBinding } from '../types/bindings';
|
|
11
|
+
export type RegisterCleanup = (fn: () => void) => void;
|
|
12
|
+
export declare const applyAttrBinding: (binding: AttrBinding, registerCleanup: RegisterCleanup) => void;
|
|
13
|
+
export declare const applyHtmlBinding: (binding: HtmlBinding, registerCleanup: RegisterCleanup) => void;
|
|
14
|
+
export declare const applyBinding: (binding: Binding, registerCleanup: RegisterCleanup) => void;
|
|
15
|
+
export declare const createAttrBindingFromValue: (el: HTMLElement, mode: "attr" | "bool", name: string, value: unknown) => AttrBinding;
|
|
16
|
+
export declare const resolveStaticText: (value: unknown) => string;
|
|
17
|
+
//# sourceMappingURL=bindings.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bindings.d.ts","sourceRoot":"","sources":["../../src/template/bindings.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,OAAO,EACL,KAAK,WAAW,EAEhB,KAAK,OAAO,EAGZ,KAAK,WAAW,EAMjB,MAAM,mBAAmB,CAAC;AAK3B,MAAM,MAAM,eAAe,GAAG,CAAC,EAAE,EAAE,MAAM,IAAI,KAAK,IAAI,CAAC;AA6FvD,eAAO,MAAM,gBAAgB,GAAI,SAAS,WAAW,EAAE,iBAAiB,eAAe,KAAG,IAwCzF,CAAC;AAsDF,eAAO,MAAM,gBAAgB,GAAI,SAAS,WAAW,EAAE,iBAAiB,eAAe,KAAG,IA6BzF,CAAC;AAgBF,eAAO,MAAM,YAAY,GAAI,SAAS,OAAO,EAAE,iBAAiB,eAAe,KAAG,IAwBjF,CAAC;AAIF,eAAO,MAAM,0BAA0B,GACrC,IAAI,WAAW,EACf,MAAM,MAAM,GAAG,MAAM,EACrB,MAAM,MAAM,EACZ,OAAO,OAAO,KACb,WAgBF,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAI,OAAO,OAAO,KAAG,MAIlD,CAAC"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{isHtmlResult as e}from"../types/bindings.js";import{isStructuredValue as t,listen as n,removeNodes as r,runAll as i,setAttr as a}from"../utils/dom.js";import{isLiveSignal as o}from"../directives/live.js";import{getPropMeta as s}from"../props.js";import{computed as c,effect as l,isReactive as u,untrack as d}from"@vielzeug/ripple";var f=(e,t,n)=>{let r=l(()=>t(e.value));n(()=>r.dispose())},p=e=>e.value,m=(e,t)=>{f(e.signal,t=>{e.node.textContent=String(t??``)},t)},h=e=>e instanceof HTMLInputElement||e instanceof HTMLTextAreaElement||e instanceof HTMLSelectElement,g=(e,t,n,r)=>{let i=t==null?``:String(t);n&&r.last!==void 0&&!Object.is(e.value,r.last)&&!Object.is(e.value,i)||(e.value=i,n&&(r.last=i))},_=(e,t,n,r)=>{let i=!!t;n&&r.last!==void 0&&e.checked!==!!r.last&&e.checked!==i||(e.checked=i,n&&(r.last=i))},v=(e,n,r,i)=>{let o=t(i)?i:n.parse(r.mode===`bool`?i?``:null:i==null||i===!1?null:String(i));if(Object.is(d(()=>n.signal.value),o)||(n.signal.value=o),!n.reflect){if(t(i))return;r.mode===`bool`?e.toggleAttribute(r.name,!!i):a(e,r.name,i)}},y=(e,n)=>{let{el:r,mode:i,name:o,propMeta:s}=e,c={last:void 0},l=n=>{if(s){v(r,s,e,n);return}if(!u(n)&&t(n)){o!==`__proto__`&&o!==`constructor`&&o!==`prototype`&&(r[o]=n);return}if(o===`value`&&h(r)){g(r,n,e.live,c);return}if(o===`checked`&&r instanceof HTMLInputElement){_(r,n,e.live,c);return}i===`bool`?r.toggleAttribute(o,!!n):a(r,o,n)};`signal`in e?f(e.signal,l,n):l(e.value)},b=(e,t)=>{t(n(e.el,e.name,e.handler,e.options))},x=(e,t)=>{let{el:n,ref:r}=e;if(typeof r==`function`){r(n),t(()=>r(null));return}r.value=n,t(()=>{r.value=null})},S=(t,n,r)=>{let i=[],a=n.parentNode;for(let o of t)if(e(o)){let e=Array.from(o.fragment.childNodes);a.insertBefore(o.fragment,n),o.apply(r),i.push(...e)}else if(o!=null&&o!==!1){let e=document.createTextNode(String(o));a.insertBefore(e,n),i.push(e)}return i},C=(e,t)=>{let{anchor:n,signal:a}=e,o=[],s=()=>{i(o),o=[]},c=[],u=l(()=>{let e=p(a);s(),r(c),c=[],!(e==null||e.length===0)&&d(()=>{c=S(e,n,e=>o.push(e))})});t(()=>{u.dispose(),s(),r(c)})},w=(e,t)=>{e.directive.mount(e.anchor,t)},T=(e,t)=>{e.spread.apply(e.el,t)},E=(e,t)=>{switch(e.type){case`attr`:y(e,t);break;case`directive`:w(e,t);break;case`event`:b(e,t);break;case`html`:C(e,t);break;case`ref`:x(e,t);break;case`spread`:T(e,t);break;case`text`:m(e,t);break}},D=(e,t,n,r)=>{let i=s(e,n);return o(r)?{el:e,live:!0,mode:t,name:n,propMeta:i,signal:r,type:`attr`}:typeof r==`function`?{el:e,mode:t,name:n,propMeta:i,signal:c(r),type:`attr`}:u(r)?{el:e,mode:t,name:n,propMeta:i,signal:r,type:`attr`}:{el:e,mode:t,name:n,propMeta:i,type:`attr`,value:r}},O=e=>e==null?``:String(e);export{y as applyAttrBinding,E as applyBinding,C as applyHtmlBinding,D as createAttrBindingFromValue,O as resolveStaticText};
|
|
2
|
+
//# sourceMappingURL=bindings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bindings.js","names":[],"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 (text, attr, event, html, ref,\n * directive, spread).\n * - Manage reactive effects and cleanup registration.\n * - Expose `applyBinding()` as the single dispatch entry point.\n */\n\nimport { computed, effect as rawEffect, isReactive, type Readable, untrack } from '@vielzeug/ripple';\n\nimport { isLiveSignal } from '../directives/live';\nimport { getPropMeta } from '../props';\nimport {\n type AttrBinding,\n type AttrPropMeta,\n type Binding,\n type DirectiveBinding,\n type EventBinding,\n type HtmlBinding,\n type HtmlBindingValue,\n isHtmlResult,\n type RefBinding,\n type SpreadBinding,\n type TextBinding,\n} from '../types/bindings';\nimport { isStructuredValue, listen, removeNodes, runAll, setAttr } from '../utils/dom';\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(() => update(signal.value));\n\n registerCleanup(() => sub.dispose());\n};\n\n/**\n * Read a signal safely during scope teardown. Disposed signals and computeds\n * return their last known value (or undefined) without throwing — no try/catch needed.\n */\nconst readSignalSafe = <T>(sig: Readable<T>): T | undefined => sig.value;\n\n// ─── Text ─────────────────────────────────────────────────────────────────────\n\nconst applyTextBinding = (binding: TextBinding, registerCleanup: RegisterCleanup): void => {\n signalEffect(\n binding.signal,\n (v) => {\n binding.node.textContent = String(v ?? '');\n },\n registerCleanup,\n );\n};\n\n// ─── Attributes ───────────────────────────────────────────────────────────────\n\nconst isNativeFormInput = (el: HTMLElement): el is HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement =>\n el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement || el instanceof HTMLSelectElement;\n\ntype LiveWriteState = { last: unknown };\n\nconst applyFormValue = (\n el: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement,\n value: unknown,\n isLive: boolean | undefined,\n state: LiveWriteState,\n): void => {\n const next = value == null ? '' : String(value);\n\n if (isLive && state.last !== undefined && !Object.is(el.value, state.last) && !Object.is(el.value, next)) return;\n\n el.value = next;\n\n if (isLive) state.last = next;\n};\n\nconst applyCheckedValue = (\n el: HTMLInputElement,\n value: unknown,\n isLive: boolean | undefined,\n state: LiveWriteState,\n): void => {\n const next = Boolean(value);\n\n if (isLive && state.last !== undefined && el.checked !== Boolean(state.last) && el.checked !== next) return;\n\n el.checked = next;\n\n if (isLive) state.last = next;\n};\n\nconst syncRegisteredProp = (el: HTMLElement, meta: AttrPropMeta, 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 as never;\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, propMeta } = binding;\n const liveState: LiveWriteState = { last: undefined };\n\n const update = (value: unknown): void => {\n if (propMeta) {\n syncRegisteredProp(el, propMeta, binding, value);\n\n return;\n }\n\n if (!isReactive(value) && isStructuredValue(value)) {\n if (name !== '__proto__' && name !== 'constructor' && name !== 'prototype') {\n (el as unknown as Record<string, unknown>)[name] = value;\n }\n\n return;\n }\n\n if (name === 'value' && isNativeFormInput(el)) {\n applyFormValue(el, value, binding.live, liveState);\n\n return;\n }\n\n if (name === 'checked' && el instanceof HTMLInputElement) {\n applyCheckedValue(el, value, binding.live, liveState);\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 registerCleanup(listen(binding.el, binding.name, binding.handler, binding.options));\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 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\nexport const applyHtmlBinding = (binding: HtmlBinding, registerCleanup: RegisterCleanup): void => {\n const { anchor, signal } = binding;\n\n let currentCleanups: (() => void)[] = [];\n const clearCurrent = (): void => {\n runAll(currentCleanups);\n currentCleanups = [];\n };\n let currentNodes: Node[] = [];\n\n const stop = rawEffect(() => {\n const raw = readSignalSafe(signal);\n\n clearCurrent();\n removeNodes(currentNodes);\n currentNodes = [];\n\n if (raw == null || raw.length === 0) return;\n\n untrack(() => {\n currentNodes = insertHtmlValues(raw, anchor, (fn) => currentCleanups.push(fn));\n });\n });\n\n registerCleanup(() => {\n stop.dispose();\n clearCurrent();\n removeNodes(currentNodes);\n });\n};\n\n// ─── Directives ───────────────────────────────────────────────────────────────\n\nconst applyDirectiveBinding = (binding: DirectiveBinding, registerCleanup: RegisterCleanup): void => {\n binding.directive.mount(binding.anchor, registerCleanup);\n};\n\n// ─── Spread ───────────────────────────────────────────────────────────────────\n\nconst applySpreadBinding = (binding: SpreadBinding, registerCleanup: RegisterCleanup): void => {\n binding.spread.apply(binding.el, 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 'ref':\n applyRefBinding(binding, registerCleanup);\n break;\n case 'spread':\n applySpreadBinding(binding, registerCleanup);\n break;\n case 'text':\n applyTextBinding(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 const propMeta = getPropMeta(el, name) as AttrPropMeta | undefined;\n\n if (isLiveSignal(value)) {\n return { el, live: true, mode, name, propMeta, signal: value as Readable<unknown>, type: 'attr' };\n }\n\n if (typeof value === 'function') {\n return { el, mode, name, propMeta, signal: computed(value as () => unknown), type: 'attr' };\n }\n\n if (isReactive(value)) {\n return { el, mode, name, propMeta, signal: value as Readable<unknown>, type: 'attr' };\n }\n\n return { el, mode, name, propMeta, 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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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var e=/\s+@([a-zA-Z_][-a-zA-Z0-9_.-]*)\s*=\s*["']?$/,t=/\s+ref\s*=\s*["']?$/,n=/\s+\?([a-zA-Z_][-a-zA-Z0-9_]*)\s*=\s*["']?$/,r=/\s+:?([a-zA-Z_][-a-zA-Z0-9_]*)\s*=\s*["']?$/,i=e=>{let t=e.lastIndexOf(`<`);return t<=e.lastIndexOf(`>`)?!1:e[t+1]!==`/`},a=a=>{let o,s=a.trimEnd();if(s.endsWith(`</`))return{kind:`closeTag`,prefix:a};if(s.endsWith(`<`))return{kind:`tagname`,prefix:a};if(o=e.exec(a)){let e=a.slice(0,-o[0].length),t=o[1].split(`.`);return{kind:`event`,modifiers:t.slice(1),name:t[0],prefix:e}}return(o=t.exec(a))?{kind:`ref`,prefix:a.slice(0,-o[0].length)}:(o=n.exec(a))?{kind:`boolAttr`,name:o[1],prefix:a.slice(0,-o[0].length)}:(o=r.exec(a))?{kind:`attr`,name:o[1],prefix:a.slice(0,-o[0].length)}:i(a)?{kind:`spread`,prefix:a.trimEnd()}:{kind:`node`,prefix:a}},o=new WeakMap,s=e=>{let t=Array.from(e);for(let e=0;e<t.length-1;e++){let n=t[e],r=n[n.length-1];if(r===`"`||r===`'`){t[e]=n.slice(0,-1);let i=t[e+1];i.startsWith(r)&&(t[e+1]=i.slice(1))}if(t[e].trimEnd().endsWith(`</`)){let n=t[e+1];n.startsWith(`>`)&&(t[e+1]=n.slice(1))}}return t},c=(e,t,n,r)=>{if(e.nodeType===Node.ELEMENT_NODE){let r=e,i=r.getAttribute(`u`);i!==null&&(n.set(Number(i),[...t]),r.removeAttribute(`u`))}else if(e.nodeType===Node.COMMENT_NODE){let n=e.nodeValue;n!==null&&/^\d+$/.test(n)&&r.set(Number(n),[...t])}let i=e.childNodes;for(let e=0;e<i.length;e++)c(i[e],[...t,e],n,r)},l=e=>{let t=s(e),n=``,r,i=0,o=0,l=[],u=[];for(let e=0;e<t.length-1;e++){let s=t[e],c=a(s);if(c.kind===`tagname`){let e=i++;r=e,u.push(e);let t=s.replace(/<\s*$/,``);n+=t+`<ore-dyn-${e} u="${e}"`,l.push({elementId:e,kind:`tagname`})}else if(c.kind===`closeTag`){let e=u.pop()??0,t=s.replace(/<\/\s*$/,``);n+=t+`</ore-dyn-${e}>`,l.push({kind:`closeTag`}),r=void 0}else if(c.kind===`node`)n+=c.prefix+`<!--${o}-->`,l.push({commentId:o,kind:`node`}),o++,r=void 0;else{r===void 0||c.prefix.lastIndexOf(`<`)>c.prefix.lastIndexOf(`>`)?(r=i++,n+=`${c.prefix} u="${r}"`):n+=c.prefix;let e=c.kind===`boolAttr`?`bool`:c.kind===`attr`?`attr`:void 0;l.push({elementId:r,kind:c.kind,mode:e,modifiers:c.modifiers,name:c.name})}}n+=t[t.length-1]??``;let d=document.createElement(`template`);d.innerHTML=n;let f=new Map,p=new Map,m=d.content.childNodes;for(let e=0;e<m.length;e++)c(m[e],[e],f,p);return{commentPaths:p,element:d,elementPaths:f,slots:l}},u=e=>{let t=o.get(e);return t||(t=l(e),o.set(e,t)),t},d=(e,t)=>{let n=e;for(let e of t)n=n.childNodes[e];return n};exports.followPath=d,exports.getStaticTemplate=u;
|
|
2
|
+
//# sourceMappingURL=compiler.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compiler.cjs","names":[],"sources":["../../src/template/compiler.ts"],"sourcesContent":["/**\n * template/compiler.ts — HTML template string parser and static template cache.\n *\n * Responsibilities:\n * - Parse TemplateStringsArray into slot metadata (slot detection).\n * - Build a cached HTMLTemplateElement with path indices for efficient node lookup.\n * - Expose `getStaticTemplate()` for use by the instantiator.\n */\n\n// ─── Slot kinds ───────────────────────────────────────────────────────────────\n\nexport type DetectedSlotKind = 'event' | 'ref' | 'boolAttr' | 'attr' | 'spread' | 'node' | 'tagname' | 'closeTag';\n\ntype DetectedSlot = {\n kind: DetectedSlotKind;\n modifiers?: string[];\n name?: string;\n prefix: string;\n};\n\n// ─── Static template types ────────────────────────────────────────────────────\n\nexport type NodePath = readonly number[];\n\nexport type SlotMeta = {\n commentId?: number;\n elementId?: number;\n kind: DetectedSlotKind;\n mode?: 'attr' | 'bool';\n modifiers?: string[];\n name?: string;\n};\n\nexport type CompiledStaticTemplate = {\n commentPaths: ReadonlyMap<number, NodePath>;\n element: HTMLTemplateElement;\n elementPaths: ReadonlyMap<number, NodePath>;\n slots: SlotMeta[];\n};\n\n// ─── Slot detection regexes ───────────────────────────────────────────────────\n\nconst EVENT_RE = /\\s+@([a-zA-Z_][-a-zA-Z0-9_.-]*)\\s*=\\s*[\"']?$/;\nconst REF_RE = /\\s+ref\\s*=\\s*[\"']?$/;\nconst BOOL_ATTR_RE = /\\s+\\?([a-zA-Z_][-a-zA-Z0-9_]*)\\s*=\\s*[\"']?$/;\nconst ATTR_RE = /\\s+:?([a-zA-Z_][-a-zA-Z0-9_]*)\\s*=\\s*[\"']?$/;\n\nconst isInsideStartTag = (str: string): boolean => {\n const lastOpen = str.lastIndexOf('<');\n const lastClose = str.lastIndexOf('>');\n\n if (lastOpen <= lastClose) return false;\n\n // Must not be a closing tag (</...)\n return str[lastOpen + 1] !== '/';\n};\n\nconst detectSlot = (str: string): DetectedSlot => {\n let m: RegExpExecArray | null;\n const trimmed = str.trimEnd();\n\n // Dynamic closing tag: interpolation is the closing tag name, e.g. strings[i] = \"</\"\n if (trimmed.endsWith('</')) {\n return { kind: 'closeTag', prefix: str };\n }\n\n // Dynamic opening tag name: interpolation is the tag name itself, e.g. strings[i] = \"<\"\n if (trimmed.endsWith('<')) {\n return { kind: 'tagname', prefix: str };\n }\n\n if ((m = EVENT_RE.exec(str))) {\n const prefix = str.slice(0, -m[0].length);\n const parts = m[1].split('.');\n\n return { kind: 'event', modifiers: parts.slice(1), name: parts[0], prefix };\n }\n\n if ((m = REF_RE.exec(str))) {\n return { kind: 'ref', prefix: str.slice(0, -m[0].length) };\n }\n\n if ((m = BOOL_ATTR_RE.exec(str))) {\n return { kind: 'boolAttr', name: m[1], prefix: str.slice(0, -m[0].length) };\n }\n\n if ((m = ATTR_RE.exec(str))) {\n return { kind: 'attr', name: m[1], prefix: str.slice(0, -m[0].length) };\n }\n\n if (isInsideStartTag(str)) {\n return { kind: 'spread', prefix: str.trimEnd() };\n }\n\n return { kind: 'node', prefix: str };\n};\n\n// ─── Static template cache ────────────────────────────────────────────────────\n\nconst templateCache = new WeakMap<TemplateStringsArray, CompiledStaticTemplate>();\n\n/**\n * Pre-process template strings to strip surrounding attribute quotes and the\n * closing `>` that follows a dynamic tag-name slot. This lets the main loop\n * operate on clean strings with no per-iteration state flags.\n */\nconst normalizeTemplateStrings = (strings: TemplateStringsArray): string[] => {\n const out = Array.from(strings);\n\n for (let i = 0; i < out.length - 1; i++) {\n const s = out[i];\n const lastChar = s[s.length - 1];\n\n // Strip wrapping attribute quotes: attr=\"${value}\" → attr=${value}\n if (lastChar === '\"' || lastChar === \"'\") {\n out[i] = s.slice(0, -1);\n\n const next = out[i + 1];\n\n if (next.startsWith(lastChar)) out[i + 1] = next.slice(1);\n }\n\n // Strip leading `>` from the string that follows a dynamic closing tag:\n // </${tagName}> — the `>` is the first character of strings[i+1]\n const cur = out[i];\n\n if (cur.trimEnd().endsWith('</')) {\n const next = out[i + 1];\n\n if (next.startsWith('>')) out[i + 1] = next.slice(1);\n }\n }\n\n return out;\n};\n\nconst walkNode = (\n node: Node,\n path: number[],\n elementPaths: Map<number, NodePath>,\n commentPaths: Map<number, NodePath>,\n): void => {\n if (node.nodeType === Node.ELEMENT_NODE) {\n const el = node as Element;\n const marker = el.getAttribute('u');\n\n if (marker !== null) {\n elementPaths.set(Number(marker), [...path]);\n el.removeAttribute('u');\n }\n } else if (node.nodeType === Node.COMMENT_NODE) {\n const content = (node as Comment).nodeValue;\n\n if (content !== null && /^\\d+$/.test(content)) {\n commentPaths.set(Number(content), [...path]);\n }\n }\n\n const children = node.childNodes;\n\n for (let i = 0; i < children.length; i++) walkNode(children[i], [...path, i], elementPaths, commentPaths);\n};\n\nconst buildStaticTemplate = (strings: TemplateStringsArray): CompiledStaticTemplate => {\n const normalized = normalizeTemplateStrings(strings);\n let html = '';\n let activeElementId: number | undefined;\n let elementCounter = 0;\n let commentCounter = 0;\n const slots: SlotMeta[] = [];\n const tagNameStack: number[] = [];\n\n for (let i = 0; i < normalized.length - 1; i++) {\n const raw = normalized[i];\n const slot = detectSlot(raw);\n\n if (slot.kind === 'tagname') {\n // Dynamic opening tag name: emit a placeholder custom element\n const id = elementCounter++;\n\n activeElementId = id;\n tagNameStack.push(id);\n\n // Remove trailing '<' from prefix and open placeholder element\n const prefixWithoutAngle = raw.replace(/<\\s*$/, '');\n\n html += prefixWithoutAngle + `<ore-dyn-${id} u=\"${id}\"`;\n slots.push({ elementId: id, kind: 'tagname' });\n } else if (slot.kind === 'closeTag') {\n // Dynamic closing tag: close the matching placeholder element\n const id = tagNameStack.pop() ?? 0;\n const prefixWithoutClose = raw.replace(/<\\/\\s*$/, '');\n\n html += prefixWithoutClose + `</ore-dyn-${id}>`;\n slots.push({ kind: 'closeTag' });\n activeElementId = undefined;\n } else if (slot.kind === 'node') {\n html += slot.prefix + `<!--${commentCounter}-->`;\n slots.push({ commentId: commentCounter, kind: 'node' });\n commentCounter++;\n activeElementId = undefined;\n } else {\n const needsNewMarker =\n activeElementId === undefined || slot.prefix.lastIndexOf('<') > slot.prefix.lastIndexOf('>');\n\n if (needsNewMarker) {\n activeElementId = elementCounter++;\n html += `${slot.prefix} u=\"${activeElementId}\"`;\n } else {\n html += slot.prefix;\n }\n\n const mode: 'attr' | 'bool' | undefined =\n slot.kind === 'boolAttr' ? 'bool' : slot.kind === 'attr' ? 'attr' : undefined;\n\n slots.push({ elementId: activeElementId, kind: slot.kind, mode, modifiers: slot.modifiers, name: slot.name });\n }\n }\n\n html += normalized[normalized.length - 1] ?? '';\n\n const tpl = document.createElement('template');\n\n tpl.innerHTML = html;\n\n const elementPaths = new Map<number, NodePath>();\n const commentPaths = new Map<number, NodePath>();\n const topChildren = tpl.content.childNodes;\n\n for (let i = 0; i < topChildren.length; i++) walkNode(topChildren[i], [i], elementPaths, commentPaths);\n\n return { commentPaths, element: tpl, elementPaths, slots };\n};\n\nexport const getStaticTemplate = (strings: TemplateStringsArray): CompiledStaticTemplate => {\n let tpl = templateCache.get(strings);\n\n if (!tpl) {\n tpl = buildStaticTemplate(strings);\n templateCache.set(strings, tpl);\n }\n\n return tpl;\n};\n\n// ─── Path navigation (used by instantiator) ───────────────────────────────────\n\nexport const followPath = (root: Node, path: NodePath): Node => {\n let node: Node = root;\n\n for (const i of path) node = node.childNodes[i];\n\n return node;\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* template/compiler.ts — HTML template string parser and static template cache.
|
|
3
|
+
*
|
|
4
|
+
* Responsibilities:
|
|
5
|
+
* - Parse TemplateStringsArray into slot metadata (slot detection).
|
|
6
|
+
* - Build a cached HTMLTemplateElement with path indices for efficient node lookup.
|
|
7
|
+
* - Expose `getStaticTemplate()` for use by the instantiator.
|
|
8
|
+
*/
|
|
9
|
+
export type DetectedSlotKind = 'event' | 'ref' | 'boolAttr' | 'attr' | 'spread' | 'node' | 'tagname' | 'closeTag';
|
|
10
|
+
export type NodePath = readonly number[];
|
|
11
|
+
export type SlotMeta = {
|
|
12
|
+
commentId?: number;
|
|
13
|
+
elementId?: number;
|
|
14
|
+
kind: DetectedSlotKind;
|
|
15
|
+
mode?: 'attr' | 'bool';
|
|
16
|
+
modifiers?: string[];
|
|
17
|
+
name?: string;
|
|
18
|
+
};
|
|
19
|
+
export type CompiledStaticTemplate = {
|
|
20
|
+
commentPaths: ReadonlyMap<number, NodePath>;
|
|
21
|
+
element: HTMLTemplateElement;
|
|
22
|
+
elementPaths: ReadonlyMap<number, NodePath>;
|
|
23
|
+
slots: SlotMeta[];
|
|
24
|
+
};
|
|
25
|
+
export declare const getStaticTemplate: (strings: TemplateStringsArray) => CompiledStaticTemplate;
|
|
26
|
+
export declare const followPath: (root: Node, path: NodePath) => Node;
|
|
27
|
+
//# sourceMappingURL=compiler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compiler.d.ts","sourceRoot":"","sources":["../../src/template/compiler.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,KAAK,GAAG,UAAU,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,GAAG,SAAS,GAAG,UAAU,CAAC;AAWlH,MAAM,MAAM,QAAQ,GAAG,SAAS,MAAM,EAAE,CAAC;AAEzC,MAAM,MAAM,QAAQ,GAAG;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,gBAAgB,CAAC;IACvB,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IACnC,YAAY,EAAE,WAAW,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAC5C,OAAO,EAAE,mBAAmB,CAAC;IAC7B,YAAY,EAAE,WAAW,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAC5C,KAAK,EAAE,QAAQ,EAAE,CAAC;CACnB,CAAC;AAoMF,eAAO,MAAM,iBAAiB,GAAI,SAAS,oBAAoB,KAAG,sBASjE,CAAC;AAIF,eAAO,MAAM,UAAU,GAAI,MAAM,IAAI,EAAE,MAAM,QAAQ,KAAG,IAMvD,CAAC"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var e=/\s+@([a-zA-Z_][-a-zA-Z0-9_.-]*)\s*=\s*["']?$/,t=/\s+ref\s*=\s*["']?$/,n=/\s+\?([a-zA-Z_][-a-zA-Z0-9_]*)\s*=\s*["']?$/,r=/\s+:?([a-zA-Z_][-a-zA-Z0-9_]*)\s*=\s*["']?$/,i=e=>{let t=e.lastIndexOf(`<`);return t<=e.lastIndexOf(`>`)?!1:e[t+1]!==`/`},a=a=>{let o,s=a.trimEnd();if(s.endsWith(`</`))return{kind:`closeTag`,prefix:a};if(s.endsWith(`<`))return{kind:`tagname`,prefix:a};if(o=e.exec(a)){let e=a.slice(0,-o[0].length),t=o[1].split(`.`);return{kind:`event`,modifiers:t.slice(1),name:t[0],prefix:e}}return(o=t.exec(a))?{kind:`ref`,prefix:a.slice(0,-o[0].length)}:(o=n.exec(a))?{kind:`boolAttr`,name:o[1],prefix:a.slice(0,-o[0].length)}:(o=r.exec(a))?{kind:`attr`,name:o[1],prefix:a.slice(0,-o[0].length)}:i(a)?{kind:`spread`,prefix:a.trimEnd()}:{kind:`node`,prefix:a}},o=new WeakMap,s=e=>{let t=Array.from(e);for(let e=0;e<t.length-1;e++){let n=t[e],r=n[n.length-1];if(r===`"`||r===`'`){t[e]=n.slice(0,-1);let i=t[e+1];i.startsWith(r)&&(t[e+1]=i.slice(1))}if(t[e].trimEnd().endsWith(`</`)){let n=t[e+1];n.startsWith(`>`)&&(t[e+1]=n.slice(1))}}return t},c=(e,t,n,r)=>{if(e.nodeType===Node.ELEMENT_NODE){let r=e,i=r.getAttribute(`u`);i!==null&&(n.set(Number(i),[...t]),r.removeAttribute(`u`))}else if(e.nodeType===Node.COMMENT_NODE){let n=e.nodeValue;n!==null&&/^\d+$/.test(n)&&r.set(Number(n),[...t])}let i=e.childNodes;for(let e=0;e<i.length;e++)c(i[e],[...t,e],n,r)},l=e=>{let t=s(e),n=``,r,i=0,o=0,l=[],u=[];for(let e=0;e<t.length-1;e++){let s=t[e],c=a(s);if(c.kind===`tagname`){let e=i++;r=e,u.push(e);let t=s.replace(/<\s*$/,``);n+=t+`<ore-dyn-${e} u="${e}"`,l.push({elementId:e,kind:`tagname`})}else if(c.kind===`closeTag`){let e=u.pop()??0,t=s.replace(/<\/\s*$/,``);n+=t+`</ore-dyn-${e}>`,l.push({kind:`closeTag`}),r=void 0}else if(c.kind===`node`)n+=c.prefix+`<!--${o}-->`,l.push({commentId:o,kind:`node`}),o++,r=void 0;else{r===void 0||c.prefix.lastIndexOf(`<`)>c.prefix.lastIndexOf(`>`)?(r=i++,n+=`${c.prefix} u="${r}"`):n+=c.prefix;let e=c.kind===`boolAttr`?`bool`:c.kind===`attr`?`attr`:void 0;l.push({elementId:r,kind:c.kind,mode:e,modifiers:c.modifiers,name:c.name})}}n+=t[t.length-1]??``;let d=document.createElement(`template`);d.innerHTML=n;let f=new Map,p=new Map,m=d.content.childNodes;for(let e=0;e<m.length;e++)c(m[e],[e],f,p);return{commentPaths:p,element:d,elementPaths:f,slots:l}},u=e=>{let t=o.get(e);return t||(t=l(e),o.set(e,t)),t},d=(e,t)=>{let n=e;for(let e of t)n=n.childNodes[e];return n};export{d as followPath,u as getStaticTemplate};
|
|
2
|
+
//# sourceMappingURL=compiler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compiler.js","names":[],"sources":["../../src/template/compiler.ts"],"sourcesContent":["/**\n * template/compiler.ts — HTML template string parser and static template cache.\n *\n * Responsibilities:\n * - Parse TemplateStringsArray into slot metadata (slot detection).\n * - Build a cached HTMLTemplateElement with path indices for efficient node lookup.\n * - Expose `getStaticTemplate()` for use by the instantiator.\n */\n\n// ─── Slot kinds ───────────────────────────────────────────────────────────────\n\nexport type DetectedSlotKind = 'event' | 'ref' | 'boolAttr' | 'attr' | 'spread' | 'node' | 'tagname' | 'closeTag';\n\ntype DetectedSlot = {\n kind: DetectedSlotKind;\n modifiers?: string[];\n name?: string;\n prefix: string;\n};\n\n// ─── Static template types ────────────────────────────────────────────────────\n\nexport type NodePath = readonly number[];\n\nexport type SlotMeta = {\n commentId?: number;\n elementId?: number;\n kind: DetectedSlotKind;\n mode?: 'attr' | 'bool';\n modifiers?: string[];\n name?: string;\n};\n\nexport type CompiledStaticTemplate = {\n commentPaths: ReadonlyMap<number, NodePath>;\n element: HTMLTemplateElement;\n elementPaths: ReadonlyMap<number, NodePath>;\n slots: SlotMeta[];\n};\n\n// ─── Slot detection regexes ───────────────────────────────────────────────────\n\nconst EVENT_RE = /\\s+@([a-zA-Z_][-a-zA-Z0-9_.-]*)\\s*=\\s*[\"']?$/;\nconst REF_RE = /\\s+ref\\s*=\\s*[\"']?$/;\nconst BOOL_ATTR_RE = /\\s+\\?([a-zA-Z_][-a-zA-Z0-9_]*)\\s*=\\s*[\"']?$/;\nconst ATTR_RE = /\\s+:?([a-zA-Z_][-a-zA-Z0-9_]*)\\s*=\\s*[\"']?$/;\n\nconst isInsideStartTag = (str: string): boolean => {\n const lastOpen = str.lastIndexOf('<');\n const lastClose = str.lastIndexOf('>');\n\n if (lastOpen <= lastClose) return false;\n\n // Must not be a closing tag (</...)\n return str[lastOpen + 1] !== '/';\n};\n\nconst detectSlot = (str: string): DetectedSlot => {\n let m: RegExpExecArray | null;\n const trimmed = str.trimEnd();\n\n // Dynamic closing tag: interpolation is the closing tag name, e.g. strings[i] = \"</\"\n if (trimmed.endsWith('</')) {\n return { kind: 'closeTag', prefix: str };\n }\n\n // Dynamic opening tag name: interpolation is the tag name itself, e.g. strings[i] = \"<\"\n if (trimmed.endsWith('<')) {\n return { kind: 'tagname', prefix: str };\n }\n\n if ((m = EVENT_RE.exec(str))) {\n const prefix = str.slice(0, -m[0].length);\n const parts = m[1].split('.');\n\n return { kind: 'event', modifiers: parts.slice(1), name: parts[0], prefix };\n }\n\n if ((m = REF_RE.exec(str))) {\n return { kind: 'ref', prefix: str.slice(0, -m[0].length) };\n }\n\n if ((m = BOOL_ATTR_RE.exec(str))) {\n return { kind: 'boolAttr', name: m[1], prefix: str.slice(0, -m[0].length) };\n }\n\n if ((m = ATTR_RE.exec(str))) {\n return { kind: 'attr', name: m[1], prefix: str.slice(0, -m[0].length) };\n }\n\n if (isInsideStartTag(str)) {\n return { kind: 'spread', prefix: str.trimEnd() };\n }\n\n return { kind: 'node', prefix: str };\n};\n\n// ─── Static template cache ────────────────────────────────────────────────────\n\nconst templateCache = new WeakMap<TemplateStringsArray, CompiledStaticTemplate>();\n\n/**\n * Pre-process template strings to strip surrounding attribute quotes and the\n * closing `>` that follows a dynamic tag-name slot. This lets the main loop\n * operate on clean strings with no per-iteration state flags.\n */\nconst normalizeTemplateStrings = (strings: TemplateStringsArray): string[] => {\n const out = Array.from(strings);\n\n for (let i = 0; i < out.length - 1; i++) {\n const s = out[i];\n const lastChar = s[s.length - 1];\n\n // Strip wrapping attribute quotes: attr=\"${value}\" → attr=${value}\n if (lastChar === '\"' || lastChar === \"'\") {\n out[i] = s.slice(0, -1);\n\n const next = out[i + 1];\n\n if (next.startsWith(lastChar)) out[i + 1] = next.slice(1);\n }\n\n // Strip leading `>` from the string that follows a dynamic closing tag:\n // </${tagName}> — the `>` is the first character of strings[i+1]\n const cur = out[i];\n\n if (cur.trimEnd().endsWith('</')) {\n const next = out[i + 1];\n\n if (next.startsWith('>')) out[i + 1] = next.slice(1);\n }\n }\n\n return out;\n};\n\nconst walkNode = (\n node: Node,\n path: number[],\n elementPaths: Map<number, NodePath>,\n commentPaths: Map<number, NodePath>,\n): void => {\n if (node.nodeType === Node.ELEMENT_NODE) {\n const el = node as Element;\n const marker = el.getAttribute('u');\n\n if (marker !== null) {\n elementPaths.set(Number(marker), [...path]);\n el.removeAttribute('u');\n }\n } else if (node.nodeType === Node.COMMENT_NODE) {\n const content = (node as Comment).nodeValue;\n\n if (content !== null && /^\\d+$/.test(content)) {\n commentPaths.set(Number(content), [...path]);\n }\n }\n\n const children = node.childNodes;\n\n for (let i = 0; i < children.length; i++) walkNode(children[i], [...path, i], elementPaths, commentPaths);\n};\n\nconst buildStaticTemplate = (strings: TemplateStringsArray): CompiledStaticTemplate => {\n const normalized = normalizeTemplateStrings(strings);\n let html = '';\n let activeElementId: number | undefined;\n let elementCounter = 0;\n let commentCounter = 0;\n const slots: SlotMeta[] = [];\n const tagNameStack: number[] = [];\n\n for (let i = 0; i < normalized.length - 1; i++) {\n const raw = normalized[i];\n const slot = detectSlot(raw);\n\n if (slot.kind === 'tagname') {\n // Dynamic opening tag name: emit a placeholder custom element\n const id = elementCounter++;\n\n activeElementId = id;\n tagNameStack.push(id);\n\n // Remove trailing '<' from prefix and open placeholder element\n const prefixWithoutAngle = raw.replace(/<\\s*$/, '');\n\n html += prefixWithoutAngle + `<ore-dyn-${id} u=\"${id}\"`;\n slots.push({ elementId: id, kind: 'tagname' });\n } else if (slot.kind === 'closeTag') {\n // Dynamic closing tag: close the matching placeholder element\n const id = tagNameStack.pop() ?? 0;\n const prefixWithoutClose = raw.replace(/<\\/\\s*$/, '');\n\n html += prefixWithoutClose + `</ore-dyn-${id}>`;\n slots.push({ kind: 'closeTag' });\n activeElementId = undefined;\n } else if (slot.kind === 'node') {\n html += slot.prefix + `<!--${commentCounter}-->`;\n slots.push({ commentId: commentCounter, kind: 'node' });\n commentCounter++;\n activeElementId = undefined;\n } else {\n const needsNewMarker =\n activeElementId === undefined || slot.prefix.lastIndexOf('<') > slot.prefix.lastIndexOf('>');\n\n if (needsNewMarker) {\n activeElementId = elementCounter++;\n html += `${slot.prefix} u=\"${activeElementId}\"`;\n } else {\n html += slot.prefix;\n }\n\n const mode: 'attr' | 'bool' | undefined =\n slot.kind === 'boolAttr' ? 'bool' : slot.kind === 'attr' ? 'attr' : undefined;\n\n slots.push({ elementId: activeElementId, kind: slot.kind, mode, modifiers: slot.modifiers, name: slot.name });\n }\n }\n\n html += normalized[normalized.length - 1] ?? '';\n\n const tpl = document.createElement('template');\n\n tpl.innerHTML = html;\n\n const elementPaths = new Map<number, NodePath>();\n const commentPaths = new Map<number, NodePath>();\n const topChildren = tpl.content.childNodes;\n\n for (let i = 0; i < topChildren.length; i++) walkNode(topChildren[i], [i], elementPaths, commentPaths);\n\n return { commentPaths, element: tpl, elementPaths, slots };\n};\n\nexport const getStaticTemplate = (strings: TemplateStringsArray): CompiledStaticTemplate => {\n let tpl = templateCache.get(strings);\n\n if (!tpl) {\n tpl = buildStaticTemplate(strings);\n templateCache.set(strings, tpl);\n }\n\n return tpl;\n};\n\n// ─── Path navigation (used by instantiator) ───────────────────────────────────\n\nexport const followPath = (root: Node, path: NodePath): Node => {\n let node: Node = root;\n\n for (const i of path) node = node.childNodes[i];\n\n return node;\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require("../_dev.cjs"),t=require("../types/bindings.cjs"),n=require("./bindings.cjs"),r=require("../utils/event-modifiers.cjs"),i=require("./compiler.cjs");let a=require("@vielzeug/ripple");var o=(o,s)=>{let c=i.getStaticTemplate(o),l=c.element.content.cloneNode(!0),u=[],d=[],f=c.slots.map((e,t)=>{let n=s[t];return e.kind===`closeTag`?{slot:e,value:n}:e.kind===`node`?{comment:i.followPath(l,c.commentPaths.get(e.commentId)),slot:e,value:n}:{el:i.followPath(l,c.elementPaths.get(e.elementId)),slot:e,value:n}}),p=new Map;for(let{el:t,slot:n,value:r}of f){if(n.kind!==`tagname`)continue;let i=String(r);if(!/^[a-z][a-z0-9._-]*$/i.test(i)){e.warn(`html\`...\`: dynamic tag name "${i}" is not a valid HTML element name — skipping slot replacement.`);continue}let a=document.createElement(i);for(let e of Array.from(t.attributes))a.setAttribute(e.name,e.value);for(;t.firstChild;)a.appendChild(t.firstChild);t.replaceWith(a),p.set(t,a)}for(let{comment:e,el:i,slot:o,value:s}of f){if(o.kind===`tagname`||o.kind===`closeTag`)continue;let c=i&&(p.get(i)??i);if(o.kind===`node`){let r=e;if(t.isDirectiveResult(s)){u.push({anchor:r,directive:s,type:`directive`});continue}if(t.isHtmlResult(s)){let e=r.parentNode;for(;s.fragment.firstChild;)e.insertBefore(s.fragment.firstChild,r);r.remove(),d.push(s.apply.bind(s));continue}if(typeof s==`function`){let e=(0,a.computed)(()=>{let e=s();return Array.isArray(e)?e:[e]});u.push({anchor:r,signal:e,type:`html`});continue}if((0,a.isReactive)(s)){let e=(0,a.computed)(()=>{let e=s.value;return Array.isArray(e)?e:[e]});u.push({anchor:r,signal:e,type:`html`});continue}if(Array.isArray(s)){let e=r.parentNode;for(let i of s)if(t.isHtmlResult(i)){for(;i.fragment.firstChild;)e.insertBefore(i.fragment.firstChild,r);d.push(i.apply.bind(i))}else e.insertBefore(document.createTextNode(n.resolveStaticText(i)),r);r.remove();continue}r.replaceWith(document.createTextNode(n.resolveStaticText(s)));continue}let l=c;if(o.kind===`event`){if(typeof s==`function`){let{handler:e,options:t}=r.applyModifiers(s,o.modifiers??[]);u.push({el:l,handler:e,name:o.name,options:t,type:`event`})}else if((0,a.isReactive)(s)){let e=s,{handler:t,options:n}=r.applyModifiers(t=>{let n=e.value;typeof n==`function`&&n(t)},o.modifiers??[]);u.push({el:l,handler:t,name:o.name,options:n,type:`event`})}continue}if(o.kind===`ref`){s&&u.push({el:l,ref:s,type:`ref`});continue}if(o.kind===`spread`){t.isSpreadObject(s)&&u.push({el:l,spread:s,type:`spread`});continue}u.push(n.createAttrBindingFromValue(l,o.mode??`attr`,o.name,s))}return t.createHtmlResult(l,e=>{for(let t of u)n.applyBinding(t,e);for(let t of d)t(e)})},s=(e,...t)=>o(e,t);exports.compileTemplate=o,exports.html=s;
|
|
2
|
+
//# sourceMappingURL=instantiator.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"instantiator.cjs","names":[],"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 } from '@vielzeug/ripple';\n\nimport { warn } from '../_dev';\nimport {\n type Binding,\n createHtmlResult,\n type HtmlBindingValue,\n type HTMLResult,\n isDirectiveResult,\n isHtmlResult,\n isSpreadObject,\n type Ref,\n type RefCallback,\n} from '../types/bindings';\nimport { applyModifiers } from '../utils/event-modifiers';\nimport { applyBinding, createAttrBindingFromValue, resolveStaticText } from './bindings';\nimport { followPath, getStaticTemplate } from './compiler';\n\n// ─── Template instantiation ──────────────────────────────────────────────────\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 whose fragment is ready to insert.\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 === 'closeTag') {\n return { slot, value };\n }\n\n if (slot.kind === 'node') {\n const comment = followPath(fragment, compiled.commentPaths.get(slot.commentId!)!) as Comment;\n\n return { comment, slot, value };\n }\n\n const el = followPath(fragment, compiled.elementPaths.get(slot.elementId!)!) as HTMLElement;\n\n return { el, slot, value };\n });\n\n // Phase 2a: Process tagname slots first — replace placeholders with real elements\n const tagReplacements = new Map<HTMLElement, HTMLElement>();\n\n for (const { el, slot, value } of boundSlots) {\n if (slot.kind !== 'tagname') continue;\n\n const tagName = String(value);\n\n if (!/^[a-z][a-z0-9._-]*$/i.test(tagName)) {\n warn(`html\\`...\\`: dynamic tag name \"${tagName}\" is not a valid HTML element name — skipping slot replacement.`);\n\n continue;\n }\n\n const realEl = document.createElement(tagName);\n\n for (const attr of Array.from(el!.attributes)) realEl.setAttribute(attr.name, attr.value);\n\n while (el!.firstChild) realEl.appendChild(el!.firstChild);\n\n el!.replaceWith(realEl);\n tagReplacements.set(el!, realEl);\n }\n\n // Phase 2b: Build bindings (may modify DOM for static content)\n for (const { comment, el: rawEl, slot, value } of boundSlots) {\n if (slot.kind === 'tagname' || slot.kind === 'closeTag') continue;\n\n // Resolve element through tagname replacements\n const el = rawEl ? (tagReplacements.get(rawEl) ?? rawEl) : rawEl;\n\n if (slot.kind === 'node') {\n const anchor = comment!;\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 const parent = anchor.parentNode!;\n\n while (value.fragment.firstChild) parent.insertBefore(value.fragment.firstChild, anchor);\n\n anchor.remove();\n chainedApplies.push(value.apply.bind(value));\n continue;\n }\n\n if (typeof value === 'function') {\n const sig = computed(() => {\n const res = (value as () => unknown)();\n\n return Array.isArray(res) ? (res as HtmlBindingValue[]) : [res as HtmlBindingValue];\n });\n\n bindings.push({ anchor, signal: sig, type: 'html' });\n continue;\n }\n\n if (isReactive(value)) {\n // Always use the html binding for signals — it handles both text values and\n // HTMLResult values, preventing silent \"[object Object]\" corruption when a\n // signal's runtime type changes from null/string to HTMLResult.\n const sig = computed(() => {\n const raw = (value as Readable<HtmlBindingValue | HtmlBindingValue[]>).value;\n\n return Array.isArray(raw) ? (raw as HtmlBindingValue[]) : [raw as HtmlBindingValue];\n });\n\n bindings.push({ anchor, signal: sig, type: 'html' });\n continue;\n }\n\n if (Array.isArray(value)) {\n const parent = anchor.parentNode!;\n\n for (const item of value) {\n if (isHtmlResult(item)) {\n while (item.fragment.firstChild) parent.insertBefore(item.fragment.firstChild, anchor);\n\n chainedApplies.push(item.apply.bind(item));\n } else {\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 const target = el!;\n\n if (slot.kind === 'event') {\n if (typeof value === 'function') {\n const { handler, options } = applyModifiers(value as (e: Event) => void, slot.modifiers ?? []);\n\n bindings.push({ el: target, handler, name: slot.name!, options, type: 'event' });\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 const { handler: wrapped, options } = applyModifiers(handler, slot.modifiers ?? []);\n\n bindings.push({ el: target, handler: wrapped, name: slot.name!, options, type: 'event' });\n }\n\n continue;\n }\n\n if (slot.kind === 'ref') {\n if (value) {\n bindings.push({ el: target, ref: value as Ref<Element> | RefCallback<Element>, type: 'ref' });\n }\n\n continue;\n }\n\n if (slot.kind === 'spread') {\n if (isSpreadObject(value)) {\n bindings.push({ el: target, spread: value, type: 'spread' });\n }\n\n continue;\n }\n\n // attr / boolAttr\n bindings.push(createAttrBindingFromValue(target, 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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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* template/instantiator.ts — Template instantiation and the `html` tagged literal.
|
|
3
|
+
*
|
|
4
|
+
* Responsibilities:
|
|
5
|
+
* - Clone a compiled static template and wire up live bindings.
|
|
6
|
+
* - Expose `compileTemplate()` and `html` as the public authoring API.
|
|
7
|
+
*/
|
|
8
|
+
import { type HTMLResult } from '../types/bindings';
|
|
9
|
+
/**
|
|
10
|
+
* Instantiate a compiled template: clone the cached DOM template, navigate
|
|
11
|
+
* to each binding target using pre-recorded paths, and build bindings with
|
|
12
|
+
* direct node references. Returns an HTMLResult whose fragment is ready to insert.
|
|
13
|
+
*/
|
|
14
|
+
export declare const compileTemplate: (strings: TemplateStringsArray, values: unknown[]) => HTMLResult;
|
|
15
|
+
export declare const html: (strings: TemplateStringsArray, ...values: unknown[]) => HTMLResult;
|
|
16
|
+
//# sourceMappingURL=instantiator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"instantiator.d.ts","sourceRoot":"","sources":["../../src/template/instantiator.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,OAAO,EAIL,KAAK,UAAU,EAMhB,MAAM,mBAAmB,CAAC;AAO3B;;;;GAIG;AACH,eAAO,MAAM,eAAe,GAAI,SAAS,oBAAoB,EAAE,QAAQ,OAAO,EAAE,KAAG,UA2KlF,CAAC;AAEF,eAAO,MAAM,IAAI,GAAI,SAAS,oBAAoB,EAAE,GAAG,QAAQ,OAAO,EAAE,KAAG,UACzC,CAAC"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{warn as e}from"../_dev.js";import{createHtmlResult as t,isDirectiveResult as n,isHtmlResult as r,isSpreadObject as i}from"../types/bindings.js";import{applyBinding as a,createAttrBindingFromValue as o,resolveStaticText as s}from"./bindings.js";import{applyModifiers as c}from"../utils/event-modifiers.js";import{followPath as l,getStaticTemplate as u}from"./compiler.js";import{computed as d,isReactive as f}from"@vielzeug/ripple";var p=(p,m)=>{let h=u(p),g=h.element.content.cloneNode(!0),_=[],v=[],y=h.slots.map((e,t)=>{let n=m[t];return e.kind===`closeTag`?{slot:e,value:n}:e.kind===`node`?{comment:l(g,h.commentPaths.get(e.commentId)),slot:e,value:n}:{el:l(g,h.elementPaths.get(e.elementId)),slot:e,value:n}}),b=new Map;for(let{el:t,slot:n,value:r}of y){if(n.kind!==`tagname`)continue;let i=String(r);if(!/^[a-z][a-z0-9._-]*$/i.test(i)){e(`html\`...\`: dynamic tag name "${i}" is not a valid HTML element name — skipping slot replacement.`);continue}let a=document.createElement(i);for(let e of Array.from(t.attributes))a.setAttribute(e.name,e.value);for(;t.firstChild;)a.appendChild(t.firstChild);t.replaceWith(a),b.set(t,a)}for(let{comment:e,el:t,slot:a,value:l}of y){if(a.kind===`tagname`||a.kind===`closeTag`)continue;let u=t&&(b.get(t)??t);if(a.kind===`node`){let t=e;if(n(l)){_.push({anchor:t,directive:l,type:`directive`});continue}if(r(l)){let e=t.parentNode;for(;l.fragment.firstChild;)e.insertBefore(l.fragment.firstChild,t);t.remove(),v.push(l.apply.bind(l));continue}if(typeof l==`function`){let e=d(()=>{let e=l();return Array.isArray(e)?e:[e]});_.push({anchor:t,signal:e,type:`html`});continue}if(f(l)){let e=d(()=>{let e=l.value;return Array.isArray(e)?e:[e]});_.push({anchor:t,signal:e,type:`html`});continue}if(Array.isArray(l)){let e=t.parentNode;for(let n of l)if(r(n)){for(;n.fragment.firstChild;)e.insertBefore(n.fragment.firstChild,t);v.push(n.apply.bind(n))}else e.insertBefore(document.createTextNode(s(n)),t);t.remove();continue}t.replaceWith(document.createTextNode(s(l)));continue}let p=u;if(a.kind===`event`){if(typeof l==`function`){let{handler:e,options:t}=c(l,a.modifiers??[]);_.push({el:p,handler:e,name:a.name,options:t,type:`event`})}else if(f(l)){let e=l,{handler:t,options:n}=c(t=>{let n=e.value;typeof n==`function`&&n(t)},a.modifiers??[]);_.push({el:p,handler:t,name:a.name,options:n,type:`event`})}continue}if(a.kind===`ref`){l&&_.push({el:p,ref:l,type:`ref`});continue}if(a.kind===`spread`){i(l)&&_.push({el:p,spread:l,type:`spread`});continue}_.push(o(p,a.mode??`attr`,a.name,l))}return t(g,e=>{for(let t of _)a(t,e);for(let t of v)t(e)})},m=(e,...t)=>p(e,t);export{p as compileTemplate,m as html};
|
|
2
|
+
//# sourceMappingURL=instantiator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"instantiator.js","names":[],"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 } from '@vielzeug/ripple';\n\nimport { warn } from '../_dev';\nimport {\n type Binding,\n createHtmlResult,\n type HtmlBindingValue,\n type HTMLResult,\n isDirectiveResult,\n isHtmlResult,\n isSpreadObject,\n type Ref,\n type RefCallback,\n} from '../types/bindings';\nimport { applyModifiers } from '../utils/event-modifiers';\nimport { applyBinding, createAttrBindingFromValue, resolveStaticText } from './bindings';\nimport { followPath, getStaticTemplate } from './compiler';\n\n// ─── Template instantiation ──────────────────────────────────────────────────\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 whose fragment is ready to insert.\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 === 'closeTag') {\n return { slot, value };\n }\n\n if (slot.kind === 'node') {\n const comment = followPath(fragment, compiled.commentPaths.get(slot.commentId!)!) as Comment;\n\n return { comment, slot, value };\n }\n\n const el = followPath(fragment, compiled.elementPaths.get(slot.elementId!)!) as HTMLElement;\n\n return { el, slot, value };\n });\n\n // Phase 2a: Process tagname slots first — replace placeholders with real elements\n const tagReplacements = new Map<HTMLElement, HTMLElement>();\n\n for (const { el, slot, value } of boundSlots) {\n if (slot.kind !== 'tagname') continue;\n\n const tagName = String(value);\n\n if (!/^[a-z][a-z0-9._-]*$/i.test(tagName)) {\n warn(`html\\`...\\`: dynamic tag name \"${tagName}\" is not a valid HTML element name — skipping slot replacement.`);\n\n continue;\n }\n\n const realEl = document.createElement(tagName);\n\n for (const attr of Array.from(el!.attributes)) realEl.setAttribute(attr.name, attr.value);\n\n while (el!.firstChild) realEl.appendChild(el!.firstChild);\n\n el!.replaceWith(realEl);\n tagReplacements.set(el!, realEl);\n }\n\n // Phase 2b: Build bindings (may modify DOM for static content)\n for (const { comment, el: rawEl, slot, value } of boundSlots) {\n if (slot.kind === 'tagname' || slot.kind === 'closeTag') continue;\n\n // Resolve element through tagname replacements\n const el = rawEl ? (tagReplacements.get(rawEl) ?? rawEl) : rawEl;\n\n if (slot.kind === 'node') {\n const anchor = comment!;\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 const parent = anchor.parentNode!;\n\n while (value.fragment.firstChild) parent.insertBefore(value.fragment.firstChild, anchor);\n\n anchor.remove();\n chainedApplies.push(value.apply.bind(value));\n continue;\n }\n\n if (typeof value === 'function') {\n const sig = computed(() => {\n const res = (value as () => unknown)();\n\n return Array.isArray(res) ? (res as HtmlBindingValue[]) : [res as HtmlBindingValue];\n });\n\n bindings.push({ anchor, signal: sig, type: 'html' });\n continue;\n }\n\n if (isReactive(value)) {\n // Always use the html binding for signals — it handles both text values and\n // HTMLResult values, preventing silent \"[object Object]\" corruption when a\n // signal's runtime type changes from null/string to HTMLResult.\n const sig = computed(() => {\n const raw = (value as Readable<HtmlBindingValue | HtmlBindingValue[]>).value;\n\n return Array.isArray(raw) ? (raw as HtmlBindingValue[]) : [raw as HtmlBindingValue];\n });\n\n bindings.push({ anchor, signal: sig, type: 'html' });\n continue;\n }\n\n if (Array.isArray(value)) {\n const parent = anchor.parentNode!;\n\n for (const item of value) {\n if (isHtmlResult(item)) {\n while (item.fragment.firstChild) parent.insertBefore(item.fragment.firstChild, anchor);\n\n chainedApplies.push(item.apply.bind(item));\n } else {\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 const target = el!;\n\n if (slot.kind === 'event') {\n if (typeof value === 'function') {\n const { handler, options } = applyModifiers(value as (e: Event) => void, slot.modifiers ?? []);\n\n bindings.push({ el: target, handler, name: slot.name!, options, type: 'event' });\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 const { handler: wrapped, options } = applyModifiers(handler, slot.modifiers ?? []);\n\n bindings.push({ el: target, handler: wrapped, name: slot.name!, options, type: 'event' });\n }\n\n continue;\n }\n\n if (slot.kind === 'ref') {\n if (value) {\n bindings.push({ el: target, ref: value as Ref<Element> | RefCallback<Element>, type: 'ref' });\n }\n\n continue;\n }\n\n if (slot.kind === 'spread') {\n if (isSpreadObject(value)) {\n bindings.push({ el: target, spread: value, type: 'spread' });\n }\n\n continue;\n }\n\n // attr / boolAttr\n bindings.push(createAttrBindingFromValue(target, 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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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require("./template/bindings.cjs");const e=require("./template/instantiator.cjs");exports.html=e.html;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { applyAttrBinding, applyBinding, applyHtmlBinding, type RegisterCleanup } from './template/bindings';
|
|
2
|
+
export { type CompiledStaticTemplate, type NodePath, type SlotMeta } from './template/compiler';
|
|
3
|
+
export { compileTemplate, html } from './template/instantiator';
|
|
4
|
+
//# sourceMappingURL=template.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template.d.ts","sourceRoot":"","sources":["../src/template.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,gBAAgB,EAAE,KAAK,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC7G,OAAO,EAAE,KAAK,sBAAsB,EAAE,KAAK,QAAQ,EAAE,KAAK,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAChG,OAAO,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAC"}
|
package/dist/template.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./template/bindings.js";import{html as e}from"./template/instantiator.js";export{e as html};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var e=(e,t={})=>typeof PointerEvent<`u`?new PointerEvent(e,t):new MouseEvent(e,t),t={blur:(e,t)=>e.dispatchEvent(new FocusEvent(`blur`,{bubbles:!0,...t})),change:(e,t)=>e.dispatchEvent(new Event(`change`,{bubbles:!0,...t})),click:(e,t)=>e.dispatchEvent(new MouseEvent(`click`,{bubbles:!0,cancelable:!0,...t})),custom(e,t,n){e.dispatchEvent(new CustomEvent(t,{bubbles:!0,cancelable:!0,composed:!1,...n}))},event(e,t){e.dispatchEvent(t)},focus:(e,t,n)=>e.dispatchEvent(new FocusEvent(`focus`,{bubbles:!0,...n})),input:(e,t)=>e.dispatchEvent(new Event(`input`,{bubbles:!0,...t})),keyboard:(e,t,n)=>e.dispatchEvent(new KeyboardEvent(t,{bubbles:!0,cancelable:!0,...n})),keyDown:(e,t)=>e.dispatchEvent(new KeyboardEvent(`keydown`,{bubbles:!0,cancelable:!0,...t})),keyUp:(e,t)=>e.dispatchEvent(new KeyboardEvent(`keyup`,{bubbles:!0,cancelable:!0,...t})),mouse:(e,t,n)=>e.dispatchEvent(new MouseEvent(t,{bubbles:!0,cancelable:!0,...n})),pointerDown:(t,n)=>t.dispatchEvent(e(`pointerdown`,{bubbles:!0,cancelable:!0,...n})),pointerEnter:(t,n)=>t.dispatchEvent(e(`pointerenter`,{bubbles:!1,...n})),pointerLeave:(t,n)=>t.dispatchEvent(e(`pointerleave`,{bubbles:!1,...n})),pointerUp:(t,n)=>t.dispatchEvent(e(`pointerup`,{bubbles:!0,cancelable:!0,...n})),submit:(e,t)=>e.dispatchEvent(new Event(`submit`,{bubbles:!0,cancelable:!0,...t})),touch(e,t,n){typeof TouchEvent<`u`?e.dispatchEvent(new TouchEvent(t,{bubbles:!0,cancelable:!0,...n})):e.dispatchEvent(new CustomEvent(t,{bubbles:!0,cancelable:!0,...n}))}};exports.createPointerEvent=e,exports.fire=t;
|
|
2
|
+
//# sourceMappingURL=events.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.cjs","names":[],"sources":["../../src/testing/events.ts"],"sourcesContent":["/**\n * Synchronous DOM event dispatchers for test environments.\n */\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\nexport const createPointerEvent = (type: string, init: PointerEventInit = {}): Event => {\n if (typeof PointerEvent !== 'undefined') {\n return new PointerEvent(type, init);\n }\n\n return new MouseEvent(type, init);\n};\n\n// ─── API ─────────────────────────────────────────────────────────────────────\n\n/**\n * Fire low-level DOM events synchronously.\n *\n * @example\n * fire.click(button);\n * fire.keyDown(input, { key: 'Enter' });\n * fire.custom(el, 'value-change', { detail: 42 });\n */\nexport const fire = {\n blur: (el: Element, opts?: FocusEventInit) => el.dispatchEvent(new FocusEvent('blur', { bubbles: true, ...opts })),\n change: (el: Element, opts?: EventInit) => el.dispatchEvent(new Event('change', { bubbles: true, ...opts })),\n click: (el: Element, opts?: PointerEventInit) =>\n el.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true, ...opts })),\n /** Fire a CustomEvent. Pass `{ detail: ... }` for event detail. */\n custom(el: Element, name: string, opts?: CustomEventInit): void {\n el.dispatchEvent(new CustomEvent(name, { bubbles: true, cancelable: true, composed: false, ...opts }));\n },\n /** Dispatch any pre-built Event instance directly. */\n event(el: Element, event: Event): void {\n el.dispatchEvent(event);\n },\n focus: (el: Element, _name?: string, opts?: FocusEventInit) =>\n el.dispatchEvent(new FocusEvent('focus', { bubbles: true, ...opts })),\n input: (el: Element, opts?: EventInit) => el.dispatchEvent(new Event('input', { bubbles: true, ...opts })),\n /** Fire a KeyboardEvent with a given type (keydown, keyup, keypress). */\n keyboard: (el: Element, type: string, opts?: KeyboardEventInit) =>\n el.dispatchEvent(new KeyboardEvent(type, { bubbles: true, cancelable: true, ...opts })),\n keyDown: (el: Element, opts?: KeyboardEventInit) =>\n el.dispatchEvent(new KeyboardEvent('keydown', { bubbles: true, cancelable: true, ...opts })),\n keyUp: (el: Element, opts?: KeyboardEventInit) =>\n el.dispatchEvent(new KeyboardEvent('keyup', { bubbles: true, cancelable: true, ...opts })),\n /** Fire a MouseEvent with a given type (click, mousedown, etc.). */\n mouse: (el: Element, type: string, opts?: MouseEventInit) =>\n el.dispatchEvent(new MouseEvent(type, { bubbles: true, cancelable: true, ...opts })),\n pointerDown: (el: Element, opts?: PointerEventInit) =>\n el.dispatchEvent(createPointerEvent('pointerdown', { bubbles: true, cancelable: true, ...opts })),\n pointerEnter: (el: Element, opts?: PointerEventInit) =>\n el.dispatchEvent(createPointerEvent('pointerenter', { bubbles: false, ...opts })),\n pointerLeave: (el: Element, opts?: PointerEventInit) =>\n el.dispatchEvent(createPointerEvent('pointerleave', { bubbles: false, ...opts })),\n pointerUp: (el: Element, opts?: PointerEventInit) =>\n el.dispatchEvent(createPointerEvent('pointerup', { bubbles: true, cancelable: true, ...opts })),\n submit: (el: Element, opts?: EventInit) =>\n el.dispatchEvent(new Event('submit', { bubbles: true, cancelable: true, ...opts })),\n /** Fire a touch event; falls back to CustomEvent in environments without TouchEvent. */\n touch(el: Element, type: string, opts?: EventInit): void {\n if (typeof TouchEvent !== 'undefined') {\n el.dispatchEvent(new TouchEvent(type, { bubbles: true, cancelable: true, ...opts }));\n } else {\n el.dispatchEvent(new CustomEvent(type, { bubbles: true, cancelable: true, ...opts }));\n }\n },\n} as const;\n"],"mappings":"AAMA,IAAa,GAAsB,EAAc,EAAyB,CAAC,IACrE,OAAO,aAAiB,IACnB,IAAI,aAAa,EAAM,CAAI,EAG7B,IAAI,WAAW,EAAM,CAAI,EAarB,EAAO,CAClB,MAAO,EAAa,IAA0B,EAAG,cAAc,IAAI,WAAW,OAAQ,CAAE,QAAS,GAAM,GAAG,CAAK,CAAC,CAAC,EACjH,QAAS,EAAa,IAAqB,EAAG,cAAc,IAAI,MAAM,SAAU,CAAE,QAAS,GAAM,GAAG,CAAK,CAAC,CAAC,EAC3G,OAAQ,EAAa,IACnB,EAAG,cAAc,IAAI,WAAW,QAAS,CAAE,QAAS,GAAM,WAAY,GAAM,GAAG,CAAK,CAAC,CAAC,EAExF,OAAO,EAAa,EAAc,EAA8B,CAC9D,EAAG,cAAc,IAAI,YAAY,EAAM,CAAE,QAAS,GAAM,WAAY,GAAM,SAAU,GAAO,GAAG,CAAK,CAAC,CAAC,CACvG,EAEA,MAAM,EAAa,EAAoB,CACrC,EAAG,cAAc,CAAK,CACxB,EACA,OAAQ,EAAa,EAAgB,IACnC,EAAG,cAAc,IAAI,WAAW,QAAS,CAAE,QAAS,GAAM,GAAG,CAAK,CAAC,CAAC,EACtE,OAAQ,EAAa,IAAqB,EAAG,cAAc,IAAI,MAAM,QAAS,CAAE,QAAS,GAAM,GAAG,CAAK,CAAC,CAAC,EAEzG,UAAW,EAAa,EAAc,IACpC,EAAG,cAAc,IAAI,cAAc,EAAM,CAAE,QAAS,GAAM,WAAY,GAAM,GAAG,CAAK,CAAC,CAAC,EACxF,SAAU,EAAa,IACrB,EAAG,cAAc,IAAI,cAAc,UAAW,CAAE,QAAS,GAAM,WAAY,GAAM,GAAG,CAAK,CAAC,CAAC,EAC7F,OAAQ,EAAa,IACnB,EAAG,cAAc,IAAI,cAAc,QAAS,CAAE,QAAS,GAAM,WAAY,GAAM,GAAG,CAAK,CAAC,CAAC,EAE3F,OAAQ,EAAa,EAAc,IACjC,EAAG,cAAc,IAAI,WAAW,EAAM,CAAE,QAAS,GAAM,WAAY,GAAM,GAAG,CAAK,CAAC,CAAC,EACrF,aAAc,EAAa,IACzB,EAAG,cAAc,EAAmB,cAAe,CAAE,QAAS,GAAM,WAAY,GAAM,GAAG,CAAK,CAAC,CAAC,EAClG,cAAe,EAAa,IAC1B,EAAG,cAAc,EAAmB,eAAgB,CAAE,QAAS,GAAO,GAAG,CAAK,CAAC,CAAC,EAClF,cAAe,EAAa,IAC1B,EAAG,cAAc,EAAmB,eAAgB,CAAE,QAAS,GAAO,GAAG,CAAK,CAAC,CAAC,EAClF,WAAY,EAAa,IACvB,EAAG,cAAc,EAAmB,YAAa,CAAE,QAAS,GAAM,WAAY,GAAM,GAAG,CAAK,CAAC,CAAC,EAChG,QAAS,EAAa,IACpB,EAAG,cAAc,IAAI,MAAM,SAAU,CAAE,QAAS,GAAM,WAAY,GAAM,GAAG,CAAK,CAAC,CAAC,EAEpF,MAAM,EAAa,EAAc,EAAwB,CACnD,OAAO,WAAe,IACxB,EAAG,cAAc,IAAI,WAAW,EAAM,CAAE,QAAS,GAAM,WAAY,GAAM,GAAG,CAAK,CAAC,CAAC,EAEnF,EAAG,cAAc,IAAI,YAAY,EAAM,CAAE,QAAS,GAAM,WAAY,GAAM,GAAG,CAAK,CAAC,CAAC,CAExF,CACF"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Synchronous DOM event dispatchers for test environments.
|
|
3
|
+
*/
|
|
4
|
+
export declare const createPointerEvent: (type: string, init?: PointerEventInit) => Event;
|
|
5
|
+
/**
|
|
6
|
+
* Fire low-level DOM events synchronously.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* fire.click(button);
|
|
10
|
+
* fire.keyDown(input, { key: 'Enter' });
|
|
11
|
+
* fire.custom(el, 'value-change', { detail: 42 });
|
|
12
|
+
*/
|
|
13
|
+
export declare const fire: {
|
|
14
|
+
readonly blur: (el: Element, opts?: FocusEventInit) => boolean;
|
|
15
|
+
readonly change: (el: Element, opts?: EventInit) => boolean;
|
|
16
|
+
readonly click: (el: Element, opts?: PointerEventInit) => boolean;
|
|
17
|
+
/** Fire a CustomEvent. Pass `{ detail: ... }` for event detail. */
|
|
18
|
+
readonly custom: (el: Element, name: string, opts?: CustomEventInit) => void;
|
|
19
|
+
/** Dispatch any pre-built Event instance directly. */
|
|
20
|
+
readonly event: (el: Element, event: Event) => void;
|
|
21
|
+
readonly focus: (el: Element, _name?: string, opts?: FocusEventInit) => boolean;
|
|
22
|
+
readonly input: (el: Element, opts?: EventInit) => boolean;
|
|
23
|
+
/** Fire a KeyboardEvent with a given type (keydown, keyup, keypress). */
|
|
24
|
+
readonly keyboard: (el: Element, type: string, opts?: KeyboardEventInit) => boolean;
|
|
25
|
+
readonly keyDown: (el: Element, opts?: KeyboardEventInit) => boolean;
|
|
26
|
+
readonly keyUp: (el: Element, opts?: KeyboardEventInit) => boolean;
|
|
27
|
+
/** Fire a MouseEvent with a given type (click, mousedown, etc.). */
|
|
28
|
+
readonly mouse: (el: Element, type: string, opts?: MouseEventInit) => boolean;
|
|
29
|
+
readonly pointerDown: (el: Element, opts?: PointerEventInit) => boolean;
|
|
30
|
+
readonly pointerEnter: (el: Element, opts?: PointerEventInit) => boolean;
|
|
31
|
+
readonly pointerLeave: (el: Element, opts?: PointerEventInit) => boolean;
|
|
32
|
+
readonly pointerUp: (el: Element, opts?: PointerEventInit) => boolean;
|
|
33
|
+
readonly submit: (el: Element, opts?: EventInit) => boolean;
|
|
34
|
+
/** Fire a touch event; falls back to CustomEvent in environments without TouchEvent. */
|
|
35
|
+
readonly touch: (el: Element, type: string, opts?: EventInit) => void;
|
|
36
|
+
};
|
|
37
|
+
//# sourceMappingURL=events.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../src/testing/events.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,eAAO,MAAM,kBAAkB,GAAI,MAAM,MAAM,EAAE,OAAM,gBAAqB,KAAG,KAM9E,CAAC;AAIF;;;;;;;GAOG;AACH,eAAO,MAAM,IAAI;wBACJ,OAAO,SAAS,cAAc;0BAC5B,OAAO,SAAS,SAAS;yBAC1B,OAAO,SAAS,gBAAgB;IAE5C,mEAAmE;0BACxD,OAAO,QAAQ,MAAM,SAAS,eAAe,KAAG,IAAI;IAG/D,sDAAsD;yBAC5C,OAAO,SAAS,KAAK,KAAG,IAAI;yBAG1B,OAAO,UAAU,MAAM,SAAS,cAAc;yBAE9C,OAAO,SAAS,SAAS;IACrC,yEAAyE;4BAC1D,OAAO,QAAQ,MAAM,SAAS,iBAAiB;2BAEhD,OAAO,SAAS,iBAAiB;yBAEnC,OAAO,SAAS,iBAAiB;IAE7C,oEAAoE;yBACxD,OAAO,QAAQ,MAAM,SAAS,cAAc;+BAEtC,OAAO,SAAS,gBAAgB;gCAE/B,OAAO,SAAS,gBAAgB;gCAEhC,OAAO,SAAS,gBAAgB;6BAEnC,OAAO,SAAS,gBAAgB;0BAEnC,OAAO,SAAS,SAAS;IAEtC,wFAAwF;yBAC9E,OAAO,QAAQ,MAAM,SAAS,SAAS,KAAG,IAAI;CAOhD,CAAC"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var e=(e,t={})=>typeof PointerEvent<`u`?new PointerEvent(e,t):new MouseEvent(e,t),t={blur:(e,t)=>e.dispatchEvent(new FocusEvent(`blur`,{bubbles:!0,...t})),change:(e,t)=>e.dispatchEvent(new Event(`change`,{bubbles:!0,...t})),click:(e,t)=>e.dispatchEvent(new MouseEvent(`click`,{bubbles:!0,cancelable:!0,...t})),custom(e,t,n){e.dispatchEvent(new CustomEvent(t,{bubbles:!0,cancelable:!0,composed:!1,...n}))},event(e,t){e.dispatchEvent(t)},focus:(e,t,n)=>e.dispatchEvent(new FocusEvent(`focus`,{bubbles:!0,...n})),input:(e,t)=>e.dispatchEvent(new Event(`input`,{bubbles:!0,...t})),keyboard:(e,t,n)=>e.dispatchEvent(new KeyboardEvent(t,{bubbles:!0,cancelable:!0,...n})),keyDown:(e,t)=>e.dispatchEvent(new KeyboardEvent(`keydown`,{bubbles:!0,cancelable:!0,...t})),keyUp:(e,t)=>e.dispatchEvent(new KeyboardEvent(`keyup`,{bubbles:!0,cancelable:!0,...t})),mouse:(e,t,n)=>e.dispatchEvent(new MouseEvent(t,{bubbles:!0,cancelable:!0,...n})),pointerDown:(t,n)=>t.dispatchEvent(e(`pointerdown`,{bubbles:!0,cancelable:!0,...n})),pointerEnter:(t,n)=>t.dispatchEvent(e(`pointerenter`,{bubbles:!1,...n})),pointerLeave:(t,n)=>t.dispatchEvent(e(`pointerleave`,{bubbles:!1,...n})),pointerUp:(t,n)=>t.dispatchEvent(e(`pointerup`,{bubbles:!0,cancelable:!0,...n})),submit:(e,t)=>e.dispatchEvent(new Event(`submit`,{bubbles:!0,cancelable:!0,...t})),touch(e,t,n){typeof TouchEvent<`u`?e.dispatchEvent(new TouchEvent(t,{bubbles:!0,cancelable:!0,...n})):e.dispatchEvent(new CustomEvent(t,{bubbles:!0,cancelable:!0,...n}))}};export{e as createPointerEvent,t as fire};
|
|
2
|
+
//# sourceMappingURL=events.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.js","names":[],"sources":["../../src/testing/events.ts"],"sourcesContent":["/**\n * Synchronous DOM event dispatchers for test environments.\n */\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\nexport const createPointerEvent = (type: string, init: PointerEventInit = {}): Event => {\n if (typeof PointerEvent !== 'undefined') {\n return new PointerEvent(type, init);\n }\n\n return new MouseEvent(type, init);\n};\n\n// ─── API ─────────────────────────────────────────────────────────────────────\n\n/**\n * Fire low-level DOM events synchronously.\n *\n * @example\n * fire.click(button);\n * fire.keyDown(input, { key: 'Enter' });\n * fire.custom(el, 'value-change', { detail: 42 });\n */\nexport const fire = {\n blur: (el: Element, opts?: FocusEventInit) => el.dispatchEvent(new FocusEvent('blur', { bubbles: true, ...opts })),\n change: (el: Element, opts?: EventInit) => el.dispatchEvent(new Event('change', { bubbles: true, ...opts })),\n click: (el: Element, opts?: PointerEventInit) =>\n el.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true, ...opts })),\n /** Fire a CustomEvent. Pass `{ detail: ... }` for event detail. */\n custom(el: Element, name: string, opts?: CustomEventInit): void {\n el.dispatchEvent(new CustomEvent(name, { bubbles: true, cancelable: true, composed: false, ...opts }));\n },\n /** Dispatch any pre-built Event instance directly. */\n event(el: Element, event: Event): void {\n el.dispatchEvent(event);\n },\n focus: (el: Element, _name?: string, opts?: FocusEventInit) =>\n el.dispatchEvent(new FocusEvent('focus', { bubbles: true, ...opts })),\n input: (el: Element, opts?: EventInit) => el.dispatchEvent(new Event('input', { bubbles: true, ...opts })),\n /** Fire a KeyboardEvent with a given type (keydown, keyup, keypress). */\n keyboard: (el: Element, type: string, opts?: KeyboardEventInit) =>\n el.dispatchEvent(new KeyboardEvent(type, { bubbles: true, cancelable: true, ...opts })),\n keyDown: (el: Element, opts?: KeyboardEventInit) =>\n el.dispatchEvent(new KeyboardEvent('keydown', { bubbles: true, cancelable: true, ...opts })),\n keyUp: (el: Element, opts?: KeyboardEventInit) =>\n el.dispatchEvent(new KeyboardEvent('keyup', { bubbles: true, cancelable: true, ...opts })),\n /** Fire a MouseEvent with a given type (click, mousedown, etc.). */\n mouse: (el: Element, type: string, opts?: MouseEventInit) =>\n el.dispatchEvent(new MouseEvent(type, { bubbles: true, cancelable: true, ...opts })),\n pointerDown: (el: Element, opts?: PointerEventInit) =>\n el.dispatchEvent(createPointerEvent('pointerdown', { bubbles: true, cancelable: true, ...opts })),\n pointerEnter: (el: Element, opts?: PointerEventInit) =>\n el.dispatchEvent(createPointerEvent('pointerenter', { bubbles: false, ...opts })),\n pointerLeave: (el: Element, opts?: PointerEventInit) =>\n el.dispatchEvent(createPointerEvent('pointerleave', { bubbles: false, ...opts })),\n pointerUp: (el: Element, opts?: PointerEventInit) =>\n el.dispatchEvent(createPointerEvent('pointerup', { bubbles: true, cancelable: true, ...opts })),\n submit: (el: Element, opts?: EventInit) =>\n el.dispatchEvent(new Event('submit', { bubbles: true, cancelable: true, ...opts })),\n /** Fire a touch event; falls back to CustomEvent in environments without TouchEvent. */\n touch(el: Element, type: string, opts?: EventInit): void {\n if (typeof TouchEvent !== 'undefined') {\n el.dispatchEvent(new TouchEvent(type, { bubbles: true, cancelable: true, ...opts }));\n } else {\n el.dispatchEvent(new CustomEvent(type, { bubbles: true, cancelable: true, ...opts }));\n }\n },\n} as const;\n"],"mappings":"AAMA,IAAa,GAAsB,EAAc,EAAyB,CAAC,IACrE,OAAO,aAAiB,IACnB,IAAI,aAAa,EAAM,CAAI,EAG7B,IAAI,WAAW,EAAM,CAAI,EAarB,EAAO,CAClB,MAAO,EAAa,IAA0B,EAAG,cAAc,IAAI,WAAW,OAAQ,CAAE,QAAS,GAAM,GAAG,CAAK,CAAC,CAAC,EACjH,QAAS,EAAa,IAAqB,EAAG,cAAc,IAAI,MAAM,SAAU,CAAE,QAAS,GAAM,GAAG,CAAK,CAAC,CAAC,EAC3G,OAAQ,EAAa,IACnB,EAAG,cAAc,IAAI,WAAW,QAAS,CAAE,QAAS,GAAM,WAAY,GAAM,GAAG,CAAK,CAAC,CAAC,EAExF,OAAO,EAAa,EAAc,EAA8B,CAC9D,EAAG,cAAc,IAAI,YAAY,EAAM,CAAE,QAAS,GAAM,WAAY,GAAM,SAAU,GAAO,GAAG,CAAK,CAAC,CAAC,CACvG,EAEA,MAAM,EAAa,EAAoB,CACrC,EAAG,cAAc,CAAK,CACxB,EACA,OAAQ,EAAa,EAAgB,IACnC,EAAG,cAAc,IAAI,WAAW,QAAS,CAAE,QAAS,GAAM,GAAG,CAAK,CAAC,CAAC,EACtE,OAAQ,EAAa,IAAqB,EAAG,cAAc,IAAI,MAAM,QAAS,CAAE,QAAS,GAAM,GAAG,CAAK,CAAC,CAAC,EAEzG,UAAW,EAAa,EAAc,IACpC,EAAG,cAAc,IAAI,cAAc,EAAM,CAAE,QAAS,GAAM,WAAY,GAAM,GAAG,CAAK,CAAC,CAAC,EACxF,SAAU,EAAa,IACrB,EAAG,cAAc,IAAI,cAAc,UAAW,CAAE,QAAS,GAAM,WAAY,GAAM,GAAG,CAAK,CAAC,CAAC,EAC7F,OAAQ,EAAa,IACnB,EAAG,cAAc,IAAI,cAAc,QAAS,CAAE,QAAS,GAAM,WAAY,GAAM,GAAG,CAAK,CAAC,CAAC,EAE3F,OAAQ,EAAa,EAAc,IACjC,EAAG,cAAc,IAAI,WAAW,EAAM,CAAE,QAAS,GAAM,WAAY,GAAM,GAAG,CAAK,CAAC,CAAC,EACrF,aAAc,EAAa,IACzB,EAAG,cAAc,EAAmB,cAAe,CAAE,QAAS,GAAM,WAAY,GAAM,GAAG,CAAK,CAAC,CAAC,EAClG,cAAe,EAAa,IAC1B,EAAG,cAAc,EAAmB,eAAgB,CAAE,QAAS,GAAO,GAAG,CAAK,CAAC,CAAC,EAClF,cAAe,EAAa,IAC1B,EAAG,cAAc,EAAmB,eAAgB,CAAE,QAAS,GAAO,GAAG,CAAK,CAAC,CAAC,EAClF,WAAY,EAAa,IACvB,EAAG,cAAc,EAAmB,YAAa,CAAE,QAAS,GAAM,WAAY,GAAM,GAAG,CAAK,CAAC,CAAC,EAChG,QAAS,EAAa,IACpB,EAAG,cAAc,IAAI,MAAM,SAAU,CAAE,QAAS,GAAM,WAAY,GAAM,GAAG,CAAK,CAAC,CAAC,EAEpF,MAAM,EAAa,EAAc,EAAwB,CACnD,OAAO,WAAe,IACxB,EAAG,cAAc,IAAI,WAAW,EAAM,CAAE,QAAS,GAAM,WAAY,GAAM,GAAG,CAAK,CAAC,CAAC,EAEnF,EAAG,cAAc,IAAI,YAAY,EAAM,CAAE,QAAS,GAAM,WAAY,GAAM,GAAG,CAAK,CAAC,CAAC,CAExF,CACF"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var e={maxTurns:12};async function t(e={}){let{logger:t,maxTurns:n=5}=e,r=async(e,n)=>{for(let r=0;r<e;r++)t?.(`[flush] ${n} turn ${r+1}/${e}`),await Promise.resolve(),await new Promise(e=>queueMicrotask(e))};t?.(`[flush] starting with maxTurns=${n}`),await r(n,`microtask`),t?.(`[flush] draining requestAnimationFrame`),await new Promise(e=>typeof requestAnimationFrame<`u`?requestAnimationFrame(()=>e()):e()),t?.(`[flush] final microtask pass`),await r(2,`final`),t?.(`[flush] complete`)}exports.FLUSH_DEEP=e,exports.flush=t;
|
|
2
|
+
//# sourceMappingURL=flush.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flush.cjs","names":[],"sources":["../../src/testing/flush.ts"],"sourcesContent":["/**\n * Reactive update flushing utilities for test environments.\n */\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\n/**\n * Options for flushing pending reactive updates.\n */\nexport interface FlushOptions {\n /**\n * Maximum number of microtask drain turns.\n * Default: 5, sufficient for normal reactive propagation depth.\n * For deep async chains use `FLUSH_DEEP` (12 turns).\n */\n maxTurns?: number;\n /**\n * Logger function called with each diagnostic message during the flush.\n * Pass `console.debug` or any `(msg: string) => void` to enable output.\n * Omit (or pass `undefined`) to disable all logging.\n *\n * Prefer `debugFlush()` from `@vielzeug/ore/devtools` over wiring this manually.\n */\n logger?: (msg: string) => void;\n}\n\n/**\n * Pre-built flush options for deep async chains (async setup, nested effects).\n * Use when the default `flush()` isn't sufficient to drain all pending work.\n *\n * @example\n * ```ts\n * await flush(FLUSH_DEEP);\n * ```\n */\nexport const FLUSH_DEEP: FlushOptions = { maxTurns: 12 };\n\n// ─── Core ────────────────────────────────────────────────────────────────────\n\n/**\n * Flush pending reactive updates.\n * Drains several microtask turns, then yields one animation frame and\n * one final microtask pass for rAF-scheduled work.\n *\n * The default drain count (5) covers typical reactive propagation depth in\n * ore components: effect → queueMicrotask (onMounted) → nested reactive\n * updates. For deeper async chains, use `FLUSH_DEEP`.\n *\n * @example\n * ```ts\n * // Standard flush for most tests\n * await flush();\n *\n * // Shorter flush for simple updates\n * await flush({ maxTurns: 4 });\n *\n * // Debug timing issues\n * await flush({ logger: console.debug });\n * ```\n */\nexport async function flush(options: FlushOptions = {}): Promise<void> {\n const { logger, maxTurns = 5 } = options;\n\n const drainMicrotasks = async (turns: number, label: string): Promise<void> => {\n for (let i = 0; i < turns; i++) {\n logger?.(`[flush] ${label} turn ${i + 1}/${turns}`);\n\n await Promise.resolve();\n await new Promise<void>((resolve) => queueMicrotask(resolve));\n }\n };\n\n logger?.(`[flush] starting with maxTurns=${maxTurns}`);\n\n await drainMicrotasks(maxTurns, 'microtask');\n\n logger?.('[flush] draining requestAnimationFrame');\n\n await new Promise<void>((resolve) =>\n typeof requestAnimationFrame !== 'undefined' ? requestAnimationFrame(() => resolve()) : resolve(),\n );\n\n logger?.('[flush] final microtask pass');\n\n await drainMicrotasks(2, 'final');\n\n logger?.('[flush] complete');\n}\n"],"mappings":"AAmCA,IAAa,EAA2B,CAAE,SAAU,EAAG,EAyBvD,eAAsB,EAAM,EAAwB,CAAC,EAAkB,CACrE,GAAM,CAAE,SAAQ,WAAW,GAAM,EAE3B,EAAkB,MAAO,EAAe,IAAiC,CAC7E,IAAK,IAAI,EAAI,EAAG,EAAI,EAAO,IACzB,IAAS,WAAW,EAAM,QAAQ,EAAI,EAAE,GAAG,GAAO,EAElD,MAAM,QAAQ,QAAQ,EACtB,MAAM,IAAI,QAAe,GAAY,eAAe,CAAO,CAAC,CAEhE,EAEA,IAAS,kCAAkC,GAAU,EAErD,MAAM,EAAgB,EAAU,WAAW,EAE3C,IAAS,wCAAwC,EAEjD,MAAM,IAAI,QAAe,GACvB,OAAO,sBAA0B,IAAc,0BAA4B,EAAQ,CAAC,EAAI,EAAQ,CAClG,EAEA,IAAS,8BAA8B,EAEvC,MAAM,EAAgB,EAAG,OAAO,EAEhC,IAAS,kBAAkB,CAC7B"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reactive update flushing utilities for test environments.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Options for flushing pending reactive updates.
|
|
6
|
+
*/
|
|
7
|
+
export interface FlushOptions {
|
|
8
|
+
/**
|
|
9
|
+
* Maximum number of microtask drain turns.
|
|
10
|
+
* Default: 5, sufficient for normal reactive propagation depth.
|
|
11
|
+
* For deep async chains use `FLUSH_DEEP` (12 turns).
|
|
12
|
+
*/
|
|
13
|
+
maxTurns?: number;
|
|
14
|
+
/**
|
|
15
|
+
* Logger function called with each diagnostic message during the flush.
|
|
16
|
+
* Pass `console.debug` or any `(msg: string) => void` to enable output.
|
|
17
|
+
* Omit (or pass `undefined`) to disable all logging.
|
|
18
|
+
*
|
|
19
|
+
* Prefer `debugFlush()` from `@vielzeug/ore/devtools` over wiring this manually.
|
|
20
|
+
*/
|
|
21
|
+
logger?: (msg: string) => void;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Pre-built flush options for deep async chains (async setup, nested effects).
|
|
25
|
+
* Use when the default `flush()` isn't sufficient to drain all pending work.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```ts
|
|
29
|
+
* await flush(FLUSH_DEEP);
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export declare const FLUSH_DEEP: FlushOptions;
|
|
33
|
+
/**
|
|
34
|
+
* Flush pending reactive updates.
|
|
35
|
+
* Drains several microtask turns, then yields one animation frame and
|
|
36
|
+
* one final microtask pass for rAF-scheduled work.
|
|
37
|
+
*
|
|
38
|
+
* The default drain count (5) covers typical reactive propagation depth in
|
|
39
|
+
* ore components: effect → queueMicrotask (onMounted) → nested reactive
|
|
40
|
+
* updates. For deeper async chains, use `FLUSH_DEEP`.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```ts
|
|
44
|
+
* // Standard flush for most tests
|
|
45
|
+
* await flush();
|
|
46
|
+
*
|
|
47
|
+
* // Shorter flush for simple updates
|
|
48
|
+
* await flush({ maxTurns: 4 });
|
|
49
|
+
*
|
|
50
|
+
* // Debug timing issues
|
|
51
|
+
* await flush({ logger: console.debug });
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
export declare function flush(options?: FlushOptions): Promise<void>;
|
|
55
|
+
//# sourceMappingURL=flush.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flush.d.ts","sourceRoot":"","sources":["../../src/testing/flush.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAChC;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,UAAU,EAAE,YAA+B,CAAC;AAIzD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,KAAK,CAAC,OAAO,GAAE,YAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CA2BrE"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var e={maxTurns:12};async function t(e={}){let{logger:t,maxTurns:n=5}=e,r=async(e,n)=>{for(let r=0;r<e;r++)t?.(`[flush] ${n} turn ${r+1}/${e}`),await Promise.resolve(),await new Promise(e=>queueMicrotask(e))};t?.(`[flush] starting with maxTurns=${n}`),await r(n,`microtask`),t?.(`[flush] draining requestAnimationFrame`),await new Promise(e=>typeof requestAnimationFrame<`u`?requestAnimationFrame(()=>e()):e()),t?.(`[flush] final microtask pass`),await r(2,`final`),t?.(`[flush] complete`)}export{e as FLUSH_DEEP,t as flush};
|
|
2
|
+
//# sourceMappingURL=flush.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flush.js","names":[],"sources":["../../src/testing/flush.ts"],"sourcesContent":["/**\n * Reactive update flushing utilities for test environments.\n */\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\n/**\n * Options for flushing pending reactive updates.\n */\nexport interface FlushOptions {\n /**\n * Maximum number of microtask drain turns.\n * Default: 5, sufficient for normal reactive propagation depth.\n * For deep async chains use `FLUSH_DEEP` (12 turns).\n */\n maxTurns?: number;\n /**\n * Logger function called with each diagnostic message during the flush.\n * Pass `console.debug` or any `(msg: string) => void` to enable output.\n * Omit (or pass `undefined`) to disable all logging.\n *\n * Prefer `debugFlush()` from `@vielzeug/ore/devtools` over wiring this manually.\n */\n logger?: (msg: string) => void;\n}\n\n/**\n * Pre-built flush options for deep async chains (async setup, nested effects).\n * Use when the default `flush()` isn't sufficient to drain all pending work.\n *\n * @example\n * ```ts\n * await flush(FLUSH_DEEP);\n * ```\n */\nexport const FLUSH_DEEP: FlushOptions = { maxTurns: 12 };\n\n// ─── Core ────────────────────────────────────────────────────────────────────\n\n/**\n * Flush pending reactive updates.\n * Drains several microtask turns, then yields one animation frame and\n * one final microtask pass for rAF-scheduled work.\n *\n * The default drain count (5) covers typical reactive propagation depth in\n * ore components: effect → queueMicrotask (onMounted) → nested reactive\n * updates. For deeper async chains, use `FLUSH_DEEP`.\n *\n * @example\n * ```ts\n * // Standard flush for most tests\n * await flush();\n *\n * // Shorter flush for simple updates\n * await flush({ maxTurns: 4 });\n *\n * // Debug timing issues\n * await flush({ logger: console.debug });\n * ```\n */\nexport async function flush(options: FlushOptions = {}): Promise<void> {\n const { logger, maxTurns = 5 } = options;\n\n const drainMicrotasks = async (turns: number, label: string): Promise<void> => {\n for (let i = 0; i < turns; i++) {\n logger?.(`[flush] ${label} turn ${i + 1}/${turns}`);\n\n await Promise.resolve();\n await new Promise<void>((resolve) => queueMicrotask(resolve));\n }\n };\n\n logger?.(`[flush] starting with maxTurns=${maxTurns}`);\n\n await drainMicrotasks(maxTurns, 'microtask');\n\n logger?.('[flush] draining requestAnimationFrame');\n\n await new Promise<void>((resolve) =>\n typeof requestAnimationFrame !== 'undefined' ? requestAnimationFrame(() => resolve()) : resolve(),\n );\n\n logger?.('[flush] final microtask pass');\n\n await drainMicrotasks(2, 'final');\n\n logger?.('[flush] complete');\n}\n"],"mappings":"AAmCA,IAAa,EAA2B,CAAE,SAAU,EAAG,EAyBvD,eAAsB,EAAM,EAAwB,CAAC,EAAkB,CACrE,GAAM,CAAE,SAAQ,WAAW,GAAM,EAE3B,EAAkB,MAAO,EAAe,IAAiC,CAC7E,IAAK,IAAI,EAAI,EAAG,EAAI,EAAO,IACzB,IAAS,WAAW,EAAM,QAAQ,EAAI,EAAE,GAAG,GAAO,EAElD,MAAM,QAAQ,QAAQ,EACtB,MAAM,IAAI,QAAe,GAAY,eAAe,CAAO,CAAC,CAEhE,EAEA,IAAS,kCAAkC,GAAU,EAErD,MAAM,EAAgB,EAAU,WAAW,EAE3C,IAAS,wCAAwC,EAEjD,MAAM,IAAI,QAAe,GACvB,OAAO,sBAA0B,IAAc,0BAA4B,EAAQ,CAAC,EAAI,EAAQ,CAClG,EAEA,IAAS,8BAA8B,EAEvC,MAAM,EAAgB,EAAG,OAAO,EAEhC,IAAS,kBAAkB,CAC7B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC"}
|