@vielzeug/ore 1.0.5 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -3
- package/dist/_dev.cjs +1 -1
- package/dist/_dev.cjs.map +1 -1
- package/dist/_dev.js +1 -1
- package/dist/_dev.js.map +1 -1
- package/dist/aria.cjs +1 -1
- package/dist/aria.cjs.map +1 -1
- package/dist/aria.d.ts +16 -8
- package/dist/aria.d.ts.map +1 -1
- package/dist/aria.js +1 -1
- package/dist/aria.js.map +1 -1
- package/dist/base-element.cjs +1 -1
- package/dist/base-element.cjs.map +1 -1
- package/dist/base-element.d.ts +36 -1
- package/dist/base-element.d.ts.map +1 -1
- package/dist/base-element.js +1 -1
- package/dist/base-element.js.map +1 -1
- package/dist/component-types.d.ts +13 -76
- package/dist/component-types.d.ts.map +1 -1
- package/dist/context.cjs +1 -1
- package/dist/context.cjs.map +1 -1
- package/dist/context.d.ts +11 -2
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +1 -1
- package/dist/context.js.map +1 -1
- package/dist/define.cjs.map +1 -1
- package/dist/define.d.ts +13 -12
- package/dist/define.d.ts.map +1 -1
- package/dist/define.js.map +1 -1
- package/dist/directives/each.cjs +1 -1
- package/dist/directives/each.cjs.map +1 -1
- package/dist/directives/each.d.ts +6 -0
- package/dist/directives/each.d.ts.map +1 -1
- package/dist/directives/each.js +1 -1
- package/dist/directives/each.js.map +1 -1
- package/dist/directives/raw.cjs +1 -1
- package/dist/directives/raw.cjs.map +1 -1
- package/dist/directives/raw.d.ts.map +1 -1
- package/dist/directives/raw.js +1 -1
- package/dist/directives/raw.js.map +1 -1
- package/dist/directives/when.cjs +1 -1
- package/dist/directives/when.cjs.map +1 -1
- package/dist/directives/when.d.ts.map +1 -1
- package/dist/directives/when.js +1 -1
- package/dist/directives/when.js.map +1 -1
- package/dist/errors.cjs +2 -2
- package/dist/errors.cjs.map +1 -1
- package/dist/errors.d.ts +23 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +2 -2
- package/dist/errors.js.map +1 -1
- package/dist/forms/context.cjs +2 -0
- package/dist/forms/context.cjs.map +1 -0
- package/dist/{form-context.d.ts → forms/context.d.ts} +5 -5
- package/dist/forms/context.d.ts.map +1 -0
- package/dist/forms/context.js +2 -0
- package/dist/forms/context.js.map +1 -0
- package/dist/forms/field.cjs +2 -0
- package/dist/forms/field.cjs.map +1 -0
- package/dist/{form-field.d.ts → forms/field.d.ts} +19 -2
- package/dist/forms/field.d.ts.map +1 -0
- package/dist/forms/field.js +2 -0
- package/dist/forms/field.js.map +1 -0
- package/dist/forms/index.d.ts +11 -0
- package/dist/forms/index.d.ts.map +1 -0
- package/dist/forms.cjs +1 -0
- package/dist/forms.js +1 -0
- package/dist/host-bind.cjs +1 -1
- package/dist/host-bind.cjs.map +1 -1
- package/dist/host-bind.d.ts +8 -2
- package/dist/host-bind.d.ts.map +1 -1
- package/dist/host-bind.js +1 -1
- package/dist/host-bind.js.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +8 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/ore.cjs +2 -2
- package/dist/ore.cjs.map +1 -1
- package/dist/ore.iife.js +2 -2
- package/dist/ore.iife.js.map +1 -1
- package/dist/ore.js +2 -2
- package/dist/ore.js.map +1 -1
- package/dist/props.cjs +1 -1
- package/dist/props.cjs.map +1 -1
- package/dist/props.d.ts.map +1 -1
- package/dist/props.js +1 -1
- package/dist/props.js.map +1 -1
- package/dist/runtime.cjs +1 -1
- package/dist/runtime.cjs.map +1 -1
- package/dist/runtime.d.ts +32 -1
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +1 -1
- package/dist/runtime.js.map +1 -1
- package/dist/slots.cjs +1 -1
- package/dist/slots.cjs.map +1 -1
- package/dist/slots.d.ts +12 -1
- package/dist/slots.d.ts.map +1 -1
- package/dist/slots.js +1 -1
- package/dist/slots.js.map +1 -1
- package/dist/template/bindings.cjs +1 -1
- package/dist/template/bindings.cjs.map +1 -1
- package/dist/template/bindings.d.ts.map +1 -1
- package/dist/template/bindings.js +1 -1
- package/dist/template/bindings.js.map +1 -1
- package/dist/template/compiler.cjs +1 -1
- package/dist/template/compiler.cjs.map +1 -1
- package/dist/template/compiler.d.ts +11 -1
- package/dist/template/compiler.d.ts.map +1 -1
- package/dist/template/compiler.js +1 -1
- package/dist/template/compiler.js.map +1 -1
- package/dist/template/instantiator.cjs +1 -1
- package/dist/template/instantiator.cjs.map +1 -1
- package/dist/template/instantiator.d.ts +0 -5
- package/dist/template/instantiator.d.ts.map +1 -1
- package/dist/template/instantiator.js +1 -1
- package/dist/template/instantiator.js.map +1 -1
- package/dist/testing/dom.cjs +2 -0
- package/dist/testing/dom.cjs.map +1 -0
- package/dist/testing/dom.d.ts +22 -0
- package/dist/testing/dom.d.ts.map +1 -0
- package/dist/testing/dom.js +2 -0
- package/dist/testing/dom.js.map +1 -0
- package/dist/testing/form-internals-polyfill.cjs +2 -0
- package/dist/testing/form-internals-polyfill.cjs.map +1 -0
- package/dist/testing/form-internals-polyfill.d.ts +17 -0
- package/dist/testing/form-internals-polyfill.d.ts.map +1 -0
- package/dist/testing/form-internals-polyfill.js +2 -0
- package/dist/testing/form-internals-polyfill.js.map +1 -0
- package/dist/testing/mount.cjs.map +1 -1
- package/dist/testing/mount.d.ts +4 -4
- package/dist/testing/mount.d.ts.map +1 -1
- package/dist/testing/mount.js.map +1 -1
- package/dist/testing/render-hook.cjs +1 -1
- package/dist/testing/render-hook.cjs.map +1 -1
- package/dist/testing/render-hook.d.ts +6 -6
- package/dist/testing/render-hook.d.ts.map +1 -1
- package/dist/testing/render-hook.js +1 -1
- package/dist/testing/render-hook.js.map +1 -1
- package/dist/testing/testing.cjs +1 -1
- package/dist/testing/testing.cjs.map +1 -1
- package/dist/testing/testing.d.ts +7 -1
- package/dist/testing/testing.d.ts.map +1 -1
- package/dist/testing/testing.js +1 -1
- package/dist/testing/testing.js.map +1 -1
- package/dist/testing.cjs +1 -1
- package/dist/testing.js +1 -1
- package/dist/utils/emit.cjs +1 -1
- package/dist/utils/emit.cjs.map +1 -1
- package/dist/utils/emit.d.ts +28 -5
- package/dist/utils/emit.d.ts.map +1 -1
- package/dist/utils/emit.js +1 -1
- package/dist/utils/emit.js.map +1 -1
- package/package.json +12 -5
- package/dist/context-bag.cjs +0 -2
- package/dist/context-bag.cjs.map +0 -1
- package/dist/context-bag.d.ts +0 -12
- package/dist/context-bag.d.ts.map +0 -1
- package/dist/context-bag.js +0 -2
- package/dist/context-bag.js.map +0 -1
- package/dist/form-context.cjs +0 -2
- package/dist/form-context.cjs.map +0 -1
- package/dist/form-context.d.ts.map +0 -1
- package/dist/form-context.js +0 -2
- package/dist/form-context.js.map +0 -1
- package/dist/form-field.cjs +0 -2
- package/dist/form-field.cjs.map +0 -1
- package/dist/form-field.d.ts.map +0 -1
- package/dist/form-field.js +0 -2
- package/dist/form-field.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bindings.d.ts","sourceRoot":"","sources":["../../src/template/bindings.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;
|
|
1
|
+
{"version":3,"file":"bindings.d.ts","sourceRoot":"","sources":["../../src/template/bindings.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAOH,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;AAwDF,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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{invariant as e}from"../errors.js";import{isHtmlResult as t}from"../types/bindings.js";import{isStructuredValue as n,listen as r,removeNodes as i,runAll as a,setAttr as o}from"../utils/dom.js";import{isLiveSignal as s}from"../directives/live.js";import{getPropMeta as c}from"../props.js";import{computed as l,effect as u,isReactive as d,untrack as f}from"@vielzeug/ripple";var p=(e,t,n)=>{let r=u(()=>t(e.value));n(()=>r.dispose())},m=e=>e.value,h=(e,t)=>{p(e.signal,t=>{e.node.textContent=String(t??``)},t)},g=e=>e instanceof HTMLInputElement||e instanceof HTMLTextAreaElement||e instanceof HTMLSelectElement,_=(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))},v=(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))},y=(e,t,r,i)=>{let a=n(i)?i:t.parse(r.mode===`bool`?i?``:null:i==null||i===!1?null:String(i));if(Object.is(f(()=>t.signal.value),a)||(t.signal.value=a),!t.reflect){if(n(i))return;r.mode===`bool`?e.toggleAttribute(r.name,!!i):o(e,r.name,i)}},b=(e,t)=>{let{el:r,mode:i,name:a,propMeta:s}=e,c={last:void 0},l=t=>{if(s){y(r,s,e,t);return}if(!d(t)&&n(t)){a!==`__proto__`&&a!==`constructor`&&a!==`prototype`&&(r[a]=t);return}if(a===`value`&&g(r)){_(r,t,e.live,c);return}if(a===`checked`&&r instanceof HTMLInputElement){v(r,t,e.live,c);return}i===`bool`?r.toggleAttribute(a,!!t):o(r,a,t)};`signal`in e?p(e.signal,l,t):l(e.value)},x=(e,t)=>{t(r(e.el,e.name,e.handler,e.options))},S=(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})},C=(n,r,i)=>{let a=[],o=r.parentNode;e(o,`html binding anchor has no parent node`);for(let e of n)if(t(e)){let t=Array.from(e.fragment.childNodes);o.insertBefore(e.fragment,r),e.apply(i),a.push(...t)}else if(e!=null&&e!==!1){let t=document.createTextNode(String(e));o.insertBefore(t,r),a.push(t)}return a},w=(e,t)=>{let{anchor:n,signal:r}=e,o=[],s=()=>{a(o),o=[]},c=[],l=u(()=>{let e=m(r);s(),i(c),c=[],!(e==null||e.length===0)&&f(()=>{c=C(e,n,e=>o.push(e))})});t(()=>{l.dispose(),s(),i(c)})},T=(e,t)=>{e.directive.mount(e.anchor,t)},E=(e,t)=>{e.spread.apply(e.el,t)},D=(e,t)=>{switch(e.type){case`attr`:b(e,t);break;case`directive`:T(e,t);break;case`event`:x(e,t);break;case`html`:w(e,t);break;case`ref`:S(e,t);break;case`spread`:E(e,t);break;case`text`:h(e,t);break}},O=(e,t,n,r)=>{let i=c(e,n);return s(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:l(r),type:`attr`}:d(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}},k=e=>e==null?``:String(e);export{b as applyAttrBinding,D as applyBinding,w as applyHtmlBinding,O as createAttrBindingFromValue,k as resolveStaticText};
|
|
2
2
|
//# sourceMappingURL=bindings.js.map
|
|
@@ -1 +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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"}
|
|
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 { invariant } from '../errors';\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 invariant(parent, 'html binding anchor has no parent node');\n\n for (const v of values) {\n if (isHtmlResult(v)) {\n const captured = Array.from(v.fragment.childNodes);\n\n parent.insertBefore(v.fragment, insertBefore);\n v.apply(registerCleanup);\n nodes.push(...captured);\n } else if (v != null && v !== false) {\n const text = document.createTextNode(String(v));\n\n parent.insertBefore(text, insertBefore);\n nodes.push(text);\n }\n }\n\n return nodes;\n};\n\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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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var e=/\s+@([a-zA-Z_][-a-zA-Z0-9_.-]*)\s*=\s*["']?$/,
|
|
1
|
+
var e={ATTR:`attr`,BOOL_ATTR:`boolAttr`,CLOSE_TAG:`closeTag`,EVENT:`event`,NODE:`node`,REF:`ref`,SPREAD:`spread`,TAG_NAME:`tagname`},t=/\s+@([a-zA-Z_][-a-zA-Z0-9_.-]*)\s*=\s*["']?$/,n=/\s+ref\s*=\s*["']?$/,r=/\s+\?([a-zA-Z_][-a-zA-Z0-9_]*)\s*=\s*["']?$/,i=/\s+:?([a-zA-Z_][-a-zA-Z0-9_]*)\s*=\s*["']?$/,a=e=>{let t=e.lastIndexOf(`<`);return t<=e.lastIndexOf(`>`)?!1:e[t+1]!==`/`},o=o=>{let s,c=o.trimEnd();if(c.endsWith(`</`))return{kind:e.CLOSE_TAG,prefix:o};if(c.endsWith(`<`))return{kind:e.TAG_NAME,prefix:o};if(s=t.exec(o)){let t=o.slice(0,-s[0].length),n=s[1].split(`.`);return{kind:e.EVENT,modifiers:n.slice(1),name:n[0],prefix:t}}return(s=n.exec(o))?{kind:e.REF,prefix:o.slice(0,-s[0].length)}:(s=r.exec(o))?{kind:e.BOOL_ATTR,name:s[1],prefix:o.slice(0,-s[0].length)}:(s=i.exec(o))?{kind:e.ATTR,name:s[1],prefix:o.slice(0,-s[0].length)}:a(o)?{kind:e.SPREAD,prefix:o.trimEnd()}:{kind:e.NODE,prefix:o}},s=new WeakMap,c=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},l=(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++)l(i[e],[...t,e],n,r)},u=t=>{let n=c(t),r=``,i,a=0,s=0,u=[],d=[];for(let t=0;t<n.length-1;t++){let c=n[t],l=o(c);if(l.kind===e.TAG_NAME){let t=a++;i=t,d.push(t);let n=c.replace(/<\s*$/,``);r+=n+`<ore-dyn-${t} u="${t}"`,u.push({elementId:t,kind:e.TAG_NAME})}else if(l.kind===e.CLOSE_TAG){let t=d.pop()??0,n=c.replace(/<\/\s*$/,``);r+=n+`</ore-dyn-${t}>`,u.push({kind:e.CLOSE_TAG}),i=void 0}else if(l.kind===e.NODE)r+=l.prefix+`<!--${s}-->`,u.push({commentId:s,kind:e.NODE}),s++,i=void 0;else{i===void 0||l.prefix.lastIndexOf(`<`)>l.prefix.lastIndexOf(`>`)?(i=a++,r+=`${l.prefix} u="${i}"`):r+=l.prefix;let t=l.kind===e.BOOL_ATTR?`bool`:l.kind===e.ATTR?`attr`:void 0;u.push({elementId:i,kind:l.kind,mode:t,modifiers:l.modifiers,name:l.name})}}r+=n[n.length-1]??``;let f=document.createElement(`template`);f.innerHTML=r;let p=new Map,m=new Map,h=f.content.childNodes;for(let e=0;e<h.length;e++)l(h[e],[e],p,m);return{commentPaths:m,element:f,elementPaths:p,slots:u}},d=e=>{let t=s.get(e);return t||(t=u(e),s.set(e,t)),t},f=(e,t)=>{let n=e;for(let e of t)n=n.childNodes[e];return n};exports.SlotKind=e,exports.followPath=f,exports.getStaticTemplate=d;
|
|
2
2
|
//# sourceMappingURL=compiler.cjs.map
|
|
@@ -1 +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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"}
|
|
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// Const object + derived union, same pattern as `ComponentPhase`/`LIFECYCLE_EVENTS`\n// in types.ts — used here (rather than plain string literals) because the kind\n// crosses a module boundary (compiler.ts produces it, instantiator.ts consumes\n// it): importing `SlotKind` gives autocomplete and a single rename point at the\n// consuming site, where a bare string literal wouldn't.\n\nexport const SlotKind = {\n ATTR: 'attr',\n BOOL_ATTR: 'boolAttr',\n CLOSE_TAG: 'closeTag',\n EVENT: 'event',\n NODE: 'node',\n REF: 'ref',\n SPREAD: 'spread',\n TAG_NAME: 'tagname',\n} as const;\n\nexport type DetectedSlotKind = (typeof SlotKind)[keyof typeof SlotKind];\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: SlotKind.CLOSE_TAG, 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: SlotKind.TAG_NAME, 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: SlotKind.EVENT, modifiers: parts.slice(1), name: parts[0], prefix };\n }\n\n if ((m = REF_RE.exec(str))) {\n return { kind: SlotKind.REF, prefix: str.slice(0, -m[0].length) };\n }\n\n if ((m = BOOL_ATTR_RE.exec(str))) {\n return { kind: SlotKind.BOOL_ATTR, name: m[1], prefix: str.slice(0, -m[0].length) };\n }\n\n if ((m = ATTR_RE.exec(str))) {\n return { kind: SlotKind.ATTR, name: m[1], prefix: str.slice(0, -m[0].length) };\n }\n\n if (isInsideStartTag(str)) {\n return { kind: SlotKind.SPREAD, prefix: str.trimEnd() };\n }\n\n return { kind: SlotKind.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 === SlotKind.TAG_NAME) {\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: SlotKind.TAG_NAME });\n } else if (slot.kind === SlotKind.CLOSE_TAG) {\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: SlotKind.CLOSE_TAG });\n activeElementId = undefined;\n } else if (slot.kind === SlotKind.NODE) {\n html += slot.prefix + `<!--${commentCounter}-->`;\n slots.push({ commentId: commentCounter, kind: SlotKind.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 === SlotKind.BOOL_ATTR ? 'bool' : slot.kind === SlotKind.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":"AAgBA,IAAa,EAAW,CACtB,KAAM,OACN,UAAW,WACX,UAAW,WACX,MAAO,QACP,KAAM,OACN,IAAK,MACL,OAAQ,SACR,SAAU,SACZ,EAiCM,EAAW,+CACX,EAAS,sBACT,EAAe,8CACf,EAAU,8CAEV,EAAoB,GAAyB,CACjD,IAAM,EAAW,EAAI,YAAY,GAAG,EAMpC,OAHI,GAFc,EAAI,YAAY,GAElB,EAAkB,GAG3B,EAAI,EAAW,KAAO,GAC/B,EAEM,EAAc,GAA8B,CAChD,IAAI,EACE,EAAU,EAAI,QAAQ,EAG5B,GAAI,EAAQ,SAAS,IAAI,EACvB,MAAO,CAAE,KAAM,EAAS,UAAW,OAAQ,CAAI,EAIjD,GAAI,EAAQ,SAAS,GAAG,EACtB,MAAO,CAAE,KAAM,EAAS,SAAU,OAAQ,CAAI,EAGhD,GAAK,EAAI,EAAS,KAAK,CAAG,EAAI,CAC5B,IAAM,EAAS,EAAI,MAAM,EAAG,CAAC,EAAE,EAAE,CAAC,MAAM,EAClC,EAAQ,EAAE,EAAE,CAAC,MAAM,GAAG,EAE5B,MAAO,CAAE,KAAM,EAAS,MAAO,UAAW,EAAM,MAAM,CAAC,EAAG,KAAM,EAAM,GAAI,QAAO,CACnF,CAkBA,OAhBK,EAAI,EAAO,KAAK,CAAG,GACf,CAAE,KAAM,EAAS,IAAK,OAAQ,EAAI,MAAM,EAAG,CAAC,EAAE,EAAE,CAAC,MAAM,CAAE,GAG7D,EAAI,EAAa,KAAK,CAAG,GACrB,CAAE,KAAM,EAAS,UAAW,KAAM,EAAE,GAAI,OAAQ,EAAI,MAAM,EAAG,CAAC,EAAE,EAAE,CAAC,MAAM,CAAE,GAG/E,EAAI,EAAQ,KAAK,CAAG,GAChB,CAAE,KAAM,EAAS,KAAM,KAAM,EAAE,GAAI,OAAQ,EAAI,MAAM,EAAG,CAAC,EAAE,EAAE,CAAC,MAAM,CAAE,EAG3E,EAAiB,CAAG,EACf,CAAE,KAAM,EAAS,OAAQ,OAAQ,EAAI,QAAQ,CAAE,EAGjD,CAAE,KAAM,EAAS,KAAM,OAAQ,CAAI,CAC5C,EAIM,EAAgB,IAAI,QAOpB,EAA4B,GAA4C,CAC5E,IAAM,EAAM,MAAM,KAAK,CAAO,EAE9B,IAAK,IAAI,EAAI,EAAG,EAAI,EAAI,OAAS,EAAG,IAAK,CACvC,IAAM,EAAI,EAAI,GACR,EAAW,EAAE,EAAE,OAAS,GAG9B,GAAI,IAAa,KAAO,IAAa,IAAK,CACxC,EAAI,GAAK,EAAE,MAAM,EAAG,EAAE,EAEtB,IAAM,EAAO,EAAI,EAAI,GAEjB,EAAK,WAAW,CAAQ,IAAG,EAAI,EAAI,GAAK,EAAK,MAAM,CAAC,EAC1D,CAMA,GAFY,EAAI,EAEZ,CAAI,QAAQ,CAAC,CAAC,SAAS,IAAI,EAAG,CAChC,IAAM,EAAO,EAAI,EAAI,GAEjB,EAAK,WAAW,GAAG,IAAG,EAAI,EAAI,GAAK,EAAK,MAAM,CAAC,EACrD,CACF,CAEA,OAAO,CACT,EAEM,GACJ,EACA,EACA,EACA,IACS,CACT,GAAI,EAAK,WAAa,KAAK,aAAc,CACvC,IAAM,EAAK,EACL,EAAS,EAAG,aAAa,GAAG,EAE9B,IAAW,OACb,EAAa,IAAI,OAAO,CAAM,EAAG,CAAC,GAAG,CAAI,CAAC,EAC1C,EAAG,gBAAgB,GAAG,EAE1B,MAAO,GAAI,EAAK,WAAa,KAAK,aAAc,CAC9C,IAAM,EAAW,EAAiB,UAE9B,IAAY,MAAQ,QAAQ,KAAK,CAAO,GAC1C,EAAa,IAAI,OAAO,CAAO,EAAG,CAAC,GAAG,CAAI,CAAC,CAE/C,CAEA,IAAM,EAAW,EAAK,WAEtB,IAAK,IAAI,EAAI,EAAG,EAAI,EAAS,OAAQ,IAAK,EAAS,EAAS,GAAI,CAAC,GAAG,EAAM,CAAC,EAAG,EAAc,CAAY,CAC1G,EAEM,EAAuB,GAA0D,CACrF,IAAM,EAAa,EAAyB,CAAO,EAC/C,EAAO,GACP,EACA,EAAiB,EACjB,EAAiB,EACf,EAAoB,CAAC,EACrB,EAAyB,CAAC,EAEhC,IAAK,IAAI,EAAI,EAAG,EAAI,EAAW,OAAS,EAAG,IAAK,CAC9C,IAAM,EAAM,EAAW,GACjB,EAAO,EAAW,CAAG,EAE3B,GAAI,EAAK,OAAS,EAAS,SAAU,CAEnC,IAAM,EAAK,IAEX,EAAkB,EAClB,EAAa,KAAK,CAAE,EAGpB,IAAM,EAAqB,EAAI,QAAQ,QAAS,EAAE,EAElD,GAAQ,EAAqB,YAAY,EAAG,MAAM,EAAG,GACrD,EAAM,KAAK,CAAE,UAAW,EAAI,KAAM,EAAS,QAAS,CAAC,CACvD,MAAO,GAAI,EAAK,OAAS,EAAS,UAAW,CAE3C,IAAM,EAAK,EAAa,IAAI,GAAK,EAC3B,EAAqB,EAAI,QAAQ,UAAW,EAAE,EAEpD,GAAQ,EAAqB,aAAa,EAAG,GAC7C,EAAM,KAAK,CAAE,KAAM,EAAS,SAAU,CAAC,EACvC,EAAkB,IAAA,EACpB,MAAO,GAAI,EAAK,OAAS,EAAS,KAChC,GAAQ,EAAK,OAAS,OAAO,EAAe,KAC5C,EAAM,KAAK,CAAE,UAAW,EAAgB,KAAM,EAAS,IAAK,CAAC,EAC7D,IACA,EAAkB,IAAA,OACb,CAEH,IAAoB,IAAA,IAAa,EAAK,OAAO,YAAY,GAAG,EAAI,EAAK,OAAO,YAAY,GAAG,GAG3F,EAAkB,IAClB,GAAQ,GAAG,EAAK,OAAO,MAAM,EAAgB,IAE7C,GAAQ,EAAK,OAGf,IAAM,EACJ,EAAK,OAAS,EAAS,UAAY,OAAS,EAAK,OAAS,EAAS,KAAO,OAAS,IAAA,GAErF,EAAM,KAAK,CAAE,UAAW,EAAiB,KAAM,EAAK,KAAM,OAAM,UAAW,EAAK,UAAW,KAAM,EAAK,IAAK,CAAC,CAC9G,CACF,CAEA,GAAQ,EAAW,EAAW,OAAS,IAAM,GAE7C,IAAM,EAAM,SAAS,cAAc,UAAU,EAE7C,EAAI,UAAY,EAEhB,IAAM,EAAe,IAAI,IACnB,EAAe,IAAI,IACnB,EAAc,EAAI,QAAQ,WAEhC,IAAK,IAAI,EAAI,EAAG,EAAI,EAAY,OAAQ,IAAK,EAAS,EAAY,GAAI,CAAC,CAAC,EAAG,EAAc,CAAY,EAErG,MAAO,CAAE,eAAc,QAAS,EAAK,eAAc,OAAM,CAC3D,EAEa,EAAqB,GAA0D,CAC1F,IAAI,EAAM,EAAc,IAAI,CAAO,EAOnC,OALK,IACH,EAAM,EAAoB,CAAO,EACjC,EAAc,IAAI,EAAS,CAAG,GAGzB,CACT,EAIa,GAAc,EAAY,IAAyB,CAC9D,IAAI,EAAa,EAEjB,IAAK,IAAM,KAAK,EAAM,EAAO,EAAK,WAAW,GAE7C,OAAO,CACT"}
|
|
@@ -6,7 +6,17 @@
|
|
|
6
6
|
* - Build a cached HTMLTemplateElement with path indices for efficient node lookup.
|
|
7
7
|
* - Expose `getStaticTemplate()` for use by the instantiator.
|
|
8
8
|
*/
|
|
9
|
-
export
|
|
9
|
+
export declare const SlotKind: {
|
|
10
|
+
readonly ATTR: "attr";
|
|
11
|
+
readonly BOOL_ATTR: "boolAttr";
|
|
12
|
+
readonly CLOSE_TAG: "closeTag";
|
|
13
|
+
readonly EVENT: "event";
|
|
14
|
+
readonly NODE: "node";
|
|
15
|
+
readonly REF: "ref";
|
|
16
|
+
readonly SPREAD: "spread";
|
|
17
|
+
readonly TAG_NAME: "tagname";
|
|
18
|
+
};
|
|
19
|
+
export type DetectedSlotKind = (typeof SlotKind)[keyof typeof SlotKind];
|
|
10
20
|
export type NodePath = readonly number[];
|
|
11
21
|
export type SlotMeta = {
|
|
12
22
|
commentId?: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compiler.d.ts","sourceRoot":"","sources":["../../src/template/compiler.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;
|
|
1
|
+
{"version":3,"file":"compiler.d.ts","sourceRoot":"","sources":["../../src/template/compiler.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AASH,eAAO,MAAM,QAAQ;;;;;;;;;CASX,CAAC;AAEX,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,QAAQ,CAAC,CAAC,MAAM,OAAO,QAAQ,CAAC,CAAC;AAWxE,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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var e=/\s+@([a-zA-Z_][-a-zA-Z0-9_.-]*)\s*=\s*["']?$/,
|
|
1
|
+
var e={ATTR:`attr`,BOOL_ATTR:`boolAttr`,CLOSE_TAG:`closeTag`,EVENT:`event`,NODE:`node`,REF:`ref`,SPREAD:`spread`,TAG_NAME:`tagname`},t=/\s+@([a-zA-Z_][-a-zA-Z0-9_.-]*)\s*=\s*["']?$/,n=/\s+ref\s*=\s*["']?$/,r=/\s+\?([a-zA-Z_][-a-zA-Z0-9_]*)\s*=\s*["']?$/,i=/\s+:?([a-zA-Z_][-a-zA-Z0-9_]*)\s*=\s*["']?$/,a=e=>{let t=e.lastIndexOf(`<`);return t<=e.lastIndexOf(`>`)?!1:e[t+1]!==`/`},o=o=>{let s,c=o.trimEnd();if(c.endsWith(`</`))return{kind:e.CLOSE_TAG,prefix:o};if(c.endsWith(`<`))return{kind:e.TAG_NAME,prefix:o};if(s=t.exec(o)){let t=o.slice(0,-s[0].length),n=s[1].split(`.`);return{kind:e.EVENT,modifiers:n.slice(1),name:n[0],prefix:t}}return(s=n.exec(o))?{kind:e.REF,prefix:o.slice(0,-s[0].length)}:(s=r.exec(o))?{kind:e.BOOL_ATTR,name:s[1],prefix:o.slice(0,-s[0].length)}:(s=i.exec(o))?{kind:e.ATTR,name:s[1],prefix:o.slice(0,-s[0].length)}:a(o)?{kind:e.SPREAD,prefix:o.trimEnd()}:{kind:e.NODE,prefix:o}},s=new WeakMap,c=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},l=(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++)l(i[e],[...t,e],n,r)},u=t=>{let n=c(t),r=``,i,a=0,s=0,u=[],d=[];for(let t=0;t<n.length-1;t++){let c=n[t],l=o(c);if(l.kind===e.TAG_NAME){let t=a++;i=t,d.push(t);let n=c.replace(/<\s*$/,``);r+=n+`<ore-dyn-${t} u="${t}"`,u.push({elementId:t,kind:e.TAG_NAME})}else if(l.kind===e.CLOSE_TAG){let t=d.pop()??0,n=c.replace(/<\/\s*$/,``);r+=n+`</ore-dyn-${t}>`,u.push({kind:e.CLOSE_TAG}),i=void 0}else if(l.kind===e.NODE)r+=l.prefix+`<!--${s}-->`,u.push({commentId:s,kind:e.NODE}),s++,i=void 0;else{i===void 0||l.prefix.lastIndexOf(`<`)>l.prefix.lastIndexOf(`>`)?(i=a++,r+=`${l.prefix} u="${i}"`):r+=l.prefix;let t=l.kind===e.BOOL_ATTR?`bool`:l.kind===e.ATTR?`attr`:void 0;u.push({elementId:i,kind:l.kind,mode:t,modifiers:l.modifiers,name:l.name})}}r+=n[n.length-1]??``;let f=document.createElement(`template`);f.innerHTML=r;let p=new Map,m=new Map,h=f.content.childNodes;for(let e=0;e<h.length;e++)l(h[e],[e],p,m);return{commentPaths:m,element:f,elementPaths:p,slots:u}},d=e=>{let t=s.get(e);return t||(t=u(e),s.set(e,t)),t},f=(e,t)=>{let n=e;for(let e of t)n=n.childNodes[e];return n};export{e as SlotKind,f as followPath,d as getStaticTemplate};
|
|
2
2
|
//# sourceMappingURL=compiler.js.map
|
|
@@ -1 +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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"}
|
|
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// Const object + derived union, same pattern as `ComponentPhase`/`LIFECYCLE_EVENTS`\n// in types.ts — used here (rather than plain string literals) because the kind\n// crosses a module boundary (compiler.ts produces it, instantiator.ts consumes\n// it): importing `SlotKind` gives autocomplete and a single rename point at the\n// consuming site, where a bare string literal wouldn't.\n\nexport const SlotKind = {\n ATTR: 'attr',\n BOOL_ATTR: 'boolAttr',\n CLOSE_TAG: 'closeTag',\n EVENT: 'event',\n NODE: 'node',\n REF: 'ref',\n SPREAD: 'spread',\n TAG_NAME: 'tagname',\n} as const;\n\nexport type DetectedSlotKind = (typeof SlotKind)[keyof typeof SlotKind];\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: SlotKind.CLOSE_TAG, 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: SlotKind.TAG_NAME, 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: SlotKind.EVENT, modifiers: parts.slice(1), name: parts[0], prefix };\n }\n\n if ((m = REF_RE.exec(str))) {\n return { kind: SlotKind.REF, prefix: str.slice(0, -m[0].length) };\n }\n\n if ((m = BOOL_ATTR_RE.exec(str))) {\n return { kind: SlotKind.BOOL_ATTR, name: m[1], prefix: str.slice(0, -m[0].length) };\n }\n\n if ((m = ATTR_RE.exec(str))) {\n return { kind: SlotKind.ATTR, name: m[1], prefix: str.slice(0, -m[0].length) };\n }\n\n if (isInsideStartTag(str)) {\n return { kind: SlotKind.SPREAD, prefix: str.trimEnd() };\n }\n\n return { kind: SlotKind.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 === SlotKind.TAG_NAME) {\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: SlotKind.TAG_NAME });\n } else if (slot.kind === SlotKind.CLOSE_TAG) {\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: SlotKind.CLOSE_TAG });\n activeElementId = undefined;\n } else if (slot.kind === SlotKind.NODE) {\n html += slot.prefix + `<!--${commentCounter}-->`;\n slots.push({ commentId: commentCounter, kind: SlotKind.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 === SlotKind.BOOL_ATTR ? 'bool' : slot.kind === SlotKind.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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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("../
|
|
1
|
+
const e=require("../errors.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="html`...`: node-slot comment anchor has no parent node",s=(s,c)=>{let l=i.getStaticTemplate(s),u=l.element.content.cloneNode(!0),d=[],f=[],p=l.slots.map((t,n)=>{let r=c[n];if(t.kind===i.SlotKind.CLOSE_TAG)return{slot:t,value:r};if(t.kind===i.SlotKind.NODE){let n=l.commentPaths.get(t.commentId);return e.invariant(n,`compiled template is missing a comment path for node slot ${t.commentId}`),{comment:i.followPath(u,n),slot:t,value:r}}let a=l.elementPaths.get(t.elementId);return e.invariant(a,`compiled template is missing an element path for slot ${t.elementId}`),{el:i.followPath(u,a),slot:t,value:r}}),m=new Map;for(let{el:t,slot:n,value:r}of p){if(n.kind!==i.SlotKind.TAG_NAME)continue;let a=String(r);if(!/^[a-z][a-z0-9._-]*$/i.test(a))throw new e.OreApiError(e.ORE_ERRORS.invalidDynamicTagName(a));let o=document.createElement(a);for(let e of Array.from(t.attributes))o.setAttribute(e.name,e.value);for(;t.firstChild;)o.appendChild(t.firstChild);t.replaceWith(o),m.set(t,o)}for(let{comment:s,el:c,slot:l,value:u}of p){if(l.kind===i.SlotKind.TAG_NAME||l.kind===i.SlotKind.CLOSE_TAG)continue;let p=c&&(m.get(c)??c);if(l.kind===i.SlotKind.NODE){let r=s;if(t.isDirectiveResult(u)){d.push({anchor:r,directive:u,type:`directive`});continue}if(t.isHtmlResult(u)){let t=r.parentNode;for(e.invariant(t,o);u.fragment.firstChild;)t.insertBefore(u.fragment.firstChild,r);r.remove(),f.push(u.apply.bind(u));continue}if(typeof u==`function`){let e=(0,a.computed)(()=>{let e=u();return Array.isArray(e)?e:[e]});d.push({anchor:r,signal:e,type:`html`});continue}if((0,a.isReactive)(u)){let e=(0,a.computed)(()=>{let e=u.value;return Array.isArray(e)?e:[e]});d.push({anchor:r,signal:e,type:`html`});continue}if(Array.isArray(u)){let i=r.parentNode;e.invariant(i,o);for(let e of u)if(t.isHtmlResult(e)){for(;e.fragment.firstChild;)i.insertBefore(e.fragment.firstChild,r);f.push(e.apply.bind(e))}else i.insertBefore(document.createTextNode(n.resolveStaticText(e)),r);r.remove();continue}r.replaceWith(document.createTextNode(n.resolveStaticText(u)));continue}let h=p;if(l.kind===i.SlotKind.EVENT){if(typeof u==`function`){let{handler:e,options:t}=r.applyModifiers(u,l.modifiers??[]);d.push({el:h,handler:e,name:l.name,options:t,type:`event`})}else if((0,a.isReactive)(u)){let e=u,{handler:t,options:n}=r.applyModifiers(t=>{let n=e.value;typeof n==`function`&&n(t)},l.modifiers??[]);d.push({el:h,handler:t,name:l.name,options:n,type:`event`})}continue}if(l.kind===i.SlotKind.REF){u&&d.push({el:h,ref:u,type:`ref`});continue}if(l.kind===i.SlotKind.SPREAD){t.isSpreadObject(u)&&d.push({el:h,spread:u,type:`spread`});continue}d.push(n.createAttrBindingFromValue(h,l.mode??`attr`,l.name,u))}return t.createHtmlResult(u,e=>{for(let t of d)n.applyBinding(t,e);for(let t of f)t(e)})},c=(e,...t)=>s(e,t);exports.compileTemplate=s,exports.html=c;
|
|
2
2
|
//# sourceMappingURL=instantiator.cjs.map
|
|
@@ -1 +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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"}
|
|
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 { invariant, ORE_ERRORS, OreApiError } from '../errors';\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, SlotKind } 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 */\nconst NODE_SLOT_NO_PARENT_MSG = 'html`...`: node-slot comment anchor has no parent node';\n\nexport const compileTemplate = (strings: TemplateStringsArray, values: unknown[]): HTMLResult => {\n const compiled = getStaticTemplate(strings);\n const fragment = compiled.element.content.cloneNode(true) as DocumentFragment;\n const bindings: Binding[] = [];\n // For static HTMLResult embeds: chain their apply calls\n const chainedApplies: Array<(rc: (fn: () => void) => void) => void> = [];\n\n // Phase 1: Resolve all binding targets BEFORE any DOM modifications\n type BoundSlot = { comment?: Comment; el?: HTMLElement; slot: (typeof compiled.slots)[number]; value: unknown };\n\n const boundSlots: BoundSlot[] = compiled.slots.map((slot, i) => {\n const value = values[i];\n\n if (slot.kind === SlotKind.CLOSE_TAG) {\n return { slot, value };\n }\n\n if (slot.kind === SlotKind.NODE) {\n const commentPath = compiled.commentPaths.get(slot.commentId!);\n\n invariant(commentPath, `compiled template is missing a comment path for node slot ${slot.commentId}`);\n\n return { comment: followPath(fragment, commentPath) as Comment, slot, value };\n }\n\n const elementPath = compiled.elementPaths.get(slot.elementId!);\n\n invariant(elementPath, `compiled template is missing an element path for slot ${slot.elementId}`);\n\n return { el: followPath(fragment, elementPath) as HTMLElement, slot, value };\n });\n\n // Phase 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 !== SlotKind.TAG_NAME) continue;\n\n const tagName = String(value);\n\n // Same \"fail immediately, every build\" treatment as each()'s duplicate-key\n // guard: a malformed dynamic tag name is a programming bug in the template,\n // not a runtime race — degrading to a silently-broken partial render would\n // hide it instead of surfacing it at the call site.\n if (!/^[a-z][a-z0-9._-]*$/i.test(tagName)) {\n throw new OreApiError(ORE_ERRORS.invalidDynamicTagName(tagName));\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 === SlotKind.TAG_NAME || slot.kind === SlotKind.CLOSE_TAG) continue;\n\n // Resolve element through tagname replacements\n const el = rawEl ? (tagReplacements.get(rawEl) ?? rawEl) : rawEl;\n\n if (slot.kind === SlotKind.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 invariant(parent, NODE_SLOT_NO_PARENT_MSG);\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 invariant(parent, NODE_SLOT_NO_PARENT_MSG);\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 === SlotKind.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 === SlotKind.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 === SlotKind.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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"}
|
|
@@ -6,11 +6,6 @@
|
|
|
6
6
|
* - Expose `compileTemplate()` and `html` as the public authoring API.
|
|
7
7
|
*/
|
|
8
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
9
|
export declare const compileTemplate: (strings: TemplateStringsArray, values: unknown[]) => HTMLResult;
|
|
15
10
|
export declare const html: (strings: TemplateStringsArray, ...values: unknown[]) => HTMLResult;
|
|
16
11
|
//# sourceMappingURL=instantiator.d.ts.map
|
|
@@ -1 +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;
|
|
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;AAc3B,eAAO,MAAM,eAAe,GAAI,SAAS,oBAAoB,EAAE,QAAQ,OAAO,EAAE,KAAG,UAqLlF,CAAC;AAEF,eAAO,MAAM,IAAI,GAAI,SAAS,oBAAoB,EAAE,GAAG,QAAQ,OAAO,EAAE,KAAG,UACzC,CAAC"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{ORE_ERRORS as e,OreApiError as t,invariant as n}from"../errors.js";import{createHtmlResult as r,isDirectiveResult as i,isHtmlResult as a,isSpreadObject as o}from"../types/bindings.js";import{applyBinding as s,createAttrBindingFromValue as c,resolveStaticText as l}from"./bindings.js";import{applyModifiers as u}from"../utils/event-modifiers.js";import{SlotKind as d,followPath as f,getStaticTemplate as p}from"./compiler.js";import{computed as m,isReactive as h}from"@vielzeug/ripple";var g="html`...`: node-slot comment anchor has no parent node",_=(_,v)=>{let y=p(_),b=y.element.content.cloneNode(!0),x=[],S=[],C=y.slots.map((e,t)=>{let r=v[t];if(e.kind===d.CLOSE_TAG)return{slot:e,value:r};if(e.kind===d.NODE){let t=y.commentPaths.get(e.commentId);return n(t,`compiled template is missing a comment path for node slot ${e.commentId}`),{comment:f(b,t),slot:e,value:r}}let i=y.elementPaths.get(e.elementId);return n(i,`compiled template is missing an element path for slot ${e.elementId}`),{el:f(b,i),slot:e,value:r}}),w=new Map;for(let{el:n,slot:r,value:i}of C){if(r.kind!==d.TAG_NAME)continue;let a=String(i);if(!/^[a-z][a-z0-9._-]*$/i.test(a))throw new t(e.invalidDynamicTagName(a));let o=document.createElement(a);for(let e of Array.from(n.attributes))o.setAttribute(e.name,e.value);for(;n.firstChild;)o.appendChild(n.firstChild);n.replaceWith(o),w.set(n,o)}for(let{comment:e,el:t,slot:r,value:s}of C){if(r.kind===d.TAG_NAME||r.kind===d.CLOSE_TAG)continue;let f=t&&(w.get(t)??t);if(r.kind===d.NODE){let t=e;if(i(s)){x.push({anchor:t,directive:s,type:`directive`});continue}if(a(s)){let e=t.parentNode;for(n(e,g);s.fragment.firstChild;)e.insertBefore(s.fragment.firstChild,t);t.remove(),S.push(s.apply.bind(s));continue}if(typeof s==`function`){let e=m(()=>{let e=s();return Array.isArray(e)?e:[e]});x.push({anchor:t,signal:e,type:`html`});continue}if(h(s)){let e=m(()=>{let e=s.value;return Array.isArray(e)?e:[e]});x.push({anchor:t,signal:e,type:`html`});continue}if(Array.isArray(s)){let e=t.parentNode;n(e,g);for(let n of s)if(a(n)){for(;n.fragment.firstChild;)e.insertBefore(n.fragment.firstChild,t);S.push(n.apply.bind(n))}else e.insertBefore(document.createTextNode(l(n)),t);t.remove();continue}t.replaceWith(document.createTextNode(l(s)));continue}let p=f;if(r.kind===d.EVENT){if(typeof s==`function`){let{handler:e,options:t}=u(s,r.modifiers??[]);x.push({el:p,handler:e,name:r.name,options:t,type:`event`})}else if(h(s)){let e=s,{handler:t,options:n}=u(t=>{let n=e.value;typeof n==`function`&&n(t)},r.modifiers??[]);x.push({el:p,handler:t,name:r.name,options:n,type:`event`})}continue}if(r.kind===d.REF){s&&x.push({el:p,ref:s,type:`ref`});continue}if(r.kind===d.SPREAD){o(s)&&x.push({el:p,spread:s,type:`spread`});continue}x.push(c(p,r.mode??`attr`,r.name,s))}return r(b,e=>{for(let t of x)s(t,e);for(let t of S)t(e)})},v=(e,...t)=>_(e,t);export{_ as compileTemplate,v as html};
|
|
2
2
|
//# sourceMappingURL=instantiator.js.map
|
|
@@ -1 +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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"}
|
|
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 { invariant, ORE_ERRORS, OreApiError } from '../errors';\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, SlotKind } 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 */\nconst NODE_SLOT_NO_PARENT_MSG = 'html`...`: node-slot comment anchor has no parent node';\n\nexport const compileTemplate = (strings: TemplateStringsArray, values: unknown[]): HTMLResult => {\n const compiled = getStaticTemplate(strings);\n const fragment = compiled.element.content.cloneNode(true) as DocumentFragment;\n const bindings: Binding[] = [];\n // For static HTMLResult embeds: chain their apply calls\n const chainedApplies: Array<(rc: (fn: () => void) => void) => void> = [];\n\n // Phase 1: Resolve all binding targets BEFORE any DOM modifications\n type BoundSlot = { comment?: Comment; el?: HTMLElement; slot: (typeof compiled.slots)[number]; value: unknown };\n\n const boundSlots: BoundSlot[] = compiled.slots.map((slot, i) => {\n const value = values[i];\n\n if (slot.kind === SlotKind.CLOSE_TAG) {\n return { slot, value };\n }\n\n if (slot.kind === SlotKind.NODE) {\n const commentPath = compiled.commentPaths.get(slot.commentId!);\n\n invariant(commentPath, `compiled template is missing a comment path for node slot ${slot.commentId}`);\n\n return { comment: followPath(fragment, commentPath) as Comment, slot, value };\n }\n\n const elementPath = compiled.elementPaths.get(slot.elementId!);\n\n invariant(elementPath, `compiled template is missing an element path for slot ${slot.elementId}`);\n\n return { el: followPath(fragment, elementPath) as HTMLElement, slot, value };\n });\n\n // Phase 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 !== SlotKind.TAG_NAME) continue;\n\n const tagName = String(value);\n\n // Same \"fail immediately, every build\" treatment as each()'s duplicate-key\n // guard: a malformed dynamic tag name is a programming bug in the template,\n // not a runtime race — degrading to a silently-broken partial render would\n // hide it instead of surfacing it at the call site.\n if (!/^[a-z][a-z0-9._-]*$/i.test(tagName)) {\n throw new OreApiError(ORE_ERRORS.invalidDynamicTagName(tagName));\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 === SlotKind.TAG_NAME || slot.kind === SlotKind.CLOSE_TAG) continue;\n\n // Resolve element through tagname replacements\n const el = rawEl ? (tagReplacements.get(rawEl) ?? rawEl) : rawEl;\n\n if (slot.kind === SlotKind.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 invariant(parent, NODE_SLOT_NO_PARENT_MSG);\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 invariant(parent, NODE_SLOT_NO_PARENT_MSG);\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 === SlotKind.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 === SlotKind.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 === SlotKind.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
|
+
{"version":3,"file":"dom.cjs","names":[],"sources":["../../src/testing/dom.ts"],"sourcesContent":["/**\n * DOM traversal helpers for tests running under jsdom, which only implements the *light* DOM\n * tree — `Element.children`/`querySelectorAll()` never cross a `<slot>` into its assigned\n * elements. Real browsers use the *flat tree* (light DOM with each `<slot>` transparently\n * replaced by what's actually assigned to it) for this — it's what \"form owner\" resolution,\n * `:has()`, and plenty of other DOM algorithms are specified against.\n */\n\n/**\n * Walks the flat tree starting at `root`, calling `visit` for every element encountered —\n * expanding each `<slot>` via `assignedElements()` instead of descending into its (always\n * empty, in jsdom) literal children.\n *\n * @example\n * ```ts\n * // Find every form-associated element slotted (possibly through nested slots) under `form`,\n * // something `form.querySelectorAll('[name]')` can't do once a shadow boundary is involved.\n * const named: HTMLElement[] = [];\n * walkFlatTree(form, (el) => { if (el.hasAttribute('name')) named.push(el); });\n * ```\n */\nexport const walkFlatTree = (root: Element, visit: (element: HTMLElement) => void): void => {\n const children = root instanceof HTMLSlotElement ? root.assignedElements({ flatten: true }) : root.children;\n\n for (const child of Array.from(children)) {\n if (!(child instanceof HTMLSlotElement)) visit(child as HTMLElement);\n\n walkFlatTree(child, visit);\n }\n};\n"],"mappings":"AAqBA,IAAa,GAAgB,EAAe,IAAgD,CAC1F,IAAM,EAAW,aAAgB,gBAAkB,EAAK,iBAAiB,CAAE,QAAS,EAAK,CAAC,EAAI,EAAK,SAEnG,IAAK,IAAM,KAAS,MAAM,KAAK,CAAQ,EAC/B,aAAiB,iBAAkB,EAAM,CAAoB,EAEnE,EAAa,EAAO,CAAK,CAE7B"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DOM traversal helpers for tests running under jsdom, which only implements the *light* DOM
|
|
3
|
+
* tree — `Element.children`/`querySelectorAll()` never cross a `<slot>` into its assigned
|
|
4
|
+
* elements. Real browsers use the *flat tree* (light DOM with each `<slot>` transparently
|
|
5
|
+
* replaced by what's actually assigned to it) for this — it's what "form owner" resolution,
|
|
6
|
+
* `:has()`, and plenty of other DOM algorithms are specified against.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Walks the flat tree starting at `root`, calling `visit` for every element encountered —
|
|
10
|
+
* expanding each `<slot>` via `assignedElements()` instead of descending into its (always
|
|
11
|
+
* empty, in jsdom) literal children.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* // Find every form-associated element slotted (possibly through nested slots) under `form`,
|
|
16
|
+
* // something `form.querySelectorAll('[name]')` can't do once a shadow boundary is involved.
|
|
17
|
+
* const named: HTMLElement[] = [];
|
|
18
|
+
* walkFlatTree(form, (el) => { if (el.hasAttribute('name')) named.push(el); });
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export declare const walkFlatTree: (root: Element, visit: (element: HTMLElement) => void) => void;
|
|
22
|
+
//# sourceMappingURL=dom.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dom.d.ts","sourceRoot":"","sources":["../../src/testing/dom.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,YAAY,GAAI,MAAM,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,KAAG,IAQnF,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dom.js","names":[],"sources":["../../src/testing/dom.ts"],"sourcesContent":["/**\n * DOM traversal helpers for tests running under jsdom, which only implements the *light* DOM\n * tree — `Element.children`/`querySelectorAll()` never cross a `<slot>` into its assigned\n * elements. Real browsers use the *flat tree* (light DOM with each `<slot>` transparently\n * replaced by what's actually assigned to it) for this — it's what \"form owner\" resolution,\n * `:has()`, and plenty of other DOM algorithms are specified against.\n */\n\n/**\n * Walks the flat tree starting at `root`, calling `visit` for every element encountered —\n * expanding each `<slot>` via `assignedElements()` instead of descending into its (always\n * empty, in jsdom) literal children.\n *\n * @example\n * ```ts\n * // Find every form-associated element slotted (possibly through nested slots) under `form`,\n * // something `form.querySelectorAll('[name]')` can't do once a shadow boundary is involved.\n * const named: HTMLElement[] = [];\n * walkFlatTree(form, (el) => { if (el.hasAttribute('name')) named.push(el); });\n * ```\n */\nexport const walkFlatTree = (root: Element, visit: (element: HTMLElement) => void): void => {\n const children = root instanceof HTMLSlotElement ? root.assignedElements({ flatten: true }) : root.children;\n\n for (const child of Array.from(children)) {\n if (!(child instanceof HTMLSlotElement)) visit(child as HTMLElement);\n\n walkFlatTree(child, visit);\n }\n};\n"],"mappings":"AAqBA,IAAa,GAAgB,EAAe,IAAgD,CAC1F,IAAM,EAAW,aAAgB,gBAAkB,EAAK,iBAAiB,CAAE,QAAS,EAAK,CAAC,EAAI,EAAK,SAEnG,IAAK,IAAM,KAAS,MAAM,KAAK,CAAQ,EAC/B,aAAiB,iBAAkB,EAAM,CAAoB,EAEnE,EAAa,EAAO,CAAK,CAE7B"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require("./dom.cjs");var t=()=>{if(typeof ElementInternals>`u`)return;let t=Symbol.for(`vielzeug.ore.testing.formInternalsPolyfillInstalled`),n=globalThis;if(n[t])return;n[t]=!0;let r=ElementInternals.prototype,i=new WeakMap,a=new WeakMap,o=new WeakMap,s=e=>{let t=i.get(e);return!t||!Object.values(t.flags).some(Boolean)};`setFormValue`in r||Object.defineProperty(r,"setFormValue",{configurable:!0,value:function(e){let t=o.get(this);t&&a.set(t,e)},writable:!0}),`setValidity`in r||Object.defineProperty(r,"setValidity",{configurable:!0,value:function(e={},t=``){if(Object.values(e).some(Boolean)&&!t)throw TypeError(`Failed to execute 'setValidity' on 'ElementInternals': The second argument must not be empty if one or more flags in the first argument are true.`);i.set(this,{flags:e,message:t})},writable:!0}),`checkValidity`in r||Object.defineProperty(r,"checkValidity",{configurable:!0,value:function(){return s(this)},writable:!0}),`reportValidity`in r||Object.defineProperty(r,"reportValidity",{configurable:!0,value:function(){return s(this)},writable:!0}),`validationMessage`in r||Object.defineProperty(r,"validationMessage",{configurable:!0,get(){return s(this)?``:i.get(this)?.message??``}}),`states`in r||Object.defineProperty(r,"states",{configurable:!0,get:function(){return this._states??=new Set,this._states}});let c=HTMLElement.prototype.attachInternals;HTMLElement.prototype.attachInternals=function(...e){let t=c.apply(this,e);return o.set(t,this),Object.defineProperties(this,{checkValidity:{configurable:!0,value:()=>t.checkValidity()},reportValidity:{configurable:!0,value:()=>t.reportValidity()},validationMessage:{configurable:!0,get:()=>t.validationMessage},validity:{configurable:!0,get:()=>t.validity}}),t};let l=(e,t,n)=>{if(n instanceof FormData){for(let[t,r]of n.entries())e.append(t,r);return}e.append(t,n)},u=globalThis.FormData;globalThis.FormData=class extends u{constructor(t,n){super(t,n),t instanceof HTMLFormElement&&e.walkFlatTree(t,e=>{let t=e.getAttribute(`name`),n=a.get(e);!t||n==null||e.hasAttribute(`disabled`)||l(this,t,n)})}};let d=HTMLFormElement.prototype.reset;HTMLFormElement.prototype.reset=function(...t){d.apply(this,t),e.walkFlatTree(this,e=>{e.formResetCallback?.()})}};exports.installFormInternalsPolyfill=t;
|
|
2
|
+
//# sourceMappingURL=form-internals-polyfill.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"form-internals-polyfill.cjs","names":[],"sources":["../../src/testing/form-internals-polyfill.ts"],"sourcesContent":["import { walkFlatTree } from './dom';\n\n/**\n * jsdom implements none of the `ElementInternals` form-association API — not `setFormValue`,\n * not `setValidity`, not the `checkValidity`/`reportValidity`/`validity`/`validationMessage`\n * mixin real browsers put on every `formAssociated: true` element, not even `FormData`\n * collecting a form-associated element's set value, or `<form>.reset()` invoking\n * `formResetCallback()`. Every one of those gaps is exercised by `useField()` (`@vielzeug/ore/forms`),\n * so any package testing a form-associated component needs all of them, not just one — this is\n * called once, automatically, by `install()`.\n *\n * Deliberately lives here rather than in each consuming package's own `vitest.setup.ts`: the\n * gap is in `ore`'s own form-association feature, not in any one consumer, so the fix belongs\n * with the feature. `@vielzeug/refine`'s `<ore-form>` adds one more wrinkle on top — its fields\n * are slotted into a shadow `<form>` — which `walkFlatTree` already handles here, so refine\n * doesn't need a second, package-local copy of this polyfill at all.\n */\nexport const installFormInternalsPolyfill = (): void => {\n // Checked *before* consuming the flag below — an environment without `ElementInternals`\n // installs nothing and should stay eligible to install for real later (e.g. a differently\n // configured environment in the same process), not get marked \"already installed\" for having\n // done nothing.\n if (typeof ElementInternals === 'undefined') return;\n\n const INSTALLED_FLAG = Symbol.for('vielzeug.ore.testing.formInternalsPolyfillInstalled');\n const flagHost = globalThis as Record<symbol, boolean | undefined>;\n\n if (flagHost[INSTALLED_FLAG]) return;\n\n flagHost[INSTALLED_FLAG] = true;\n\n const proto = ElementInternals.prototype as unknown as Record<string, unknown>;\n const validityState = new WeakMap<ElementInternals, { flags: ValidityStateFlags; message: string }>();\n const formValueByHost = new WeakMap<HTMLElement, File | FormData | string | null>();\n const hostByInternals = new WeakMap<ElementInternals, HTMLElement>();\n\n const isValid = (internals: ElementInternals): boolean => {\n const state = validityState.get(internals);\n\n return !state || !Object.values(state.flags).some(Boolean);\n };\n\n if (!('setFormValue' in proto)) {\n Object.defineProperty(proto, 'setFormValue', {\n configurable: true,\n value: function (this: ElementInternals, value: File | FormData | string | null) {\n const host = hostByInternals.get(this);\n\n if (host) formValueByHost.set(host, value);\n },\n writable: true,\n });\n }\n\n if (!('setValidity' in proto)) {\n Object.defineProperty(proto, 'setValidity', {\n configurable: true,\n // Matches the real platform contract: throws if any flag is true and message is empty.\n // `useField()` itself already guards against triggering this — see forms/field.ts — but\n // the polyfill enforcing it too means a test would still catch a *different* caller doing\n // the same thing wrong, instead of that bug only surfacing in a real browser.\n value: function (this: ElementInternals, flags: ValidityStateFlags = {}, message = '') {\n if (Object.values(flags).some(Boolean) && !message) {\n throw new TypeError(\n \"Failed to execute 'setValidity' on 'ElementInternals': The second argument must not be empty if \" +\n 'one or more flags in the first argument are true.',\n );\n }\n\n validityState.set(this, { flags, message });\n },\n writable: true,\n });\n }\n\n if (!('checkValidity' in proto)) {\n Object.defineProperty(proto, 'checkValidity', {\n configurable: true,\n value: function (this: ElementInternals) {\n return isValid(this);\n },\n writable: true,\n });\n }\n\n if (!('reportValidity' in proto)) {\n Object.defineProperty(proto, 'reportValidity', {\n configurable: true,\n value: function (this: ElementInternals) {\n return isValid(this);\n },\n writable: true,\n });\n }\n\n if (!('validationMessage' in proto)) {\n Object.defineProperty(proto, 'validationMessage', {\n configurable: true,\n get(this: ElementInternals) {\n return isValid(this) ? '' : (validityState.get(this)?.message ?? '');\n },\n });\n }\n\n if (!('states' in proto)) {\n Object.defineProperty(proto, 'states', {\n configurable: true,\n get: function (this: ElementInternals & { _states?: Set<string> }) {\n this._states ??= new Set();\n\n return this._states;\n },\n });\n }\n\n // Real browsers mix `checkValidity`/`reportValidity`/`validity`/`validationMessage` onto any\n // `formAssociated: true` custom element itself (delegating to its internals) — not just onto\n // `ElementInternals`. jsdom does neither; mirror it here so a component's own\n // `checkValidity()`/`reportValidity()`, and a test asserting against the element directly,\n // behave the same as they would in a real browser.\n const originalAttachInternals = HTMLElement.prototype.attachInternals;\n\n HTMLElement.prototype.attachInternals = function (this: HTMLElement, ...args: []): ElementInternals {\n const internals = originalAttachInternals.apply(this, args);\n\n hostByInternals.set(internals, this);\n\n Object.defineProperties(this, {\n checkValidity: { configurable: true, value: () => internals.checkValidity() },\n reportValidity: { configurable: true, value: () => internals.reportValidity() },\n validationMessage: { configurable: true, get: () => internals.validationMessage },\n validity: { configurable: true, get: () => internals.validity },\n });\n\n return internals;\n };\n\n const appendFormValue = (formData: FormData, name: string, value: File | FormData | string): void => {\n if (value instanceof FormData) {\n for (const [entryName, entryValue] of value.entries()) formData.append(entryName, entryValue);\n\n return;\n }\n\n formData.append(name, value);\n };\n\n // jsdom's `FormData` constructor never collects a form-associated custom element's set form\n // value — real browsers walk the flat tree for every form-associated element with a `name`.\n const NativeFormData = globalThis.FormData;\n\n globalThis.FormData = class FormDataWithFormAssociatedElements extends NativeFormData {\n constructor(form?: HTMLFormElement, submitter?: HTMLElement | null) {\n super(form as HTMLFormElement | undefined, submitter as HTMLElement | null | undefined);\n\n if (!(form instanceof HTMLFormElement)) return;\n\n walkFlatTree(form, (element) => {\n const name = element.getAttribute('name');\n const value = formValueByHost.get(element);\n\n if (!name || value == null || element.hasAttribute('disabled')) return;\n\n appendFormValue(this, name, value);\n });\n }\n };\n\n // jsdom never invokes the native `formResetCallback` lifecycle method on form-associated\n // custom elements when their form resets — patch `reset()` to do it (via the same flat-tree\n // walk, since a reset field can be nested behind a shadow boundary too), so `onFormReset()`\n // (see `@vielzeug/ore`'s runtime.ts) is testable against a real `<form>.reset()` call instead\n // of only by calling `element.formResetCallback()` directly.\n const originalReset = HTMLFormElement.prototype.reset;\n\n HTMLFormElement.prototype.reset = function (this: HTMLFormElement, ...args: []) {\n originalReset.apply(this, args);\n\n walkFlatTree(this, (element) => {\n (element as Partial<{ formResetCallback: () => void }>).formResetCallback?.();\n });\n };\n};\n"],"mappings":"6BAiBA,IAAa,MAA2C,CAKtD,GAAI,OAAO,iBAAqB,IAAa,OAE7C,IAAM,EAAiB,OAAO,IAAI,qDAAqD,EACjF,EAAW,WAEjB,GAAI,EAAS,GAAiB,OAE9B,EAAS,GAAkB,GAE3B,IAAM,EAAQ,iBAAiB,UACzB,EAAgB,IAAI,QACpB,EAAkB,IAAI,QACtB,EAAkB,IAAI,QAEtB,EAAW,GAAyC,CACxD,IAAM,EAAQ,EAAc,IAAI,CAAS,EAEzC,MAAO,CAAC,GAAS,CAAC,OAAO,OAAO,EAAM,KAAK,CAAC,CAAC,KAAK,OAAO,CAC3D,EAEM,iBAAkB,GACtB,OAAO,eAAe,EAAO,eAAgB,CAC3C,aAAc,GACd,MAAO,SAAkC,EAAwC,CAC/E,IAAM,EAAO,EAAgB,IAAI,IAAI,EAEjC,GAAM,EAAgB,IAAI,EAAM,CAAK,CAC3C,EACA,SAAU,EACZ,CAAC,EAGG,gBAAiB,GACrB,OAAO,eAAe,EAAO,cAAe,CAC1C,aAAc,GAKd,MAAO,SAAkC,EAA4B,CAAC,EAAG,EAAU,GAAI,CACrF,GAAI,OAAO,OAAO,CAAK,CAAC,CAAC,KAAK,OAAO,GAAK,CAAC,EACzC,MAAU,UACR,mJAEF,EAGF,EAAc,IAAI,KAAM,CAAE,QAAO,SAAQ,CAAC,CAC5C,EACA,SAAU,EACZ,CAAC,EAGG,kBAAmB,GACvB,OAAO,eAAe,EAAO,gBAAiB,CAC5C,aAAc,GACd,MAAO,UAAkC,CACvC,OAAO,EAAQ,IAAI,CACrB,EACA,SAAU,EACZ,CAAC,EAGG,mBAAoB,GACxB,OAAO,eAAe,EAAO,iBAAkB,CAC7C,aAAc,GACd,MAAO,UAAkC,CACvC,OAAO,EAAQ,IAAI,CACrB,EACA,SAAU,EACZ,CAAC,EAGG,sBAAuB,GAC3B,OAAO,eAAe,EAAO,oBAAqB,CAChD,aAAc,GACd,KAA4B,CAC1B,OAAO,EAAQ,IAAI,EAAI,GAAM,EAAc,IAAI,IAAI,CAAC,EAAE,SAAW,EACnE,CACF,CAAC,EAGG,WAAY,GAChB,OAAO,eAAe,EAAO,SAAU,CACrC,aAAc,GACd,IAAK,UAA8D,CAGjE,MAFA,MAAK,UAAY,IAAI,IAEd,KAAK,OACd,CACF,CAAC,EAQH,IAAM,EAA0B,YAAY,UAAU,gBAEtD,YAAY,UAAU,gBAAkB,SAA6B,GAAG,EAA4B,CAClG,IAAM,EAAY,EAAwB,MAAM,KAAM,CAAI,EAW1D,OATA,EAAgB,IAAI,EAAW,IAAI,EAEnC,OAAO,iBAAiB,KAAM,CAC5B,cAAe,CAAE,aAAc,GAAM,UAAa,EAAU,cAAc,CAAE,EAC5E,eAAgB,CAAE,aAAc,GAAM,UAAa,EAAU,eAAe,CAAE,EAC9E,kBAAmB,CAAE,aAAc,GAAM,QAAW,EAAU,iBAAkB,EAChF,SAAU,CAAE,aAAc,GAAM,QAAW,EAAU,QAAS,CAChE,CAAC,EAEM,CACT,EAEA,IAAM,GAAmB,EAAoB,EAAc,IAA0C,CACnG,GAAI,aAAiB,SAAU,CAC7B,IAAK,GAAM,CAAC,EAAW,KAAe,EAAM,QAAQ,EAAG,EAAS,OAAO,EAAW,CAAU,EAE5F,MACF,CAEA,EAAS,OAAO,EAAM,CAAK,CAC7B,EAIM,EAAiB,WAAW,SAElC,WAAW,SAAW,cAAiD,CAAe,CACpF,YAAY,EAAwB,EAAgC,CAClE,MAAM,EAAqC,CAA2C,EAEhF,aAAgB,iBAEtB,EAAA,aAAa,EAAO,GAAY,CAC9B,IAAM,EAAO,EAAQ,aAAa,MAAM,EAClC,EAAQ,EAAgB,IAAI,CAAO,EAErC,CAAC,GAAQ,GAAS,MAAQ,EAAQ,aAAa,UAAU,GAE7D,EAAgB,KAAM,EAAM,CAAK,CACnC,CAAC,CACH,CACF,EAOA,IAAM,EAAgB,gBAAgB,UAAU,MAEhD,gBAAgB,UAAU,MAAQ,SAAiC,GAAG,EAAU,CAC9E,EAAc,MAAM,KAAM,CAAI,EAE9B,EAAA,aAAa,KAAO,GAAY,CAC9B,EAAwD,oBAAoB,CAC9E,CAAC,CACH,CACF"}
|