@vielzeug/craftit 1.0.1 → 2.0.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 +112 -401
- package/dist/core/component.cjs +2 -0
- package/dist/core/component.cjs.map +1 -0
- package/dist/core/component.d.ts +172 -0
- package/dist/core/component.d.ts.map +1 -0
- package/dist/core/component.js +2 -0
- package/dist/core/component.js.map +1 -0
- package/dist/core/host.cjs +2 -0
- package/dist/core/host.cjs.map +1 -0
- package/dist/core/host.d.ts +77 -0
- package/dist/core/host.d.ts.map +1 -0
- package/dist/core/host.js +2 -0
- package/dist/core/host.js.map +1 -0
- package/dist/core/internal.cjs +2 -0
- package/dist/core/internal.cjs.map +1 -0
- package/dist/core/internal.d.ts +105 -0
- package/dist/core/internal.d.ts.map +1 -0
- package/dist/core/internal.js +2 -0
- package/dist/core/internal.js.map +1 -0
- package/dist/core/runtime-bindings.cjs +2 -0
- package/dist/core/runtime-bindings.cjs.map +1 -0
- package/dist/core/runtime-bindings.d.ts +6 -0
- package/dist/core/runtime-bindings.d.ts.map +1 -0
- package/dist/core/runtime-bindings.js +2 -0
- package/dist/core/runtime-bindings.js.map +1 -0
- package/dist/core/runtime-lifecycle.cjs +2 -0
- package/dist/core/runtime-lifecycle.cjs.map +1 -0
- package/dist/core/runtime-lifecycle.d.ts +116 -0
- package/dist/core/runtime-lifecycle.d.ts.map +1 -0
- package/dist/core/runtime-lifecycle.js +2 -0
- package/dist/core/runtime-lifecycle.js.map +1 -0
- package/dist/core/runtime.cjs +1 -0
- package/dist/core/runtime.d.ts +3 -0
- package/dist/core/runtime.d.ts.map +1 -0
- package/dist/core/runtime.js +1 -0
- package/dist/core/template-bindings.cjs +2 -0
- package/dist/core/template-bindings.cjs.map +1 -0
- package/dist/core/template-bindings.d.ts +59 -0
- package/dist/core/template-bindings.d.ts.map +1 -0
- package/dist/core/template-bindings.js +2 -0
- package/dist/core/template-bindings.js.map +1 -0
- package/dist/core/template-compiler.cjs +2 -0
- package/dist/core/template-compiler.cjs.map +1 -0
- package/dist/core/template-compiler.d.ts +25 -0
- package/dist/core/template-compiler.d.ts.map +1 -0
- package/dist/core/template-compiler.js +2 -0
- package/dist/core/template-compiler.js.map +1 -0
- package/dist/core/template-dom.cjs +2 -0
- package/dist/core/template-dom.cjs.map +1 -0
- package/dist/core/template-dom.d.ts +13 -0
- package/dist/core/template-dom.d.ts.map +1 -0
- package/dist/core/template-dom.js +2 -0
- package/dist/core/template-dom.js.map +1 -0
- package/dist/core/template-html.cjs +2 -0
- package/dist/core/template-html.cjs.map +1 -0
- package/dist/core/template-html.d.ts +26 -0
- package/dist/core/template-html.d.ts.map +1 -0
- package/dist/core/template-html.js +2 -0
- package/dist/core/template-html.js.map +1 -0
- package/dist/core/template.cjs +2 -0
- package/dist/core/template.cjs.map +1 -0
- package/dist/core/template.d.ts +11 -0
- package/dist/core/template.d.ts.map +1 -0
- package/dist/core/template.js +2 -0
- package/dist/core/template.js.map +1 -0
- package/dist/core/utilities.cjs +2 -0
- package/dist/core/utilities.cjs.map +1 -0
- package/dist/core/utilities.d.ts +68 -0
- package/dist/core/utilities.d.ts.map +1 -0
- package/dist/core/utilities.js +2 -0
- package/dist/core/utilities.js.map +1 -0
- package/dist/craftit.cjs +2 -18
- package/dist/craftit.cjs.map +1 -1
- package/dist/craftit.js +2 -580
- package/dist/craftit.js.map +1 -1
- package/dist/directives/attr.cjs +2 -0
- package/dist/directives/attr.cjs.map +1 -0
- package/dist/directives/attr.d.ts +14 -0
- package/dist/directives/attr.d.ts.map +1 -0
- package/dist/directives/attr.js +2 -0
- package/dist/directives/attr.js.map +1 -0
- package/dist/directives/bind.cjs +2 -0
- package/dist/directives/bind.cjs.map +1 -0
- package/dist/directives/bind.d.ts +30 -0
- package/dist/directives/bind.d.ts.map +1 -0
- package/dist/directives/bind.js +2 -0
- package/dist/directives/bind.js.map +1 -0
- package/dist/directives/choose.cjs +2 -0
- package/dist/directives/choose.cjs.map +1 -0
- package/dist/directives/choose.d.ts +34 -0
- package/dist/directives/choose.d.ts.map +1 -0
- package/dist/directives/choose.js +2 -0
- package/dist/directives/choose.js.map +1 -0
- package/dist/directives/classes.cjs +2 -0
- package/dist/directives/classes.cjs.map +1 -0
- package/dist/directives/classes.d.ts +20 -0
- package/dist/directives/classes.d.ts.map +1 -0
- package/dist/directives/classes.js +2 -0
- package/dist/directives/classes.js.map +1 -0
- package/dist/directives/each.cjs +2 -0
- package/dist/directives/each.cjs.map +1 -0
- package/dist/directives/each.d.ts +68 -0
- package/dist/directives/each.d.ts.map +1 -0
- package/dist/directives/each.js +2 -0
- package/dist/directives/each.js.map +1 -0
- package/dist/directives/index.cjs +1 -0
- package/dist/directives/index.d.ts +14 -0
- package/dist/directives/index.d.ts.map +1 -0
- package/dist/directives/index.js +1 -0
- package/dist/directives/match.cjs +2 -0
- package/dist/directives/match.cjs.map +1 -0
- package/dist/directives/match.d.ts +31 -0
- package/dist/directives/match.d.ts.map +1 -0
- package/dist/directives/match.js +2 -0
- package/dist/directives/match.js.map +1 -0
- package/dist/directives/memo.cjs +2 -0
- package/dist/directives/memo.cjs.map +1 -0
- package/dist/directives/memo.d.ts +23 -0
- package/dist/directives/memo.d.ts.map +1 -0
- package/dist/directives/memo.js +2 -0
- package/dist/directives/memo.js.map +1 -0
- package/dist/directives/on.cjs +2 -0
- package/dist/directives/on.cjs.map +1 -0
- package/dist/directives/on.d.ts +25 -0
- package/dist/directives/on.d.ts.map +1 -0
- package/dist/directives/on.js +2 -0
- package/dist/directives/on.js.map +1 -0
- package/dist/directives/raw.cjs +2 -0
- package/dist/directives/raw.cjs.map +1 -0
- package/dist/directives/raw.d.ts +25 -0
- package/dist/directives/raw.d.ts.map +1 -0
- package/dist/directives/raw.js +2 -0
- package/dist/directives/raw.js.map +1 -0
- package/dist/directives/spread.cjs +2 -0
- package/dist/directives/spread.cjs.map +1 -0
- package/dist/directives/spread.d.ts +14 -0
- package/dist/directives/spread.d.ts.map +1 -0
- package/dist/directives/spread.js +2 -0
- package/dist/directives/spread.js.map +1 -0
- package/dist/directives/style.cjs +2 -0
- package/dist/directives/style.cjs.map +1 -0
- package/dist/directives/style.d.ts +22 -0
- package/dist/directives/style.d.ts.map +1 -0
- package/dist/directives/style.js +2 -0
- package/dist/directives/style.js.map +1 -0
- package/dist/directives/until.cjs +2 -0
- package/dist/directives/until.cjs.map +1 -0
- package/dist/directives/until.d.ts +26 -0
- package/dist/directives/until.d.ts.map +1 -0
- package/dist/directives/until.js +2 -0
- package/dist/directives/until.js.map +1 -0
- package/dist/directives/when.cjs +2 -0
- package/dist/directives/when.cjs.map +1 -0
- package/dist/directives/when.d.ts +17 -0
- package/dist/directives/when.d.ts.map +1 -0
- package/dist/directives/when.js +2 -0
- package/dist/directives/when.js.map +1 -0
- package/dist/index.cjs +1 -2
- package/dist/index.d.ts +10 -265
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1 -13
- package/dist/labs/a11y.cjs +2 -0
- package/dist/labs/a11y.cjs.map +1 -0
- package/dist/labs/a11y.d.ts +61 -0
- package/dist/labs/a11y.d.ts.map +1 -0
- package/dist/labs/a11y.js +2 -0
- package/dist/labs/a11y.js.map +1 -0
- package/dist/labs/index.d.ts +8 -0
- package/dist/labs/index.d.ts.map +1 -0
- package/dist/labs/list.cjs +2 -0
- package/dist/labs/list.cjs.map +1 -0
- package/dist/labs/list.d.ts +26 -0
- package/dist/labs/list.d.ts.map +1 -0
- package/dist/labs/list.js +2 -0
- package/dist/labs/list.js.map +1 -0
- package/dist/labs/observers.cjs +2 -0
- package/dist/labs/observers.cjs.map +1 -0
- package/dist/labs/observers.d.ts +42 -0
- package/dist/labs/observers.d.ts.map +1 -0
- package/dist/labs/observers.js +2 -0
- package/dist/labs/observers.js.map +1 -0
- package/dist/labs/overlay.cjs +2 -0
- package/dist/labs/overlay.cjs.map +1 -0
- package/dist/labs/overlay.d.ts +35 -0
- package/dist/labs/overlay.d.ts.map +1 -0
- package/dist/labs/overlay.js +2 -0
- package/dist/labs/overlay.js.map +1 -0
- package/dist/labs/selectable.cjs +2 -0
- package/dist/labs/selectable.cjs.map +1 -0
- package/dist/labs/selectable.d.ts +70 -0
- package/dist/labs/selectable.d.ts.map +1 -0
- package/dist/labs/selectable.js +2 -0
- package/dist/labs/selectable.js.map +1 -0
- package/dist/labs/selection.cjs +2 -0
- package/dist/labs/selection.cjs.map +1 -0
- package/dist/labs/selection.d.ts +68 -0
- package/dist/labs/selection.d.ts.map +1 -0
- package/dist/labs/selection.js +2 -0
- package/dist/labs/selection.js.map +1 -0
- package/dist/labs.cjs +1 -0
- package/dist/labs.js +1 -0
- package/dist/test/index.d.ts +2 -0
- package/dist/test/index.d.ts.map +1 -0
- package/dist/test/test.cjs +2 -0
- package/dist/test/test.cjs.map +1 -0
- package/dist/test/test.d.ts +198 -0
- package/dist/test/test.d.ts.map +1 -0
- package/dist/test/test.js +2 -0
- package/dist/test/test.js.map +1 -0
- package/dist/test.cjs +1 -0
- package/dist/test.js +1 -0
- package/package.json +37 -9
- package/dist/index.cjs.map +0 -1
- package/dist/index.js.map +0 -1
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{listen as e}from"./utilities.js";import{computed as t,isSignal as n}from"@vielzeug/stateit";var r=e=>{if(n(e))return e;if(typeof e==`function`)return t(e)},i=e=>{if(!n(e))return!1;let t=Object.getPrototypeOf(e);for(;t;){let e=Object.getOwnPropertyDescriptor(t,`value`);if(e)return typeof e.set==`function`;t=Object.getPrototypeOf(t)}return!1},a=(e,t)=>{if(!Object.is(e.value,t))try{e.value=t}catch{}},o=(t,n,r,i)=>{if(r){if(n===`value`){(t instanceof HTMLInputElement||t instanceof HTMLTextAreaElement||t instanceof HTMLSelectElement)&&i(e(t,t instanceof HTMLSelectElement?`change`:`input`,()=>{a(r,t.value)}));return}n===`checked`&&t instanceof HTMLInputElement&&i(e(t,`change`,()=>{a(r,t.checked)}))}};export{o as bindPropertyModel,i as hasWritableValueSetter,r as toReactiveBindingSource};
|
|
2
|
+
//# sourceMappingURL=runtime-bindings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime-bindings.js","names":[],"sources":["../../src/core/runtime-bindings.ts"],"sourcesContent":["import { computed, isSignal, type ReadonlySignal, type Signal } from '@vielzeug/stateit';\n\nimport { listen } from './utilities';\n\nexport type RegisterPropertyCleanup = (fn: () => void) => void;\n\nexport const toReactiveBindingSource = (value: unknown): ReadonlySignal<unknown> | undefined => {\n if (isSignal(value)) return value as ReadonlySignal<unknown>;\n\n if (typeof value === 'function') return computed(value as () => unknown);\n\n return undefined;\n};\n\nexport const hasWritableValueSetter = (value: unknown): value is Signal<unknown> => {\n if (!isSignal(value)) return false;\n\n let proto: object | null = Object.getPrototypeOf(value);\n\n while (proto) {\n const descriptor = Object.getOwnPropertyDescriptor(proto, 'value');\n\n if (descriptor) return typeof descriptor.set === 'function';\n\n proto = Object.getPrototypeOf(proto);\n }\n\n return false;\n};\n\nconst updateModelValue = (model: Signal<unknown>, next: unknown): void => {\n if (Object.is(model.value, next)) return;\n\n try {\n model.value = next;\n } catch {\n // Readonly signal/computed source: keep one-way behavior.\n }\n};\n\nexport const bindPropertyModel = (\n el: HTMLElement,\n name: string,\n model: Signal<unknown> | undefined,\n registerCleanup: RegisterPropertyCleanup,\n): void => {\n if (!model) return;\n\n if (name === 'value') {\n if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement || el instanceof HTMLSelectElement) {\n const eventName = el instanceof HTMLSelectElement ? 'change' : 'input';\n\n registerCleanup(\n listen(el, eventName, () => {\n updateModelValue(model, el.value);\n }),\n );\n }\n\n return;\n }\n\n if (name === 'checked' && el instanceof HTMLInputElement) {\n registerCleanup(\n listen(el, 'change', () => {\n updateModelValue(model, el.checked);\n }),\n );\n }\n};\n"],"mappings":"mGAMA,IAAa,EAA2B,GAAwD,CAC9F,GAAI,EAAS,EAAM,CAAE,OAAO,EAE5B,GAAI,OAAO,GAAU,WAAY,OAAO,EAAS,EAAuB,EAK7D,EAA0B,GAA6C,CAClF,GAAI,CAAC,EAAS,EAAM,CAAE,MAAO,GAE7B,IAAI,EAAuB,OAAO,eAAe,EAAM,CAEvD,KAAO,GAAO,CACZ,IAAM,EAAa,OAAO,yBAAyB,EAAO,QAAQ,CAElE,GAAI,EAAY,OAAO,OAAO,EAAW,KAAQ,WAEjD,EAAQ,OAAO,eAAe,EAAM,CAGtC,MAAO,IAGH,GAAoB,EAAwB,IAAwB,CACpE,WAAO,GAAG,EAAM,MAAO,EAAK,CAEhC,GAAI,CACF,EAAM,MAAQ,OACR,IAKG,GACX,EACA,EACA,EACA,IACS,CACJ,KAEL,IAAI,IAAS,QAAS,EAChB,aAAc,kBAAoB,aAAc,qBAAuB,aAAc,oBAGvF,EACE,EAAO,EAHS,aAAc,kBAAoB,SAAW,YAGjC,CAC1B,EAAiB,EAAO,EAAG,MAAM,EACjC,CACH,CAGH,OAGE,IAAS,WAAa,aAAc,kBACtC,EACE,EAAO,EAAI,aAAgB,CACzB,EAAiB,EAAO,EAAG,QAAQ,EACnC,CACH"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
require(`./utilities.cjs`);let e=require(`@vielzeug/stateit`);var t=[],n=()=>{let e=t[t.length-1];if(!e)throw Error(`[craftit:E1] lifecycle outside setup`);return e},r=e=>{n().onMount.push(e)},i=r=>{t.length>0?n().cleanups.push(r):(0,e.onCleanup)(r)},a=e=>{n().errorHandlers.push(e)},o=e=>{t.length>0&&i(e)},s=(t,n)=>{let r=(0,e.effect)(t,n);return o(r),r};function c(t,n,r){if(Array.isArray(t)){let i=r,a=!1,s=!1,c=(0,e.effect)(()=>{for(let e of t)e.value;if(a)(0,e.untrack)(n),i?.once&&c();else if(a=!0,i?.immediate&&((0,e.untrack)(n),i.once)){s=!0;return}});return o(c),s&&c(),c}let i=(0,e.watch)(t,n,r);return o(i),i}function l(e,t,n,r){e&&(e.addEventListener(t,n,r),i(()=>e.removeEventListener(t,n,r)))}var u={bubbles:!0,cancelable:!0,composed:!0},d={basic(e,t,n={}){return e.dispatchEvent(new Event(t,{...u,...n}))},custom(e,t,n={}){return e.dispatchEvent(new CustomEvent(t,{...u,...n}))},event(e,t){return e.dispatchEvent(t)},focus(e,t,n={}){return e.dispatchEvent(new FocusEvent(t,{...u,...n}))},keyboard(e,t,n={}){return e.dispatchEvent(new KeyboardEvent(t,{...u,...n}))},mouse(e,t,n={}){return e.dispatchEvent(new MouseEvent(t,{...u,...n}))},touch(e,t,n={}){return typeof TouchEvent<`u`?e.dispatchEvent(new TouchEvent(t,{...u,...n})):e.dispatchEvent(new CustomEvent(t,{...u,...n}))}};function f(e,t){let r=t===void 0?n().el:e,i=t===void 0?e:t,a=(e,t)=>{let n=`aria-${e}`,i=typeof t==`function`?t():t;i==null||i===!1?r.removeAttribute(n):r.setAttribute(n,String(i))},o=Object.entries(i).map(([e,t])=>s(()=>a(e,t)));if(t!==void 0)return()=>{for(let e of o)e()}}exports.aria=f,exports.autoCleanup=o,exports.currentRuntime=n,exports.effect=s,exports.fire=d,exports.handle=l,exports.onCleanup=i,exports.onError=a,exports.onMount=r,exports.runtimeStack=t,exports.watch=c;
|
|
2
|
+
//# sourceMappingURL=runtime-lifecycle.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime-lifecycle.cjs","names":[],"sources":["../../src/core/runtime-lifecycle.ts"],"sourcesContent":["import {\n effect as _effect,\n onCleanup as _onCleanup,\n watch as _watch,\n type CleanupFn,\n type EffectCallback,\n type EffectOptions,\n type ReadonlySignal,\n type Subscription,\n untrack,\n type WatchOptions,\n} from '@vielzeug/stateit';\n\nimport { type CSSResult } from './utilities';\n\n// ─── Component runtime ────────────────────────────────────────────────────────\nexport type ComponentRuntime = {\n cleanups: CleanupFn[];\n el: HTMLElement;\n errorHandlers: ((err: unknown) => void)[];\n onMount: (() => CleanupFn | undefined | void)[];\n styles?: (string | CSSStyleSheet | CSSResult)[];\n};\n\nexport const runtimeStack: ComponentRuntime[] = [];\n\nexport const currentRuntime = (): ComponentRuntime => {\n const rt = runtimeStack[runtimeStack.length - 1];\n\n if (!rt) throw new Error('[craftit:E1] lifecycle outside setup');\n\n return rt;\n};\n\n// ─── Lifecycle hooks ──────────────────────────────────────────────────────────\nexport const onMount = (fn: () => CleanupFn | undefined | void): void => {\n currentRuntime().onMount.push(fn);\n};\n\n/**\n * Registers a cleanup function.\n * - Inside a component setup or `onMount` callback: runs when the component\n * unmounts. Prefer {@link onCleanup} for explicit lifecycle cleanup.\n * - Outside a component context (e.g. inside a plain `effect()`): delegates to\n * stateit's `onCleanup`, which runs before each effect re-run.\n */\nexport const onCleanup = (fn: CleanupFn): void => {\n if (runtimeStack.length > 0) {\n currentRuntime().cleanups.push(fn);\n } else {\n _onCleanup(fn);\n }\n};\n\n/**\n * Registers a scoped error handler for this component.\n * Called when an unhandled error is thrown during setup, `onMount` callbacks,\n * or rendering. If no handler is registered, errors are logged to the console.\n *\n * @example\n * defineComponent({\n * setup() {\n * onError((err) => console.error('Component error:', err));\n * // ... rest of setup\n * return html``;\n * },\n * tag: 'my-component',\n * });\n */\nexport const onError = (fn: (err: unknown) => void): void => {\n currentRuntime().errorHandlers.push(fn);\n};\n\n/** @internal — Cleanup only when inside a component context. */\nexport const autoCleanup = (dispose: Subscription | CleanupFn): void => {\n if (runtimeStack.length > 0) onCleanup(dispose);\n};\n\n/**\n * Creates a reactive effect that re-runs whenever its signal dependencies change.\n * When called inside a component setup function or `onMount` callback, the effect is\n * automatically cleaned up when the component unmounts — no manual `onCleanup` needed.\n * Outside a component context, behaves identically to stateit's `effect`.\n *\n * @example\n * // Inside setup — auto-cleaned on unmount:\n * effect(() => { document.title = props.title.value; });\n *\n * // Inside onMount — also auto-cleaned:\n * onMount(() => { effect(() => syncExternal(data.value)); });\n */\nexport const effect = (fn: EffectCallback, options?: EffectOptions): Subscription => {\n const dispose = _effect(fn, options);\n\n autoCleanup(dispose);\n\n return dispose;\n};\n\n// ─── Watch ────────────────────────────────────────────────────────────────────\n/**\n * Watch a signal or array of signals for changes and call a callback.\n * Auto-cleaned on unmount when called during setup.\n *\n * Single signal: callback receives (next, prev) values.\n * Multiple signals: callback receives no args, fires when any signal changes.\n */\nexport function watch<T>(\n source: ReadonlySignal<T>,\n cb: (value: T, prev: T) => void,\n options?: WatchOptions<T>,\n): Subscription;\nexport function watch(\n sources: ReadonlyArray<ReadonlySignal<unknown>>,\n cb: () => void,\n options?: WatchOptions<unknown>,\n): Subscription;\nexport function watch(\n source: ReadonlySignal<unknown> | ReadonlyArray<ReadonlySignal<unknown>>,\n cb: ((value: unknown, prev: unknown) => void) | (() => void),\n options?: WatchOptions<unknown>,\n): Subscription {\n if (Array.isArray(source)) {\n const opts = options;\n let initialized = false;\n let pendingDispose = false;\n const dispose = _effect(() => {\n for (const s of source) void s.value; // register all listed deps\n\n if (!initialized) {\n initialized = true;\n\n if (opts?.immediate) {\n untrack(cb as () => void);\n\n if (opts.once) {\n pendingDispose = true;\n\n return;\n }\n }\n } else {\n untrack(cb as () => void);\n\n if (opts?.once) dispose();\n }\n });\n\n autoCleanup(dispose);\n\n if (pendingDispose) dispose();\n\n return dispose;\n }\n\n const stop = _watch(source as ReadonlySignal<unknown>, cb as (value: unknown, prev: unknown) => void, options);\n\n autoCleanup(stop);\n\n return stop;\n}\n\n// ─── Event helpers ────────────────────────────────────────────────────────────\n/**\n * Register an event listener with automatic cleanup on unmount.\n * Use inside onMount() for auto-cleanup, or handle() return value manually.\n *\n * @example\n * onMount(() => {\n * handle(host, 'click', onClick);\n * handle(window, 'resize', onResize);\n * // auto-cleanup\n * });\n */\nexport function handle<K extends keyof HTMLElementEventMap>(\n target: EventTarget | null | undefined,\n event: K,\n listener: (e: HTMLElementEventMap[K]) => void,\n options?: AddEventListenerOptions,\n): void;\nexport function handle(\n target: EventTarget | null | undefined,\n event: string,\n listener: EventListener,\n options?: AddEventListenerOptions,\n): void;\nexport function handle(\n target: EventTarget | null | undefined,\n event: string,\n listener: EventListener,\n options?: AddEventListenerOptions,\n): void {\n if (!target) return;\n\n target.addEventListener(event, listener, options);\n onCleanup(() => target.removeEventListener(event, listener, options));\n}\n\ntype FireDefaults = Pick<EventInit, 'bubbles' | 'cancelable' | 'composed'>;\n\nexport type FireApi = {\n basic(target: EventTarget, type: string, options?: EventInit): boolean;\n custom<Detail = unknown>(target: EventTarget, type: string, options?: CustomEventInit<Detail>): boolean;\n event(target: EventTarget, event: Event): boolean;\n focus(target: EventTarget, type: string, options?: FocusEventInit): boolean;\n keyboard(target: EventTarget, type: string, options?: KeyboardEventInit): boolean;\n mouse(target: EventTarget, type: string, options?: MouseEventInit): boolean;\n touch(target: EventTarget, type: string, options?: TouchEventInit): boolean;\n};\n\nconst DEFAULT_FIRE_OPTIONS: FireDefaults = { bubbles: true, cancelable: true, composed: true };\n\n/**\n * Dispatch DOM events explicitly without guessing constructors from the event name.\n *\n * @example\n * fire.mouse(el, 'click');\n * fire.keyboard(el, 'keydown', { key: 'Enter' });\n * fire.custom(el, 'change', { detail: { value: 42 } });\n * fire.event(el, new PointerEvent('pointerdown'));\n */\nexport const fire: FireApi = {\n basic(target, type, options = {}) {\n return target.dispatchEvent(new Event(type, { ...DEFAULT_FIRE_OPTIONS, ...options }));\n },\n custom<Detail = unknown>(target: EventTarget, type: string, options: CustomEventInit<Detail> = {}) {\n return target.dispatchEvent(new CustomEvent<Detail>(type, { ...DEFAULT_FIRE_OPTIONS, ...options }));\n },\n event(target, event) {\n return target.dispatchEvent(event);\n },\n focus(target, type, options = {}) {\n return target.dispatchEvent(new FocusEvent(type, { ...DEFAULT_FIRE_OPTIONS, ...options }));\n },\n keyboard(target, type, options = {}) {\n return target.dispatchEvent(new KeyboardEvent(type, { ...DEFAULT_FIRE_OPTIONS, ...options }));\n },\n mouse(target, type, options = {}) {\n return target.dispatchEvent(new MouseEvent(type, { ...DEFAULT_FIRE_OPTIONS, ...options }));\n },\n touch(target, type, options = {}) {\n if (typeof TouchEvent !== 'undefined') {\n return target.dispatchEvent(new TouchEvent(type, { ...DEFAULT_FIRE_OPTIONS, ...options }));\n }\n\n return target.dispatchEvent(new CustomEvent(type, { ...DEFAULT_FIRE_OPTIONS, ...options }));\n },\n};\n\n// ─── ARIA helpers ────────────────────────────────────────────────────────────\ntype AriaAttrValue =\n | (() => string | boolean | number | null | undefined)\n | string\n | boolean\n | number\n | null\n | undefined;\n\n/**\n * Reactively set ARIA attributes on an element.\n *\n * - `aria(attrs)` — targets the component host (call during setup)\n * - `aria(target, attrs)` — targets any element; returns a cleanup function\n *\n * Pass getter functions for reactive values. Plain values are set once.\n * `null`, `undefined`, or `false` remove the attribute.\n *\n * @example\n * // Host (during setup):\n * aria({ role: 'checkbox', checked: () => checked.value });\n *\n * // Inner element (inside onMount):\n * onMount(() => {\n * return aria(inputEl, { invalid: () => !!error.value });\n * });\n */\nexport function aria(attrs: Record<string, AriaAttrValue>): void;\nexport function aria(target: Element, attrs: Record<string, AriaAttrValue>): CleanupFn;\nexport function aria(\n targetOrAttrs: Element | Record<string, AriaAttrValue>,\n maybeAttrs?: Record<string, AriaAttrValue>,\n): CleanupFn | undefined {\n const target = maybeAttrs !== undefined ? (targetOrAttrs as Element) : currentRuntime().el;\n const attrs = maybeAttrs !== undefined ? maybeAttrs : (targetOrAttrs as Record<string, AriaAttrValue>);\n\n const applyAttr = (name: string, val: AriaAttrValue) => {\n const attrName = `aria-${name}`;\n const value = typeof val === 'function' ? (val as () => AriaAttrValue)() : val;\n\n if (value === null || value === undefined || value === false) {\n target.removeAttribute(attrName);\n } else {\n target.setAttribute(attrName, String(value));\n }\n };\n\n // Each key gets its own effect so changes to one attr don't re-apply all others.\n const stops = Object.entries(attrs).map(([name, val]) => effect(() => applyAttr(name, val)));\n\n if (maybeAttrs !== undefined) {\n return () => {\n for (const stop of stops) stop();\n };\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { type CleanupFn, type EffectCallback, type EffectOptions, type ReadonlySignal, type Subscription, type WatchOptions } from '@vielzeug/stateit';
|
|
2
|
+
import { type CSSResult } from './utilities';
|
|
3
|
+
export type ComponentRuntime = {
|
|
4
|
+
cleanups: CleanupFn[];
|
|
5
|
+
el: HTMLElement;
|
|
6
|
+
errorHandlers: ((err: unknown) => void)[];
|
|
7
|
+
onMount: (() => CleanupFn | undefined | void)[];
|
|
8
|
+
styles?: (string | CSSStyleSheet | CSSResult)[];
|
|
9
|
+
};
|
|
10
|
+
export declare const runtimeStack: ComponentRuntime[];
|
|
11
|
+
export declare const currentRuntime: () => ComponentRuntime;
|
|
12
|
+
export declare const onMount: (fn: () => CleanupFn | undefined | void) => void;
|
|
13
|
+
/**
|
|
14
|
+
* Registers a cleanup function.
|
|
15
|
+
* - Inside a component setup or `onMount` callback: runs when the component
|
|
16
|
+
* unmounts. Prefer {@link onCleanup} for explicit lifecycle cleanup.
|
|
17
|
+
* - Outside a component context (e.g. inside a plain `effect()`): delegates to
|
|
18
|
+
* stateit's `onCleanup`, which runs before each effect re-run.
|
|
19
|
+
*/
|
|
20
|
+
export declare const onCleanup: (fn: CleanupFn) => void;
|
|
21
|
+
/**
|
|
22
|
+
* Registers a scoped error handler for this component.
|
|
23
|
+
* Called when an unhandled error is thrown during setup, `onMount` callbacks,
|
|
24
|
+
* or rendering. If no handler is registered, errors are logged to the console.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* defineComponent({
|
|
28
|
+
* setup() {
|
|
29
|
+
* onError((err) => console.error('Component error:', err));
|
|
30
|
+
* // ... rest of setup
|
|
31
|
+
* return html``;
|
|
32
|
+
* },
|
|
33
|
+
* tag: 'my-component',
|
|
34
|
+
* });
|
|
35
|
+
*/
|
|
36
|
+
export declare const onError: (fn: (err: unknown) => void) => void;
|
|
37
|
+
/** @internal — Cleanup only when inside a component context. */
|
|
38
|
+
export declare const autoCleanup: (dispose: Subscription | CleanupFn) => void;
|
|
39
|
+
/**
|
|
40
|
+
* Creates a reactive effect that re-runs whenever its signal dependencies change.
|
|
41
|
+
* When called inside a component setup function or `onMount` callback, the effect is
|
|
42
|
+
* automatically cleaned up when the component unmounts — no manual `onCleanup` needed.
|
|
43
|
+
* Outside a component context, behaves identically to stateit's `effect`.
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* // Inside setup — auto-cleaned on unmount:
|
|
47
|
+
* effect(() => { document.title = props.title.value; });
|
|
48
|
+
*
|
|
49
|
+
* // Inside onMount — also auto-cleaned:
|
|
50
|
+
* onMount(() => { effect(() => syncExternal(data.value)); });
|
|
51
|
+
*/
|
|
52
|
+
export declare const effect: (fn: EffectCallback, options?: EffectOptions) => Subscription;
|
|
53
|
+
/**
|
|
54
|
+
* Watch a signal or array of signals for changes and call a callback.
|
|
55
|
+
* Auto-cleaned on unmount when called during setup.
|
|
56
|
+
*
|
|
57
|
+
* Single signal: callback receives (next, prev) values.
|
|
58
|
+
* Multiple signals: callback receives no args, fires when any signal changes.
|
|
59
|
+
*/
|
|
60
|
+
export declare function watch<T>(source: ReadonlySignal<T>, cb: (value: T, prev: T) => void, options?: WatchOptions<T>): Subscription;
|
|
61
|
+
export declare function watch(sources: ReadonlyArray<ReadonlySignal<unknown>>, cb: () => void, options?: WatchOptions<unknown>): Subscription;
|
|
62
|
+
/**
|
|
63
|
+
* Register an event listener with automatic cleanup on unmount.
|
|
64
|
+
* Use inside onMount() for auto-cleanup, or handle() return value manually.
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* onMount(() => {
|
|
68
|
+
* handle(host, 'click', onClick);
|
|
69
|
+
* handle(window, 'resize', onResize);
|
|
70
|
+
* // auto-cleanup
|
|
71
|
+
* });
|
|
72
|
+
*/
|
|
73
|
+
export declare function handle<K extends keyof HTMLElementEventMap>(target: EventTarget | null | undefined, event: K, listener: (e: HTMLElementEventMap[K]) => void, options?: AddEventListenerOptions): void;
|
|
74
|
+
export declare function handle(target: EventTarget | null | undefined, event: string, listener: EventListener, options?: AddEventListenerOptions): void;
|
|
75
|
+
export type FireApi = {
|
|
76
|
+
basic(target: EventTarget, type: string, options?: EventInit): boolean;
|
|
77
|
+
custom<Detail = unknown>(target: EventTarget, type: string, options?: CustomEventInit<Detail>): boolean;
|
|
78
|
+
event(target: EventTarget, event: Event): boolean;
|
|
79
|
+
focus(target: EventTarget, type: string, options?: FocusEventInit): boolean;
|
|
80
|
+
keyboard(target: EventTarget, type: string, options?: KeyboardEventInit): boolean;
|
|
81
|
+
mouse(target: EventTarget, type: string, options?: MouseEventInit): boolean;
|
|
82
|
+
touch(target: EventTarget, type: string, options?: TouchEventInit): boolean;
|
|
83
|
+
};
|
|
84
|
+
/**
|
|
85
|
+
* Dispatch DOM events explicitly without guessing constructors from the event name.
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* fire.mouse(el, 'click');
|
|
89
|
+
* fire.keyboard(el, 'keydown', { key: 'Enter' });
|
|
90
|
+
* fire.custom(el, 'change', { detail: { value: 42 } });
|
|
91
|
+
* fire.event(el, new PointerEvent('pointerdown'));
|
|
92
|
+
*/
|
|
93
|
+
export declare const fire: FireApi;
|
|
94
|
+
type AriaAttrValue = (() => string | boolean | number | null | undefined) | string | boolean | number | null | undefined;
|
|
95
|
+
/**
|
|
96
|
+
* Reactively set ARIA attributes on an element.
|
|
97
|
+
*
|
|
98
|
+
* - `aria(attrs)` — targets the component host (call during setup)
|
|
99
|
+
* - `aria(target, attrs)` — targets any element; returns a cleanup function
|
|
100
|
+
*
|
|
101
|
+
* Pass getter functions for reactive values. Plain values are set once.
|
|
102
|
+
* `null`, `undefined`, or `false` remove the attribute.
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* // Host (during setup):
|
|
106
|
+
* aria({ role: 'checkbox', checked: () => checked.value });
|
|
107
|
+
*
|
|
108
|
+
* // Inner element (inside onMount):
|
|
109
|
+
* onMount(() => {
|
|
110
|
+
* return aria(inputEl, { invalid: () => !!error.value });
|
|
111
|
+
* });
|
|
112
|
+
*/
|
|
113
|
+
export declare function aria(attrs: Record<string, AriaAttrValue>): void;
|
|
114
|
+
export declare function aria(target: Element, attrs: Record<string, AriaAttrValue>): CleanupFn;
|
|
115
|
+
export {};
|
|
116
|
+
//# sourceMappingURL=runtime-lifecycle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime-lifecycle.d.ts","sourceRoot":"","sources":["../../src/core/runtime-lifecycle.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import"./utilities.js";import{effect as e,onCleanup as t,untrack as n,watch as r}from"@vielzeug/stateit";var i=[],a=()=>{let e=i[i.length-1];if(!e)throw Error(`[craftit:E1] lifecycle outside setup`);return e},o=e=>{a().onMount.push(e)},s=e=>{i.length>0?a().cleanups.push(e):t(e)},c=e=>{a().errorHandlers.push(e)},l=e=>{i.length>0&&s(e)},u=(t,n)=>{let r=e(t,n);return l(r),r};function d(t,i,a){if(Array.isArray(t)){let r=a,o=!1,s=!1,c=e(()=>{for(let e of t)e.value;if(o)n(i),r?.once&&c();else if(o=!0,r?.immediate&&(n(i),r.once)){s=!0;return}});return l(c),s&&c(),c}let o=r(t,i,a);return l(o),o}function f(e,t,n,r){e&&(e.addEventListener(t,n,r),s(()=>e.removeEventListener(t,n,r)))}var p={bubbles:!0,cancelable:!0,composed:!0},m={basic(e,t,n={}){return e.dispatchEvent(new Event(t,{...p,...n}))},custom(e,t,n={}){return e.dispatchEvent(new CustomEvent(t,{...p,...n}))},event(e,t){return e.dispatchEvent(t)},focus(e,t,n={}){return e.dispatchEvent(new FocusEvent(t,{...p,...n}))},keyboard(e,t,n={}){return e.dispatchEvent(new KeyboardEvent(t,{...p,...n}))},mouse(e,t,n={}){return e.dispatchEvent(new MouseEvent(t,{...p,...n}))},touch(e,t,n={}){return typeof TouchEvent<`u`?e.dispatchEvent(new TouchEvent(t,{...p,...n})):e.dispatchEvent(new CustomEvent(t,{...p,...n}))}};function h(e,t){let n=t===void 0?a().el:e,r=t===void 0?e:t,i=(e,t)=>{let r=`aria-${e}`,i=typeof t==`function`?t():t;i==null||i===!1?n.removeAttribute(r):n.setAttribute(r,String(i))},o=Object.entries(r).map(([e,t])=>u(()=>i(e,t)));if(t!==void 0)return()=>{for(let e of o)e()}}export{h as aria,l as autoCleanup,a as currentRuntime,u as effect,m as fire,f as handle,s as onCleanup,c as onError,o as onMount,i as runtimeStack,d as watch};
|
|
2
|
+
//# sourceMappingURL=runtime-lifecycle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime-lifecycle.js","names":[],"sources":["../../src/core/runtime-lifecycle.ts"],"sourcesContent":["import {\n effect as _effect,\n onCleanup as _onCleanup,\n watch as _watch,\n type CleanupFn,\n type EffectCallback,\n type EffectOptions,\n type ReadonlySignal,\n type Subscription,\n untrack,\n type WatchOptions,\n} from '@vielzeug/stateit';\n\nimport { type CSSResult } from './utilities';\n\n// ─── Component runtime ────────────────────────────────────────────────────────\nexport type ComponentRuntime = {\n cleanups: CleanupFn[];\n el: HTMLElement;\n errorHandlers: ((err: unknown) => void)[];\n onMount: (() => CleanupFn | undefined | void)[];\n styles?: (string | CSSStyleSheet | CSSResult)[];\n};\n\nexport const runtimeStack: ComponentRuntime[] = [];\n\nexport const currentRuntime = (): ComponentRuntime => {\n const rt = runtimeStack[runtimeStack.length - 1];\n\n if (!rt) throw new Error('[craftit:E1] lifecycle outside setup');\n\n return rt;\n};\n\n// ─── Lifecycle hooks ──────────────────────────────────────────────────────────\nexport const onMount = (fn: () => CleanupFn | undefined | void): void => {\n currentRuntime().onMount.push(fn);\n};\n\n/**\n * Registers a cleanup function.\n * - Inside a component setup or `onMount` callback: runs when the component\n * unmounts. Prefer {@link onCleanup} for explicit lifecycle cleanup.\n * - Outside a component context (e.g. inside a plain `effect()`): delegates to\n * stateit's `onCleanup`, which runs before each effect re-run.\n */\nexport const onCleanup = (fn: CleanupFn): void => {\n if (runtimeStack.length > 0) {\n currentRuntime().cleanups.push(fn);\n } else {\n _onCleanup(fn);\n }\n};\n\n/**\n * Registers a scoped error handler for this component.\n * Called when an unhandled error is thrown during setup, `onMount` callbacks,\n * or rendering. If no handler is registered, errors are logged to the console.\n *\n * @example\n * defineComponent({\n * setup() {\n * onError((err) => console.error('Component error:', err));\n * // ... rest of setup\n * return html``;\n * },\n * tag: 'my-component',\n * });\n */\nexport const onError = (fn: (err: unknown) => void): void => {\n currentRuntime().errorHandlers.push(fn);\n};\n\n/** @internal — Cleanup only when inside a component context. */\nexport const autoCleanup = (dispose: Subscription | CleanupFn): void => {\n if (runtimeStack.length > 0) onCleanup(dispose);\n};\n\n/**\n * Creates a reactive effect that re-runs whenever its signal dependencies change.\n * When called inside a component setup function or `onMount` callback, the effect is\n * automatically cleaned up when the component unmounts — no manual `onCleanup` needed.\n * Outside a component context, behaves identically to stateit's `effect`.\n *\n * @example\n * // Inside setup — auto-cleaned on unmount:\n * effect(() => { document.title = props.title.value; });\n *\n * // Inside onMount — also auto-cleaned:\n * onMount(() => { effect(() => syncExternal(data.value)); });\n */\nexport const effect = (fn: EffectCallback, options?: EffectOptions): Subscription => {\n const dispose = _effect(fn, options);\n\n autoCleanup(dispose);\n\n return dispose;\n};\n\n// ─── Watch ────────────────────────────────────────────────────────────────────\n/**\n * Watch a signal or array of signals for changes and call a callback.\n * Auto-cleaned on unmount when called during setup.\n *\n * Single signal: callback receives (next, prev) values.\n * Multiple signals: callback receives no args, fires when any signal changes.\n */\nexport function watch<T>(\n source: ReadonlySignal<T>,\n cb: (value: T, prev: T) => void,\n options?: WatchOptions<T>,\n): Subscription;\nexport function watch(\n sources: ReadonlyArray<ReadonlySignal<unknown>>,\n cb: () => void,\n options?: WatchOptions<unknown>,\n): Subscription;\nexport function watch(\n source: ReadonlySignal<unknown> | ReadonlyArray<ReadonlySignal<unknown>>,\n cb: ((value: unknown, prev: unknown) => void) | (() => void),\n options?: WatchOptions<unknown>,\n): Subscription {\n if (Array.isArray(source)) {\n const opts = options;\n let initialized = false;\n let pendingDispose = false;\n const dispose = _effect(() => {\n for (const s of source) void s.value; // register all listed deps\n\n if (!initialized) {\n initialized = true;\n\n if (opts?.immediate) {\n untrack(cb as () => void);\n\n if (opts.once) {\n pendingDispose = true;\n\n return;\n }\n }\n } else {\n untrack(cb as () => void);\n\n if (opts?.once) dispose();\n }\n });\n\n autoCleanup(dispose);\n\n if (pendingDispose) dispose();\n\n return dispose;\n }\n\n const stop = _watch(source as ReadonlySignal<unknown>, cb as (value: unknown, prev: unknown) => void, options);\n\n autoCleanup(stop);\n\n return stop;\n}\n\n// ─── Event helpers ────────────────────────────────────────────────────────────\n/**\n * Register an event listener with automatic cleanup on unmount.\n * Use inside onMount() for auto-cleanup, or handle() return value manually.\n *\n * @example\n * onMount(() => {\n * handle(host, 'click', onClick);\n * handle(window, 'resize', onResize);\n * // auto-cleanup\n * });\n */\nexport function handle<K extends keyof HTMLElementEventMap>(\n target: EventTarget | null | undefined,\n event: K,\n listener: (e: HTMLElementEventMap[K]) => void,\n options?: AddEventListenerOptions,\n): void;\nexport function handle(\n target: EventTarget | null | undefined,\n event: string,\n listener: EventListener,\n options?: AddEventListenerOptions,\n): void;\nexport function handle(\n target: EventTarget | null | undefined,\n event: string,\n listener: EventListener,\n options?: AddEventListenerOptions,\n): void {\n if (!target) return;\n\n target.addEventListener(event, listener, options);\n onCleanup(() => target.removeEventListener(event, listener, options));\n}\n\ntype FireDefaults = Pick<EventInit, 'bubbles' | 'cancelable' | 'composed'>;\n\nexport type FireApi = {\n basic(target: EventTarget, type: string, options?: EventInit): boolean;\n custom<Detail = unknown>(target: EventTarget, type: string, options?: CustomEventInit<Detail>): boolean;\n event(target: EventTarget, event: Event): boolean;\n focus(target: EventTarget, type: string, options?: FocusEventInit): boolean;\n keyboard(target: EventTarget, type: string, options?: KeyboardEventInit): boolean;\n mouse(target: EventTarget, type: string, options?: MouseEventInit): boolean;\n touch(target: EventTarget, type: string, options?: TouchEventInit): boolean;\n};\n\nconst DEFAULT_FIRE_OPTIONS: FireDefaults = { bubbles: true, cancelable: true, composed: true };\n\n/**\n * Dispatch DOM events explicitly without guessing constructors from the event name.\n *\n * @example\n * fire.mouse(el, 'click');\n * fire.keyboard(el, 'keydown', { key: 'Enter' });\n * fire.custom(el, 'change', { detail: { value: 42 } });\n * fire.event(el, new PointerEvent('pointerdown'));\n */\nexport const fire: FireApi = {\n basic(target, type, options = {}) {\n return target.dispatchEvent(new Event(type, { ...DEFAULT_FIRE_OPTIONS, ...options }));\n },\n custom<Detail = unknown>(target: EventTarget, type: string, options: CustomEventInit<Detail> = {}) {\n return target.dispatchEvent(new CustomEvent<Detail>(type, { ...DEFAULT_FIRE_OPTIONS, ...options }));\n },\n event(target, event) {\n return target.dispatchEvent(event);\n },\n focus(target, type, options = {}) {\n return target.dispatchEvent(new FocusEvent(type, { ...DEFAULT_FIRE_OPTIONS, ...options }));\n },\n keyboard(target, type, options = {}) {\n return target.dispatchEvent(new KeyboardEvent(type, { ...DEFAULT_FIRE_OPTIONS, ...options }));\n },\n mouse(target, type, options = {}) {\n return target.dispatchEvent(new MouseEvent(type, { ...DEFAULT_FIRE_OPTIONS, ...options }));\n },\n touch(target, type, options = {}) {\n if (typeof TouchEvent !== 'undefined') {\n return target.dispatchEvent(new TouchEvent(type, { ...DEFAULT_FIRE_OPTIONS, ...options }));\n }\n\n return target.dispatchEvent(new CustomEvent(type, { ...DEFAULT_FIRE_OPTIONS, ...options }));\n },\n};\n\n// ─── ARIA helpers ────────────────────────────────────────────────────────────\ntype AriaAttrValue =\n | (() => string | boolean | number | null | undefined)\n | string\n | boolean\n | number\n | null\n | undefined;\n\n/**\n * Reactively set ARIA attributes on an element.\n *\n * - `aria(attrs)` — targets the component host (call during setup)\n * - `aria(target, attrs)` — targets any element; returns a cleanup function\n *\n * Pass getter functions for reactive values. Plain values are set once.\n * `null`, `undefined`, or `false` remove the attribute.\n *\n * @example\n * // Host (during setup):\n * aria({ role: 'checkbox', checked: () => checked.value });\n *\n * // Inner element (inside onMount):\n * onMount(() => {\n * return aria(inputEl, { invalid: () => !!error.value });\n * });\n */\nexport function aria(attrs: Record<string, AriaAttrValue>): void;\nexport function aria(target: Element, attrs: Record<string, AriaAttrValue>): CleanupFn;\nexport function aria(\n targetOrAttrs: Element | Record<string, AriaAttrValue>,\n maybeAttrs?: Record<string, AriaAttrValue>,\n): CleanupFn | undefined {\n const target = maybeAttrs !== undefined ? (targetOrAttrs as Element) : currentRuntime().el;\n const attrs = maybeAttrs !== undefined ? maybeAttrs : (targetOrAttrs as Record<string, AriaAttrValue>);\n\n const applyAttr = (name: string, val: AriaAttrValue) => {\n const attrName = `aria-${name}`;\n const value = typeof val === 'function' ? (val as () => AriaAttrValue)() : val;\n\n if (value === null || value === undefined || value === false) {\n target.removeAttribute(attrName);\n } else {\n target.setAttribute(attrName, String(value));\n }\n };\n\n // Each key gets its own effect so changes to one attr don't re-apply all others.\n const stops = Object.entries(attrs).map(([name, val]) => effect(() => applyAttr(name, val)));\n\n if (maybeAttrs !== undefined) {\n return () => {\n for (const stop of stops) stop();\n };\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require(`./runtime-lifecycle.cjs`),require(`./runtime-bindings.cjs`);
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { aria, effect, fire, handle, onCleanup, onError, onMount, watch, autoCleanup, currentRuntime, runtimeStack, type ComponentRuntime, } from './runtime-lifecycle';
|
|
2
|
+
export { bindPropertyModel, hasWritableValueSetter, toReactiveBindingSource, type RegisterPropertyCleanup, } from './runtime-bindings';
|
|
3
|
+
//# sourceMappingURL=runtime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/core/runtime.ts"],"names":[],"mappings":"AACA,OAAO,EACL,IAAI,EACJ,MAAM,EACN,IAAI,EACJ,MAAM,EACN,SAAS,EACT,OAAO,EACP,OAAO,EACP,KAAK,EACL,WAAW,EACX,cAAc,EACd,YAAY,EACZ,KAAK,gBAAgB,GACtB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,iBAAiB,EACjB,sBAAsB,EACtB,uBAAuB,EACvB,KAAK,uBAAuB,GAC7B,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./runtime-lifecycle.js";import"./runtime-bindings.js";
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
require(`./internal.cjs`);const e=require(`./runtime-lifecycle.cjs`),t=require(`./utilities.cjs`),n=require(`./runtime-bindings.cjs`);require(`./template-dom.cjs`);const r=require(`./component.cjs`);require(`@vielzeug/stateit`);var i=e=>Array.isArray(e)||typeof e==`object`&&!!e,a=(t,n,r)=>{r(e.effect(()=>n(t.value)))},o=(e,n,o)=>{let s=a=>{let o=r.propRegistry.get(e)?.get(n.name);if(!o&&i(a)){e[n.name]=a;return}if((!o||o.reflect)&&(n.mode===`bool`?e.toggleAttribute(n.name,!!a):t.setAttr(e,n.name,a)),!o)return;let s=i(a)?a:o.parse(n.mode===`bool`?a?``:null:a==null||a===!1?null:String(a));Object.is(o.signal.peek(),s)||(o.signal.value=s)};n.signal?a(n.signal,s,o):s(n.value)},s=(e,t,r)=>{let i=n=>{e[t.name]=n};t.signal?a(t.signal,i,r):i(t.value),n.bindPropertyModel(e,t.name,t.model,r)},c=(e,n,r)=>{let{modifiers:i}=n,a=i?{capture:!!i.capture,once:!!i.once,passive:!!i.passive}:void 0;r(t.listen(e,n.name,e=>{i?.self&&e.target!==e.currentTarget||(i?.stop&&e.stopPropagation(),i?.prevent&&!i?.passive&&e.preventDefault(),n.handler(e))},a))},l=(e,t,n)=>{let{ref:r}=t;if(typeof r==`function`){r(e),n(()=>r(null));return}if(Array.isArray(r)){r.push(e),n(()=>{let t=r.indexOf(e);t!==-1&&r.splice(t,1)});return}r.value=e,n(()=>{r.value=null})},u=(e,t,n,r)=>{let i=new Map;for(let o of e){let e=o.uid;if(o.type===`text`){let r=n.comments.get(e);if(r){let i=document.createTextNode(``);r.replaceWith(i),n.comments.delete(e),a(o.signal,e=>{i.textContent=String(e)},t)}}else o.type===`html`?r?.onHtml?.(o):(i.has(e)||i.set(e,[]),i.get(e).push(o))}for(let[e,r]of i){let i=n.elements.get(e);if(i){i.removeAttribute(`u`),n.elements.delete(e);for(let e of r)switch(e.type){case`attr`:o(i,e,t);break;case`callback`:e.apply(i,t);break;case`event`:c(i,e,t);break;case`prop`:s(i,e,t);break;case`ref`:l(i,e,t);break}}}},d=(e,t,r,i)=>{let a=n.toReactiveBindingSource(i);return a?{mode:e,name:t,signal:a,type:`attr`,uid:r}:{mode:e,name:t,type:`attr`,uid:r,value:i}},f=(e,t,r)=>{let i=n.toReactiveBindingSource(r);return i?{model:n.hasWritableValueSetter(r)?r:void 0,name:e,signal:i,type:`prop`,uid:t}:{name:e,type:`prop`,uid:t,value:r}};exports.applyBindingsWithTargets=u,exports.createAttrBinding=d,exports.createPropBinding=f;
|
|
2
|
+
//# sourceMappingURL=template-bindings.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template-bindings.cjs","names":[],"sources":["../../src/core/template-bindings.ts"],"sourcesContent":["import { type CleanupFn, type ReadonlySignal } from '@vielzeug/stateit';\n\nimport { propRegistry } from './component';\nimport {\n type AttrBinding,\n type EventBinding,\n type PropBinding,\n type RefBinding,\n type Binding,\n type HtmlBinding,\n CF_ID_ATTR,\n} from './internal';\nimport { bindPropertyModel } from './runtime-bindings';\nimport { effect } from './runtime-lifecycle';\nimport { listen, setAttr } from './utilities';\n\nexport type RegisterCleanup = (fn: CleanupFn) => void;\n\n// ─── Helper utilities ────────────────────────────────────────────────────────\n\n/** Check if a value is a structured type (object or array), not a primitive. */\nconst isStructuredValue = (value: unknown): value is object =>\n Array.isArray(value) || (typeof value === 'object' && value !== null);\n\n/**\n * Register a reactive effect that updates when a signal changes.\n * Common pattern: `if (signal) registerCleanup(effect(() => update(signal.value)))`\n */\nconst signalEffect = (\n signal: ReadonlySignal<unknown>,\n update: (v: unknown) => void,\n registerCleanup: RegisterCleanup,\n): void => {\n registerCleanup(effect(() => update(signal.value)));\n};\n\n// ─── Individual binding application functions ─────────────────────────────────\n\n/**\n * Apply an attribute binding to an element.\n * Handles bool/attr modes, prop pre-upgrade, and reactive updates.\n */\nexport const applyAttrBinding = (el: HTMLElement, binding: AttrBinding, registerCleanup: RegisterCleanup) => {\n const update = (value: unknown) => {\n const meta = propRegistry.get(el)?.get(binding.name);\n\n // Preserve structured values as pre-upgrade properties\n if (!meta && isStructuredValue(value)) {\n (el as any)[binding.name] = value;\n\n return;\n }\n\n if (!meta || meta.reflect) {\n if (binding.mode === 'bool') {\n el.toggleAttribute(binding.name, Boolean(value));\n } else {\n setAttr(el, binding.name, value);\n }\n }\n\n if (!meta) return;\n\n const parsedValue = isStructuredValue(value)\n ? value\n : meta.parse(\n binding.mode === 'bool' ? (value ? '' : null) : value == null || value === false ? null : String(value),\n );\n\n if (!Object.is(meta.signal.peek(), parsedValue)) {\n meta.signal.value = parsedValue as never;\n }\n };\n\n if (binding.signal) {\n signalEffect(binding.signal, update, registerCleanup);\n } else {\n update(binding.value!);\n }\n};\n\n/**\n * Apply a property binding to an element.\n * Handles reactive updates and two-way binding via property models.\n */\nexport const applyPropBinding = (el: HTMLElement, binding: PropBinding, registerCleanup: RegisterCleanup) => {\n const update = (value: unknown) => {\n (el as any)[binding.name] = value;\n };\n\n if (binding.signal) {\n signalEffect(binding.signal, update, registerCleanup);\n } else {\n update(binding.value!);\n }\n\n bindPropertyModel(el, binding.name, binding.model, registerCleanup);\n};\n\n/**\n * Apply an event listener binding to an element.\n * Handles event modifiers (stop, prevent, self, capture, once, passive).\n */\nexport const applyEventBinding = (el: HTMLElement, binding: EventBinding, registerCleanup: RegisterCleanup) => {\n const { modifiers } = binding;\n const listenerOptions = modifiers\n ? { capture: !!modifiers.capture, once: !!modifiers.once, passive: !!modifiers.passive }\n : undefined;\n\n const wrappedHandler = (event: Event) => {\n if (modifiers?.self && event.target !== event.currentTarget) return;\n\n if (modifiers?.stop) event.stopPropagation();\n\n if (modifiers?.prevent && !modifiers?.passive) event.preventDefault();\n\n binding.handler(event);\n };\n\n registerCleanup(listen(el, binding.name, wrappedHandler, listenerOptions));\n};\n\n/**\n * Apply a ref binding to an element.\n * Supports function refs, ref arrays, and signal refs with cleanup.\n */\nexport const applyRefBinding = (el: HTMLElement, binding: RefBinding, registerCleanup: RegisterCleanup) => {\n const { ref } = binding;\n\n if (typeof ref === 'function') {\n ref(el as never);\n registerCleanup(() => ref(null));\n\n return;\n }\n\n if (Array.isArray(ref)) {\n ref.push(el);\n registerCleanup(() => {\n const idx = ref.indexOf(el);\n\n if (idx !== -1) ref.splice(idx, 1);\n });\n\n return;\n }\n\n ref.value = el as never;\n registerCleanup(() => {\n ref.value = null;\n });\n};\n\n// ─── Binding orchestration ────────────────────────────────────────────────────\n\nimport { type BindingTargets } from './template-dom';\n\n/**\n * Apply all bindings to target elements.\n *\n * - Text bindings: Create text nodes, register reactive effects\n * - HTML bindings: Notify caller for keyed reconciliation\n * - Element bindings: Group by ID, apply attr/prop/event/ref in one pass per element\n *\n * @param bindings Array of compiled bindings to apply\n * @param registerCleanup Function to register cleanup callbacks\n * @param targets Indexed comment/element targets from DOM\n * @param opts Optional callbacks (e.g., onHtml for keyed reconciliation)\n */\nexport const applyBindingsWithTargets = (\n bindings: Binding[],\n registerCleanup: RegisterCleanup,\n targets: BindingTargets,\n opts?: { onHtml?: (b: HtmlBinding) => void },\n) => {\n const bindingMap = new Map<string, Binding[]>();\n\n for (const b of bindings) {\n const id = b.uid;\n\n if (b.type === 'text') {\n const found = targets.comments.get(id);\n\n if (found) {\n const textNode = document.createTextNode('');\n\n found.replaceWith(textNode);\n targets.comments.delete(id);\n signalEffect(\n b.signal,\n (v) => {\n textNode.textContent = String(v);\n },\n registerCleanup,\n );\n }\n } else if (b.type === 'html') {\n opts?.onHtml?.(b);\n } else {\n if (!bindingMap.has(id)) bindingMap.set(id, []);\n\n bindingMap.get(id)!.push(b);\n }\n }\n\n for (const [id, elBindings] of bindingMap) {\n const el = targets.elements.get(id);\n\n if (!el) continue;\n\n el.removeAttribute(CF_ID_ATTR);\n targets.elements.delete(id);\n\n for (const b of elBindings) {\n switch (b.type) {\n case 'attr':\n applyAttrBinding(el, b, registerCleanup);\n break;\n case 'callback':\n b.apply(el, registerCleanup);\n break;\n case 'event':\n applyEventBinding(el, b, registerCleanup);\n break;\n case 'prop':\n applyPropBinding(el, b, registerCleanup);\n break;\n case 'ref':\n applyRefBinding(el, b, registerCleanup);\n break;\n }\n }\n }\n};\n\n// ─── Binding factories ────────────────────────────────────────────────────────\n\nimport { type Signal } from '@vielzeug/stateit';\n\nimport { hasWritableValueSetter, toReactiveBindingSource } from './runtime-bindings';\n\n/**\n * Create an attribute binding descriptor.\n * Called during template compilation for each attribute interpolation.\n *\n * @param mode 'bool' for boolean attributes (presence = true), 'attr' for string values\n * @param name Attribute name (e.g., 'disabled', 'aria-label')\n * @param uid Unique binding ID\n * @param value Attribute value (signal or static)\n */\nexport const createAttrBinding = (mode: 'bool' | 'attr', name: string, uid: string, value: unknown): AttrBinding => {\n const source = toReactiveBindingSource(value);\n\n return source ? { mode, name, signal: source, type: 'attr', uid } : { mode, name, type: 'attr', uid, value };\n};\n\n/**\n * Create a property binding descriptor.\n * Called during template compilation for each `.property` interpolation.\n *\n * @param name Property name (e.g., 'value', 'checked')\n * @param uid Unique binding ID\n * @param value Property value (signal, function, or static)\n */\nexport const createPropBinding = (name: string, uid: string, value: unknown): PropBinding => {\n const source = toReactiveBindingSource(value);\n\n if (source) {\n return {\n model: hasWritableValueSetter(value) ? (value as Signal<unknown>) : undefined,\n name,\n signal: source,\n type: 'prop',\n uid,\n };\n }\n\n return { name, type: 'prop', uid, value };\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { type CleanupFn } from '@vielzeug/stateit';
|
|
2
|
+
import { type AttrBinding, type EventBinding, type PropBinding, type RefBinding, type Binding, type HtmlBinding } from './internal';
|
|
3
|
+
export type RegisterCleanup = (fn: CleanupFn) => void;
|
|
4
|
+
/**
|
|
5
|
+
* Apply an attribute binding to an element.
|
|
6
|
+
* Handles bool/attr modes, prop pre-upgrade, and reactive updates.
|
|
7
|
+
*/
|
|
8
|
+
export declare const applyAttrBinding: (el: HTMLElement, binding: AttrBinding, registerCleanup: RegisterCleanup) => void;
|
|
9
|
+
/**
|
|
10
|
+
* Apply a property binding to an element.
|
|
11
|
+
* Handles reactive updates and two-way binding via property models.
|
|
12
|
+
*/
|
|
13
|
+
export declare const applyPropBinding: (el: HTMLElement, binding: PropBinding, registerCleanup: RegisterCleanup) => void;
|
|
14
|
+
/**
|
|
15
|
+
* Apply an event listener binding to an element.
|
|
16
|
+
* Handles event modifiers (stop, prevent, self, capture, once, passive).
|
|
17
|
+
*/
|
|
18
|
+
export declare const applyEventBinding: (el: HTMLElement, binding: EventBinding, registerCleanup: RegisterCleanup) => void;
|
|
19
|
+
/**
|
|
20
|
+
* Apply a ref binding to an element.
|
|
21
|
+
* Supports function refs, ref arrays, and signal refs with cleanup.
|
|
22
|
+
*/
|
|
23
|
+
export declare const applyRefBinding: (el: HTMLElement, binding: RefBinding, registerCleanup: RegisterCleanup) => void;
|
|
24
|
+
import { type BindingTargets } from './template-dom';
|
|
25
|
+
/**
|
|
26
|
+
* Apply all bindings to target elements.
|
|
27
|
+
*
|
|
28
|
+
* - Text bindings: Create text nodes, register reactive effects
|
|
29
|
+
* - HTML bindings: Notify caller for keyed reconciliation
|
|
30
|
+
* - Element bindings: Group by ID, apply attr/prop/event/ref in one pass per element
|
|
31
|
+
*
|
|
32
|
+
* @param bindings Array of compiled bindings to apply
|
|
33
|
+
* @param registerCleanup Function to register cleanup callbacks
|
|
34
|
+
* @param targets Indexed comment/element targets from DOM
|
|
35
|
+
* @param opts Optional callbacks (e.g., onHtml for keyed reconciliation)
|
|
36
|
+
*/
|
|
37
|
+
export declare const applyBindingsWithTargets: (bindings: Binding[], registerCleanup: RegisterCleanup, targets: BindingTargets, opts?: {
|
|
38
|
+
onHtml?: (b: HtmlBinding) => void;
|
|
39
|
+
}) => void;
|
|
40
|
+
/**
|
|
41
|
+
* Create an attribute binding descriptor.
|
|
42
|
+
* Called during template compilation for each attribute interpolation.
|
|
43
|
+
*
|
|
44
|
+
* @param mode 'bool' for boolean attributes (presence = true), 'attr' for string values
|
|
45
|
+
* @param name Attribute name (e.g., 'disabled', 'aria-label')
|
|
46
|
+
* @param uid Unique binding ID
|
|
47
|
+
* @param value Attribute value (signal or static)
|
|
48
|
+
*/
|
|
49
|
+
export declare const createAttrBinding: (mode: "bool" | "attr", name: string, uid: string, value: unknown) => AttrBinding;
|
|
50
|
+
/**
|
|
51
|
+
* Create a property binding descriptor.
|
|
52
|
+
* Called during template compilation for each `.property` interpolation.
|
|
53
|
+
*
|
|
54
|
+
* @param name Property name (e.g., 'value', 'checked')
|
|
55
|
+
* @param uid Unique binding ID
|
|
56
|
+
* @param value Property value (signal, function, or static)
|
|
57
|
+
*/
|
|
58
|
+
export declare const createPropBinding: (name: string, uid: string, value: unknown) => PropBinding;
|
|
59
|
+
//# sourceMappingURL=template-bindings.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template-bindings.d.ts","sourceRoot":"","sources":["../../src/core/template-bindings.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAuB,MAAM,mBAAmB,CAAC;AAGxE,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,OAAO,EACZ,KAAK,WAAW,EAEjB,MAAM,YAAY,CAAC;AAKpB,MAAM,MAAM,eAAe,GAAG,CAAC,EAAE,EAAE,SAAS,KAAK,IAAI,CAAC;AAsBtD;;;GAGG;AACH,eAAO,MAAM,gBAAgB,GAAI,IAAI,WAAW,EAAE,SAAS,WAAW,EAAE,iBAAiB,eAAe,SAqCvG,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,gBAAgB,GAAI,IAAI,WAAW,EAAE,SAAS,WAAW,EAAE,iBAAiB,eAAe,SAYvG,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,iBAAiB,GAAI,IAAI,WAAW,EAAE,SAAS,YAAY,EAAE,iBAAiB,eAAe,SAiBzG,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,eAAe,GAAI,IAAI,WAAW,EAAE,SAAS,UAAU,EAAE,iBAAiB,eAAe,SAyBrG,CAAC;AAIF,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAErD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,wBAAwB,GACnC,UAAU,OAAO,EAAE,EACnB,iBAAiB,eAAe,EAChC,SAAS,cAAc,EACvB,OAAO;IAAE,MAAM,CAAC,EAAE,CAAC,CAAC,EAAE,WAAW,KAAK,IAAI,CAAA;CAAE,SA4D7C,CAAC;AAQF;;;;;;;;GAQG;AACH,eAAO,MAAM,iBAAiB,GAAI,MAAM,MAAM,GAAG,MAAM,EAAE,MAAM,MAAM,EAAE,KAAK,MAAM,EAAE,OAAO,OAAO,KAAG,WAIpG,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB,GAAI,MAAM,MAAM,EAAE,KAAK,MAAM,EAAE,OAAO,OAAO,KAAG,WAc7E,CAAC"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import"./internal.js";import{effect as e}from"./runtime-lifecycle.js";import{listen as t,setAttr as n}from"./utilities.js";import{bindPropertyModel as r,hasWritableValueSetter as i,toReactiveBindingSource as a}from"./runtime-bindings.js";import"./template-dom.js";import{propRegistry as o}from"./component.js";import"@vielzeug/stateit";var s=e=>Array.isArray(e)||typeof e==`object`&&!!e,c=(t,n,r)=>{r(e(()=>n(t.value)))},l=(e,t,r)=>{let i=r=>{let i=o.get(e)?.get(t.name);if(!i&&s(r)){e[t.name]=r;return}if((!i||i.reflect)&&(t.mode===`bool`?e.toggleAttribute(t.name,!!r):n(e,t.name,r)),!i)return;let a=s(r)?r:i.parse(t.mode===`bool`?r?``:null:r==null||r===!1?null:String(r));Object.is(i.signal.peek(),a)||(i.signal.value=a)};t.signal?c(t.signal,i,r):i(t.value)},u=(e,t,n)=>{let i=n=>{e[t.name]=n};t.signal?c(t.signal,i,n):i(t.value),r(e,t.name,t.model,n)},d=(e,n,r)=>{let{modifiers:i}=n,a=i?{capture:!!i.capture,once:!!i.once,passive:!!i.passive}:void 0;r(t(e,n.name,e=>{i?.self&&e.target!==e.currentTarget||(i?.stop&&e.stopPropagation(),i?.prevent&&!i?.passive&&e.preventDefault(),n.handler(e))},a))},f=(e,t,n)=>{let{ref:r}=t;if(typeof r==`function`){r(e),n(()=>r(null));return}if(Array.isArray(r)){r.push(e),n(()=>{let t=r.indexOf(e);t!==-1&&r.splice(t,1)});return}r.value=e,n(()=>{r.value=null})},p=(e,t,n,r)=>{let i=new Map;for(let a of e){let e=a.uid;if(a.type===`text`){let r=n.comments.get(e);if(r){let i=document.createTextNode(``);r.replaceWith(i),n.comments.delete(e),c(a.signal,e=>{i.textContent=String(e)},t)}}else a.type===`html`?r?.onHtml?.(a):(i.has(e)||i.set(e,[]),i.get(e).push(a))}for(let[e,r]of i){let i=n.elements.get(e);if(i){i.removeAttribute(`u`),n.elements.delete(e);for(let e of r)switch(e.type){case`attr`:l(i,e,t);break;case`callback`:e.apply(i,t);break;case`event`:d(i,e,t);break;case`prop`:u(i,e,t);break;case`ref`:f(i,e,t);break}}}},m=(e,t,n,r)=>{let i=a(r);return i?{mode:e,name:t,signal:i,type:`attr`,uid:n}:{mode:e,name:t,type:`attr`,uid:n,value:r}},h=(e,t,n)=>{let r=a(n);return r?{model:i(n)?n:void 0,name:e,signal:r,type:`prop`,uid:t}:{name:e,type:`prop`,uid:t,value:n}};export{p as applyBindingsWithTargets,m as createAttrBinding,h as createPropBinding};
|
|
2
|
+
//# sourceMappingURL=template-bindings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template-bindings.js","names":[],"sources":["../../src/core/template-bindings.ts"],"sourcesContent":["import { type CleanupFn, type ReadonlySignal } from '@vielzeug/stateit';\n\nimport { propRegistry } from './component';\nimport {\n type AttrBinding,\n type EventBinding,\n type PropBinding,\n type RefBinding,\n type Binding,\n type HtmlBinding,\n CF_ID_ATTR,\n} from './internal';\nimport { bindPropertyModel } from './runtime-bindings';\nimport { effect } from './runtime-lifecycle';\nimport { listen, setAttr } from './utilities';\n\nexport type RegisterCleanup = (fn: CleanupFn) => void;\n\n// ─── Helper utilities ────────────────────────────────────────────────────────\n\n/** Check if a value is a structured type (object or array), not a primitive. */\nconst isStructuredValue = (value: unknown): value is object =>\n Array.isArray(value) || (typeof value === 'object' && value !== null);\n\n/**\n * Register a reactive effect that updates when a signal changes.\n * Common pattern: `if (signal) registerCleanup(effect(() => update(signal.value)))`\n */\nconst signalEffect = (\n signal: ReadonlySignal<unknown>,\n update: (v: unknown) => void,\n registerCleanup: RegisterCleanup,\n): void => {\n registerCleanup(effect(() => update(signal.value)));\n};\n\n// ─── Individual binding application functions ─────────────────────────────────\n\n/**\n * Apply an attribute binding to an element.\n * Handles bool/attr modes, prop pre-upgrade, and reactive updates.\n */\nexport const applyAttrBinding = (el: HTMLElement, binding: AttrBinding, registerCleanup: RegisterCleanup) => {\n const update = (value: unknown) => {\n const meta = propRegistry.get(el)?.get(binding.name);\n\n // Preserve structured values as pre-upgrade properties\n if (!meta && isStructuredValue(value)) {\n (el as any)[binding.name] = value;\n\n return;\n }\n\n if (!meta || meta.reflect) {\n if (binding.mode === 'bool') {\n el.toggleAttribute(binding.name, Boolean(value));\n } else {\n setAttr(el, binding.name, value);\n }\n }\n\n if (!meta) return;\n\n const parsedValue = isStructuredValue(value)\n ? value\n : meta.parse(\n binding.mode === 'bool' ? (value ? '' : null) : value == null || value === false ? null : String(value),\n );\n\n if (!Object.is(meta.signal.peek(), parsedValue)) {\n meta.signal.value = parsedValue as never;\n }\n };\n\n if (binding.signal) {\n signalEffect(binding.signal, update, registerCleanup);\n } else {\n update(binding.value!);\n }\n};\n\n/**\n * Apply a property binding to an element.\n * Handles reactive updates and two-way binding via property models.\n */\nexport const applyPropBinding = (el: HTMLElement, binding: PropBinding, registerCleanup: RegisterCleanup) => {\n const update = (value: unknown) => {\n (el as any)[binding.name] = value;\n };\n\n if (binding.signal) {\n signalEffect(binding.signal, update, registerCleanup);\n } else {\n update(binding.value!);\n }\n\n bindPropertyModel(el, binding.name, binding.model, registerCleanup);\n};\n\n/**\n * Apply an event listener binding to an element.\n * Handles event modifiers (stop, prevent, self, capture, once, passive).\n */\nexport const applyEventBinding = (el: HTMLElement, binding: EventBinding, registerCleanup: RegisterCleanup) => {\n const { modifiers } = binding;\n const listenerOptions = modifiers\n ? { capture: !!modifiers.capture, once: !!modifiers.once, passive: !!modifiers.passive }\n : undefined;\n\n const wrappedHandler = (event: Event) => {\n if (modifiers?.self && event.target !== event.currentTarget) return;\n\n if (modifiers?.stop) event.stopPropagation();\n\n if (modifiers?.prevent && !modifiers?.passive) event.preventDefault();\n\n binding.handler(event);\n };\n\n registerCleanup(listen(el, binding.name, wrappedHandler, listenerOptions));\n};\n\n/**\n * Apply a ref binding to an element.\n * Supports function refs, ref arrays, and signal refs with cleanup.\n */\nexport const applyRefBinding = (el: HTMLElement, binding: RefBinding, registerCleanup: RegisterCleanup) => {\n const { ref } = binding;\n\n if (typeof ref === 'function') {\n ref(el as never);\n registerCleanup(() => ref(null));\n\n return;\n }\n\n if (Array.isArray(ref)) {\n ref.push(el);\n registerCleanup(() => {\n const idx = ref.indexOf(el);\n\n if (idx !== -1) ref.splice(idx, 1);\n });\n\n return;\n }\n\n ref.value = el as never;\n registerCleanup(() => {\n ref.value = null;\n });\n};\n\n// ─── Binding orchestration ────────────────────────────────────────────────────\n\nimport { type BindingTargets } from './template-dom';\n\n/**\n * Apply all bindings to target elements.\n *\n * - Text bindings: Create text nodes, register reactive effects\n * - HTML bindings: Notify caller for keyed reconciliation\n * - Element bindings: Group by ID, apply attr/prop/event/ref in one pass per element\n *\n * @param bindings Array of compiled bindings to apply\n * @param registerCleanup Function to register cleanup callbacks\n * @param targets Indexed comment/element targets from DOM\n * @param opts Optional callbacks (e.g., onHtml for keyed reconciliation)\n */\nexport const applyBindingsWithTargets = (\n bindings: Binding[],\n registerCleanup: RegisterCleanup,\n targets: BindingTargets,\n opts?: { onHtml?: (b: HtmlBinding) => void },\n) => {\n const bindingMap = new Map<string, Binding[]>();\n\n for (const b of bindings) {\n const id = b.uid;\n\n if (b.type === 'text') {\n const found = targets.comments.get(id);\n\n if (found) {\n const textNode = document.createTextNode('');\n\n found.replaceWith(textNode);\n targets.comments.delete(id);\n signalEffect(\n b.signal,\n (v) => {\n textNode.textContent = String(v);\n },\n registerCleanup,\n );\n }\n } else if (b.type === 'html') {\n opts?.onHtml?.(b);\n } else {\n if (!bindingMap.has(id)) bindingMap.set(id, []);\n\n bindingMap.get(id)!.push(b);\n }\n }\n\n for (const [id, elBindings] of bindingMap) {\n const el = targets.elements.get(id);\n\n if (!el) continue;\n\n el.removeAttribute(CF_ID_ATTR);\n targets.elements.delete(id);\n\n for (const b of elBindings) {\n switch (b.type) {\n case 'attr':\n applyAttrBinding(el, b, registerCleanup);\n break;\n case 'callback':\n b.apply(el, registerCleanup);\n break;\n case 'event':\n applyEventBinding(el, b, registerCleanup);\n break;\n case 'prop':\n applyPropBinding(el, b, registerCleanup);\n break;\n case 'ref':\n applyRefBinding(el, b, registerCleanup);\n break;\n }\n }\n }\n};\n\n// ─── Binding factories ────────────────────────────────────────────────────────\n\nimport { type Signal } from '@vielzeug/stateit';\n\nimport { hasWritableValueSetter, toReactiveBindingSource } from './runtime-bindings';\n\n/**\n * Create an attribute binding descriptor.\n * Called during template compilation for each attribute interpolation.\n *\n * @param mode 'bool' for boolean attributes (presence = true), 'attr' for string values\n * @param name Attribute name (e.g., 'disabled', 'aria-label')\n * @param uid Unique binding ID\n * @param value Attribute value (signal or static)\n */\nexport const createAttrBinding = (mode: 'bool' | 'attr', name: string, uid: string, value: unknown): AttrBinding => {\n const source = toReactiveBindingSource(value);\n\n return source ? { mode, name, signal: source, type: 'attr', uid } : { mode, name, type: 'attr', uid, value };\n};\n\n/**\n * Create a property binding descriptor.\n * Called during template compilation for each `.property` interpolation.\n *\n * @param name Property name (e.g., 'value', 'checked')\n * @param uid Unique binding ID\n * @param value Property value (signal, function, or static)\n */\nexport const createPropBinding = (name: string, uid: string, value: unknown): PropBinding => {\n const source = toReactiveBindingSource(value);\n\n if (source) {\n return {\n model: hasWritableValueSetter(value) ? (value as Signal<unknown>) : undefined,\n name,\n signal: source,\n type: 'prop',\n uid,\n };\n }\n\n return { name, type: 'prop', uid, value };\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require(`./internal.cjs`),t=require(`./utilities.cjs`),n=require(`./template-bindings.cjs`);let r=require(`@vielzeug/stateit`);var i=RegExp(`u="([^"]+)"`,`g`),a=e=>typeof e==`object`&&!!e&&`__html`in e,o=e=>e.replace(/>\s+</g,`><`).trim(),s=[{kind:`event`,regex:/\s+@([a-zA-Z_][-a-zA-Z0-9_.]*)\s*=\s*["']?$/},{kind:`ref`,regex:/\s+ref\s*=\s*["']?$/},{kind:`specialAttr`,regex:/\s+([:?])([a-zA-Z_][-a-zA-Z0-9_]*)\s*=\s*["']?$/},{kind:`prop`,regex:/\.([a-zA-Z_][-a-zA-Z0-9_]*)\s*=\s*["']?$/},{kind:`plainAttr`,regex:/\s+([a-zA-Z_][-a-zA-Z0-9_]*)\s*=\s*["']?$/}],c=new WeakMap,l=e=>{let[t,...n]=e.split(`.`),r={};for(let e of n)e===`capture`?r.capture=!0:e===`once`?r.once=!0:e===`passive`?r.passive=!0:e===`prevent`?r.prevent=!0:e===`self`?r.self=!0:e===`stop`&&(r.stop=!0);return{modifiers:Object.keys(r).length?r:void 0,name:t}},u=e=>{let t=[];for(let n=0;n<e.length-1;n++){let r=e[n],i=!1;for(let e of s){let n=e.regex.exec(r);if(!n)continue;let a=r.slice(0,-n[0].length);if(i=!0,e.kind===`event`){let e=l(n[1]);t.push({kind:`event`,modifiers:e.modifiers,name:e.name,prefix:a,raw:r})}else e.kind===`ref`?t.push({kind:`ref`,prefix:a,raw:r}):e.kind===`specialAttr`?t.push({kind:`specialAttr`,mode:n[1]===`?`?`bool`:`attr`,name:n[2],prefix:a,raw:r}):e.kind===`prop`?t.push({kind:`prop`,name:n[1],prefix:a,raw:r}):e.kind===`plainAttr`&&t.push({kind:`plainAttr`,name:n[1],prefix:a,raw:r});break}i||t.push({kind:`node`,prefix:r,raw:r})}return{slots:t,tail:e[e.length-1]??``}},d=e=>{let t=c.get(e);return t||(t=u(e),c.set(e,t)),t},f=()=>{let e=0;return()=>String(e++)},p=(e,t)=>{let n=new Map,r=e=>{let r=n.get(e);if(r)return r;let i=t();return n.set(e,i),i};return{bindings:e.__bindings.map(e=>({...e,uid:r(e.uid)})),html:e.__html.replace(i,(e,t)=>`u="${r(t)}"`).replace(/<!--(\d+)-->/g,(e,t)=>`<!--${r(t)}-->`)}},m=t=>typeof t!=`object`||!t||!(e.EACH_SIGNAL in t)?null:t[e.EACH_SIGNAL],h=e=>typeof e==`string`?t.escapeHtml(e):e==null?``:a(e)?e.__html:t.escapeHtml(String(e)),g=(e,t)=>{let n={bindings:[],html:``},i=(0,r.signal)(n);return t(()=>{let t=e(),r=Array.isArray(t)?t:[t],o=f(),s=``,c=[];for(let e of r)if(a(e)){let t=p(e,o);s+=t.html,c.push(...t.bindings)}else s+=h(e);let l=c.length!==n.bindings.length||c.some((e,t)=>e!==n.bindings[t]);(s!==n.html||l)&&(n={bindings:c,html:s},i.value=n)}),{bindings:[],signal:i}},_=(e,t)=>{let n=m(e);if(n)return{keyed:!0,signal:n};if(typeof e==`function`&&!(0,r.isSignal)(e)){let{signal:n}=g(e,t);return{keyed:!1,signal:n}}return(0,r.isSignal)(e)&&a(e.value)?{keyed:!1,signal:(0,r.computed)(()=>{let t=e.value;if(!a(t))return{bindings:[],html:String(t)};let n=p(t,f());return{bindings:n.bindings,html:n.html}})}:null},v=()=>{},y=(t,i,s)=>{let c=d(t),l=``,u=[],m=null,g=f(),v=e=>e.lastIndexOf(`<`)>e.lastIndexOf(`>`),y=e=>((!m||v(e))&&(m=g()),m),b=()=>{m=null};for(let e=0;e<c.slots.length;e++){let t=c.slots[e],o=i[e];if(t.kind===`event`){if(typeof o==`function`){let e=y(t.prefix);l+=`${t.prefix} u="${e}"`,u.push({handler:o,modifiers:t.modifiers,name:t.name,type:`event`,uid:e})}else l+=t.raw;continue}if(t.kind===`ref`){if(o){let e=y(t.prefix);l+=`${t.prefix} u="${e}"`,u.push({ref:o,type:`ref`,uid:e})}else l+=t.raw;continue}if(t.kind===`specialAttr`){let e=y(t.prefix);l+=`${t.prefix} u="${e}"`,u.push(n.createAttrBinding(t.mode,t.name,e,o));continue}if(t.kind===`prop`){let e=y(t.prefix);l+=`${t.prefix} u="${e}"`,u.push(n.createPropBinding(t.name,e,o));continue}if(t.kind===`plainAttr`){let e=y(t.prefix);l+=`${t.prefix} u="${e}"`,u.push(n.createAttrBinding(`attr`,t.name,e,o));continue}if(typeof o==`object`&&o&&(`mount`in o||`render`in o)){let e=`render`in o,n=e?g():y(t.raw);e?l+=`${t.raw}<!--${n}-->`:l+=`${t.raw} u="${n}"`;let i=o.mount?.bind(o);if(i&&u.push({apply:(e,t)=>{i(e,{registerCleanup:t})},type:`callback`,uid:n}),e){let e=o.render.bind(o),t={bindings:[],html:``},i=(0,r.signal)(t);s(()=>{let n=e(),r=Array.isArray(n)?n:[n],o=f(),s=``,c=[];for(let e of r)if(a(e)){let t=p(e,o);s+=t.html,c.push(...t.bindings)}else s+=h(e);let l=c.length!==t.bindings.length||c.some((e,n)=>e!==t.bindings[n]);(s!==t.html||l)&&(t={bindings:c,html:s},i.value=t)}),u.push({keyed:!1,signal:i,type:`html`,uid:n})}continue}b();let d=_(o,s);if(d){let e=g();l+=`${t.raw}<!--${e}-->`,u.push({keyed:d.keyed,signal:d.signal,type:`html`,uid:e});continue}if(Array.isArray(o)){let e=``;for(let t of o)if(a(t)){let n=p(t,g);e+=n.html,u.push(...n.bindings)}else e+=h(t);l+=t.raw+e;continue}if((0,r.isSignal)(o)){let e=g();l+=`${t.raw}<!--${e}-->`,u.push({signal:o,type:`text`,uid:e})}else if(a(o)){let e=p(o,g);l+=t.raw+e.html,u.push(...e.bindings)}else l+=t.raw+h(o)}return l+=c.tail,e.htmlResult(o(l),u)};exports.compileTemplate=y,exports.resetTemplateCompilerState=v;
|
|
2
|
+
//# sourceMappingURL=template-compiler.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template-compiler.cjs","names":[],"sources":["../../src/core/template-compiler.ts"],"sourcesContent":["import { computed, isSignal, signal, type ReadonlySignal, type Signal } from '@vielzeug/stateit';\n\nimport {\n CF_ID_ATTR,\n EACH_SIGNAL,\n htmlResult,\n type Binding,\n type Directive,\n type EventBinding,\n type HTMLResult,\n type Ref,\n type RefCallback,\n} from './internal';\nimport { createAttrBinding, createPropBinding } from './template-bindings';\nimport { escapeHtml } from './utilities';\n\nconst ATTR_ID_RE = new RegExp(`${CF_ID_ATTR}=\"([^\"]+)\"`, 'g');\n\nconst isHtmlResult = (value: unknown): value is HTMLResult => typeof value === 'object' && !!value && '__html' in value;\nconst normalizeCompiledHtml = (html: string): string => html.replace(/>\\s+</g, '><').trim();\n\n// Slot patterns applied in priority order; first match wins\nconst SLOT_PATTERNS = [\n { kind: 'event' as const, regex: /\\s+@([a-zA-Z_][-a-zA-Z0-9_.]*)\\s*=\\s*[\"']?$/ },\n { kind: 'ref' as const, regex: /\\s+ref\\s*=\\s*[\"']?$/ },\n { kind: 'specialAttr' as const, regex: /\\s+([:?])([a-zA-Z_][-a-zA-Z0-9_]*)\\s*=\\s*[\"']?$/ },\n { kind: 'prop' as const, regex: /\\.([a-zA-Z_][-a-zA-Z0-9_]*)\\s*=\\s*[\"']?$/ },\n { kind: 'plainAttr' as const, regex: /\\s+([a-zA-Z_][-a-zA-Z0-9_]*)\\s*=\\s*[\"']?$/ },\n] as const;\n\ntype CompiledTemplateSlot = {\n kind: (typeof SLOT_PATTERNS)[number]['kind'] | 'node';\n // For 'specialAttr' slots\n mode?: 'attr' | 'bool';\n modifiers?: EventBinding['modifiers'];\n // For 'event' slots\n name?: string;\n prefix: string;\n raw: string;\n};\n\ntype CompiledTemplatePlan = {\n slots: CompiledTemplateSlot[];\n tail: string;\n};\n\nconst templatePlanCache = new WeakMap<TemplateStringsArray, CompiledTemplatePlan>();\n\n/**\n * Parses event descriptor string into name and modifiers.\n * @example\n * parseEventDescriptor('click.stop.prevent') → { name: 'click', modifiers: { stop, prevent } }\n */\nconst parseEventDescriptor = (descriptor: string): { modifiers: EventBinding['modifiers']; name: string } => {\n const [name, ...rawModifiers] = descriptor.split('.');\n const modifiers: NonNullable<EventBinding['modifiers']> = {};\n\n for (const modifier of rawModifiers) {\n if (modifier === 'capture') modifiers.capture = true;\n else if (modifier === 'once') modifiers.once = true;\n else if (modifier === 'passive') modifiers.passive = true;\n else if (modifier === 'prevent') modifiers.prevent = true;\n else if (modifier === 'self') modifiers.self = true;\n else if (modifier === 'stop') modifiers.stop = true;\n }\n\n return { modifiers: Object.keys(modifiers).length ? modifiers : undefined, name };\n};\n\nconst buildTemplatePlan = (strings: TemplateStringsArray): CompiledTemplatePlan => {\n const slots: CompiledTemplateSlot[] = [];\n\n for (let i = 0; i < strings.length - 1; i++) {\n const str = strings[i];\n let matched = false;\n\n for (const pattern of SLOT_PATTERNS) {\n const m = pattern.regex.exec(str);\n\n if (!m) continue;\n\n const prefix = str.slice(0, -m[0].length);\n\n matched = true;\n\n if (pattern.kind === 'event') {\n const parsed = parseEventDescriptor(m[1]);\n\n slots.push({ kind: 'event', modifiers: parsed.modifiers, name: parsed.name, prefix, raw: str });\n } else if (pattern.kind === 'ref') {\n slots.push({ kind: 'ref', prefix, raw: str });\n } else if (pattern.kind === 'specialAttr') {\n slots.push({ kind: 'specialAttr', mode: m[1] === '?' ? 'bool' : 'attr', name: m[2], prefix, raw: str });\n } else if (pattern.kind === 'prop') {\n slots.push({ kind: 'prop', name: m[1], prefix, raw: str });\n } else if (pattern.kind === 'plainAttr') {\n slots.push({ kind: 'plainAttr', name: m[1], prefix, raw: str });\n }\n\n break; // first match wins\n }\n\n if (!matched) {\n slots.push({ kind: 'node', prefix: str, raw: str });\n }\n }\n\n return { slots, tail: strings[strings.length - 1] ?? '' };\n};\n\nconst getCompiledTemplatePlan = (strings: TemplateStringsArray): CompiledTemplatePlan => {\n let plan = templatePlanCache.get(strings);\n\n if (!plan) {\n plan = buildTemplatePlan(strings);\n templatePlanCache.set(strings, plan);\n }\n\n return plan;\n};\n\nconst createMarkerIdFactory = (): (() => string) => {\n let markerIndex = 0;\n\n return () => String(markerIndex++);\n};\n\nconst rekeyHtmlResult = (\n result: HTMLResult,\n getNextId: () => string,\n): {\n bindings: Binding[];\n html: string;\n} => {\n const idMap = new Map<string, string>();\n const getMappedId = (id: string): string => {\n const mapped = idMap.get(id);\n\n if (mapped) return mapped;\n\n const next = getNextId();\n\n idMap.set(id, next);\n\n return next;\n };\n\n return {\n bindings: result.__bindings.map((binding) => ({ ...binding, uid: getMappedId(binding.uid) }) as Binding),\n html: result.__html\n .replace(ATTR_ID_RE, (_, id: string) => `${CF_ID_ATTR}=\"${getMappedId(id)}\"`)\n .replace(/<!--(\\d+)-->/g, (_, id: string) => `<!--${getMappedId(id)}-->`),\n };\n};\n\nconst getEachSignalSource = (\n value: unknown,\n): ReadonlySignal<{\n bindings: Binding[];\n html: string;\n items?: Array<{ bindings: Binding[]; html: string }>;\n keys?: (string | number)[];\n}> | null => {\n if (typeof value !== 'object' || value === null || !(EACH_SIGNAL in value)) return null;\n\n return (value as { [EACH_SIGNAL]: ReadonlySignal<{ bindings: Binding[]; html: string }> })[EACH_SIGNAL];\n};\n\nconst resolveDirectiveValue = (value: unknown): string => {\n if (typeof value === 'string') return escapeHtml(value);\n\n if (value == null) return '';\n\n if (isHtmlResult(value)) return value.__html;\n\n return escapeHtml(String(value));\n};\n\nconst renderHtmlItems = (\n getter: () => unknown,\n effect: (fn: () => void) => void,\n): { bindings: Binding[]; signal: Signal<any> } => {\n let cached = { bindings: [] as Binding[], html: '' };\n const fnSignal = signal(cached);\n\n effect(() => {\n const res = getter();\n const items = Array.isArray(res) ? res : [res];\n const getNestedId = createMarkerIdFactory();\n let html = '';\n const nextBindings: Binding[] = [];\n\n for (const item of items) {\n if (isHtmlResult(item)) {\n const entry = rekeyHtmlResult(item, getNestedId);\n\n html += entry.html;\n nextBindings.push(...entry.bindings);\n } else {\n html += resolveDirectiveValue(item);\n }\n }\n\n const bindingsChanged =\n nextBindings.length !== cached.bindings.length || nextBindings.some((b, i) => b !== cached.bindings[i]);\n\n if (html !== cached.html || bindingsChanged) {\n cached = { bindings: nextBindings, html };\n fnSignal.value = cached;\n }\n });\n\n return { bindings: [], signal: fnSignal };\n};\n\nconst createHtmlWrapperSignal = (\n value: unknown,\n effect: (fn: () => void) => void,\n): {\n keyed: boolean;\n signal: ReadonlySignal<{\n bindings: Binding[];\n html: string;\n items?: Array<{ bindings: Binding[]; html: string }>;\n keys?: (string | number)[];\n }>;\n} | null => {\n const eachSignal = getEachSignalSource(value);\n\n if (eachSignal) {\n return { keyed: true, signal: eachSignal };\n }\n\n if (typeof value === 'function' && !isSignal(value)) {\n const { signal: sig } = renderHtmlItems(value as () => unknown, effect);\n\n return { keyed: false, signal: sig };\n }\n\n if (isSignal(value) && isHtmlResult(value.value)) {\n return {\n keyed: false,\n signal: computed(() => {\n const next = (value as ReadonlySignal<unknown>).value;\n\n if (!isHtmlResult(next)) {\n return { bindings: [], html: String(next) };\n }\n\n const entry = rekeyHtmlResult(next, createMarkerIdFactory());\n\n return { bindings: entry.bindings, html: entry.html };\n }),\n };\n }\n\n return null;\n};\n\nexport const resetTemplateCompilerState = (): void => {\n // Marker IDs are deterministic per compiled template; no global state to reset.\n};\n\n/**\n * Compiles a tagged template into an HTMLResult with reactive bindings.\n *\n * Detects interpolation slots using regex patterns:\n * - `@event-name` → event listener binding\n * - `ref` → ref binding\n * - `:prop` or `?bool` → special attributes\n * - `.prop` → property binding\n * - plain attributes → attribute binding\n *\n * Rekeys nested HTMLResult bindings to avoid ID collisions.\n *\n * @param strings - Template string parts\n * @param values - Interpolated values (signals, functions, directives, primitives)\n * @param effect - Effect hook for reactive bindings\n * @returns HTMLResult with compiled HTML and bindings array\n *\n * @example\n * const name = signal('Alice');\n * const html = compileTemplate`<h1>${() => name.value}</h1>`;\n */\nexport const compileTemplate = (\n strings: TemplateStringsArray,\n values: unknown[],\n effect: (fn: () => void) => void,\n): HTMLResult => {\n const plan = getCompiledTemplatePlan(strings);\n let result = '';\n const bindings: Binding[] = [];\n let activeElementId: string | null = null;\n\n const getNextId = createMarkerIdFactory();\n const isInsideStartTag = (prefix: string) => prefix.lastIndexOf('<') > prefix.lastIndexOf('>');\n const getElementBindingId = (prefix: string): string => {\n if (!activeElementId || isInsideStartTag(prefix)) {\n activeElementId = getNextId();\n }\n\n return activeElementId;\n };\n const resetElementBindingId = (): void => {\n activeElementId = null;\n };\n\n for (let i = 0; i < plan.slots.length; i++) {\n const slot = plan.slots[i];\n const value = values[i];\n\n if (slot.kind === 'event') {\n if (typeof value === 'function') {\n const id = getElementBindingId(slot.prefix);\n\n result += `${slot.prefix} ${CF_ID_ATTR}=\"${id}\"`;\n bindings.push({\n handler: value as (e: Event) => void,\n modifiers: slot.modifiers,\n name: slot.name!,\n type: 'event',\n uid: id,\n });\n } else {\n result += slot.raw;\n }\n\n continue;\n }\n\n if (slot.kind === 'ref') {\n if (value) {\n const id = getElementBindingId(slot.prefix);\n\n result += `${slot.prefix} ${CF_ID_ATTR}=\"${id}\"`;\n bindings.push({\n ref: value as Ref<Element> | RefCallback<Element>,\n type: 'ref',\n uid: id,\n });\n } else {\n result += slot.raw;\n }\n\n continue;\n }\n\n if (slot.kind === 'specialAttr') {\n const id = getElementBindingId(slot.prefix);\n\n result += `${slot.prefix} ${CF_ID_ATTR}=\"${id}\"`;\n bindings.push(createAttrBinding(slot.mode!, slot.name!, id, value));\n continue;\n }\n\n if (slot.kind === 'prop') {\n const id = getElementBindingId(slot.prefix);\n\n result += `${slot.prefix} ${CF_ID_ATTR}=\"${id}\"`;\n bindings.push(createPropBinding(slot.name!, id, value));\n continue;\n }\n\n if (slot.kind === 'plainAttr') {\n const id = getElementBindingId(slot.prefix);\n\n result += `${slot.prefix} ${CF_ID_ATTR}=\"${id}\"`;\n bindings.push(createAttrBinding('attr', slot.name!, id, value));\n continue;\n }\n\n if (typeof value === 'object' && value !== null && ('mount' in value || 'render' in value)) {\n const isInterpolation = 'render' in value;\n const id = isInterpolation ? getNextId() : getElementBindingId(slot.raw);\n\n if (isInterpolation) result += `${slot.raw}<!--${id}-->`;\n else result += `${slot.raw} ${CF_ID_ATTR}=\"${id}\"`;\n\n const apply = (value as Directive).mount?.bind(value);\n\n if (apply) {\n bindings.push({\n apply: (el: HTMLElement, registerCleanup: (fn: () => void) => void) => {\n apply(el, { registerCleanup });\n },\n type: 'callback',\n uid: id,\n });\n }\n\n if (isInterpolation) {\n const render = (value as Directive).render!.bind(value);\n let cached = { bindings: [] as Binding[], html: '' };\n const fnSignal = signal(cached);\n\n effect(() => {\n const res = render();\n const items = Array.isArray(res) ? res : [res];\n const getNestedId = createMarkerIdFactory();\n let html = '';\n const nextBindings: Binding[] = [];\n\n for (const item of items) {\n if (isHtmlResult(item)) {\n const entry = rekeyHtmlResult(item, getNestedId);\n\n html += entry.html;\n nextBindings.push(...entry.bindings);\n } else {\n html += resolveDirectiveValue(item);\n }\n }\n\n const bindingsChanged =\n nextBindings.length !== cached.bindings.length || nextBindings.some((b, i) => b !== cached.bindings[i]);\n\n if (html !== cached.html || bindingsChanged) {\n cached = { bindings: nextBindings, html };\n fnSignal.value = cached;\n }\n });\n\n bindings.push({ keyed: false, signal: fnSignal, type: 'html', uid: id });\n }\n\n continue;\n }\n\n resetElementBindingId();\n\n const htmlWrapper = createHtmlWrapperSignal(value, effect);\n\n if (htmlWrapper) {\n const id = getNextId();\n\n result += `${slot.raw}<!--${id}-->`;\n bindings.push({ keyed: htmlWrapper.keyed, signal: htmlWrapper.signal, type: 'html', uid: id });\n continue;\n }\n\n if (Array.isArray(value)) {\n let combinedHtml = '';\n\n for (const item of value) {\n if (isHtmlResult(item)) {\n const entry = rekeyHtmlResult(item, getNextId);\n\n combinedHtml += entry.html;\n bindings.push(...entry.bindings);\n } else {\n combinedHtml += resolveDirectiveValue(item);\n }\n }\n result += slot.raw + combinedHtml;\n continue;\n }\n\n if (isSignal(value)) {\n const id = getNextId();\n\n result += `${slot.raw}<!--${id}-->`;\n bindings.push({ signal: value as Signal<unknown>, type: 'text', uid: id });\n } else if (isHtmlResult(value)) {\n const entry = rekeyHtmlResult(value, getNextId);\n\n result += slot.raw + entry.html;\n bindings.push(...entry.bindings);\n } else {\n result += slot.raw + resolveDirectiveValue(value);\n }\n }\n\n result += plan.tail;\n\n return htmlResult(normalizeCompiledHtml(result), bindings);\n};\n"],"mappings":"uIAgBA,IAAM,EAAiB,OAAO,cAA2B,IAAI,CAEvD,EAAgB,GAAwC,OAAO,GAAU,UAAY,CAAC,CAAC,GAAS,WAAY,EAC5G,EAAyB,GAAyB,EAAK,QAAQ,SAAU,KAAK,CAAC,MAAM,CAGrF,EAAgB,CACpB,CAAE,KAAM,QAAkB,MAAO,8CAA+C,CAChF,CAAE,KAAM,MAAgB,MAAO,sBAAuB,CACtD,CAAE,KAAM,cAAwB,MAAO,kDAAmD,CAC1F,CAAE,KAAM,OAAiB,MAAO,2CAA4C,CAC5E,CAAE,KAAM,YAAsB,MAAO,4CAA6C,CACnF,CAkBK,EAAoB,IAAI,QAOxB,EAAwB,GAA+E,CAC3G,GAAM,CAAC,EAAM,GAAG,GAAgB,EAAW,MAAM,IAAI,CAC/C,EAAoD,EAAE,CAE5D,IAAK,IAAM,KAAY,EACjB,IAAa,UAAW,EAAU,QAAU,GACvC,IAAa,OAAQ,EAAU,KAAO,GACtC,IAAa,UAAW,EAAU,QAAU,GAC5C,IAAa,UAAW,EAAU,QAAU,GAC5C,IAAa,OAAQ,EAAU,KAAO,GACtC,IAAa,SAAQ,EAAU,KAAO,IAGjD,MAAO,CAAE,UAAW,OAAO,KAAK,EAAU,CAAC,OAAS,EAAY,IAAA,GAAW,OAAM,EAG7E,EAAqB,GAAwD,CACjF,IAAM,EAAgC,EAAE,CAExC,IAAK,IAAI,EAAI,EAAG,EAAI,EAAQ,OAAS,EAAG,IAAK,CAC3C,IAAM,EAAM,EAAQ,GAChB,EAAU,GAEd,IAAK,IAAM,KAAW,EAAe,CACnC,IAAM,EAAI,EAAQ,MAAM,KAAK,EAAI,CAEjC,GAAI,CAAC,EAAG,SAER,IAAM,EAAS,EAAI,MAAM,EAAG,CAAC,EAAE,GAAG,OAAO,CAIzC,GAFA,EAAU,GAEN,EAAQ,OAAS,QAAS,CAC5B,IAAM,EAAS,EAAqB,EAAE,GAAG,CAEzC,EAAM,KAAK,CAAE,KAAM,QAAS,UAAW,EAAO,UAAW,KAAM,EAAO,KAAM,SAAQ,IAAK,EAAK,CAAC,MACtF,EAAQ,OAAS,MAC1B,EAAM,KAAK,CAAE,KAAM,MAAO,SAAQ,IAAK,EAAK,CAAC,CACpC,EAAQ,OAAS,cAC1B,EAAM,KAAK,CAAE,KAAM,cAAe,KAAM,EAAE,KAAO,IAAM,OAAS,OAAQ,KAAM,EAAE,GAAI,SAAQ,IAAK,EAAK,CAAC,CAC9F,EAAQ,OAAS,OAC1B,EAAM,KAAK,CAAE,KAAM,OAAQ,KAAM,EAAE,GAAI,SAAQ,IAAK,EAAK,CAAC,CACjD,EAAQ,OAAS,aAC1B,EAAM,KAAK,CAAE,KAAM,YAAa,KAAM,EAAE,GAAI,SAAQ,IAAK,EAAK,CAAC,CAGjE,MAGG,GACH,EAAM,KAAK,CAAE,KAAM,OAAQ,OAAQ,EAAK,IAAK,EAAK,CAAC,CAIvD,MAAO,CAAE,QAAO,KAAM,EAAQ,EAAQ,OAAS,IAAM,GAAI,EAGrD,EAA2B,GAAwD,CACvF,IAAI,EAAO,EAAkB,IAAI,EAAQ,CAOzC,OALK,IACH,EAAO,EAAkB,EAAQ,CACjC,EAAkB,IAAI,EAAS,EAAK,EAG/B,GAGH,MAA8C,CAClD,IAAI,EAAc,EAElB,UAAa,OAAO,IAAc,EAG9B,GACJ,EACA,IAIG,CACH,IAAM,EAAQ,IAAI,IACZ,EAAe,GAAuB,CAC1C,IAAM,EAAS,EAAM,IAAI,EAAG,CAE5B,GAAI,EAAQ,OAAO,EAEnB,IAAM,EAAO,GAAW,CAIxB,OAFA,EAAM,IAAI,EAAI,EAAK,CAEZ,GAGT,MAAO,CACL,SAAU,EAAO,WAAW,IAAK,IAAa,CAAE,GAAG,EAAS,IAAK,EAAY,EAAQ,IAAI,CAAE,EAAa,CACxG,KAAM,EAAO,OACV,QAAQ,GAAa,EAAG,IAAe,MAAkB,EAAY,EAAG,CAAC,GAAG,CAC5E,QAAQ,iBAAkB,EAAG,IAAe,OAAO,EAAY,EAAG,CAAC,KAAK,CAC5E,EAGG,EACJ,GAOI,OAAO,GAAU,WAAY,GAAkB,EAAE,EAAA,eAAe,GAAe,KAE3E,EAAmF,EAAA,aAGvF,EAAyB,GACzB,OAAO,GAAU,SAAiB,EAAA,WAAW,EAAM,CAEnD,GAAS,KAAa,GAEtB,EAAa,EAAM,CAAS,EAAM,OAE/B,EAAA,WAAW,OAAO,EAAM,CAAC,CAG5B,GACJ,EACA,IACiD,CACjD,IAAI,EAAS,CAAE,SAAU,EAAE,CAAe,KAAM,GAAI,CAC9C,GAAA,EAAA,EAAA,QAAkB,EAAO,CA6B/B,OA3BA,MAAa,CACX,IAAM,EAAM,GAAQ,CACd,EAAQ,MAAM,QAAQ,EAAI,CAAG,EAAM,CAAC,EAAI,CACxC,EAAc,GAAuB,CACvC,EAAO,GACL,EAA0B,EAAE,CAElC,IAAK,IAAM,KAAQ,EACjB,GAAI,EAAa,EAAK,CAAE,CACtB,IAAM,EAAQ,EAAgB,EAAM,EAAY,CAEhD,GAAQ,EAAM,KACd,EAAa,KAAK,GAAG,EAAM,SAAS,MAEpC,GAAQ,EAAsB,EAAK,CAIvC,IAAM,EACJ,EAAa,SAAW,EAAO,SAAS,QAAU,EAAa,MAAM,EAAG,IAAM,IAAM,EAAO,SAAS,GAAG,EAErG,IAAS,EAAO,MAAQ,KAC1B,EAAS,CAAE,SAAU,EAAc,OAAM,CACzC,EAAS,MAAQ,IAEnB,CAEK,CAAE,SAAU,EAAE,CAAE,OAAQ,EAAU,EAGrC,GACJ,EACA,IASU,CACV,IAAM,EAAa,EAAoB,EAAM,CAE7C,GAAI,EACF,MAAO,CAAE,MAAO,GAAM,OAAQ,EAAY,CAG5C,GAAI,OAAO,GAAU,YAAc,EAAA,EAAA,EAAA,UAAU,EAAM,CAAE,CACnD,GAAM,CAAE,OAAQ,GAAQ,EAAgB,EAAwB,EAAO,CAEvE,MAAO,CAAE,MAAO,GAAO,OAAQ,EAAK,CAoBtC,OAjBA,EAAA,EAAA,UAAa,EAAM,EAAI,EAAa,EAAM,MAAM,CACvC,CACL,MAAO,GACP,QAAA,EAAA,EAAA,cAAuB,CACrB,IAAM,EAAQ,EAAkC,MAEhD,GAAI,CAAC,EAAa,EAAK,CACrB,MAAO,CAAE,SAAU,EAAE,CAAE,KAAM,OAAO,EAAK,CAAE,CAG7C,IAAM,EAAQ,EAAgB,EAAM,GAAuB,CAAC,CAE5D,MAAO,CAAE,SAAU,EAAM,SAAU,KAAM,EAAM,KAAM,EACrD,CACH,CAGI,MAGI,MAAyC,GAyBzC,GACX,EACA,EACA,IACe,CACf,IAAM,EAAO,EAAwB,EAAQ,CACzC,EAAS,GACP,EAAsB,EAAE,CAC1B,EAAiC,KAE/B,EAAY,GAAuB,CACnC,EAAoB,GAAmB,EAAO,YAAY,IAAI,CAAG,EAAO,YAAY,IAAI,CACxF,EAAuB,KACvB,CAAC,GAAmB,EAAiB,EAAO,IAC9C,EAAkB,GAAW,EAGxB,GAEH,MAAoC,CACxC,EAAkB,MAGpB,IAAK,IAAI,EAAI,EAAG,EAAI,EAAK,MAAM,OAAQ,IAAK,CAC1C,IAAM,EAAO,EAAK,MAAM,GAClB,EAAQ,EAAO,GAErB,GAAI,EAAK,OAAS,QAAS,CACzB,GAAI,OAAO,GAAU,WAAY,CAC/B,IAAM,EAAK,EAAoB,EAAK,OAAO,CAE3C,GAAU,GAAG,EAAK,OAAO,MAAkB,EAAG,GAC9C,EAAS,KAAK,CACZ,QAAS,EACT,UAAW,EAAK,UAChB,KAAM,EAAK,KACX,KAAM,QACN,IAAK,EACN,CAAC,MAEF,GAAU,EAAK,IAGjB,SAGF,GAAI,EAAK,OAAS,MAAO,CACvB,GAAI,EAAO,CACT,IAAM,EAAK,EAAoB,EAAK,OAAO,CAE3C,GAAU,GAAG,EAAK,OAAO,MAAkB,EAAG,GAC9C,EAAS,KAAK,CACZ,IAAK,EACL,KAAM,MACN,IAAK,EACN,CAAC,MAEF,GAAU,EAAK,IAGjB,SAGF,GAAI,EAAK,OAAS,cAAe,CAC/B,IAAM,EAAK,EAAoB,EAAK,OAAO,CAE3C,GAAU,GAAG,EAAK,OAAO,MAAkB,EAAG,GAC9C,EAAS,KAAK,EAAA,kBAAkB,EAAK,KAAO,EAAK,KAAO,EAAI,EAAM,CAAC,CACnE,SAGF,GAAI,EAAK,OAAS,OAAQ,CACxB,IAAM,EAAK,EAAoB,EAAK,OAAO,CAE3C,GAAU,GAAG,EAAK,OAAO,MAAkB,EAAG,GAC9C,EAAS,KAAK,EAAA,kBAAkB,EAAK,KAAO,EAAI,EAAM,CAAC,CACvD,SAGF,GAAI,EAAK,OAAS,YAAa,CAC7B,IAAM,EAAK,EAAoB,EAAK,OAAO,CAE3C,GAAU,GAAG,EAAK,OAAO,MAAkB,EAAG,GAC9C,EAAS,KAAK,EAAA,kBAAkB,OAAQ,EAAK,KAAO,EAAI,EAAM,CAAC,CAC/D,SAGF,GAAI,OAAO,GAAU,UAAY,IAAmB,UAAW,GAAS,WAAY,GAAQ,CAC1F,IAAM,EAAkB,WAAY,EAC9B,EAAK,EAAkB,GAAW,CAAG,EAAoB,EAAK,IAAI,CAEpE,EAAiB,GAAU,GAAG,EAAK,IAAI,MAAM,EAAG,KAC/C,GAAU,GAAG,EAAK,IAAI,MAAkB,EAAG,GAEhD,IAAM,EAAS,EAAoB,OAAO,KAAK,EAAM,CAYrD,GAVI,GACF,EAAS,KAAK,CACZ,OAAQ,EAAiB,IAA8C,CACrE,EAAM,EAAI,CAAE,kBAAiB,CAAC,EAEhC,KAAM,WACN,IAAK,EACN,CAAC,CAGA,EAAiB,CACnB,IAAM,EAAU,EAAoB,OAAQ,KAAK,EAAM,CACnD,EAAS,CAAE,SAAU,EAAE,CAAe,KAAM,GAAI,CAC9C,GAAA,EAAA,EAAA,QAAkB,EAAO,CAE/B,MAAa,CACX,IAAM,EAAM,GAAQ,CACd,EAAQ,MAAM,QAAQ,EAAI,CAAG,EAAM,CAAC,EAAI,CACxC,EAAc,GAAuB,CACvC,EAAO,GACL,EAA0B,EAAE,CAElC,IAAK,IAAM,KAAQ,EACjB,GAAI,EAAa,EAAK,CAAE,CACtB,IAAM,EAAQ,EAAgB,EAAM,EAAY,CAEhD,GAAQ,EAAM,KACd,EAAa,KAAK,GAAG,EAAM,SAAS,MAEpC,GAAQ,EAAsB,EAAK,CAIvC,IAAM,EACJ,EAAa,SAAW,EAAO,SAAS,QAAU,EAAa,MAAM,EAAG,IAAM,IAAM,EAAO,SAAS,GAAG,EAErG,IAAS,EAAO,MAAQ,KAC1B,EAAS,CAAE,SAAU,EAAc,OAAM,CACzC,EAAS,MAAQ,IAEnB,CAEF,EAAS,KAAK,CAAE,MAAO,GAAO,OAAQ,EAAU,KAAM,OAAQ,IAAK,EAAI,CAAC,CAG1E,SAGF,GAAuB,CAEvB,IAAM,EAAc,EAAwB,EAAO,EAAO,CAE1D,GAAI,EAAa,CACf,IAAM,EAAK,GAAW,CAEtB,GAAU,GAAG,EAAK,IAAI,MAAM,EAAG,KAC/B,EAAS,KAAK,CAAE,MAAO,EAAY,MAAO,OAAQ,EAAY,OAAQ,KAAM,OAAQ,IAAK,EAAI,CAAC,CAC9F,SAGF,GAAI,MAAM,QAAQ,EAAM,CAAE,CACxB,IAAI,EAAe,GAEnB,IAAK,IAAM,KAAQ,EACjB,GAAI,EAAa,EAAK,CAAE,CACtB,IAAM,EAAQ,EAAgB,EAAM,EAAU,CAE9C,GAAgB,EAAM,KACtB,EAAS,KAAK,GAAG,EAAM,SAAS,MAEhC,GAAgB,EAAsB,EAAK,CAG/C,GAAU,EAAK,IAAM,EACrB,SAGF,IAAA,EAAA,EAAA,UAAa,EAAM,CAAE,CACnB,IAAM,EAAK,GAAW,CAEtB,GAAU,GAAG,EAAK,IAAI,MAAM,EAAG,KAC/B,EAAS,KAAK,CAAE,OAAQ,EAA0B,KAAM,OAAQ,IAAK,EAAI,CAAC,SACjE,EAAa,EAAM,CAAE,CAC9B,IAAM,EAAQ,EAAgB,EAAO,EAAU,CAE/C,GAAU,EAAK,IAAM,EAAM,KAC3B,EAAS,KAAK,GAAG,EAAM,SAAS,MAEhC,GAAU,EAAK,IAAM,EAAsB,EAAM,CAMrD,MAFA,IAAU,EAAK,KAER,EAAA,WAAW,EAAsB,EAAO,CAAE,EAAS"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type HTMLResult } from './internal';
|
|
2
|
+
export declare const resetTemplateCompilerState: () => void;
|
|
3
|
+
/**
|
|
4
|
+
* Compiles a tagged template into an HTMLResult with reactive bindings.
|
|
5
|
+
*
|
|
6
|
+
* Detects interpolation slots using regex patterns:
|
|
7
|
+
* - `@event-name` → event listener binding
|
|
8
|
+
* - `ref` → ref binding
|
|
9
|
+
* - `:prop` or `?bool` → special attributes
|
|
10
|
+
* - `.prop` → property binding
|
|
11
|
+
* - plain attributes → attribute binding
|
|
12
|
+
*
|
|
13
|
+
* Rekeys nested HTMLResult bindings to avoid ID collisions.
|
|
14
|
+
*
|
|
15
|
+
* @param strings - Template string parts
|
|
16
|
+
* @param values - Interpolated values (signals, functions, directives, primitives)
|
|
17
|
+
* @param effect - Effect hook for reactive bindings
|
|
18
|
+
* @returns HTMLResult with compiled HTML and bindings array
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* const name = signal('Alice');
|
|
22
|
+
* const html = compileTemplate`<h1>${() => name.value}</h1>`;
|
|
23
|
+
*/
|
|
24
|
+
export declare const compileTemplate: (strings: TemplateStringsArray, values: unknown[], effect: (fn: () => void) => void) => HTMLResult;
|
|
25
|
+
//# sourceMappingURL=template-compiler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template-compiler.d.ts","sourceRoot":"","sources":["../../src/core/template-compiler.ts"],"names":[],"mappings":"AAEA,OAAO,EAOL,KAAK,UAAU,EAGhB,MAAM,YAAY,CAAC;AAuPpB,eAAO,MAAM,0BAA0B,QAAO,IAE7C,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,eAAe,GAC1B,SAAS,oBAAoB,EAC7B,QAAQ,OAAO,EAAE,EACjB,QAAQ,CAAC,EAAE,EAAE,MAAM,IAAI,KAAK,IAAI,KAC/B,UA2LF,CAAC"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{EACH_SIGNAL as e,htmlResult as t}from"./internal.js";import{escapeHtml as n}from"./utilities.js";import{createAttrBinding as r,createPropBinding as i}from"./template-bindings.js";import{computed as a,isSignal as o,signal as s}from"@vielzeug/stateit";var c=RegExp(`u="([^"]+)"`,`g`),l=e=>typeof e==`object`&&!!e&&`__html`in e,u=e=>e.replace(/>\s+</g,`><`).trim(),d=[{kind:`event`,regex:/\s+@([a-zA-Z_][-a-zA-Z0-9_.]*)\s*=\s*["']?$/},{kind:`ref`,regex:/\s+ref\s*=\s*["']?$/},{kind:`specialAttr`,regex:/\s+([:?])([a-zA-Z_][-a-zA-Z0-9_]*)\s*=\s*["']?$/},{kind:`prop`,regex:/\.([a-zA-Z_][-a-zA-Z0-9_]*)\s*=\s*["']?$/},{kind:`plainAttr`,regex:/\s+([a-zA-Z_][-a-zA-Z0-9_]*)\s*=\s*["']?$/}],f=new WeakMap,p=e=>{let[t,...n]=e.split(`.`),r={};for(let e of n)e===`capture`?r.capture=!0:e===`once`?r.once=!0:e===`passive`?r.passive=!0:e===`prevent`?r.prevent=!0:e===`self`?r.self=!0:e===`stop`&&(r.stop=!0);return{modifiers:Object.keys(r).length?r:void 0,name:t}},m=e=>{let t=[];for(let n=0;n<e.length-1;n++){let r=e[n],i=!1;for(let e of d){let n=e.regex.exec(r);if(!n)continue;let a=r.slice(0,-n[0].length);if(i=!0,e.kind===`event`){let e=p(n[1]);t.push({kind:`event`,modifiers:e.modifiers,name:e.name,prefix:a,raw:r})}else e.kind===`ref`?t.push({kind:`ref`,prefix:a,raw:r}):e.kind===`specialAttr`?t.push({kind:`specialAttr`,mode:n[1]===`?`?`bool`:`attr`,name:n[2],prefix:a,raw:r}):e.kind===`prop`?t.push({kind:`prop`,name:n[1],prefix:a,raw:r}):e.kind===`plainAttr`&&t.push({kind:`plainAttr`,name:n[1],prefix:a,raw:r});break}i||t.push({kind:`node`,prefix:r,raw:r})}return{slots:t,tail:e[e.length-1]??``}},h=e=>{let t=f.get(e);return t||(t=m(e),f.set(e,t)),t},g=()=>{let e=0;return()=>String(e++)},_=(e,t)=>{let n=new Map,r=e=>{let r=n.get(e);if(r)return r;let i=t();return n.set(e,i),i};return{bindings:e.__bindings.map(e=>({...e,uid:r(e.uid)})),html:e.__html.replace(c,(e,t)=>`u="${r(t)}"`).replace(/<!--(\d+)-->/g,(e,t)=>`<!--${r(t)}-->`)}},v=t=>typeof t!=`object`||!t||!(e in t)?null:t[e],y=e=>typeof e==`string`?n(e):e==null?``:l(e)?e.__html:n(String(e)),b=(e,t)=>{let n={bindings:[],html:``},r=s(n);return t(()=>{let t=e(),i=Array.isArray(t)?t:[t],a=g(),o=``,s=[];for(let e of i)if(l(e)){let t=_(e,a);o+=t.html,s.push(...t.bindings)}else o+=y(e);let c=s.length!==n.bindings.length||s.some((e,t)=>e!==n.bindings[t]);(o!==n.html||c)&&(n={bindings:s,html:o},r.value=n)}),{bindings:[],signal:r}},x=(e,t)=>{let n=v(e);if(n)return{keyed:!0,signal:n};if(typeof e==`function`&&!o(e)){let{signal:n}=b(e,t);return{keyed:!1,signal:n}}return o(e)&&l(e.value)?{keyed:!1,signal:a(()=>{let t=e.value;if(!l(t))return{bindings:[],html:String(t)};let n=_(t,g());return{bindings:n.bindings,html:n.html}})}:null},S=()=>{},C=(e,n,a)=>{let c=h(e),d=``,f=[],p=null,m=g(),v=e=>e.lastIndexOf(`<`)>e.lastIndexOf(`>`),b=e=>((!p||v(e))&&(p=m()),p),S=()=>{p=null};for(let e=0;e<c.slots.length;e++){let t=c.slots[e],u=n[e];if(t.kind===`event`){if(typeof u==`function`){let e=b(t.prefix);d+=`${t.prefix} u="${e}"`,f.push({handler:u,modifiers:t.modifiers,name:t.name,type:`event`,uid:e})}else d+=t.raw;continue}if(t.kind===`ref`){if(u){let e=b(t.prefix);d+=`${t.prefix} u="${e}"`,f.push({ref:u,type:`ref`,uid:e})}else d+=t.raw;continue}if(t.kind===`specialAttr`){let e=b(t.prefix);d+=`${t.prefix} u="${e}"`,f.push(r(t.mode,t.name,e,u));continue}if(t.kind===`prop`){let e=b(t.prefix);d+=`${t.prefix} u="${e}"`,f.push(i(t.name,e,u));continue}if(t.kind===`plainAttr`){let e=b(t.prefix);d+=`${t.prefix} u="${e}"`,f.push(r(`attr`,t.name,e,u));continue}if(typeof u==`object`&&u&&(`mount`in u||`render`in u)){let e=`render`in u,n=e?m():b(t.raw);e?d+=`${t.raw}<!--${n}-->`:d+=`${t.raw} u="${n}"`;let r=u.mount?.bind(u);if(r&&f.push({apply:(e,t)=>{r(e,{registerCleanup:t})},type:`callback`,uid:n}),e){let e=u.render.bind(u),t={bindings:[],html:``},r=s(t);a(()=>{let n=e(),i=Array.isArray(n)?n:[n],a=g(),o=``,s=[];for(let e of i)if(l(e)){let t=_(e,a);o+=t.html,s.push(...t.bindings)}else o+=y(e);let c=s.length!==t.bindings.length||s.some((e,n)=>e!==t.bindings[n]);(o!==t.html||c)&&(t={bindings:s,html:o},r.value=t)}),f.push({keyed:!1,signal:r,type:`html`,uid:n})}continue}S();let p=x(u,a);if(p){let e=m();d+=`${t.raw}<!--${e}-->`,f.push({keyed:p.keyed,signal:p.signal,type:`html`,uid:e});continue}if(Array.isArray(u)){let e=``;for(let t of u)if(l(t)){let n=_(t,m);e+=n.html,f.push(...n.bindings)}else e+=y(t);d+=t.raw+e;continue}if(o(u)){let e=m();d+=`${t.raw}<!--${e}-->`,f.push({signal:u,type:`text`,uid:e})}else if(l(u)){let e=_(u,m);d+=t.raw+e.html,f.push(...e.bindings)}else d+=t.raw+y(u)}return d+=c.tail,t(u(d),f)};export{C as compileTemplate,S as resetTemplateCompilerState};
|
|
2
|
+
//# sourceMappingURL=template-compiler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template-compiler.js","names":[],"sources":["../../src/core/template-compiler.ts"],"sourcesContent":["import { computed, isSignal, signal, type ReadonlySignal, type Signal } from '@vielzeug/stateit';\n\nimport {\n CF_ID_ATTR,\n EACH_SIGNAL,\n htmlResult,\n type Binding,\n type Directive,\n type EventBinding,\n type HTMLResult,\n type Ref,\n type RefCallback,\n} from './internal';\nimport { createAttrBinding, createPropBinding } from './template-bindings';\nimport { escapeHtml } from './utilities';\n\nconst ATTR_ID_RE = new RegExp(`${CF_ID_ATTR}=\"([^\"]+)\"`, 'g');\n\nconst isHtmlResult = (value: unknown): value is HTMLResult => typeof value === 'object' && !!value && '__html' in value;\nconst normalizeCompiledHtml = (html: string): string => html.replace(/>\\s+</g, '><').trim();\n\n// Slot patterns applied in priority order; first match wins\nconst SLOT_PATTERNS = [\n { kind: 'event' as const, regex: /\\s+@([a-zA-Z_][-a-zA-Z0-9_.]*)\\s*=\\s*[\"']?$/ },\n { kind: 'ref' as const, regex: /\\s+ref\\s*=\\s*[\"']?$/ },\n { kind: 'specialAttr' as const, regex: /\\s+([:?])([a-zA-Z_][-a-zA-Z0-9_]*)\\s*=\\s*[\"']?$/ },\n { kind: 'prop' as const, regex: /\\.([a-zA-Z_][-a-zA-Z0-9_]*)\\s*=\\s*[\"']?$/ },\n { kind: 'plainAttr' as const, regex: /\\s+([a-zA-Z_][-a-zA-Z0-9_]*)\\s*=\\s*[\"']?$/ },\n] as const;\n\ntype CompiledTemplateSlot = {\n kind: (typeof SLOT_PATTERNS)[number]['kind'] | 'node';\n // For 'specialAttr' slots\n mode?: 'attr' | 'bool';\n modifiers?: EventBinding['modifiers'];\n // For 'event' slots\n name?: string;\n prefix: string;\n raw: string;\n};\n\ntype CompiledTemplatePlan = {\n slots: CompiledTemplateSlot[];\n tail: string;\n};\n\nconst templatePlanCache = new WeakMap<TemplateStringsArray, CompiledTemplatePlan>();\n\n/**\n * Parses event descriptor string into name and modifiers.\n * @example\n * parseEventDescriptor('click.stop.prevent') → { name: 'click', modifiers: { stop, prevent } }\n */\nconst parseEventDescriptor = (descriptor: string): { modifiers: EventBinding['modifiers']; name: string } => {\n const [name, ...rawModifiers] = descriptor.split('.');\n const modifiers: NonNullable<EventBinding['modifiers']> = {};\n\n for (const modifier of rawModifiers) {\n if (modifier === 'capture') modifiers.capture = true;\n else if (modifier === 'once') modifiers.once = true;\n else if (modifier === 'passive') modifiers.passive = true;\n else if (modifier === 'prevent') modifiers.prevent = true;\n else if (modifier === 'self') modifiers.self = true;\n else if (modifier === 'stop') modifiers.stop = true;\n }\n\n return { modifiers: Object.keys(modifiers).length ? modifiers : undefined, name };\n};\n\nconst buildTemplatePlan = (strings: TemplateStringsArray): CompiledTemplatePlan => {\n const slots: CompiledTemplateSlot[] = [];\n\n for (let i = 0; i < strings.length - 1; i++) {\n const str = strings[i];\n let matched = false;\n\n for (const pattern of SLOT_PATTERNS) {\n const m = pattern.regex.exec(str);\n\n if (!m) continue;\n\n const prefix = str.slice(0, -m[0].length);\n\n matched = true;\n\n if (pattern.kind === 'event') {\n const parsed = parseEventDescriptor(m[1]);\n\n slots.push({ kind: 'event', modifiers: parsed.modifiers, name: parsed.name, prefix, raw: str });\n } else if (pattern.kind === 'ref') {\n slots.push({ kind: 'ref', prefix, raw: str });\n } else if (pattern.kind === 'specialAttr') {\n slots.push({ kind: 'specialAttr', mode: m[1] === '?' ? 'bool' : 'attr', name: m[2], prefix, raw: str });\n } else if (pattern.kind === 'prop') {\n slots.push({ kind: 'prop', name: m[1], prefix, raw: str });\n } else if (pattern.kind === 'plainAttr') {\n slots.push({ kind: 'plainAttr', name: m[1], prefix, raw: str });\n }\n\n break; // first match wins\n }\n\n if (!matched) {\n slots.push({ kind: 'node', prefix: str, raw: str });\n }\n }\n\n return { slots, tail: strings[strings.length - 1] ?? '' };\n};\n\nconst getCompiledTemplatePlan = (strings: TemplateStringsArray): CompiledTemplatePlan => {\n let plan = templatePlanCache.get(strings);\n\n if (!plan) {\n plan = buildTemplatePlan(strings);\n templatePlanCache.set(strings, plan);\n }\n\n return plan;\n};\n\nconst createMarkerIdFactory = (): (() => string) => {\n let markerIndex = 0;\n\n return () => String(markerIndex++);\n};\n\nconst rekeyHtmlResult = (\n result: HTMLResult,\n getNextId: () => string,\n): {\n bindings: Binding[];\n html: string;\n} => {\n const idMap = new Map<string, string>();\n const getMappedId = (id: string): string => {\n const mapped = idMap.get(id);\n\n if (mapped) return mapped;\n\n const next = getNextId();\n\n idMap.set(id, next);\n\n return next;\n };\n\n return {\n bindings: result.__bindings.map((binding) => ({ ...binding, uid: getMappedId(binding.uid) }) as Binding),\n html: result.__html\n .replace(ATTR_ID_RE, (_, id: string) => `${CF_ID_ATTR}=\"${getMappedId(id)}\"`)\n .replace(/<!--(\\d+)-->/g, (_, id: string) => `<!--${getMappedId(id)}-->`),\n };\n};\n\nconst getEachSignalSource = (\n value: unknown,\n): ReadonlySignal<{\n bindings: Binding[];\n html: string;\n items?: Array<{ bindings: Binding[]; html: string }>;\n keys?: (string | number)[];\n}> | null => {\n if (typeof value !== 'object' || value === null || !(EACH_SIGNAL in value)) return null;\n\n return (value as { [EACH_SIGNAL]: ReadonlySignal<{ bindings: Binding[]; html: string }> })[EACH_SIGNAL];\n};\n\nconst resolveDirectiveValue = (value: unknown): string => {\n if (typeof value === 'string') return escapeHtml(value);\n\n if (value == null) return '';\n\n if (isHtmlResult(value)) return value.__html;\n\n return escapeHtml(String(value));\n};\n\nconst renderHtmlItems = (\n getter: () => unknown,\n effect: (fn: () => void) => void,\n): { bindings: Binding[]; signal: Signal<any> } => {\n let cached = { bindings: [] as Binding[], html: '' };\n const fnSignal = signal(cached);\n\n effect(() => {\n const res = getter();\n const items = Array.isArray(res) ? res : [res];\n const getNestedId = createMarkerIdFactory();\n let html = '';\n const nextBindings: Binding[] = [];\n\n for (const item of items) {\n if (isHtmlResult(item)) {\n const entry = rekeyHtmlResult(item, getNestedId);\n\n html += entry.html;\n nextBindings.push(...entry.bindings);\n } else {\n html += resolveDirectiveValue(item);\n }\n }\n\n const bindingsChanged =\n nextBindings.length !== cached.bindings.length || nextBindings.some((b, i) => b !== cached.bindings[i]);\n\n if (html !== cached.html || bindingsChanged) {\n cached = { bindings: nextBindings, html };\n fnSignal.value = cached;\n }\n });\n\n return { bindings: [], signal: fnSignal };\n};\n\nconst createHtmlWrapperSignal = (\n value: unknown,\n effect: (fn: () => void) => void,\n): {\n keyed: boolean;\n signal: ReadonlySignal<{\n bindings: Binding[];\n html: string;\n items?: Array<{ bindings: Binding[]; html: string }>;\n keys?: (string | number)[];\n }>;\n} | null => {\n const eachSignal = getEachSignalSource(value);\n\n if (eachSignal) {\n return { keyed: true, signal: eachSignal };\n }\n\n if (typeof value === 'function' && !isSignal(value)) {\n const { signal: sig } = renderHtmlItems(value as () => unknown, effect);\n\n return { keyed: false, signal: sig };\n }\n\n if (isSignal(value) && isHtmlResult(value.value)) {\n return {\n keyed: false,\n signal: computed(() => {\n const next = (value as ReadonlySignal<unknown>).value;\n\n if (!isHtmlResult(next)) {\n return { bindings: [], html: String(next) };\n }\n\n const entry = rekeyHtmlResult(next, createMarkerIdFactory());\n\n return { bindings: entry.bindings, html: entry.html };\n }),\n };\n }\n\n return null;\n};\n\nexport const resetTemplateCompilerState = (): void => {\n // Marker IDs are deterministic per compiled template; no global state to reset.\n};\n\n/**\n * Compiles a tagged template into an HTMLResult with reactive bindings.\n *\n * Detects interpolation slots using regex patterns:\n * - `@event-name` → event listener binding\n * - `ref` → ref binding\n * - `:prop` or `?bool` → special attributes\n * - `.prop` → property binding\n * - plain attributes → attribute binding\n *\n * Rekeys nested HTMLResult bindings to avoid ID collisions.\n *\n * @param strings - Template string parts\n * @param values - Interpolated values (signals, functions, directives, primitives)\n * @param effect - Effect hook for reactive bindings\n * @returns HTMLResult with compiled HTML and bindings array\n *\n * @example\n * const name = signal('Alice');\n * const html = compileTemplate`<h1>${() => name.value}</h1>`;\n */\nexport const compileTemplate = (\n strings: TemplateStringsArray,\n values: unknown[],\n effect: (fn: () => void) => void,\n): HTMLResult => {\n const plan = getCompiledTemplatePlan(strings);\n let result = '';\n const bindings: Binding[] = [];\n let activeElementId: string | null = null;\n\n const getNextId = createMarkerIdFactory();\n const isInsideStartTag = (prefix: string) => prefix.lastIndexOf('<') > prefix.lastIndexOf('>');\n const getElementBindingId = (prefix: string): string => {\n if (!activeElementId || isInsideStartTag(prefix)) {\n activeElementId = getNextId();\n }\n\n return activeElementId;\n };\n const resetElementBindingId = (): void => {\n activeElementId = null;\n };\n\n for (let i = 0; i < plan.slots.length; i++) {\n const slot = plan.slots[i];\n const value = values[i];\n\n if (slot.kind === 'event') {\n if (typeof value === 'function') {\n const id = getElementBindingId(slot.prefix);\n\n result += `${slot.prefix} ${CF_ID_ATTR}=\"${id}\"`;\n bindings.push({\n handler: value as (e: Event) => void,\n modifiers: slot.modifiers,\n name: slot.name!,\n type: 'event',\n uid: id,\n });\n } else {\n result += slot.raw;\n }\n\n continue;\n }\n\n if (slot.kind === 'ref') {\n if (value) {\n const id = getElementBindingId(slot.prefix);\n\n result += `${slot.prefix} ${CF_ID_ATTR}=\"${id}\"`;\n bindings.push({\n ref: value as Ref<Element> | RefCallback<Element>,\n type: 'ref',\n uid: id,\n });\n } else {\n result += slot.raw;\n }\n\n continue;\n }\n\n if (slot.kind === 'specialAttr') {\n const id = getElementBindingId(slot.prefix);\n\n result += `${slot.prefix} ${CF_ID_ATTR}=\"${id}\"`;\n bindings.push(createAttrBinding(slot.mode!, slot.name!, id, value));\n continue;\n }\n\n if (slot.kind === 'prop') {\n const id = getElementBindingId(slot.prefix);\n\n result += `${slot.prefix} ${CF_ID_ATTR}=\"${id}\"`;\n bindings.push(createPropBinding(slot.name!, id, value));\n continue;\n }\n\n if (slot.kind === 'plainAttr') {\n const id = getElementBindingId(slot.prefix);\n\n result += `${slot.prefix} ${CF_ID_ATTR}=\"${id}\"`;\n bindings.push(createAttrBinding('attr', slot.name!, id, value));\n continue;\n }\n\n if (typeof value === 'object' && value !== null && ('mount' in value || 'render' in value)) {\n const isInterpolation = 'render' in value;\n const id = isInterpolation ? getNextId() : getElementBindingId(slot.raw);\n\n if (isInterpolation) result += `${slot.raw}<!--${id}-->`;\n else result += `${slot.raw} ${CF_ID_ATTR}=\"${id}\"`;\n\n const apply = (value as Directive).mount?.bind(value);\n\n if (apply) {\n bindings.push({\n apply: (el: HTMLElement, registerCleanup: (fn: () => void) => void) => {\n apply(el, { registerCleanup });\n },\n type: 'callback',\n uid: id,\n });\n }\n\n if (isInterpolation) {\n const render = (value as Directive).render!.bind(value);\n let cached = { bindings: [] as Binding[], html: '' };\n const fnSignal = signal(cached);\n\n effect(() => {\n const res = render();\n const items = Array.isArray(res) ? res : [res];\n const getNestedId = createMarkerIdFactory();\n let html = '';\n const nextBindings: Binding[] = [];\n\n for (const item of items) {\n if (isHtmlResult(item)) {\n const entry = rekeyHtmlResult(item, getNestedId);\n\n html += entry.html;\n nextBindings.push(...entry.bindings);\n } else {\n html += resolveDirectiveValue(item);\n }\n }\n\n const bindingsChanged =\n nextBindings.length !== cached.bindings.length || nextBindings.some((b, i) => b !== cached.bindings[i]);\n\n if (html !== cached.html || bindingsChanged) {\n cached = { bindings: nextBindings, html };\n fnSignal.value = cached;\n }\n });\n\n bindings.push({ keyed: false, signal: fnSignal, type: 'html', uid: id });\n }\n\n continue;\n }\n\n resetElementBindingId();\n\n const htmlWrapper = createHtmlWrapperSignal(value, effect);\n\n if (htmlWrapper) {\n const id = getNextId();\n\n result += `${slot.raw}<!--${id}-->`;\n bindings.push({ keyed: htmlWrapper.keyed, signal: htmlWrapper.signal, type: 'html', uid: id });\n continue;\n }\n\n if (Array.isArray(value)) {\n let combinedHtml = '';\n\n for (const item of value) {\n if (isHtmlResult(item)) {\n const entry = rekeyHtmlResult(item, getNextId);\n\n combinedHtml += entry.html;\n bindings.push(...entry.bindings);\n } else {\n combinedHtml += resolveDirectiveValue(item);\n }\n }\n result += slot.raw + combinedHtml;\n continue;\n }\n\n if (isSignal(value)) {\n const id = getNextId();\n\n result += `${slot.raw}<!--${id}-->`;\n bindings.push({ signal: value as Signal<unknown>, type: 'text', uid: id });\n } else if (isHtmlResult(value)) {\n const entry = rekeyHtmlResult(value, getNextId);\n\n result += slot.raw + entry.html;\n bindings.push(...entry.bindings);\n } else {\n result += slot.raw + resolveDirectiveValue(value);\n }\n }\n\n result += plan.tail;\n\n return htmlResult(normalizeCompiledHtml(result), bindings);\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
require(`./internal.cjs`);var e=new Map,t=1e3,n=n=>{let r=e.get(n);if(!r){if(r=document.createElement(`template`),r.innerHTML=n,e.size>=t){let t=e.keys().next().value;t!==void 0&&e.delete(t)}e.set(n,r)}return r},r=e=>n(e).content.cloneNode(!0),i=(e,t)=>{if(e.nodeType===Node.COMMENT_NODE){let n=e.nodeValue;n&&t.comments.set(n,e);return}if(e.nodeType!==Node.ELEMENT_NODE)return;let n=e.getAttribute(`u`);n&&t.elements.set(n,e)},a=e=>{let t={comments:new Map,elements:new Map},n=document.createTreeWalker(e,NodeFilter.SHOW_COMMENT|NodeFilter.SHOW_ELEMENT);for(i(e,t);n.nextNode();)i(n.currentNode,t);return t},o=e=>{let t={comments:new Map,elements:new Map};for(let n of e){let e=document.createTreeWalker(n,NodeFilter.SHOW_COMMENT|NodeFilter.SHOW_ELEMENT);for(i(n,t);e.nextNode();)i(e.currentNode,t)}return t},s=(e,t)=>{let n=document.createTreeWalker(e,NodeFilter.SHOW_COMMENT);for(;n.nextNode();){let e=n.currentNode;if(e.nodeValue===t)return e}return null},c=e=>Array.from(r(e).childNodes),l=(e,t,n)=>{if(e.parentNode)for(let r of t)e.parentNode.insertBefore(r,n)};exports.createNodes=c,exports.findCommentMarker=s,exports.indexBindings=a,exports.indexBindingsInNodes=o,exports.insertNodes=l,exports.parseHTML=r;
|
|
2
|
+
//# sourceMappingURL=template-dom.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template-dom.cjs","names":[],"sources":["../../src/core/template-dom.ts"],"sourcesContent":["import { CF_ID_ATTR } from './internal';\n\nexport type BindingTargets = {\n comments: Map<string, Comment>;\n elements: Map<string, HTMLElement>;\n};\n\nconst templateCache = new Map<string, HTMLTemplateElement>();\nconst TEMPLATE_CACHE_MAX = 1000;\n\nconst getCachedTemplate = (html: string): HTMLTemplateElement => {\n let tpl = templateCache.get(html);\n\n if (!tpl) {\n tpl = document.createElement('template');\n tpl.innerHTML = html;\n\n if (templateCache.size >= TEMPLATE_CACHE_MAX) {\n const oldestKey = templateCache.keys().next().value as string | undefined;\n\n if (oldestKey !== undefined) templateCache.delete(oldestKey);\n }\n\n templateCache.set(html, tpl);\n }\n\n return tpl;\n};\n\nexport const parseHTML = (html: string): DocumentFragment =>\n getCachedTemplate(html).content.cloneNode(true) as DocumentFragment;\n\nconst collectBindingTarget = (node: Node, targets: BindingTargets): void => {\n if (node.nodeType === Node.COMMENT_NODE) {\n const marker = (node as Comment).nodeValue;\n\n if (marker) targets.comments.set(marker, node as Comment);\n\n return;\n }\n\n if (node.nodeType !== Node.ELEMENT_NODE) return;\n\n const id = (node as Element).getAttribute(CF_ID_ATTR);\n\n if (id) targets.elements.set(id, node as HTMLElement);\n};\n\nexport const indexBindings = (root: Node): BindingTargets => {\n const targets: BindingTargets = { comments: new Map(), elements: new Map() };\n const walker = document.createTreeWalker(root, NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_ELEMENT);\n\n collectBindingTarget(root, targets);\n\n while (walker.nextNode()) collectBindingTarget(walker.currentNode, targets);\n\n return targets;\n};\n\nexport const indexBindingsInNodes = (nodes: Iterable<Node>): BindingTargets => {\n const targets: BindingTargets = { comments: new Map(), elements: new Map() };\n\n for (const node of nodes) {\n const walker = document.createTreeWalker(node, NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_ELEMENT);\n\n collectBindingTarget(node, targets);\n\n while (walker.nextNode()) collectBindingTarget(walker.currentNode, targets);\n }\n\n return targets;\n};\nexport const findCommentMarker = (root: Node, marker: string): Comment | null => {\n const walker = document.createTreeWalker(root, NodeFilter.SHOW_COMMENT);\n\n while (walker.nextNode()) {\n const comment = walker.currentNode as Comment;\n\n if (comment.nodeValue === marker) return comment;\n }\n\n return null;\n};\n\nexport const isHtmlBindingMarker = (node: Node): boolean =>\n node.nodeType === Node.COMMENT_NODE &&\n ((node as Comment).data === 'html-binding' || (node as Comment).data.startsWith('__h_'));\n\nexport const clearAfterMarker = (marker: Comment): void => {\n let next = marker.nextSibling;\n\n while (next) {\n if (isHtmlBindingMarker(next)) break;\n\n const toRemove = next;\n\n next = next.nextSibling;\n toRemove.remove();\n }\n};\n\nexport const createNodes = (htmlString: string): Node[] => Array.from(parseHTML(htmlString).childNodes);\n\nexport const insertNodes = (marker: Comment, nodes: Node[], before: Node | null): void => {\n if (marker.parentNode) {\n for (const node of nodes) marker.parentNode.insertBefore(node, before);\n }\n};\n"],"mappings":"0BAOA,IAAM,EAAgB,IAAI,IACpB,EAAqB,IAErB,EAAqB,GAAsC,CAC/D,IAAI,EAAM,EAAc,IAAI,EAAK,CAEjC,GAAI,CAAC,EAAK,CAIR,GAHA,EAAM,SAAS,cAAc,WAAW,CACxC,EAAI,UAAY,EAEZ,EAAc,MAAQ,EAAoB,CAC5C,IAAM,EAAY,EAAc,MAAM,CAAC,MAAM,CAAC,MAE1C,IAAc,IAAA,IAAW,EAAc,OAAO,EAAU,CAG9D,EAAc,IAAI,EAAM,EAAI,CAG9B,OAAO,GAGI,EAAa,GACxB,EAAkB,EAAK,CAAC,QAAQ,UAAU,GAAK,CAE3C,GAAwB,EAAY,IAAkC,CAC1E,GAAI,EAAK,WAAa,KAAK,aAAc,CACvC,IAAM,EAAU,EAAiB,UAE7B,GAAQ,EAAQ,SAAS,IAAI,EAAQ,EAAgB,CAEzD,OAGF,GAAI,EAAK,WAAa,KAAK,aAAc,OAEzC,IAAM,EAAM,EAAiB,aAAA,IAAwB,CAEjD,GAAI,EAAQ,SAAS,IAAI,EAAI,EAAoB,EAG1C,EAAiB,GAA+B,CAC3D,IAAM,EAA0B,CAAE,SAAU,IAAI,IAAO,SAAU,IAAI,IAAO,CACtE,EAAS,SAAS,iBAAiB,EAAM,WAAW,aAAe,WAAW,aAAa,CAIjG,IAFA,EAAqB,EAAM,EAAQ,CAE5B,EAAO,UAAU,EAAE,EAAqB,EAAO,YAAa,EAAQ,CAE3E,OAAO,GAGI,EAAwB,GAA0C,CAC7E,IAAM,EAA0B,CAAE,SAAU,IAAI,IAAO,SAAU,IAAI,IAAO,CAE5E,IAAK,IAAM,KAAQ,EAAO,CACxB,IAAM,EAAS,SAAS,iBAAiB,EAAM,WAAW,aAAe,WAAW,aAAa,CAIjG,IAFA,EAAqB,EAAM,EAAQ,CAE5B,EAAO,UAAU,EAAE,EAAqB,EAAO,YAAa,EAAQ,CAG7E,OAAO,GAEI,GAAqB,EAAY,IAAmC,CAC/E,IAAM,EAAS,SAAS,iBAAiB,EAAM,WAAW,aAAa,CAEvE,KAAO,EAAO,UAAU,EAAE,CACxB,IAAM,EAAU,EAAO,YAEvB,GAAI,EAAQ,YAAc,EAAQ,OAAO,EAG3C,OAAO,MAoBI,EAAe,GAA+B,MAAM,KAAK,EAAU,EAAW,CAAC,WAAW,CAE1F,GAAe,EAAiB,EAAe,IAA8B,CACxF,GAAI,EAAO,WACT,IAAK,IAAM,KAAQ,EAAO,EAAO,WAAW,aAAa,EAAM,EAAO"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type BindingTargets = {
|
|
2
|
+
comments: Map<string, Comment>;
|
|
3
|
+
elements: Map<string, HTMLElement>;
|
|
4
|
+
};
|
|
5
|
+
export declare const parseHTML: (html: string) => DocumentFragment;
|
|
6
|
+
export declare const indexBindings: (root: Node) => BindingTargets;
|
|
7
|
+
export declare const indexBindingsInNodes: (nodes: Iterable<Node>) => BindingTargets;
|
|
8
|
+
export declare const findCommentMarker: (root: Node, marker: string) => Comment | null;
|
|
9
|
+
export declare const isHtmlBindingMarker: (node: Node) => boolean;
|
|
10
|
+
export declare const clearAfterMarker: (marker: Comment) => void;
|
|
11
|
+
export declare const createNodes: (htmlString: string) => Node[];
|
|
12
|
+
export declare const insertNodes: (marker: Comment, nodes: Node[], before: Node | null) => void;
|
|
13
|
+
//# sourceMappingURL=template-dom.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template-dom.d.ts","sourceRoot":"","sources":["../../src/core/template-dom.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;CACpC,CAAC;AAwBF,eAAO,MAAM,SAAS,GAAI,MAAM,MAAM,KAAG,gBAC4B,CAAC;AAkBtE,eAAO,MAAM,aAAa,GAAI,MAAM,IAAI,KAAG,cAS1C,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAAI,OAAO,QAAQ,CAAC,IAAI,CAAC,KAAG,cAY5D,CAAC;AACF,eAAO,MAAM,iBAAiB,GAAI,MAAM,IAAI,EAAE,QAAQ,MAAM,KAAG,OAAO,GAAG,IAUxE,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAAI,MAAM,IAAI,KAAG,OAEyC,CAAC;AAE3F,eAAO,MAAM,gBAAgB,GAAI,QAAQ,OAAO,KAAG,IAWlD,CAAC;AAEF,eAAO,MAAM,WAAW,GAAI,YAAY,MAAM,KAAG,IAAI,EAAkD,CAAC;AAExG,eAAO,MAAM,WAAW,GAAI,QAAQ,OAAO,EAAE,OAAO,IAAI,EAAE,EAAE,QAAQ,IAAI,GAAG,IAAI,KAAG,IAIjF,CAAC"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import"./internal.js";var e=new Map,t=1e3,n=n=>{let r=e.get(n);if(!r){if(r=document.createElement(`template`),r.innerHTML=n,e.size>=t){let t=e.keys().next().value;t!==void 0&&e.delete(t)}e.set(n,r)}return r},r=e=>n(e).content.cloneNode(!0),i=(e,t)=>{if(e.nodeType===Node.COMMENT_NODE){let n=e.nodeValue;n&&t.comments.set(n,e);return}if(e.nodeType!==Node.ELEMENT_NODE)return;let n=e.getAttribute(`u`);n&&t.elements.set(n,e)},a=e=>{let t={comments:new Map,elements:new Map},n=document.createTreeWalker(e,NodeFilter.SHOW_COMMENT|NodeFilter.SHOW_ELEMENT);for(i(e,t);n.nextNode();)i(n.currentNode,t);return t},o=e=>{let t={comments:new Map,elements:new Map};for(let n of e){let e=document.createTreeWalker(n,NodeFilter.SHOW_COMMENT|NodeFilter.SHOW_ELEMENT);for(i(n,t);e.nextNode();)i(e.currentNode,t)}return t},s=(e,t)=>{let n=document.createTreeWalker(e,NodeFilter.SHOW_COMMENT);for(;n.nextNode();){let e=n.currentNode;if(e.nodeValue===t)return e}return null},c=e=>Array.from(r(e).childNodes),l=(e,t,n)=>{if(e.parentNode)for(let r of t)e.parentNode.insertBefore(r,n)};export{c as createNodes,s as findCommentMarker,a as indexBindings,o as indexBindingsInNodes,l as insertNodes,r as parseHTML};
|
|
2
|
+
//# sourceMappingURL=template-dom.js.map
|