@vielzeug/craftit 2.1.0 → 3.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +58 -124
- package/dist/controls/a11y-control.cjs +1 -1
- package/dist/controls/a11y-control.cjs.map +1 -1
- package/dist/controls/a11y-control.d.ts +1 -1
- package/dist/controls/a11y-control.d.ts.map +1 -1
- package/dist/controls/a11y-control.js +1 -1
- package/dist/controls/a11y-control.js.map +1 -1
- package/dist/controls/checkable-control.cjs +1 -1
- package/dist/controls/checkable-control.cjs.map +1 -1
- package/dist/controls/checkable-control.d.ts +7 -7
- package/dist/controls/checkable-control.d.ts.map +1 -1
- package/dist/controls/checkable-control.js +1 -1
- package/dist/controls/checkable-control.js.map +1 -1
- package/dist/controls/choice-field-control.cjs +2 -0
- package/dist/controls/choice-field-control.cjs.map +1 -0
- package/dist/controls/choice-field-control.d.ts +3 -0
- package/dist/controls/choice-field-control.d.ts.map +1 -0
- package/dist/controls/choice-field-control.js +2 -0
- package/dist/controls/choice-field-control.js.map +1 -0
- package/dist/controls/field-control.cjs +1 -1
- package/dist/controls/field-control.cjs.map +1 -1
- package/dist/controls/field-control.d.ts +28 -73
- package/dist/controls/field-control.d.ts.map +1 -1
- package/dist/controls/field-control.js +1 -1
- package/dist/controls/field-control.js.map +1 -1
- package/dist/controls/index.d.ts +11 -9
- package/dist/controls/index.d.ts.map +1 -1
- package/dist/controls/internal/control-state.cjs +1 -1
- package/dist/controls/internal/control-state.cjs.map +1 -1
- package/dist/controls/internal/control-state.d.ts +6 -4
- package/dist/controls/internal/control-state.d.ts.map +1 -1
- package/dist/controls/internal/control-state.js +1 -1
- package/dist/controls/internal/control-state.js.map +1 -1
- package/dist/controls/internal/keyboard-utils.cjs.map +1 -1
- package/dist/controls/internal/keyboard-utils.js.map +1 -1
- package/dist/controls/internal/number-utils.cjs.map +1 -1
- package/dist/controls/internal/number-utils.js.map +1 -1
- package/dist/controls/internal/validation-utils.cjs.map +1 -1
- package/dist/controls/internal/validation-utils.js.map +1 -1
- package/dist/controls/list-control.cjs +1 -1
- package/dist/controls/list-control.cjs.map +1 -1
- package/dist/controls/list-control.d.ts +10 -8
- package/dist/controls/list-control.d.ts.map +1 -1
- package/dist/controls/list-control.js +1 -1
- package/dist/controls/list-control.js.map +1 -1
- package/dist/controls/overlay-control.cjs +1 -1
- package/dist/controls/overlay-control.cjs.map +1 -1
- package/dist/controls/overlay-control.d.ts +17 -14
- package/dist/controls/overlay-control.d.ts.map +1 -1
- package/dist/controls/overlay-control.js +1 -1
- package/dist/controls/overlay-control.js.map +1 -1
- package/dist/controls/popup-list-control.cjs +2 -0
- package/dist/controls/popup-list-control.cjs.map +1 -0
- package/dist/controls/popup-list-control.d.ts +160 -0
- package/dist/controls/popup-list-control.d.ts.map +1 -0
- package/dist/controls/popup-list-control.js +2 -0
- package/dist/controls/popup-list-control.js.map +1 -0
- package/dist/controls/press-control.cjs.map +1 -1
- package/dist/controls/press-control.js.map +1 -1
- package/dist/controls/slider-control.cjs.map +1 -1
- package/dist/controls/slider-control.js.map +1 -1
- package/dist/controls/spinner-control.cjs.map +1 -1
- package/dist/controls/spinner-control.js.map +1 -1
- package/dist/controls/swipe-control.cjs +2 -0
- package/dist/controls/swipe-control.cjs.map +1 -0
- package/dist/controls/swipe-control.d.ts +32 -0
- package/dist/controls/swipe-control.d.ts.map +1 -0
- package/dist/controls/swipe-control.js +2 -0
- package/dist/controls/swipe-control.js.map +1 -0
- package/dist/controls/text-field-control.cjs +2 -0
- package/dist/controls/text-field-control.cjs.map +1 -0
- package/dist/controls/text-field-control.d.ts +3 -0
- package/dist/controls/text-field-control.d.ts.map +1 -0
- package/dist/controls/text-field-control.js +2 -0
- package/dist/controls/text-field-control.js.map +1 -0
- package/dist/controls.cjs +1 -1
- package/dist/controls.js +1 -1
- package/dist/craftit.cjs +1 -1
- package/dist/craftit.cjs.map +1 -1
- package/dist/craftit.js +1 -1
- package/dist/craftit.js.map +1 -1
- package/dist/directives/classMap.cjs +2 -0
- package/dist/directives/classMap.cjs.map +1 -0
- package/dist/directives/classMap.d.ts +19 -0
- package/dist/directives/classMap.d.ts.map +1 -0
- package/dist/directives/classMap.js +2 -0
- package/dist/directives/classMap.js.map +1 -0
- package/dist/directives/each.cjs +1 -1
- package/dist/directives/each.cjs.map +1 -1
- package/dist/directives/each.d.ts +5 -30
- package/dist/directives/each.d.ts.map +1 -1
- package/dist/directives/each.js +1 -1
- package/dist/directives/each.js.map +1 -1
- package/dist/directives/guard.cjs +2 -0
- package/dist/directives/guard.cjs.map +1 -0
- package/dist/directives/guard.d.ts +10 -0
- package/dist/directives/guard.d.ts.map +1 -0
- package/dist/directives/guard.js +2 -0
- package/dist/directives/guard.js.map +1 -0
- package/dist/directives/live.cjs +2 -0
- package/dist/directives/live.cjs.map +1 -0
- package/dist/directives/live.d.ts +23 -0
- package/dist/directives/live.d.ts.map +1 -0
- package/dist/directives/live.js +2 -0
- package/dist/directives/live.js.map +1 -0
- package/dist/directives/raw.cjs +1 -1
- package/dist/directives/raw.cjs.map +1 -1
- package/dist/directives/raw.d.ts +3 -5
- package/dist/directives/raw.d.ts.map +1 -1
- package/dist/directives/raw.js +1 -1
- package/dist/directives/raw.js.map +1 -1
- package/dist/directives/resource.cjs +2 -0
- package/dist/directives/resource.cjs.map +1 -0
- package/dist/directives/resource.d.ts +32 -0
- package/dist/directives/resource.d.ts.map +1 -0
- package/dist/directives/resource.js +2 -0
- package/dist/directives/resource.js.map +1 -0
- package/dist/directives/styleMap.cjs +2 -0
- package/dist/directives/styleMap.cjs.map +1 -0
- package/dist/directives/styleMap.d.ts +11 -0
- package/dist/directives/styleMap.d.ts.map +1 -0
- package/dist/directives/styleMap.js +2 -0
- package/dist/directives/styleMap.js.map +1 -0
- package/dist/directives/when.cjs +1 -1
- package/dist/directives/when.cjs.map +1 -1
- package/dist/directives/when.d.ts +6 -19
- package/dist/directives/when.d.ts.map +1 -1
- package/dist/directives/when.js +1 -1
- package/dist/directives/when.js.map +1 -1
- package/dist/errors.cjs +2 -0
- package/dist/errors.cjs.map +1 -0
- package/dist/errors.d.ts +12 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +2 -0
- package/dist/errors.js.map +1 -0
- package/dist/form.cjs +1 -1
- package/dist/form.cjs.map +1 -1
- package/dist/form.d.ts +3 -17
- package/dist/form.d.ts.map +1 -1
- package/dist/form.js +1 -1
- package/dist/form.js.map +1 -1
- package/dist/host.cjs +1 -1
- package/dist/host.cjs.map +1 -1
- package/dist/host.d.ts +40 -37
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +1 -1
- package/dist/host.js.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +16 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/internal.cjs +1 -1
- package/dist/internal.cjs.map +1 -1
- package/dist/internal.d.ts +60 -120
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +1 -1
- package/dist/internal.js.map +1 -1
- package/dist/observers/index.d.ts +1 -0
- package/dist/observers/index.d.ts.map +1 -1
- package/dist/observers/intersection-observe.cjs +1 -1
- package/dist/observers/intersection-observe.cjs.map +1 -1
- package/dist/observers/intersection-observe.d.ts +1 -1
- package/dist/observers/intersection-observe.js +1 -1
- package/dist/observers/intersection-observe.js.map +1 -1
- package/dist/observers/media-observe.cjs +1 -1
- package/dist/observers/media-observe.cjs.map +1 -1
- package/dist/observers/media-observe.d.ts +1 -1
- package/dist/observers/media-observe.js +1 -1
- package/dist/observers/media-observe.js.map +1 -1
- package/dist/observers/mutation-observe.cjs +2 -0
- package/dist/observers/mutation-observe.cjs.map +1 -0
- package/dist/observers/mutation-observe.d.ts +10 -0
- package/dist/observers/mutation-observe.d.ts.map +1 -0
- package/dist/observers/mutation-observe.js +2 -0
- package/dist/observers/mutation-observe.js.map +1 -0
- package/dist/observers/resize-observe.cjs +1 -1
- package/dist/observers/resize-observe.cjs.map +1 -1
- package/dist/observers/resize-observe.d.ts +1 -1
- package/dist/observers/resize-observe.js +1 -1
- package/dist/observers/resize-observe.js.map +1 -1
- package/dist/observers.cjs +1 -1
- package/dist/observers.js +1 -1
- package/dist/props.cjs +1 -1
- package/dist/props.cjs.map +1 -1
- package/dist/props.d.ts +18 -31
- package/dist/props.d.ts.map +1 -1
- package/dist/props.js +1 -1
- package/dist/props.js.map +1 -1
- package/dist/registration.cjs +1 -1
- package/dist/registration.cjs.map +1 -1
- package/dist/registration.d.ts +27 -7
- package/dist/registration.d.ts.map +1 -1
- package/dist/registration.js +1 -1
- package/dist/registration.js.map +1 -1
- package/dist/runtime.cjs +1 -1
- package/dist/runtime.cjs.map +1 -1
- package/dist/runtime.d.ts +29 -17
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +1 -1
- package/dist/runtime.js.map +1 -1
- package/dist/template-bindings.cjs +1 -1
- package/dist/template-bindings.cjs.map +1 -1
- package/dist/template-bindings.d.ts +10 -47
- package/dist/template-bindings.d.ts.map +1 -1
- package/dist/template-bindings.js +1 -1
- package/dist/template-bindings.js.map +1 -1
- package/dist/template-compiler.cjs +1 -1
- package/dist/template-compiler.cjs.map +1 -1
- package/dist/template-compiler.d.ts +1 -21
- package/dist/template-compiler.d.ts.map +1 -1
- package/dist/template-compiler.js +1 -1
- package/dist/template-compiler.js.map +1 -1
- package/dist/testing/testing.cjs +1 -1
- package/dist/testing/testing.cjs.map +1 -1
- package/dist/testing/testing.d.ts +12 -5
- package/dist/testing/testing.d.ts.map +1 -1
- package/dist/testing/testing.js +1 -1
- package/dist/testing/testing.js.map +1 -1
- package/package.json +6 -12
- package/dist/component.cjs +0 -2
- package/dist/component.cjs.map +0 -1
- package/dist/component.d.ts +0 -39
- package/dist/component.d.ts.map +0 -1
- package/dist/component.js +0 -2
- package/dist/component.js.map +0 -1
- package/dist/controls/list-key-control.cjs +0 -2
- package/dist/controls/list-key-control.cjs.map +0 -1
- package/dist/controls/list-key-control.d.ts +0 -14
- package/dist/controls/list-key-control.d.ts.map +0 -1
- package/dist/controls/list-key-control.js +0 -2
- package/dist/controls/list-key-control.js.map +0 -1
- package/dist/directives/attr.cjs +0 -2
- package/dist/directives/attr.cjs.map +0 -1
- package/dist/directives/attr.d.ts +0 -12
- package/dist/directives/attr.d.ts.map +0 -1
- package/dist/directives/attr.js +0 -2
- package/dist/directives/attr.js.map +0 -1
- package/dist/directives/bind.cjs +0 -2
- package/dist/directives/bind.cjs.map +0 -1
- package/dist/directives/bind.d.ts +0 -38
- package/dist/directives/bind.d.ts.map +0 -1
- package/dist/directives/bind.js +0 -2
- package/dist/directives/bind.js.map +0 -1
- package/dist/directives/choose.cjs +0 -2
- package/dist/directives/choose.cjs.map +0 -1
- package/dist/directives/choose.d.ts +0 -39
- package/dist/directives/choose.d.ts.map +0 -1
- package/dist/directives/choose.js +0 -2
- package/dist/directives/choose.js.map +0 -1
- package/dist/directives/classes.cjs +0 -2
- package/dist/directives/classes.cjs.map +0 -1
- package/dist/directives/classes.d.ts +0 -20
- package/dist/directives/classes.d.ts.map +0 -1
- package/dist/directives/classes.js +0 -2
- package/dist/directives/classes.js.map +0 -1
- package/dist/directives/index.d.ts +0 -13
- package/dist/directives/index.d.ts.map +0 -1
- package/dist/directives/memo.cjs +0 -2
- package/dist/directives/memo.cjs.map +0 -1
- package/dist/directives/memo.d.ts +0 -27
- package/dist/directives/memo.d.ts.map +0 -1
- package/dist/directives/memo.js +0 -2
- package/dist/directives/memo.js.map +0 -1
- package/dist/directives/on.cjs +0 -2
- package/dist/directives/on.cjs.map +0 -1
- package/dist/directives/on.d.ts +0 -25
- package/dist/directives/on.d.ts.map +0 -1
- package/dist/directives/on.js +0 -2
- package/dist/directives/on.js.map +0 -1
- package/dist/directives/spread.cjs +0 -2
- package/dist/directives/spread.cjs.map +0 -1
- package/dist/directives/spread.d.ts +0 -14
- package/dist/directives/spread.d.ts.map +0 -1
- package/dist/directives/spread.js +0 -2
- package/dist/directives/spread.js.map +0 -1
- package/dist/directives/style.cjs +0 -2
- package/dist/directives/style.cjs.map +0 -1
- package/dist/directives/style.d.ts +0 -22
- package/dist/directives/style.d.ts.map +0 -1
- package/dist/directives/style.js +0 -2
- package/dist/directives/style.js.map +0 -1
- package/dist/directives/until.cjs +0 -2
- package/dist/directives/until.cjs.map +0 -1
- package/dist/directives/until.d.ts +0 -26
- package/dist/directives/until.d.ts.map +0 -1
- package/dist/directives/until.js +0 -2
- package/dist/directives/until.js.map +0 -1
- package/dist/directives.cjs +0 -1
- package/dist/directives.js +0 -1
- package/dist/runtime-bindings.cjs +0 -2
- package/dist/runtime-bindings.cjs.map +0 -1
- package/dist/runtime-bindings.d.ts +0 -6
- package/dist/runtime-bindings.d.ts.map +0 -1
- package/dist/runtime-bindings.js +0 -2
- package/dist/runtime-bindings.js.map +0 -1
- package/dist/runtime-core.cjs +0 -2
- package/dist/runtime-core.cjs.map +0 -1
- package/dist/runtime-core.d.ts +0 -21
- package/dist/runtime-core.d.ts.map +0 -1
- package/dist/runtime-core.js +0 -2
- package/dist/runtime-core.js.map +0 -1
- package/dist/runtime-lifecycle.cjs +0 -2
- package/dist/runtime-lifecycle.cjs.map +0 -1
- package/dist/runtime-lifecycle.d.ts +0 -24
- package/dist/runtime-lifecycle.d.ts.map +0 -1
- package/dist/runtime-lifecycle.js +0 -2
- package/dist/runtime-lifecycle.js.map +0 -1
- package/dist/template-dom.cjs +0 -2
- package/dist/template-dom.cjs.map +0 -1
- package/dist/template-dom.d.ts +0 -13
- package/dist/template-dom.d.ts.map +0 -1
- package/dist/template-dom.js +0 -2
- package/dist/template-dom.js.map +0 -1
- package/dist/template-html.cjs +0 -2
- package/dist/template-html.cjs.map +0 -1
- package/dist/template-html.d.ts +0 -23
- package/dist/template-html.d.ts.map +0 -1
- package/dist/template-html.js +0 -2
- package/dist/template-html.js.map +0 -1
- package/dist/template.cjs +0 -2
- package/dist/template.cjs.map +0 -1
- package/dist/template.d.ts +0 -10
- package/dist/template.d.ts.map +0 -1
- package/dist/template.js +0 -2
- package/dist/template.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"template-compiler.cjs","names":[],"sources":["../src/template-compiler.ts"],"sourcesContent":["import { computed, isSignal, type ReadonlySignal, type Signal } from '@vielzeug/stateit';\n\nimport {\n CF_ID_ATTR,\n EACH_SIGNAL,\n escapeHtml,\n htmlResult,\n isHtmlResult,\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';\n\nconst ATTR_ID_RE = new RegExp(`${CF_ID_ATTR}=\"([^\"]+)\"`, 'g');\n\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 = (getter: () => unknown): { bindings: Binding[]; signal: ReadonlySignal<any> } => {\n let cached = { bindings: [] as Binding[], html: '' };\n const fnSignal = computed(() => {\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 }\n\n return cached;\n });\n\n return { bindings: [], signal: fnSignal };\n};\n\nconst createHtmlWrapperSignal = (\n value: unknown,\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);\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: resolveDirectiveValue(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\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 = (strings: TemplateStringsArray, values: unknown[]): 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 = computed(() => {\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 }\n\n return cached;\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);\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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"}
|
|
1
|
+
{"version":3,"file":"template-compiler.cjs","names":[],"sources":["../src/template-compiler.ts"],"sourcesContent":["import { computed, isSignal, type ReadonlySignal, type Signal } from '@vielzeug/stateit';\n\nimport {\n CF_ID_ATTR,\n createMarkerIdFactory,\n escapeHtml,\n htmlResult,\n isDirectiveResult,\n isHtmlResult,\n rekeyHtmlResult,\n type Binding,\n type HTMLResult,\n type Ref,\n type RefCallback,\n} from './internal';\nimport { createAttrBinding } from './template-bindings';\n\n// Templates use the HTML as-is; no aggressive whitespace normalization\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: 'boolAttr' as const, regex: /\\s+\\?([a-zA-Z_][-a-zA-Z0-9_]*)\\s*=\\s*[\"']?$/ },\n { kind: 'attr' 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 mode?: 'attr' | 'bool';\n modifiers?: string[];\n // For 'event' and attribute slots\n name?: string;\n prefix: string;\n raw: string;\n};\n\ntype CompiledTemplatePlan = {\n slots: CompiledTemplateSlot[];\n tail: string;\n};\n\ntype HtmlWrapperSignal = ReadonlySignal<{\n bindings: Binding[];\n html: string;\n}>;\n\nconst templatePlanCache = new WeakMap<TemplateStringsArray, CompiledTemplatePlan>();\nconst htmlGetterSignalCache = new WeakMap<() => unknown, HtmlWrapperSignal>();\nconst htmlSignalWrapperCache = new WeakMap<ReadonlySignal<unknown>, HtmlWrapperSignal>();\n\nconst MODIFIER_WRAPPERS: Record<string, (fn: (e: Event) => void) => (e: Event) => void> = {\n prevent: (fn) => (e) => {\n e.preventDefault();\n fn(e);\n },\n self: (fn) => (e) => {\n if (e.target === e.currentTarget) fn(e);\n },\n stop: (fn) => (e) => {\n e.stopPropagation();\n fn(e);\n },\n};\n\nconst applyModifiers = (\n handler: (e: Event) => void,\n modifiers: string[],\n): { handler: (e: Event) => void; options?: AddEventListenerOptions } => {\n const wrapped = modifiers.reduce((fn, m) => MODIFIER_WRAPPERS[m]?.(fn) ?? fn, handler);\n\n const options: AddEventListenerOptions = {};\n\n if (modifiers.includes('capture')) options.capture = true;\n\n if (modifiers.includes('once')) options.once = true;\n\n if (modifiers.includes('passive')) options.passive = true;\n\n return { handler: wrapped, ...(Object.keys(options).length ? { options } : {}) };\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 = (getter: () => unknown): { bindings: Binding[]; signal: ReadonlySignal<any> } => {\n let cached = { bindings: [] as Binding[], html: '' };\n const fnSignal = computed(() => {\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 }\n\n return cached;\n });\n\n return { bindings: [], signal: fnSignal };\n};\n\nconst createHtmlWrapperSignal = (\n value: unknown,\n): {\n signal: HtmlWrapperSignal;\n} | null => {\n if (typeof value === 'function') {\n const getter = value as () => unknown;\n const cached = htmlGetterSignalCache.get(getter);\n\n if (cached) {\n return { signal: cached };\n }\n\n const { signal: sig } = renderHtmlItems(getter);\n\n htmlGetterSignalCache.set(getter, sig);\n\n return { signal: sig };\n }\n\n if (isSignal(value) && isHtmlResult((value as ReadonlySignal<unknown>).value)) {\n const htmlSignal = value as ReadonlySignal<unknown>;\n const cached = htmlSignalWrapperCache.get(htmlSignal);\n\n if (cached) {\n return { signal: cached };\n }\n\n const wrapped = computed(() => {\n const next = htmlSignal.value;\n\n if (!isHtmlResult(next)) {\n return { bindings: [], html: resolveDirectiveValue(next) };\n }\n\n const entry = rekeyHtmlResult(next, createMarkerIdFactory());\n\n return { bindings: entry.bindings, html: entry.html };\n });\n\n htmlSignalWrapperCache.set(htmlSignal, wrapped);\n\n return { signal: wrapped };\n }\n\n return null;\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 parts = m[1].split('.');\n const eventName = parts[0];\n const modifiers = parts.slice(1);\n\n slots.push({ kind: 'event', modifiers, name: eventName, prefix, raw: str });\n } else if (pattern.kind === 'ref') {\n slots.push({ kind: 'ref', prefix, raw: str });\n } else if (pattern.kind === 'boolAttr') {\n slots.push({ kind: 'boolAttr', mode: 'bool', name: m[1], prefix, raw: str });\n } else if (pattern.kind === 'attr') {\n slots.push({ kind: 'attr', mode: 'attr', name: m[1], prefix, raw: str });\n }\n\n break;\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\nexport const compileTemplate = (strings: TemplateStringsArray, values: unknown[]): 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 const { handler, options } = applyModifiers(value as (e: Event) => void, slot.modifiers ?? []);\n\n result += `${slot.prefix} ${CF_ID_ATTR}=\"${id}\"`;\n bindings.push({ handler, name: slot.name!, options, type: 'event', uid: id });\n } else if (isSignal(value)) {\n // If a signal is passed to an event binding, we assume its current value\n // is the intended handler.\n const id = getElementBindingId(slot.prefix);\n const signalHandler = (e: Event) => {\n const currentHandler = (value as ReadonlySignal<unknown>).value;\n\n if (typeof currentHandler === 'function') {\n (currentHandler as (e: Event) => void)(e);\n }\n };\n const { handler, options } = applyModifiers(signalHandler, slot.modifiers ?? []);\n\n result += `${slot.prefix} ${CF_ID_ATTR}=\"${id}\"`;\n bindings.push({ handler, name: slot.name!, options, type: 'event', uid: id });\n } else result += slot.raw;\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 result += slot.raw;\n\n continue;\n }\n\n if (slot.kind === 'boolAttr' || slot.kind === 'attr') {\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 === 'node') {\n resetElementBindingId();\n\n if (isDirectiveResult(value)) {\n const id = getNextId();\n\n result += `${slot.raw}<!--${id}-->`;\n bindings.push({ directive: value, type: 'directive', uid: id });\n continue;\n }\n\n const htmlWrapper = createHtmlWrapperSignal(value);\n\n if (htmlWrapper) {\n const id = getNextId();\n\n result += `${slot.raw}<!--${id}-->`;\n bindings.push({ 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 continue;\n }\n }\n\n result += plan.tail;\n\n return htmlResult(result, bindings);\n};\n\nexport const html = (strings: TemplateStringsArray, ...values: unknown[]): HTMLResult =>\n compileTemplate(strings, values);\n"],"mappings":"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"}
|
|
@@ -1,24 +1,4 @@
|
|
|
1
1
|
import { type HTMLResult } from './internal';
|
|
2
|
-
/**
|
|
3
|
-
* Compiles a tagged template into an HTMLResult with reactive bindings.
|
|
4
|
-
*
|
|
5
|
-
* Detects interpolation slots using regex patterns:
|
|
6
|
-
* - `@event-name` → event listener binding
|
|
7
|
-
* - `ref` → ref binding
|
|
8
|
-
* - `:prop` or `?bool` → special attributes
|
|
9
|
-
* - `.prop` → property binding
|
|
10
|
-
* - plain attributes → attribute binding
|
|
11
|
-
*
|
|
12
|
-
* Rekeys nested HTMLResult bindings to avoid ID collisions.
|
|
13
|
-
*
|
|
14
|
-
* @param strings - Template string parts
|
|
15
|
-
* @param values - Interpolated values (signals, functions, directives, primitives)
|
|
16
|
-
* @param effect - Effect hook for reactive bindings
|
|
17
|
-
* @returns HTMLResult with compiled HTML and bindings array
|
|
18
|
-
*
|
|
19
|
-
* @example
|
|
20
|
-
* const name = signal('Alice');
|
|
21
|
-
* const html = compileTemplate`<h1>${() => name.value}</h1>`;
|
|
22
|
-
*/
|
|
23
2
|
export declare const compileTemplate: (strings: TemplateStringsArray, values: unknown[]) => HTMLResult;
|
|
3
|
+
export declare const html: (strings: TemplateStringsArray, ...values: unknown[]) => HTMLResult;
|
|
24
4
|
//# sourceMappingURL=template-compiler.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"template-compiler.d.ts","sourceRoot":"","sources":["../src/template-compiler.ts"],"names":[],"mappings":"AAEA,OAAO,EASL,KAAK,UAAU,EAGhB,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"template-compiler.d.ts","sourceRoot":"","sources":["../src/template-compiler.ts"],"names":[],"mappings":"AAEA,OAAO,EASL,KAAK,UAAU,EAGhB,MAAM,YAAY,CAAC;AAmNpB,eAAO,MAAM,eAAe,GAAI,SAAS,oBAAoB,EAAE,QAAQ,OAAO,EAAE,KAAG,UAoIlF,CAAC;AAEF,eAAO,MAAM,IAAI,GAAI,SAAS,oBAAoB,EAAE,GAAG,QAAQ,OAAO,EAAE,KAAG,UACzC,CAAC"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{createMarkerIdFactory as e,escapeHtml as t,htmlResult as n,isDirectiveResult as r,isHtmlResult as i,rekeyHtmlResult as a}from"./internal.js";import{createAttrBinding as o}from"./template-bindings.js";import{computed as s,isSignal as c}from"@vielzeug/stateit";var l=[{kind:`event`,regex:/\s+@([a-zA-Z_][-a-zA-Z0-9_.-]*)\s*=\s*["']?$/},{kind:`ref`,regex:/\s+ref\s*=\s*["']?$/},{kind:`boolAttr`,regex:/\s+\?([a-zA-Z_][-a-zA-Z0-9_]*)\s*=\s*["']?$/},{kind:`attr`,regex:/\s+:?([a-zA-Z_][-a-zA-Z0-9_]*)\s*=\s*["']?$/}],u=new WeakMap,d=new WeakMap,f=new WeakMap,p={prevent:e=>t=>{t.preventDefault(),e(t)},self:e=>t=>{t.target===t.currentTarget&&e(t)},stop:e=>t=>{t.stopPropagation(),e(t)}},m=(e,t)=>{let n=t.reduce((e,t)=>p[t]?.(e)??e,e),r={};return t.includes(`capture`)&&(r.capture=!0),t.includes(`once`)&&(r.once=!0),t.includes(`passive`)&&(r.passive=!0),{handler:n,...Object.keys(r).length?{options:r}:{}}},h=e=>typeof e==`string`?t(e):e==null?``:i(e)?e.__html:t(String(e)),g=t=>{let n={bindings:[],html:``};return{bindings:[],signal:s(()=>{let r=t(),o=Array.isArray(r)?r:[r],s=e(),c=``,l=[];for(let e of o)if(i(e)){let t=a(e,s);c+=t.html,l.push(...t.bindings)}else c+=h(e);let u=l.length!==n.bindings.length||l.some((e,t)=>e!==n.bindings[t]);return(c!==n.html||u)&&(n={bindings:l,html:c}),n})}},_=t=>{if(typeof t==`function`){let e=t,n=d.get(e);if(n)return{signal:n};let{signal:r}=g(e);return d.set(e,r),{signal:r}}if(c(t)&&i(t.value)){let n=t,r=f.get(n);if(r)return{signal:r};let o=s(()=>{let t=n.value;if(!i(t))return{bindings:[],html:h(t)};let r=a(t,e());return{bindings:r.bindings,html:r.html}});return f.set(n,o),{signal:o}}return null},v=e=>{let t=[];for(let n=0;n<e.length-1;n++){let r=e[n],i=!1;for(let e of l){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=n[1].split(`.`),i=e[0],o=e.slice(1);t.push({kind:`event`,modifiers:o,name:i,prefix:a,raw:r})}else e.kind===`ref`?t.push({kind:`ref`,prefix:a,raw:r}):e.kind===`boolAttr`?t.push({kind:`boolAttr`,mode:`bool`,name:n[1],prefix:a,raw:r}):e.kind===`attr`&&t.push({kind:`attr`,mode:`attr`,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]??``}},y=e=>{let t=u.get(e);return t||(t=v(e),u.set(e,t)),t},b=(t,s)=>{let l=y(t),u=``,d=[],f=null,p=e(),g=e=>e.lastIndexOf(`<`)>e.lastIndexOf(`>`),v=e=>((!f||g(e))&&(f=p()),f),b=()=>{f=null};for(let e=0;e<l.slots.length;e++){let t=l.slots[e],n=s[e];if(t.kind===`event`){if(typeof n==`function`){let e=v(t.prefix),{handler:r,options:i}=m(n,t.modifiers??[]);u+=`${t.prefix} u="${e}"`,d.push({handler:r,name:t.name,options:i,type:`event`,uid:e})}else if(c(n)){let e=v(t.prefix),{handler:r,options:i}=m(e=>{let t=n.value;typeof t==`function`&&t(e)},t.modifiers??[]);u+=`${t.prefix} u="${e}"`,d.push({handler:r,name:t.name,options:i,type:`event`,uid:e})}else u+=t.raw;continue}if(t.kind===`ref`){if(n){let e=v(t.prefix);u+=`${t.prefix} u="${e}"`,d.push({ref:n,type:`ref`,uid:e})}else u+=t.raw;continue}if(t.kind===`boolAttr`||t.kind===`attr`){let e=v(t.prefix);u+=`${t.prefix} u="${e}"`,d.push(o(t.mode,t.name,e,n));continue}if(t.kind===`node`){if(b(),r(n)){let e=p();u+=`${t.raw}<!--${e}-->`,d.push({directive:n,type:`directive`,uid:e});continue}let e=_(n);if(e){let n=p();u+=`${t.raw}<!--${n}-->`,d.push({signal:e.signal,type:`html`,uid:n});continue}if(Array.isArray(n)){let e=``;for(let t of n)if(i(t)){let n=a(t,p);e+=n.html,d.push(...n.bindings)}else e+=h(t);u+=t.raw+e;continue}if(c(n)){let e=p();u+=`${t.raw}<!--${e}-->`,d.push({signal:n,type:`text`,uid:e})}else if(i(n)){let e=a(n,p);u+=t.raw+e.html,d.push(...e.bindings)}else u+=t.raw+h(n);continue}}return u+=l.tail,n(u,d)},x=(e,...t)=>b(e,t);export{x as html};
|
|
2
2
|
//# sourceMappingURL=template-compiler.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"template-compiler.js","names":[],"sources":["../src/template-compiler.ts"],"sourcesContent":["import { computed, isSignal, type ReadonlySignal, type Signal } from '@vielzeug/stateit';\n\nimport {\n CF_ID_ATTR,\n EACH_SIGNAL,\n escapeHtml,\n htmlResult,\n isHtmlResult,\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';\n\nconst ATTR_ID_RE = new RegExp(`${CF_ID_ATTR}=\"([^\"]+)\"`, 'g');\n\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 = (getter: () => unknown): { bindings: Binding[]; signal: ReadonlySignal<any> } => {\n let cached = { bindings: [] as Binding[], html: '' };\n const fnSignal = computed(() => {\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 }\n\n return cached;\n });\n\n return { bindings: [], signal: fnSignal };\n};\n\nconst createHtmlWrapperSignal = (\n value: unknown,\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);\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: resolveDirectiveValue(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\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 = (strings: TemplateStringsArray, values: unknown[]): 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 = computed(() => {\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 }\n\n return cached;\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);\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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"}
|
|
1
|
+
{"version":3,"file":"template-compiler.js","names":[],"sources":["../src/template-compiler.ts"],"sourcesContent":["import { computed, isSignal, type ReadonlySignal, type Signal } from '@vielzeug/stateit';\n\nimport {\n CF_ID_ATTR,\n createMarkerIdFactory,\n escapeHtml,\n htmlResult,\n isDirectiveResult,\n isHtmlResult,\n rekeyHtmlResult,\n type Binding,\n type HTMLResult,\n type Ref,\n type RefCallback,\n} from './internal';\nimport { createAttrBinding } from './template-bindings';\n\n// Templates use the HTML as-is; no aggressive whitespace normalization\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: 'boolAttr' as const, regex: /\\s+\\?([a-zA-Z_][-a-zA-Z0-9_]*)\\s*=\\s*[\"']?$/ },\n { kind: 'attr' 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 mode?: 'attr' | 'bool';\n modifiers?: string[];\n // For 'event' and attribute slots\n name?: string;\n prefix: string;\n raw: string;\n};\n\ntype CompiledTemplatePlan = {\n slots: CompiledTemplateSlot[];\n tail: string;\n};\n\ntype HtmlWrapperSignal = ReadonlySignal<{\n bindings: Binding[];\n html: string;\n}>;\n\nconst templatePlanCache = new WeakMap<TemplateStringsArray, CompiledTemplatePlan>();\nconst htmlGetterSignalCache = new WeakMap<() => unknown, HtmlWrapperSignal>();\nconst htmlSignalWrapperCache = new WeakMap<ReadonlySignal<unknown>, HtmlWrapperSignal>();\n\nconst MODIFIER_WRAPPERS: Record<string, (fn: (e: Event) => void) => (e: Event) => void> = {\n prevent: (fn) => (e) => {\n e.preventDefault();\n fn(e);\n },\n self: (fn) => (e) => {\n if (e.target === e.currentTarget) fn(e);\n },\n stop: (fn) => (e) => {\n e.stopPropagation();\n fn(e);\n },\n};\n\nconst applyModifiers = (\n handler: (e: Event) => void,\n modifiers: string[],\n): { handler: (e: Event) => void; options?: AddEventListenerOptions } => {\n const wrapped = modifiers.reduce((fn, m) => MODIFIER_WRAPPERS[m]?.(fn) ?? fn, handler);\n\n const options: AddEventListenerOptions = {};\n\n if (modifiers.includes('capture')) options.capture = true;\n\n if (modifiers.includes('once')) options.once = true;\n\n if (modifiers.includes('passive')) options.passive = true;\n\n return { handler: wrapped, ...(Object.keys(options).length ? { options } : {}) };\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 = (getter: () => unknown): { bindings: Binding[]; signal: ReadonlySignal<any> } => {\n let cached = { bindings: [] as Binding[], html: '' };\n const fnSignal = computed(() => {\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 }\n\n return cached;\n });\n\n return { bindings: [], signal: fnSignal };\n};\n\nconst createHtmlWrapperSignal = (\n value: unknown,\n): {\n signal: HtmlWrapperSignal;\n} | null => {\n if (typeof value === 'function') {\n const getter = value as () => unknown;\n const cached = htmlGetterSignalCache.get(getter);\n\n if (cached) {\n return { signal: cached };\n }\n\n const { signal: sig } = renderHtmlItems(getter);\n\n htmlGetterSignalCache.set(getter, sig);\n\n return { signal: sig };\n }\n\n if (isSignal(value) && isHtmlResult((value as ReadonlySignal<unknown>).value)) {\n const htmlSignal = value as ReadonlySignal<unknown>;\n const cached = htmlSignalWrapperCache.get(htmlSignal);\n\n if (cached) {\n return { signal: cached };\n }\n\n const wrapped = computed(() => {\n const next = htmlSignal.value;\n\n if (!isHtmlResult(next)) {\n return { bindings: [], html: resolveDirectiveValue(next) };\n }\n\n const entry = rekeyHtmlResult(next, createMarkerIdFactory());\n\n return { bindings: entry.bindings, html: entry.html };\n });\n\n htmlSignalWrapperCache.set(htmlSignal, wrapped);\n\n return { signal: wrapped };\n }\n\n return null;\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 parts = m[1].split('.');\n const eventName = parts[0];\n const modifiers = parts.slice(1);\n\n slots.push({ kind: 'event', modifiers, name: eventName, prefix, raw: str });\n } else if (pattern.kind === 'ref') {\n slots.push({ kind: 'ref', prefix, raw: str });\n } else if (pattern.kind === 'boolAttr') {\n slots.push({ kind: 'boolAttr', mode: 'bool', name: m[1], prefix, raw: str });\n } else if (pattern.kind === 'attr') {\n slots.push({ kind: 'attr', mode: 'attr', name: m[1], prefix, raw: str });\n }\n\n break;\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\nexport const compileTemplate = (strings: TemplateStringsArray, values: unknown[]): 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 const { handler, options } = applyModifiers(value as (e: Event) => void, slot.modifiers ?? []);\n\n result += `${slot.prefix} ${CF_ID_ATTR}=\"${id}\"`;\n bindings.push({ handler, name: slot.name!, options, type: 'event', uid: id });\n } else if (isSignal(value)) {\n // If a signal is passed to an event binding, we assume its current value\n // is the intended handler.\n const id = getElementBindingId(slot.prefix);\n const signalHandler = (e: Event) => {\n const currentHandler = (value as ReadonlySignal<unknown>).value;\n\n if (typeof currentHandler === 'function') {\n (currentHandler as (e: Event) => void)(e);\n }\n };\n const { handler, options } = applyModifiers(signalHandler, slot.modifiers ?? []);\n\n result += `${slot.prefix} ${CF_ID_ATTR}=\"${id}\"`;\n bindings.push({ handler, name: slot.name!, options, type: 'event', uid: id });\n } else result += slot.raw;\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 result += slot.raw;\n\n continue;\n }\n\n if (slot.kind === 'boolAttr' || slot.kind === 'attr') {\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 === 'node') {\n resetElementBindingId();\n\n if (isDirectiveResult(value)) {\n const id = getNextId();\n\n result += `${slot.raw}<!--${id}-->`;\n bindings.push({ directive: value, type: 'directive', uid: id });\n continue;\n }\n\n const htmlWrapper = createHtmlWrapperSignal(value);\n\n if (htmlWrapper) {\n const id = getNextId();\n\n result += `${slot.raw}<!--${id}-->`;\n bindings.push({ 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 continue;\n }\n }\n\n result += plan.tail;\n\n return htmlResult(result, bindings);\n};\n\nexport const html = (strings: TemplateStringsArray, ...values: unknown[]): HTMLResult =>\n compileTemplate(strings, values);\n"],"mappings":"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"}
|
package/dist/testing/testing.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require(`../internal.cjs`),t=require(`../
|
|
1
|
+
require(`../runtime.cjs`);const e=require(`../internal.cjs`),t=require(`../registration.cjs`);var n=[],r=0,i=()=>{e._resetIdCounter(),r=0};async function a(){let e=async e=>{for(let t=0;t<e;t++)await Promise.resolve(),await new Promise(e=>queueMicrotask(e))};await e(12),await new Promise(e=>typeof requestAnimationFrame<`u`?requestAnimationFrame(()=>e()):e()),await e(2)}function o(e){e(x)}function s(e,t,n){n===!1?e.removeAttribute(t):e.setAttribute(t,n===!0?``:String(n))}var c=e=>e instanceof Error?e:Error(String(e)),l=async e=>{if(typeof window>`u`)return e();let t=null,n=e=>{t=c(e.error??e.message),e.preventDefault()},r=e=>{t=c(e.reason),e.preventDefault()};window.addEventListener(`error`,n),window.addEventListener(`unhandledrejection`,r);try{let n=await e();if(t)throw t;return n}finally{window.removeEventListener(`error`,n),window.removeEventListener(`unhandledrejection`,r)}};async function u(e,i={}){let{attrs:o={},componentOptions:c,container:u=document.body,html:p,props:m={}}=i,h=e=>(t,n)=>{let r=e(t,n);return typeof r==`function`?r:()=>r},g,_;typeof e==`string`?g=e:(g=`trial-${++r}`,_={...c??{},setup:h(e)}),_&&t.define(g,_);let v=document.createElement(g);p&&(v.innerHTML=p),Object.keys(m).length&&Object.assign(v,m);for(let[e,t]of Object.entries(o))s(v,e,t);return await l(async()=>{u.appendChild(v),n.push(v),await a()}),{async act(e){await e(),await a()},async attr(e,t){s(v,e,t),await a()},async attrs(e){for(let[t,n]of Object.entries(e))s(v,t,n);await a()},destroy(){v.remove();let e=n.indexOf(v);e!==-1&&n.splice(e,1)},element:v,flush:a,query(e){return v.shadowRoot?.querySelector(e)??null},queryAll(e){return Array.from(v.shadowRoot?.querySelectorAll(e)??[])},queryAllByTestId(e){return Array.from(v.shadowRoot?.querySelectorAll(`[data-testid="${e}"]`)??[])},queryAllByText(e,t=`*`){return f(v.shadowRoot,e,t)},queryByTestId(e){return v.shadowRoot?.querySelector(`[data-testid="${e}"]`)??null},queryByText(e,t=`*`){return d(v.shadowRoot,e,t)},get shadow(){return v.shadowRoot}}}function d(e,t,n){for(let r of e.querySelectorAll(n))if(r.textContent?.trim()===t)return r;return null}function f(e,t,n){return Array.from(e.querySelectorAll(n)).filter(e=>e.textContent?.trim()===t)}function p(e){return{query:t=>e.querySelector(t),queryAll:t=>Array.from(e.querySelectorAll(t)),queryAllByTestId:t=>Array.from(e.querySelectorAll(`[data-testid="${t}"]`)),queryAllByText:(t,n=`*`)=>f(e,t,n),queryByTestId:t=>e.querySelector(`[data-testid="${t}"]`),queryByText:(t,n=`*`)=>d(e,t,n)}}var m=(e,t={})=>typeof PointerEvent<`u`?new PointerEvent(e,t):new MouseEvent(e,t),h={blur:(t,n)=>e.fire.event(t,new FocusEvent(`blur`,{bubbles:!0,...n})),change:(t,n)=>e.fire.event(t,new Event(`change`,{bubbles:!0,...n})),click:(t,n)=>e.fire.event(t,new MouseEvent(`click`,{bubbles:!0,cancelable:!0,...n})),custom(t,n,r,i){e.fire.event(t,new CustomEvent(n,{bubbles:!0,cancelable:!0,detail:r,...i}))},focus:(t,n)=>e.fire.event(t,new FocusEvent(`focus`,{bubbles:!0,...n})),input:(t,n)=>e.fire.event(t,new Event(`input`,{bubbles:!0,...n})),keyDown:(t,n)=>e.fire.event(t,new KeyboardEvent(`keydown`,{bubbles:!0,cancelable:!0,...n})),keyUp:(t,n)=>e.fire.event(t,new KeyboardEvent(`keyup`,{bubbles:!0,cancelable:!0,...n})),pointerDown:(t,n)=>e.fire.event(t,m(`pointerdown`,{bubbles:!0,cancelable:!0,...n})),pointerEnter:(t,n)=>e.fire.event(t,m(`pointerenter`,{bubbles:!1,...n})),pointerLeave:(t,n)=>e.fire.event(t,m(`pointerleave`,{bubbles:!1,...n})),pointerUp:(t,n)=>e.fire.event(t,m(`pointerup`,{bubbles:!0,cancelable:!0,...n})),submit:(t,n)=>e.fire.event(t,new Event(`submit`,{bubbles:!0,cancelable:!0,...n}))},g=()=>a(),_={async clear(e){e.focus(),e.value=``,h.input(e),h.change(e),await g()},async click(e,t){h.pointerEnter(e,t),h.click(e,t),await g()},async dblClick(e){for(let t=0;t<2;t++)h.pointerDown(e),h.pointerUp(e),h.click(e);e.dispatchEvent(new MouseEvent(`dblclick`,{bubbles:!0,cancelable:!0})),await g()},async fill(e,t){e.focus(),e.value=``;for(let n of t)e.value+=n,h.input(e),h.keyDown(e,{key:n}),h.keyUp(e,{key:n}),await g();h.change(e)},async hover(e){h.pointerEnter(e),await g()},async press(e,t,n){h.keyDown(e,{key:t,...n}),h.keyUp(e,{key:t,...n}),await g()},async select(e,t){let n=Array.isArray(t)?t:[t];for(let t of e.options)t.selected=n.includes(t.value);h.change(e),await g()},async type(e,t){e.focus();for(let n of t)e.value+=n,h.input(e),h.keyDown(e,{key:n}),h.keyUp(e,{key:n}),await g();h.change(e)},async unhover(e){h.pointerLeave(e),await g()}};async function v(e,{interval:t=50,message:n,timeout:r=1e3}={}){let i=Date.now()+r,a,o=async()=>{try{let t=await e();return t===void 0||!!t}catch(e){return a=e,!1}};for(;Date.now()<i;){if(await o())return;await new Promise(e=>setTimeout(e,t))}if(await o())return;let s=n??`waitFor timed out after ${r}ms`;throw a instanceof Error?(a.message=`${s}\n${a.message}`,a):Error(a==null?s:`${s}\nCause: ${a}`)}function y(e,t,n=1e3){return new Promise((r,i)=>{let a=setTimeout(()=>i(Error(`waitForEvent: "${t}" timed out after ${n}ms`)),n);e.addEventListener(t,e=>{clearTimeout(a),r(e)},{once:!0})})}function b(e,t=``){customElements.get(e)||customElements.define(e,class extends HTMLElement{connectedCallback(){this.innerHTML=t}})}function x(){for(let e of n)e.remove();n.length=0}exports._resetCounters=i,exports.cleanup=x,exports.fire=h,exports.flush=a,exports.install=o,exports.mock=b,exports.mount=u,exports.user=_,exports.waitFor=v,exports.waitForEvent=y,exports.within=p;
|
|
2
2
|
//# sourceMappingURL=testing.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testing.cjs","names":[],"sources":["../../src/testing/testing.ts"],"sourcesContent":["/**\n * Testing utilities for Craftit components\n *\n * ⚠️ Requires DOM environment (browser / jsdom / happy-dom)\n */\n\nimport { define, type ComponentDefinition } from '../component';\nimport { _resetIdCounter } from '../internal';\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\nexport interface Fixture<T extends HTMLElement = HTMLElement> {\n /** The component element */\n element: T;\n /** The component's shadow root */\n readonly shadow: ShadowRoot;\n /** Query a single element within shadow root */\n query<E extends Element = Element>(selector: string): E | null;\n /** Query all elements within shadow root */\n queryAll<E extends Element = Element>(selector: string): E[];\n /** Query the first element whose trimmed text content matches */\n queryByText<E extends Element = Element>(text: string, selector?: string): E | null;\n /** Query all elements whose trimmed text content matches */\n queryAllByText<E extends Element = Element>(text: string, selector?: string): E[];\n /** Query a single element by its `data-testid` attribute */\n queryByTestId<E extends Element = Element>(testId: string): E | null;\n /** Query all elements by their `data-testid` attribute */\n queryAllByTestId<E extends Element = Element>(testId: string): E[];\n /** Set an attribute (boolean `false` removes it) then flush */\n attr(name: string, value: string | number | boolean): Promise<void>;\n /** Set multiple attributes then flush */\n attrs(record: Record<string, string | number | boolean>): Promise<void>;\n /** Wait for all reactive updates and animation frames */\n flush(): Promise<void>;\n /** Run a callback then flush — the standard way to trigger and assert a reactive update */\n act(fn: () => unknown): Promise<void>;\n /** Remove the component from the DOM */\n destroy(): void;\n}\n\n/** Scoped query helpers for any DOM element — see {@link within} */\nexport interface QueryScope {\n query<E extends Element = Element>(selector: string): E | null;\n queryAll<E extends Element = Element>(selector: string): E[];\n queryByText<E extends Element = Element>(text: string, selector?: string): E | null;\n queryAllByText<E extends Element = Element>(text: string, selector?: string): E[];\n queryByTestId<E extends Element = Element>(testId: string): E | null;\n queryAllByTestId<E extends Element = Element>(testId: string): E[];\n}\n\nexport interface MountOptions {\n /** Properties assigned directly onto the element */\n props?: Record<string, unknown>;\n /** HTML attributes to set on the element */\n attrs?: Record<string, string | number | boolean>;\n /** Inner HTML for slot content */\n html?: string;\n /** Parent container (default: document.body) */\n container?: HTMLElement;\n /** Extra component options when passing an inline setup function */\n componentOptions?: Omit<ComponentDefinition<any, any>, 'setup'>;\n}\n\ntype TestComponentOptions<\n Props extends Record<string, unknown> = Record<string, never>,\n Events extends Record<string, unknown> = Record<string, unknown>,\n> = Omit<ComponentDefinition<Props, Events>, 'setup'> & Pick<ComponentDefinition<Props, Events>, 'setup'>;\n\nexport interface WaitOptions {\n /** Maximum wait time in ms (default: 1000) */\n timeout?: number;\n /** Polling interval in ms (default: 50) */\n interval?: number;\n /** Message included in timeout error */\n message?: string;\n}\n\n// ─── Test environment state ───────────────────────────────────────────────────\n\nconst _mountedElements: HTMLElement[] = [];\nlet _componentTagCounter = 0;\n\n/**\n * Resets global test counters used for deterministic IDs/markers.\n * @internal\n */\nexport const _resetCounters = (): void => {\n _resetIdCounter();\n};\n\n// ─── Core ────────────────────────────────────────────────────────────────────\n\n/**\n * Flush pending reactive updates.\n * Drains several microtask turns, then yields one animation frame and\n * one final microtask pass for rAF-scheduled work.\n */\nexport async function flush(): Promise<void> {\n const drainMicrotasks = async (turns: number): Promise<void> => {\n for (let i = 0; i < turns; i++) {\n await Promise.resolve();\n await new Promise<void>((resolve) => queueMicrotask(resolve));\n }\n };\n\n await drainMicrotasks(12);\n\n await new Promise<void>((resolve) =>\n typeof requestAnimationFrame !== 'undefined' ? requestAnimationFrame(() => resolve()) : resolve(),\n );\n\n await drainMicrotasks(2);\n}\n\n/**\n * Register auto-cleanup after each test. Call once in your test setup file.\n *\n * @example\n * // vitest.setup.ts\n * import { afterEach } from 'vitest';\n * import { install } from '@vielzeug/craftit/testing';\n * install(afterEach);\n */\nexport function install(afterEachHook: (fn: () => void) => void): void {\n afterEachHook(cleanup);\n}\n\nfunction applyAttr(element: Element, name: string, value: string | number | boolean): void {\n if (value === false) element.removeAttribute(name);\n else element.setAttribute(name, value === true ? '' : String(value));\n}\n\nconst toError = (value: unknown): Error => {\n return value instanceof Error ? value : new Error(String(value));\n};\n\nconst withWindowErrorCapture = async <T>(action: () => Promise<T>): Promise<T> => {\n if (typeof window === 'undefined') return action();\n\n let captured: Error | null = null;\n const onError = (event: ErrorEvent) => {\n captured = toError(event.error ?? event.message);\n event.preventDefault();\n };\n const onUnhandledRejection = (event: PromiseRejectionEvent) => {\n captured = toError(event.reason);\n event.preventDefault();\n };\n\n window.addEventListener('error', onError);\n window.addEventListener('unhandledrejection', onUnhandledRejection);\n\n try {\n const result = await action();\n\n if (captured) throw captured;\n\n return result;\n } finally {\n window.removeEventListener('error', onError);\n window.removeEventListener('unhandledrejection', onUnhandledRejection);\n }\n};\n\n/**\n * Mount a component into the DOM and return a test fixture.\n *\n * Accepts a registered tag name, an inline setup function, or a component\n * options object. Setup functions are auto-registered with generated tag names.\n *\n * @example — inline setup function\n * const { query } = await mount(() => {\n * const count = signal(0);\n * return html`<button @click=${() => count.value++}>${count}</button>`;\n * });\n *\n * @example — registered tag name\n * const { query } = await mount('my-counter');\n */\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetupOrOptions: string,\n options?: MountOptions,\n): Promise<Fixture<T>>;\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetupOrOptions: TestComponentOptions['setup'],\n options?: MountOptions,\n): Promise<Fixture<T>>;\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetupOrOptions: TestComponentOptions<any, any>,\n options?: MountOptions,\n): Promise<Fixture<T>>;\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetupOrOptions: string | TestComponentOptions['setup'] | TestComponentOptions<any, any>,\n options: MountOptions = {},\n): Promise<Fixture<T>> {\n const { attrs = {}, componentOptions, container = document.body, html, props = {} } = options;\n\n let tagName: string;\n let inlineDefinition: TestComponentOptions<any, any> | undefined;\n\n if (typeof tagOrSetupOrOptions === 'string') {\n tagName = tagOrSetupOrOptions;\n } else if (typeof tagOrSetupOrOptions === 'function') {\n tagName = `trial-${++_componentTagCounter}`;\n inlineDefinition = {\n ...(componentOptions ?? {}),\n setup: tagOrSetupOrOptions as TestComponentOptions<any, any>['setup'],\n };\n } else {\n tagName = `trial-${++_componentTagCounter}`;\n inlineDefinition = tagOrSetupOrOptions;\n }\n\n if (inlineDefinition) {\n define(tagName, inlineDefinition as TestComponentOptions<any, any>);\n }\n\n const element = document.createElement(tagName) as T;\n\n if (html) element.innerHTML = html;\n\n if (Object.keys(props).length) Object.assign(element, props);\n\n for (const [name, value] of Object.entries(attrs)) applyAttr(element, name, value);\n\n await withWindowErrorCapture(async () => {\n container.appendChild(element);\n _mountedElements.push(element);\n await flush();\n });\n\n return {\n async act(fn) {\n await fn();\n await flush();\n },\n\n async attr(name, value) {\n applyAttr(element, name, value);\n await flush();\n },\n\n async attrs(record) {\n for (const [name, value] of Object.entries(record)) applyAttr(element, name, value);\n await flush();\n },\n\n destroy() {\n element.remove();\n\n const i = _mountedElements.indexOf(element);\n\n if (i !== -1) _mountedElements.splice(i, 1);\n },\n element,\n\n flush,\n\n query<E extends Element = Element>(selector: string): E | null {\n return element.shadowRoot?.querySelector<E>(selector) ?? null;\n },\n\n queryAll<E extends Element = Element>(selector: string): E[] {\n return Array.from(element.shadowRoot?.querySelectorAll<E>(selector) ?? []);\n },\n\n queryAllByTestId<E extends Element = Element>(testId: string): E[] {\n return Array.from(element.shadowRoot?.querySelectorAll<E>(`[data-testid=\"${testId}\"]`) ?? []);\n },\n\n queryAllByText<E extends Element = Element>(text: string, selector = '*'): E[] {\n return queryAllByText<E>(element.shadowRoot!, text, selector);\n },\n\n queryByTestId<E extends Element = Element>(testId: string): E | null {\n return element.shadowRoot?.querySelector<E>(`[data-testid=\"${testId}\"]`) ?? null;\n },\n\n queryByText<E extends Element = Element>(text: string, selector = '*'): E | null {\n return queryByText<E>(element.shadowRoot!, text, selector);\n },\n\n get shadow(): ShadowRoot {\n return element.shadowRoot!;\n },\n };\n}\n\n// ─── Scoped queries ───────────────────────────────────────────────────────────\n\nfunction queryByText<E extends Element = Element>(\n root: Element | ShadowRoot,\n text: string,\n selector: string,\n): E | null {\n for (const el of root.querySelectorAll<E>(selector)) {\n if (el.textContent?.trim() === text) return el;\n }\n\n return null;\n}\n\nfunction queryAllByText<E extends Element = Element>(root: Element | ShadowRoot, text: string, selector: string): E[] {\n return Array.from(root.querySelectorAll<E>(selector)).filter((el) => el.textContent?.trim() === text);\n}\n\n/**\n * Create query helpers scoped to any element — useful for slotted/light DOM content.\n *\n * @example\n * const panel = fixture.query('.panel')!;\n * const { query } = within(panel);\n * expect(query('.title')?.textContent).toBe('Hello');\n */\nexport function within(element: Element): QueryScope {\n return {\n query: <E extends Element = Element>(selector: string) => element.querySelector<E>(selector),\n queryAll: <E extends Element = Element>(selector: string) => Array.from(element.querySelectorAll<E>(selector)),\n queryAllByTestId: <E extends Element = Element>(testId: string) =>\n Array.from(element.querySelectorAll<E>(`[data-testid=\"${testId}\"]`)),\n queryAllByText: <E extends Element = Element>(text: string, selector = '*') =>\n queryAllByText<E>(element, text, selector),\n queryByTestId: <E extends Element = Element>(testId: string) =>\n element.querySelector<E>(`[data-testid=\"${testId}\"]`),\n queryByText: <E extends Element = Element>(text: string, selector = '*') => queryByText<E>(element, text, selector),\n };\n}\n\n// ─── Events ──────────────────────────────────────────────────────────────────\n\nconst createPointerEvent = (type: string, init: PointerEventInit = {}): Event => {\n if (typeof PointerEvent !== 'undefined') {\n return new PointerEvent(type, init);\n }\n\n return new MouseEvent(type, init);\n};\n\n/**\n * Fire low-level DOM events synchronously.\n *\n * @example\n * fire.click(button);\n * fire.keyDown(input, { key: 'Enter' });\n * fire.custom(el, 'value-change', 42);\n */\nexport const fire = {\n blur: (el: Element, opts?: FocusEventInit) => el.dispatchEvent(new FocusEvent('blur', { bubbles: true, ...opts })),\n change: (el: Element, opts?: EventInit) => el.dispatchEvent(new Event('change', { bubbles: true, ...opts })),\n click: (el: Element, opts?: PointerEventInit) =>\n el.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true, ...opts })),\n custom<D = unknown>(el: Element, name: string, detail?: D, opts?: Omit<CustomEventInit<D>, 'detail'>): void {\n el.dispatchEvent(new CustomEvent<D>(name, { bubbles: true, cancelable: true, detail, ...opts }));\n },\n focus: (el: Element, opts?: FocusEventInit) => el.dispatchEvent(new FocusEvent('focus', { bubbles: true, ...opts })),\n input: (el: Element, opts?: EventInit) => el.dispatchEvent(new Event('input', { bubbles: true, ...opts })),\n keyDown: (el: Element, opts?: KeyboardEventInit) =>\n el.dispatchEvent(new KeyboardEvent('keydown', { bubbles: true, cancelable: true, ...opts })),\n keyUp: (el: Element, opts?: KeyboardEventInit) =>\n el.dispatchEvent(new KeyboardEvent('keyup', { bubbles: true, cancelable: true, ...opts })),\n pointerDown: (el: Element, opts?: PointerEventInit) =>\n el.dispatchEvent(createPointerEvent('pointerdown', { bubbles: true, cancelable: true, ...opts })),\n pointerEnter: (el: Element, opts?: PointerEventInit) =>\n el.dispatchEvent(createPointerEvent('pointerenter', { bubbles: false, ...opts })),\n pointerLeave: (el: Element, opts?: PointerEventInit) =>\n el.dispatchEvent(createPointerEvent('pointerleave', { bubbles: false, ...opts })),\n pointerUp: (el: Element, opts?: PointerEventInit) =>\n el.dispatchEvent(createPointerEvent('pointerup', { bubbles: true, cancelable: true, ...opts })),\n submit: (el: Element, opts?: EventInit) =>\n el.dispatchEvent(new Event('submit', { bubbles: true, cancelable: true, ...opts })),\n} as const;\n\n// ─── User interactions ────────────────────────────────────────────────────────\n\nconst tick = (): Promise<void> => flush();\n\n/**\n * Higher-level async user interactions that mirror real browser behavior.\n *\n * @example\n * await user.type(input, 'hello');\n * await user.fill(input, 'replacement'); // clear then type\n * await user.click(button);\n * await user.press(input, 'Enter');\n */\nexport const user = {\n async clear(el: HTMLInputElement | HTMLTextAreaElement): Promise<void> {\n el.focus();\n el.value = '';\n fire.input(el);\n fire.change(el);\n await tick();\n },\n async click(el: Element, opts?: PointerEventInit): Promise<void> {\n fire.pointerEnter(el, opts);\n fire.click(el, opts);\n await tick();\n },\n\n async dblClick(el: Element): Promise<void> {\n for (let i = 0; i < 2; i++) {\n fire.pointerDown(el);\n fire.pointerUp(el);\n fire.click(el);\n }\n el.dispatchEvent(new MouseEvent('dblclick', { bubbles: true, cancelable: true }));\n await tick();\n },\n\n /** Clear existing value and type new text (select-all-and-replace semantics) */\n async fill(el: HTMLInputElement | HTMLTextAreaElement, text: string): Promise<void> {\n el.focus();\n el.value = '';\n for (const char of text) {\n el.value += char;\n fire.input(el);\n fire.keyDown(el, { key: char });\n fire.keyUp(el, { key: char });\n await tick();\n }\n fire.change(el);\n },\n\n async hover(el: Element): Promise<void> {\n fire.pointerEnter(el);\n await tick();\n },\n\n /** Dispatch keydown + keyup for a single key */\n async press(el: Element, key: string, opts?: KeyboardEventInit): Promise<void> {\n fire.keyDown(el, { key, ...opts });\n fire.keyUp(el, { key, ...opts });\n await tick();\n },\n\n async select(el: HTMLSelectElement, value: string | string[]): Promise<void> {\n const values = Array.isArray(value) ? value : [value];\n\n for (const opt of el.options) opt.selected = values.includes(opt.value);\n fire.change(el);\n await tick();\n },\n\n /** Type text character-by-character, appending to any existing value */\n async type(el: HTMLInputElement | HTMLTextAreaElement, text: string): Promise<void> {\n el.focus();\n for (const char of text) {\n el.value += char;\n fire.input(el);\n fire.keyDown(el, { key: char });\n fire.keyUp(el, { key: char });\n await tick();\n }\n fire.change(el);\n },\n\n async unhover(el: Element): Promise<void> {\n fire.pointerLeave(el);\n await tick();\n },\n} as const;\n\n// ─── Waiting ─────────────────────────────────────────────────────────────────\n\n/**\n * Poll until a callback returns truthy (or void) without throwing.\n * Supports both boolean conditions and `expect()` assertions.\n *\n * - Returns truthy → success\n * - Returns `undefined` (e.g. bare `expect()` call) → success\n * - Returns falsy value → retry\n * - Throws → retry, re-throw original error on timeout\n *\n * @example\n * await waitFor(() => query('.status')?.textContent === 'loaded');\n * await waitFor(() => expect(count).toBe(3));\n */\nexport async function waitFor(\n fn: () => unknown,\n { interval = 50, message, timeout = 1000 }: WaitOptions = {},\n): Promise<void> {\n const deadline = Date.now() + timeout;\n let lastError: unknown;\n\n const attempt = async (): Promise<boolean> => {\n try {\n const result = await fn();\n\n return result === undefined || !!result;\n } catch (e) {\n lastError = e;\n\n return false;\n }\n };\n\n while (Date.now() < deadline) {\n if (await attempt()) return;\n\n await new Promise((r) => setTimeout(r, interval));\n }\n\n if (await attempt()) return;\n\n const base = message ?? `waitFor timed out after ${timeout}ms`;\n\n if (lastError instanceof Error) {\n lastError.message = `${base}\\n${lastError.message}`;\n throw lastError;\n }\n\n throw new Error(lastError != null ? `${base}\\nCause: ${lastError}` : base);\n}\n\n/**\n * Resolve when the target element emits the given event.\n *\n * @example\n * const promise = waitForEvent(el, 'change');\n * fire.click(trigger);\n * const event = await promise;\n */\nexport function waitForEvent<T extends Event = Event>(element: Element, name: string, timeout = 1000): Promise<T> {\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => reject(new Error(`waitForEvent: \"${name}\" timed out after ${timeout}ms`)), timeout);\n\n element.addEventListener(\n name,\n (e) => {\n clearTimeout(timer);\n resolve(e as T);\n },\n { once: true },\n );\n });\n}\n\n// ─── Stubs & cleanup ─────────────────────────────────────────────────────────\n\n/**\n * Register a stub custom element (no-op if already defined).\n *\n * @example\n * mock('child-button', '<slot></slot>');\n */\nexport function mock(tagName: string, template = ''): void {\n if (!customElements.get(tagName)) {\n customElements.define(\n tagName,\n class extends HTMLElement {\n connectedCallback() {\n this.innerHTML = template;\n }\n },\n );\n }\n}\n\n/**\n * Remove all elements mounted via `mount()`.\n * Call in `afterEach` to keep tests isolated.\n *\n * @example\n * afterEach(() => cleanup());\n */\nexport function cleanup(): void {\n for (const el of _mountedElements) el.remove();\n _mountedElements.length = 0;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"testing.cjs","names":[],"sources":["../../src/testing/testing.ts"],"sourcesContent":["/**\n * Testing utilities for Craftit components\n *\n * ⚠️ Requires DOM environment (browser / jsdom / happy-dom)\n */\n\nimport type { Signal } from '@vielzeug/stateit';\n\nimport type { ComponentTemplate } from '../registration';\n\nimport { _resetIdCounter, type HTMLResult } from '../internal';\nimport { define, type ComponentDefinition, type SetupContextBag } from '../registration';\nimport { fire as runtimeFire } from '../runtime';\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\nexport interface Fixture<T extends HTMLElement = HTMLElement> {\n /** The component element */\n element: T;\n /** The component's shadow root */\n readonly shadow: ShadowRoot;\n /** Query a single element within shadow root */\n query<E extends Element = Element>(selector: string): E | null;\n /** Query all elements within shadow root */\n queryAll<E extends Element = Element>(selector: string): E[];\n /** Query the first element whose trimmed text content matches */\n queryByText<E extends Element = Element>(text: string, selector?: string): E | null;\n /** Query all elements whose trimmed text content matches */\n queryAllByText<E extends Element = Element>(text: string, selector?: string): E[];\n /** Query a single element by its `data-testid` attribute */\n queryByTestId<E extends Element = Element>(testId: string): E | null;\n /** Query all elements by their `data-testid` attribute */\n queryAllByTestId<E extends Element = Element>(testId: string): E[];\n /** Set an attribute (boolean `false` removes it) then flush */\n attr(name: string, value: string | number | boolean): Promise<void>;\n /** Set multiple attributes then flush */\n attrs(record: Record<string, string | number | boolean>): Promise<void>;\n /** Wait for all reactive updates and animation frames */\n flush(): Promise<void>;\n /** Run a callback then flush — the standard way to trigger and assert a reactive update */\n act(fn: () => unknown): Promise<void>;\n /** Remove the component from the DOM */\n destroy(): void;\n}\n\n/** Scoped query helpers for any DOM element — see {@link within} */\nexport interface QueryScope {\n query<E extends Element = Element>(selector: string): E | null;\n queryAll<E extends Element = Element>(selector: string): E[];\n queryByText<E extends Element = Element>(text: string, selector?: string): E | null;\n queryAllByText<E extends Element = Element>(text: string, selector?: string): E[];\n queryByTestId<E extends Element = Element>(testId: string): E | null;\n queryAllByTestId<E extends Element = Element>(testId: string): E[];\n}\n\nexport interface MountOptions {\n /** Properties assigned directly onto the element */\n props?: Record<string, unknown>;\n /** HTML attributes to set on the element */\n attrs?: Record<string, string | number | boolean>;\n /** Inner HTML for slot content */\n html?: string;\n /** Parent container (default: document.body) */\n container?: HTMLElement;\n /** Extra component options when passing an inline setup function */\n componentOptions?: Omit<ComponentDefinition<any, any>, 'setup'>;\n}\n\ntype TestSetup<\n Props extends Record<string, unknown> = Record<string, never>,\n Events extends Record<string, unknown> = Record<string, unknown>,\n> = (...args: Parameters<ComponentDefinition<Props, Events>['setup']>) => ComponentTemplate | HTMLResult;\n\ntype MountProps = { readonly [x: string]: Signal<unknown> };\n\n// Bivariant callback type keeps inline test callbacks ergonomic across varying setup context specializations.\nexport type MountSetup = {\n bivarianceHack: (props: MountProps, ctx: SetupContextBag<any>) => ComponentTemplate | HTMLResult;\n}['bivarianceHack'];\n\nexport interface WaitOptions {\n /** Maximum wait time in ms (default: 1000) */\n timeout?: number;\n /** Polling interval in ms (default: 50) */\n interval?: number;\n /** Message included in timeout error */\n message?: string;\n}\n\n// ─── Test environment state ───────────────────────────────────────────────────\n\nconst _mountedElements: HTMLElement[] = [];\nlet _componentTagCounter = 0;\n\n/**\n * Resets global test counters used for deterministic IDs/markers.\n * @internal\n */\nexport const _resetCounters = (): void => {\n _resetIdCounter();\n _componentTagCounter = 0;\n};\n\n// ─── Core ────────────────────────────────────────────────────────────────────\n\n/**\n * Flush pending reactive updates.\n * Drains several microtask turns, then yields one animation frame and\n * one final microtask pass for rAF-scheduled work.\n */\nexport async function flush(): Promise<void> {\n const drainMicrotasks = async (turns: number): Promise<void> => {\n for (let i = 0; i < turns; i++) {\n await Promise.resolve();\n await new Promise<void>((resolve) => queueMicrotask(resolve));\n }\n };\n\n await drainMicrotasks(12);\n\n await new Promise<void>((resolve) =>\n typeof requestAnimationFrame !== 'undefined' ? requestAnimationFrame(() => resolve()) : resolve(),\n );\n\n await drainMicrotasks(2);\n}\n\n/**\n * Register auto-cleanup after each test. Call once in your test setup file.\n *\n * @example\n * // vitest.setup.ts\n * import { afterEach } from 'vitest';\n * import { install } from '@vielzeug/craftit/testing';\n * install(afterEach);\n */\nexport function install(afterEachHook: (fn: () => void) => void): void {\n afterEachHook(cleanup);\n}\n\nfunction applyAttr(element: Element, name: string, value: string | number | boolean): void {\n if (value === false) element.removeAttribute(name);\n else element.setAttribute(name, value === true ? '' : String(value));\n}\n\nconst toError = (value: unknown): Error => {\n return value instanceof Error ? value : new Error(String(value));\n};\n\nconst withWindowErrorCapture = async <T>(action: () => Promise<T>): Promise<T> => {\n if (typeof window === 'undefined') return action();\n\n let captured: Error | null = null;\n const onError = (event: ErrorEvent) => {\n captured = toError(event.error ?? event.message);\n event.preventDefault();\n };\n const onUnhandledRejection = (event: PromiseRejectionEvent) => {\n captured = toError(event.reason);\n event.preventDefault();\n };\n\n window.addEventListener('error', onError);\n window.addEventListener('unhandledrejection', onUnhandledRejection);\n\n try {\n const result = await action();\n\n if (captured) throw captured;\n\n return result;\n } finally {\n window.removeEventListener('error', onError);\n window.removeEventListener('unhandledrejection', onUnhandledRejection);\n }\n};\n\n/**\n * Mount a component into the DOM and return a test fixture.\n *\n * Accepts a registered tag name, an inline setup function, or a component\n * options object. Setup functions are auto-registered with generated tag names.\n *\n * @example — inline setup function\n * const { query } = await mount(() => {\n * const count = signal(0);\n * return html`<button @click=${() => count.value++}>${count}</button>`;\n * });\n *\n * @example — registered tag name\n * const { query } = await mount('my-counter');\n */\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetup: string,\n options?: MountOptions,\n): Promise<Fixture<T>>;\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetup: MountSetup,\n options?: MountOptions,\n): Promise<Fixture<T>>;\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetup: string | MountSetup,\n options: MountOptions = {},\n): Promise<Fixture<T>> {\n const { attrs = {}, componentOptions, container = document.body, html, props = {} } = options;\n\n const normalizeSetup = <P extends Record<string, unknown>, E extends Record<string, unknown>>(\n setup: TestSetup<P, E>,\n ): ComponentDefinition<P, E>['setup'] => {\n return (setupProps, setupCtx) => {\n const result = setup(setupProps, setupCtx);\n\n if (typeof result === 'function') {\n return result;\n }\n\n return () => result;\n };\n };\n\n let tagName: string;\n let inlineDefinition: ComponentDefinition<any, any> | undefined;\n\n if (typeof tagOrSetup === 'string') {\n tagName = tagOrSetup;\n } else {\n tagName = `trial-${++_componentTagCounter}`;\n inlineDefinition = {\n ...(componentOptions ?? {}),\n setup: normalizeSetup(tagOrSetup as TestSetup<any, any>),\n };\n }\n\n if (inlineDefinition) {\n define(tagName, inlineDefinition);\n }\n\n const element = document.createElement(tagName) as T;\n\n if (html) element.innerHTML = html;\n\n if (Object.keys(props).length) Object.assign(element, props);\n\n for (const [name, value] of Object.entries(attrs)) applyAttr(element, name, value);\n\n await withWindowErrorCapture(async () => {\n container.appendChild(element);\n _mountedElements.push(element);\n await flush();\n });\n\n return {\n async act(fn) {\n await fn();\n await flush();\n },\n\n async attr(name, value) {\n applyAttr(element, name, value);\n await flush();\n },\n\n async attrs(record) {\n for (const [name, value] of Object.entries(record)) applyAttr(element, name, value);\n await flush();\n },\n\n destroy() {\n element.remove();\n\n const i = _mountedElements.indexOf(element);\n\n if (i !== -1) _mountedElements.splice(i, 1);\n },\n element,\n\n flush,\n\n query<E extends Element = Element>(selector: string): E | null {\n return element.shadowRoot?.querySelector<E>(selector) ?? null;\n },\n\n queryAll<E extends Element = Element>(selector: string): E[] {\n return Array.from(element.shadowRoot?.querySelectorAll<E>(selector) ?? []);\n },\n\n queryAllByTestId<E extends Element = Element>(testId: string): E[] {\n return Array.from(element.shadowRoot?.querySelectorAll<E>(`[data-testid=\"${testId}\"]`) ?? []);\n },\n\n queryAllByText<E extends Element = Element>(text: string, selector = '*'): E[] {\n return queryAllByText<E>(element.shadowRoot!, text, selector);\n },\n\n queryByTestId<E extends Element = Element>(testId: string): E | null {\n return element.shadowRoot?.querySelector<E>(`[data-testid=\"${testId}\"]`) ?? null;\n },\n\n queryByText<E extends Element = Element>(text: string, selector = '*'): E | null {\n return queryByText<E>(element.shadowRoot!, text, selector);\n },\n\n get shadow(): ShadowRoot {\n return element.shadowRoot!;\n },\n };\n}\n\n// ─── Scoped queries ───────────────────────────────────────────────────────────\n\nfunction queryByText<E extends Element = Element>(\n root: Element | ShadowRoot,\n text: string,\n selector: string,\n): E | null {\n for (const el of root.querySelectorAll<E>(selector)) {\n if (el.textContent?.trim() === text) return el;\n }\n\n return null;\n}\n\nfunction queryAllByText<E extends Element = Element>(root: Element | ShadowRoot, text: string, selector: string): E[] {\n return Array.from(root.querySelectorAll<E>(selector)).filter((el) => el.textContent?.trim() === text);\n}\n\n/**\n * Create query helpers scoped to any element — useful for slotted/light DOM content.\n *\n * @example\n * const panel = fixture.query('.panel')!;\n * const { query } = within(panel);\n * expect(query('.title')?.textContent).toBe('Hello');\n */\nexport function within(element: Element): QueryScope {\n return {\n query: <E extends Element = Element>(selector: string) => element.querySelector<E>(selector),\n queryAll: <E extends Element = Element>(selector: string) => Array.from(element.querySelectorAll<E>(selector)),\n queryAllByTestId: <E extends Element = Element>(testId: string) =>\n Array.from(element.querySelectorAll<E>(`[data-testid=\"${testId}\"]`)),\n queryAllByText: <E extends Element = Element>(text: string, selector = '*') =>\n queryAllByText<E>(element, text, selector),\n queryByTestId: <E extends Element = Element>(testId: string) =>\n element.querySelector<E>(`[data-testid=\"${testId}\"]`),\n queryByText: <E extends Element = Element>(text: string, selector = '*') => queryByText<E>(element, text, selector),\n };\n}\n\n// ─── Events ──────────────────────────────────────────────────────────────────\n\nconst createPointerEvent = (type: string, init: PointerEventInit = {}): Event => {\n if (typeof PointerEvent !== 'undefined') {\n return new PointerEvent(type, init);\n }\n\n return new MouseEvent(type, init);\n};\n\n/**\n * Fire low-level DOM events synchronously.\n *\n * @example\n * fire.click(button);\n * fire.keyDown(input, { key: 'Enter' });\n * fire.custom(el, 'value-change', 42);\n */\nexport const fire = {\n blur: (el: Element, opts?: FocusEventInit) =>\n runtimeFire.event(el, new FocusEvent('blur', { bubbles: true, ...opts })),\n change: (el: Element, opts?: EventInit) => runtimeFire.event(el, new Event('change', { bubbles: true, ...opts })),\n click: (el: Element, opts?: PointerEventInit) =>\n runtimeFire.event(el, new MouseEvent('click', { bubbles: true, cancelable: true, ...opts })),\n custom<D = unknown>(el: Element, name: string, detail?: D, opts?: Omit<CustomEventInit<D>, 'detail'>): void {\n runtimeFire.event(el, new CustomEvent<D>(name, { bubbles: true, cancelable: true, detail, ...opts }));\n },\n focus: (el: Element, opts?: FocusEventInit) =>\n runtimeFire.event(el, new FocusEvent('focus', { bubbles: true, ...opts })),\n input: (el: Element, opts?: EventInit) => runtimeFire.event(el, new Event('input', { bubbles: true, ...opts })),\n keyDown: (el: Element, opts?: KeyboardEventInit) =>\n runtimeFire.event(el, new KeyboardEvent('keydown', { bubbles: true, cancelable: true, ...opts })),\n keyUp: (el: Element, opts?: KeyboardEventInit) =>\n runtimeFire.event(el, new KeyboardEvent('keyup', { bubbles: true, cancelable: true, ...opts })),\n pointerDown: (el: Element, opts?: PointerEventInit) =>\n runtimeFire.event(el, createPointerEvent('pointerdown', { bubbles: true, cancelable: true, ...opts })),\n pointerEnter: (el: Element, opts?: PointerEventInit) =>\n runtimeFire.event(el, createPointerEvent('pointerenter', { bubbles: false, ...opts })),\n pointerLeave: (el: Element, opts?: PointerEventInit) =>\n runtimeFire.event(el, createPointerEvent('pointerleave', { bubbles: false, ...opts })),\n pointerUp: (el: Element, opts?: PointerEventInit) =>\n runtimeFire.event(el, createPointerEvent('pointerup', { bubbles: true, cancelable: true, ...opts })),\n submit: (el: Element, opts?: EventInit) =>\n runtimeFire.event(el, new Event('submit', { bubbles: true, cancelable: true, ...opts })),\n} as const;\n\n// ─── User interactions ────────────────────────────────────────────────────────\n\nconst tick = (): Promise<void> => flush();\n\n/**\n * Higher-level async user interactions that mirror real browser behavior.\n *\n * @example\n * await user.type(input, 'hello');\n * await user.fill(input, 'replacement'); // clear then type\n * await user.click(button);\n * await user.press(input, 'Enter');\n */\nexport const user = {\n async clear(el: HTMLInputElement | HTMLTextAreaElement): Promise<void> {\n el.focus();\n el.value = '';\n fire.input(el);\n fire.change(el);\n await tick();\n },\n async click(el: Element, opts?: PointerEventInit): Promise<void> {\n fire.pointerEnter(el, opts);\n fire.click(el, opts);\n await tick();\n },\n\n async dblClick(el: Element): Promise<void> {\n for (let i = 0; i < 2; i++) {\n fire.pointerDown(el);\n fire.pointerUp(el);\n fire.click(el);\n }\n el.dispatchEvent(new MouseEvent('dblclick', { bubbles: true, cancelable: true }));\n await tick();\n },\n\n /** Clear existing value and type new text (select-all-and-replace semantics) */\n async fill(el: HTMLInputElement | HTMLTextAreaElement, text: string): Promise<void> {\n el.focus();\n el.value = '';\n for (const char of text) {\n el.value += char;\n fire.input(el);\n fire.keyDown(el, { key: char });\n fire.keyUp(el, { key: char });\n await tick();\n }\n fire.change(el);\n },\n\n async hover(el: Element): Promise<void> {\n fire.pointerEnter(el);\n await tick();\n },\n\n /** Dispatch keydown + keyup for a single key */\n async press(el: Element, key: string, opts?: KeyboardEventInit): Promise<void> {\n fire.keyDown(el, { key, ...opts });\n fire.keyUp(el, { key, ...opts });\n await tick();\n },\n\n async select(el: HTMLSelectElement, value: string | string[]): Promise<void> {\n const values = Array.isArray(value) ? value : [value];\n\n for (const opt of el.options) opt.selected = values.includes(opt.value);\n fire.change(el);\n await tick();\n },\n\n /** Type text character-by-character, appending to any existing value */\n async type(el: HTMLInputElement | HTMLTextAreaElement, text: string): Promise<void> {\n el.focus();\n for (const char of text) {\n el.value += char;\n fire.input(el);\n fire.keyDown(el, { key: char });\n fire.keyUp(el, { key: char });\n await tick();\n }\n fire.change(el);\n },\n\n async unhover(el: Element): Promise<void> {\n fire.pointerLeave(el);\n await tick();\n },\n} as const;\n\n// ─── Waiting ─────────────────────────────────────────────────────────────────\n\n/**\n * Poll until a callback returns truthy (or void) without throwing.\n * Supports both boolean conditions and `expect()` assertions.\n *\n * - Returns truthy → success\n * - Returns `undefined` (e.g. bare `expect()` call) → success\n * - Returns falsy value → retry\n * - Throws → retry, re-throw original error on timeout\n *\n * @example\n * await waitFor(() => query('.status')?.textContent === 'loaded');\n * await waitFor(() => expect(count).toBe(3));\n */\nexport async function waitFor(\n fn: () => unknown,\n { interval = 50, message, timeout = 1000 }: WaitOptions = {},\n): Promise<void> {\n const deadline = Date.now() + timeout;\n let lastError: unknown;\n\n const attempt = async (): Promise<boolean> => {\n try {\n const result = await fn();\n\n return result === undefined || !!result;\n } catch (e) {\n lastError = e;\n\n return false;\n }\n };\n\n while (Date.now() < deadline) {\n if (await attempt()) return;\n\n await new Promise((r) => setTimeout(r, interval));\n }\n\n if (await attempt()) return;\n\n const base = message ?? `waitFor timed out after ${timeout}ms`;\n\n if (lastError instanceof Error) {\n lastError.message = `${base}\\n${lastError.message}`;\n throw lastError;\n }\n\n throw new Error(lastError != null ? `${base}\\nCause: ${lastError}` : base);\n}\n\n/**\n * Resolve when the target element emits the given event.\n *\n * @example\n * const promise = waitForEvent(el, 'change');\n * fire.click(trigger);\n * const event = await promise;\n */\nexport function waitForEvent<T extends Event = Event>(element: Element, name: string, timeout = 1000): Promise<T> {\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => reject(new Error(`waitForEvent: \"${name}\" timed out after ${timeout}ms`)), timeout);\n\n element.addEventListener(\n name,\n (e) => {\n clearTimeout(timer);\n resolve(e as T);\n },\n { once: true },\n );\n });\n}\n\n// ─── Stubs & cleanup ─────────────────────────────────────────────────────────\n\n/**\n * Register a stub custom element (no-op if already defined).\n *\n * @example\n * mock('child-button', '<slot></slot>');\n */\nexport function mock(tagName: string, template = ''): void {\n if (!customElements.get(tagName)) {\n customElements.define(\n tagName,\n class extends HTMLElement {\n connectedCallback() {\n this.innerHTML = template;\n }\n },\n );\n }\n}\n\n/**\n * Remove all elements mounted via `mount()`.\n * Call in `afterEach` to keep tests isolated.\n *\n * @example\n * afterEach(() => cleanup());\n */\nexport function cleanup(): void {\n for (const el of _mountedElements) el.remove();\n _mountedElements.length = 0;\n}\n"],"mappings":"8FA2FA,IAAM,EAAkC,CAAC,EACrC,EAAuB,EAMd,MAA6B,CACxC,EAAA,gBAAgB,EAChB,EAAuB,CACzB,EASA,eAAsB,GAAuB,CAC3C,IAAM,EAAkB,KAAO,IAAiC,CAC9D,IAAK,IAAI,EAAI,EAAG,EAAI,EAAO,IACzB,MAAM,QAAQ,QAAQ,EACtB,MAAM,IAAI,QAAe,GAAY,eAAe,CAAO,CAAC,CAEhE,EAEA,MAAM,EAAgB,EAAE,EAExB,MAAM,IAAI,QAAe,GACvB,OAAO,sBAA0B,IAAc,0BAA4B,EAAQ,CAAC,EAAI,EAAQ,CAClG,EAEA,MAAM,EAAgB,CAAC,CACzB,CAWA,SAAgB,EAAQ,EAA+C,CACrE,EAAc,CAAO,CACvB,CAEA,SAAS,EAAU,EAAkB,EAAc,EAAwC,CACrF,IAAU,GAAO,EAAQ,gBAAgB,CAAI,EAC5C,EAAQ,aAAa,EAAM,IAAU,GAAO,GAAK,OAAO,CAAK,CAAC,CACrE,CAEA,IAAM,EAAW,GACR,aAAiB,MAAQ,EAAY,MAAM,OAAO,CAAK,CAAC,EAG3D,EAAyB,KAAU,IAAyC,CAChF,GAAI,OAAO,OAAW,IAAa,OAAO,EAAO,EAEjD,IAAI,EAAyB,KACvB,EAAW,GAAsB,CACrC,EAAW,EAAQ,EAAM,OAAS,EAAM,OAAO,EAC/C,EAAM,eAAe,CACvB,EACM,EAAwB,GAAiC,CAC7D,EAAW,EAAQ,EAAM,MAAM,EAC/B,EAAM,eAAe,CACvB,EAEA,OAAO,iBAAiB,QAAS,CAAO,EACxC,OAAO,iBAAiB,qBAAsB,CAAoB,EAElE,GAAI,CACF,IAAM,EAAS,MAAM,EAAO,EAE5B,GAAI,EAAU,MAAM,EAEpB,OAAO,CACT,QAAU,CACR,OAAO,oBAAoB,QAAS,CAAO,EAC3C,OAAO,oBAAoB,qBAAsB,CAAoB,CACvE,CACF,EAyBA,eAAsB,EACpB,EACA,EAAwB,CAAC,EACJ,CACrB,GAAM,CAAE,QAAQ,CAAC,EAAG,mBAAkB,YAAY,SAAS,KAAM,OAAM,QAAQ,CAAC,GAAM,EAEhF,EACJ,IAEQ,EAAY,IAAa,CAC/B,IAAM,EAAS,EAAM,EAAY,CAAQ,EAMzC,OAJI,OAAO,GAAW,WACb,MAGI,CACf,EAGE,EACA,EAEA,OAAO,GAAe,SACxB,EAAU,GAEV,EAAU,SAAS,EAAE,IACrB,EAAmB,CACjB,GAAI,GAAoB,CAAC,EACzB,MAAO,EAAe,CAAiC,CACzD,GAGE,GACF,EAAA,OAAO,EAAS,CAAgB,EAGlC,IAAM,EAAU,SAAS,cAAc,CAAO,EAE1C,IAAM,EAAQ,UAAY,GAE1B,OAAO,KAAK,CAAK,EAAE,QAAQ,OAAO,OAAO,EAAS,CAAK,EAE3D,IAAK,GAAM,CAAC,EAAM,KAAU,OAAO,QAAQ,CAAK,EAAG,EAAU,EAAS,EAAM,CAAK,EAQjF,OANA,MAAM,EAAuB,SAAY,CACvC,EAAU,YAAY,CAAO,EAC7B,EAAiB,KAAK,CAAO,EAC7B,MAAM,EAAM,CACd,CAAC,EAEM,CACL,MAAM,IAAI,EAAI,CACZ,MAAM,EAAG,EACT,MAAM,EAAM,CACd,EAEA,MAAM,KAAK,EAAM,EAAO,CACtB,EAAU,EAAS,EAAM,CAAK,EAC9B,MAAM,EAAM,CACd,EAEA,MAAM,MAAM,EAAQ,CAClB,IAAK,GAAM,CAAC,EAAM,KAAU,OAAO,QAAQ,CAAM,EAAG,EAAU,EAAS,EAAM,CAAK,EAClF,MAAM,EAAM,CACd,EAEA,SAAU,CACR,EAAQ,OAAO,EAEf,IAAM,EAAI,EAAiB,QAAQ,CAAO,EAEtC,IAAM,IAAI,EAAiB,OAAO,EAAG,CAAC,CAC5C,EACA,UAEA,QAEA,MAAmC,EAA4B,CAC7D,OAAO,EAAQ,YAAY,cAAiB,CAAQ,GAAK,IAC3D,EAEA,SAAsC,EAAuB,CAC3D,OAAO,MAAM,KAAK,EAAQ,YAAY,iBAAoB,CAAQ,GAAK,CAAC,CAAC,CAC3E,EAEA,iBAA8C,EAAqB,CACjE,OAAO,MAAM,KAAK,EAAQ,YAAY,iBAAoB,iBAAiB,EAAO,GAAG,GAAK,CAAC,CAAC,CAC9F,EAEA,eAA4C,EAAc,EAAW,IAAU,CAC7E,OAAO,EAAkB,EAAQ,WAAa,EAAM,CAAQ,CAC9D,EAEA,cAA2C,EAA0B,CACnE,OAAO,EAAQ,YAAY,cAAiB,iBAAiB,EAAO,GAAG,GAAK,IAC9E,EAEA,YAAyC,EAAc,EAAW,IAAe,CAC/E,OAAO,EAAe,EAAQ,WAAa,EAAM,CAAQ,CAC3D,EAEA,IAAI,QAAqB,CACvB,OAAO,EAAQ,UACjB,CACF,CACF,CAIA,SAAS,EACP,EACA,EACA,EACU,CACV,IAAK,IAAM,KAAM,EAAK,iBAAoB,CAAQ,EAChD,GAAI,EAAG,aAAa,KAAK,IAAM,EAAM,OAAO,EAG9C,OAAO,IACT,CAEA,SAAS,EAA4C,EAA4B,EAAc,EAAuB,CACpH,OAAO,MAAM,KAAK,EAAK,iBAAoB,CAAQ,CAAC,EAAE,OAAQ,GAAO,EAAG,aAAa,KAAK,IAAM,CAAI,CACtG,CAUA,SAAgB,EAAO,EAA8B,CACnD,MAAO,CACL,MAAqC,GAAqB,EAAQ,cAAiB,CAAQ,EAC3F,SAAwC,GAAqB,MAAM,KAAK,EAAQ,iBAAoB,CAAQ,CAAC,EAC7G,iBAAgD,GAC9C,MAAM,KAAK,EAAQ,iBAAoB,iBAAiB,EAAO,GAAG,CAAC,EACrE,gBAA8C,EAAc,EAAW,MACrE,EAAkB,EAAS,EAAM,CAAQ,EAC3C,cAA6C,GAC3C,EAAQ,cAAiB,iBAAiB,EAAO,GAAG,EACtD,aAA2C,EAAc,EAAW,MAAQ,EAAe,EAAS,EAAM,CAAQ,CACpH,CACF,CAIA,IAAM,GAAsB,EAAc,EAAyB,CAAC,IAC9D,OAAO,aAAiB,IACnB,IAAI,aAAa,EAAM,CAAI,EAG7B,IAAI,WAAW,EAAM,CAAI,EAWrB,EAAO,CAClB,MAAO,EAAa,IAClB,EAAA,KAAY,MAAM,EAAI,IAAI,WAAW,OAAQ,CAAE,QAAS,GAAM,GAAG,CAAK,CAAC,CAAC,EAC1E,QAAS,EAAa,IAAqB,EAAA,KAAY,MAAM,EAAI,IAAI,MAAM,SAAU,CAAE,QAAS,GAAM,GAAG,CAAK,CAAC,CAAC,EAChH,OAAQ,EAAa,IACnB,EAAA,KAAY,MAAM,EAAI,IAAI,WAAW,QAAS,CAAE,QAAS,GAAM,WAAY,GAAM,GAAG,CAAK,CAAC,CAAC,EAC7F,OAAoB,EAAa,EAAc,EAAY,EAAiD,CAC1G,EAAA,KAAY,MAAM,EAAI,IAAI,YAAe,EAAM,CAAE,QAAS,GAAM,WAAY,GAAM,SAAQ,GAAG,CAAK,CAAC,CAAC,CACtG,EACA,OAAQ,EAAa,IACnB,EAAA,KAAY,MAAM,EAAI,IAAI,WAAW,QAAS,CAAE,QAAS,GAAM,GAAG,CAAK,CAAC,CAAC,EAC3E,OAAQ,EAAa,IAAqB,EAAA,KAAY,MAAM,EAAI,IAAI,MAAM,QAAS,CAAE,QAAS,GAAM,GAAG,CAAK,CAAC,CAAC,EAC9G,SAAU,EAAa,IACrB,EAAA,KAAY,MAAM,EAAI,IAAI,cAAc,UAAW,CAAE,QAAS,GAAM,WAAY,GAAM,GAAG,CAAK,CAAC,CAAC,EAClG,OAAQ,EAAa,IACnB,EAAA,KAAY,MAAM,EAAI,IAAI,cAAc,QAAS,CAAE,QAAS,GAAM,WAAY,GAAM,GAAG,CAAK,CAAC,CAAC,EAChG,aAAc,EAAa,IACzB,EAAA,KAAY,MAAM,EAAI,EAAmB,cAAe,CAAE,QAAS,GAAM,WAAY,GAAM,GAAG,CAAK,CAAC,CAAC,EACvG,cAAe,EAAa,IAC1B,EAAA,KAAY,MAAM,EAAI,EAAmB,eAAgB,CAAE,QAAS,GAAO,GAAG,CAAK,CAAC,CAAC,EACvF,cAAe,EAAa,IAC1B,EAAA,KAAY,MAAM,EAAI,EAAmB,eAAgB,CAAE,QAAS,GAAO,GAAG,CAAK,CAAC,CAAC,EACvF,WAAY,EAAa,IACvB,EAAA,KAAY,MAAM,EAAI,EAAmB,YAAa,CAAE,QAAS,GAAM,WAAY,GAAM,GAAG,CAAK,CAAC,CAAC,EACrG,QAAS,EAAa,IACpB,EAAA,KAAY,MAAM,EAAI,IAAI,MAAM,SAAU,CAAE,QAAS,GAAM,WAAY,GAAM,GAAG,CAAK,CAAC,CAAC,CAC3F,EAIM,MAA4B,EAAM,EAW3B,EAAO,CAClB,MAAM,MAAM,EAA2D,CACrE,EAAG,MAAM,EACT,EAAG,MAAQ,GACX,EAAK,MAAM,CAAE,EACb,EAAK,OAAO,CAAE,EACd,MAAM,EAAK,CACb,EACA,MAAM,MAAM,EAAa,EAAwC,CAC/D,EAAK,aAAa,EAAI,CAAI,EAC1B,EAAK,MAAM,EAAI,CAAI,EACnB,MAAM,EAAK,CACb,EAEA,MAAM,SAAS,EAA4B,CACzC,IAAK,IAAI,EAAI,EAAG,EAAI,EAAG,IACrB,EAAK,YAAY,CAAE,EACnB,EAAK,UAAU,CAAE,EACjB,EAAK,MAAM,CAAE,EAEf,EAAG,cAAc,IAAI,WAAW,WAAY,CAAE,QAAS,GAAM,WAAY,EAAK,CAAC,CAAC,EAChF,MAAM,EAAK,CACb,EAGA,MAAM,KAAK,EAA4C,EAA6B,CAClF,EAAG,MAAM,EACT,EAAG,MAAQ,GACX,IAAK,IAAM,KAAQ,EACjB,EAAG,OAAS,EACZ,EAAK,MAAM,CAAE,EACb,EAAK,QAAQ,EAAI,CAAE,IAAK,CAAK,CAAC,EAC9B,EAAK,MAAM,EAAI,CAAE,IAAK,CAAK,CAAC,EAC5B,MAAM,EAAK,EAEb,EAAK,OAAO,CAAE,CAChB,EAEA,MAAM,MAAM,EAA4B,CACtC,EAAK,aAAa,CAAE,EACpB,MAAM,EAAK,CACb,EAGA,MAAM,MAAM,EAAa,EAAa,EAAyC,CAC7E,EAAK,QAAQ,EAAI,CAAE,MAAK,GAAG,CAAK,CAAC,EACjC,EAAK,MAAM,EAAI,CAAE,MAAK,GAAG,CAAK,CAAC,EAC/B,MAAM,EAAK,CACb,EAEA,MAAM,OAAO,EAAuB,EAAyC,CAC3E,IAAM,EAAS,MAAM,QAAQ,CAAK,EAAI,EAAQ,CAAC,CAAK,EAEpD,IAAK,IAAM,KAAO,EAAG,QAAS,EAAI,SAAW,EAAO,SAAS,EAAI,KAAK,EACtE,EAAK,OAAO,CAAE,EACd,MAAM,EAAK,CACb,EAGA,MAAM,KAAK,EAA4C,EAA6B,CAClF,EAAG,MAAM,EACT,IAAK,IAAM,KAAQ,EACjB,EAAG,OAAS,EACZ,EAAK,MAAM,CAAE,EACb,EAAK,QAAQ,EAAI,CAAE,IAAK,CAAK,CAAC,EAC9B,EAAK,MAAM,EAAI,CAAE,IAAK,CAAK,CAAC,EAC5B,MAAM,EAAK,EAEb,EAAK,OAAO,CAAE,CAChB,EAEA,MAAM,QAAQ,EAA4B,CACxC,EAAK,aAAa,CAAE,EACpB,MAAM,EAAK,CACb,CACF,EAiBA,eAAsB,EACpB,EACA,CAAE,WAAW,GAAI,UAAS,UAAU,KAAsB,CAAC,EAC5C,CACf,IAAM,EAAW,KAAK,IAAI,EAAI,EAC1B,EAEE,EAAU,SAA8B,CAC5C,GAAI,CACF,IAAM,EAAS,MAAM,EAAG,EAExB,OAAO,IAAW,IAAA,IAAa,CAAC,CAAC,CACnC,OAAS,EAAG,CAGV,MAFA,GAAY,EAEL,EACT,CACF,EAEA,KAAO,KAAK,IAAI,EAAI,GAAU,CAC5B,GAAI,MAAM,EAAQ,EAAG,OAErB,MAAM,IAAI,QAAS,GAAM,WAAW,EAAG,CAAQ,CAAC,CAClD,CAEA,GAAI,MAAM,EAAQ,EAAG,OAErB,IAAM,EAAO,GAAW,2BAA2B,EAAQ,IAO3D,MALI,aAAqB,OACvB,EAAU,QAAU,GAAG,EAAK,IAAI,EAAU,UACpC,GAGE,MAAM,GAAa,KAAwC,EAAjC,GAAG,EAAK,WAAW,GAAkB,CAC3E,CAUA,SAAgB,EAAsC,EAAkB,EAAc,EAAU,IAAkB,CAChH,OAAO,IAAI,SAAS,EAAS,IAAW,CACtC,IAAM,EAAQ,eAAiB,EAAW,MAAM,kBAAkB,EAAK,oBAAoB,EAAQ,GAAG,CAAC,EAAG,CAAO,EAEjH,EAAQ,iBACN,EACC,GAAM,CACL,aAAa,CAAK,EAClB,EAAQ,CAAM,CAChB,EACA,CAAE,KAAM,EAAK,CACf,CACF,CAAC,CACH,CAUA,SAAgB,EAAK,EAAiB,EAAW,GAAU,CACpD,eAAe,IAAI,CAAO,GAC7B,eAAe,OACb,EACA,cAAc,WAAY,CACxB,mBAAoB,CAClB,KAAK,UAAY,CACnB,CACF,CACF,CAEJ,CASA,SAAgB,GAAgB,CAC9B,IAAK,IAAM,KAAM,EAAkB,EAAG,OAAO,EAC7C,EAAiB,OAAS,CAC5B"}
|
|
@@ -3,7 +3,10 @@
|
|
|
3
3
|
*
|
|
4
4
|
* ⚠️ Requires DOM environment (browser / jsdom / happy-dom)
|
|
5
5
|
*/
|
|
6
|
-
import {
|
|
6
|
+
import type { Signal } from '@vielzeug/stateit';
|
|
7
|
+
import type { ComponentTemplate } from '../registration';
|
|
8
|
+
import { type HTMLResult } from '../internal';
|
|
9
|
+
import { type ComponentDefinition, type SetupContextBag } from '../registration';
|
|
7
10
|
export interface Fixture<T extends HTMLElement = HTMLElement> {
|
|
8
11
|
/** The component element */
|
|
9
12
|
element: T;
|
|
@@ -53,7 +56,12 @@ export interface MountOptions {
|
|
|
53
56
|
/** Extra component options when passing an inline setup function */
|
|
54
57
|
componentOptions?: Omit<ComponentDefinition<any, any>, 'setup'>;
|
|
55
58
|
}
|
|
56
|
-
type
|
|
59
|
+
type MountProps = {
|
|
60
|
+
readonly [x: string]: Signal<unknown>;
|
|
61
|
+
};
|
|
62
|
+
export type MountSetup = {
|
|
63
|
+
bivarianceHack: (props: MountProps, ctx: SetupContextBag<any>) => ComponentTemplate | HTMLResult;
|
|
64
|
+
}['bivarianceHack'];
|
|
57
65
|
export interface WaitOptions {
|
|
58
66
|
/** Maximum wait time in ms (default: 1000) */
|
|
59
67
|
timeout?: number;
|
|
@@ -98,9 +106,8 @@ export declare function install(afterEachHook: (fn: () => void) => void): void;
|
|
|
98
106
|
* @example — registered tag name
|
|
99
107
|
* const { query } = await mount('my-counter');
|
|
100
108
|
*/
|
|
101
|
-
export declare function mount<T extends HTMLElement = HTMLElement>(
|
|
102
|
-
export declare function mount<T extends HTMLElement = HTMLElement>(
|
|
103
|
-
export declare function mount<T extends HTMLElement = HTMLElement>(tagOrSetupOrOptions: TestComponentOptions<any, any>, options?: MountOptions): Promise<Fixture<T>>;
|
|
109
|
+
export declare function mount<T extends HTMLElement = HTMLElement>(tagOrSetup: string, options?: MountOptions): Promise<Fixture<T>>;
|
|
110
|
+
export declare function mount<T extends HTMLElement = HTMLElement>(tagOrSetup: MountSetup, options?: MountOptions): Promise<Fixture<T>>;
|
|
104
111
|
/**
|
|
105
112
|
* Create query helpers scoped to any element — useful for slotted/light DOM content.
|
|
106
113
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../../src/testing/testing.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAU,KAAK,mBAAmB,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../../src/testing/testing.ts"],"names":[],"mappings":"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"}
|
package/dist/testing/testing.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{_resetIdCounter as e}from"../internal.js";import{define as
|
|
1
|
+
import"../runtime.js";import{_resetIdCounter as e,fire as t}from"../internal.js";import{define as n}from"../registration.js";var r=[],i=0,a=()=>{e(),i=0};async function o(){let e=async e=>{for(let t=0;t<e;t++)await Promise.resolve(),await new Promise(e=>queueMicrotask(e))};await e(12),await new Promise(e=>typeof requestAnimationFrame<`u`?requestAnimationFrame(()=>e()):e()),await e(2)}function s(e){e(S)}function c(e,t,n){n===!1?e.removeAttribute(t):e.setAttribute(t,n===!0?``:String(n))}var l=e=>e instanceof Error?e:Error(String(e)),u=async e=>{if(typeof window>`u`)return e();let t=null,n=e=>{t=l(e.error??e.message),e.preventDefault()},r=e=>{t=l(e.reason),e.preventDefault()};window.addEventListener(`error`,n),window.addEventListener(`unhandledrejection`,r);try{let n=await e();if(t)throw t;return n}finally{window.removeEventListener(`error`,n),window.removeEventListener(`unhandledrejection`,r)}};async function d(e,t={}){let{attrs:a={},componentOptions:s,container:l=document.body,html:d,props:m={}}=t,h=e=>(t,n)=>{let r=e(t,n);return typeof r==`function`?r:()=>r},g,_;typeof e==`string`?g=e:(g=`trial-${++i}`,_={...s??{},setup:h(e)}),_&&n(g,_);let v=document.createElement(g);d&&(v.innerHTML=d),Object.keys(m).length&&Object.assign(v,m);for(let[e,t]of Object.entries(a))c(v,e,t);return await u(async()=>{l.appendChild(v),r.push(v),await o()}),{async act(e){await e(),await o()},async attr(e,t){c(v,e,t),await o()},async attrs(e){for(let[t,n]of Object.entries(e))c(v,t,n);await o()},destroy(){v.remove();let e=r.indexOf(v);e!==-1&&r.splice(e,1)},element:v,flush:o,query(e){return v.shadowRoot?.querySelector(e)??null},queryAll(e){return Array.from(v.shadowRoot?.querySelectorAll(e)??[])},queryAllByTestId(e){return Array.from(v.shadowRoot?.querySelectorAll(`[data-testid="${e}"]`)??[])},queryAllByText(e,t=`*`){return p(v.shadowRoot,e,t)},queryByTestId(e){return v.shadowRoot?.querySelector(`[data-testid="${e}"]`)??null},queryByText(e,t=`*`){return f(v.shadowRoot,e,t)},get shadow(){return v.shadowRoot}}}function f(e,t,n){for(let r of e.querySelectorAll(n))if(r.textContent?.trim()===t)return r;return null}function p(e,t,n){return Array.from(e.querySelectorAll(n)).filter(e=>e.textContent?.trim()===t)}function m(e){return{query:t=>e.querySelector(t),queryAll:t=>Array.from(e.querySelectorAll(t)),queryAllByTestId:t=>Array.from(e.querySelectorAll(`[data-testid="${t}"]`)),queryAllByText:(t,n=`*`)=>p(e,t,n),queryByTestId:t=>e.querySelector(`[data-testid="${t}"]`),queryByText:(t,n=`*`)=>f(e,t,n)}}var h=(e,t={})=>typeof PointerEvent<`u`?new PointerEvent(e,t):new MouseEvent(e,t),g={blur:(e,n)=>t.event(e,new FocusEvent(`blur`,{bubbles:!0,...n})),change:(e,n)=>t.event(e,new Event(`change`,{bubbles:!0,...n})),click:(e,n)=>t.event(e,new MouseEvent(`click`,{bubbles:!0,cancelable:!0,...n})),custom(e,n,r,i){t.event(e,new CustomEvent(n,{bubbles:!0,cancelable:!0,detail:r,...i}))},focus:(e,n)=>t.event(e,new FocusEvent(`focus`,{bubbles:!0,...n})),input:(e,n)=>t.event(e,new Event(`input`,{bubbles:!0,...n})),keyDown:(e,n)=>t.event(e,new KeyboardEvent(`keydown`,{bubbles:!0,cancelable:!0,...n})),keyUp:(e,n)=>t.event(e,new KeyboardEvent(`keyup`,{bubbles:!0,cancelable:!0,...n})),pointerDown:(e,n)=>t.event(e,h(`pointerdown`,{bubbles:!0,cancelable:!0,...n})),pointerEnter:(e,n)=>t.event(e,h(`pointerenter`,{bubbles:!1,...n})),pointerLeave:(e,n)=>t.event(e,h(`pointerleave`,{bubbles:!1,...n})),pointerUp:(e,n)=>t.event(e,h(`pointerup`,{bubbles:!0,cancelable:!0,...n})),submit:(e,n)=>t.event(e,new Event(`submit`,{bubbles:!0,cancelable:!0,...n}))},_=()=>o(),v={async clear(e){e.focus(),e.value=``,g.input(e),g.change(e),await _()},async click(e,t){g.pointerEnter(e,t),g.click(e,t),await _()},async dblClick(e){for(let t=0;t<2;t++)g.pointerDown(e),g.pointerUp(e),g.click(e);e.dispatchEvent(new MouseEvent(`dblclick`,{bubbles:!0,cancelable:!0})),await _()},async fill(e,t){e.focus(),e.value=``;for(let n of t)e.value+=n,g.input(e),g.keyDown(e,{key:n}),g.keyUp(e,{key:n}),await _();g.change(e)},async hover(e){g.pointerEnter(e),await _()},async press(e,t,n){g.keyDown(e,{key:t,...n}),g.keyUp(e,{key:t,...n}),await _()},async select(e,t){let n=Array.isArray(t)?t:[t];for(let t of e.options)t.selected=n.includes(t.value);g.change(e),await _()},async type(e,t){e.focus();for(let n of t)e.value+=n,g.input(e),g.keyDown(e,{key:n}),g.keyUp(e,{key:n}),await _();g.change(e)},async unhover(e){g.pointerLeave(e),await _()}};async function y(e,{interval:t=50,message:n,timeout:r=1e3}={}){let i=Date.now()+r,a,o=async()=>{try{let t=await e();return t===void 0||!!t}catch(e){return a=e,!1}};for(;Date.now()<i;){if(await o())return;await new Promise(e=>setTimeout(e,t))}if(await o())return;let s=n??`waitFor timed out after ${r}ms`;throw a instanceof Error?(a.message=`${s}\n${a.message}`,a):Error(a==null?s:`${s}\nCause: ${a}`)}function b(e,t,n=1e3){return new Promise((r,i)=>{let a=setTimeout(()=>i(Error(`waitForEvent: "${t}" timed out after ${n}ms`)),n);e.addEventListener(t,e=>{clearTimeout(a),r(e)},{once:!0})})}function x(e,t=``){customElements.get(e)||customElements.define(e,class extends HTMLElement{connectedCallback(){this.innerHTML=t}})}function S(){for(let e of r)e.remove();r.length=0}export{a as _resetCounters,S as cleanup,g as fire,o as flush,s as install,x as mock,d as mount,v as user,y as waitFor,b as waitForEvent,m as within};
|
|
2
2
|
//# sourceMappingURL=testing.js.map
|