{"version":3,"file":"compiler.cjs","names":[],"sources":["../../src/template/compiler.ts"],"sourcesContent":["/**\n * template/compiler.ts — HTML template string parser and static template cache.\n *\n * Responsibilities:\n * - Parse TemplateStringsArray into slot metadata (slot detection).\n * - Build a cached HTMLTemplateElement with path indices for efficient node lookup.\n * - Expose `getStaticTemplate()` for use by the instantiator.\n */\n\nimport { ORE_ERRORS, OreApiError } from '../errors';\n\n// ─── Slot kinds ───────────────────────────────────────────────────────────────\n// Const object + derived union, same pattern as `ComponentPhase`/`LIFECYCLE_EVENTS`\n// in types.ts — used here (rather than plain string literals) because the kind\n// crosses a module boundary (compiler.ts produces it, instantiator.ts consumes\n// it): importing `SlotKind` gives autocomplete and a single rename point at the\n// consuming site, where a bare string literal wouldn't.\n\nexport const SlotKind = {\n  ATTR: 'attr',\n  BOOL_ATTR: 'boolAttr',\n  EVENT: 'event',\n  NODE: 'node',\n  REF: 'ref',\n} as const;\n\nexport type DetectedSlotKind = (typeof SlotKind)[keyof typeof SlotKind];\n\ntype DetectedSlot = {\n  kind: DetectedSlotKind;\n  name?: string;\n  prefix: string;\n};\n\n// ─── Static template types ────────────────────────────────────────────────────\n\nexport type NodePath = readonly number[];\n\nexport type SlotMeta = {\n  commentId?: number;\n  elementId?: number;\n  kind: DetectedSlotKind;\n  mode?: 'attr' | 'bool';\n  name?: string;\n};\n\nexport type CompiledStaticTemplate = {\n  commentPaths: ReadonlyMap<number, NodePath>;\n  element: HTMLTemplateElement;\n  elementPaths: ReadonlyMap<number, NodePath>;\n  slots: SlotMeta[];\n};\n\n// ─── Slot detection regexes ───────────────────────────────────────────────────\n\nconst EVENT_RE = /\\s+@([a-zA-Z_][-a-zA-Z0-9_.-]*)\\s*=\\s*[\"']?$/;\nconst REF_RE = /\\s+ref\\s*=\\s*[\"']?$/;\nconst BOOL_ATTR_RE = /\\s+\\?([a-zA-Z_][-a-zA-Z0-9_]*)\\s*=\\s*[\"']?$/;\nconst ATTR_RE = /\\s+([a-zA-Z_][-a-zA-Z0-9_]*)\\s*=\\s*[\"']?$/;\n\nconst detectSlot = (str: string): DetectedSlot => {\n  let m: RegExpExecArray | null;\n\n  if ((m = EVENT_RE.exec(str))) {\n    const prefix = str.slice(0, -m[0].length);\n    const [name, ...modifiers] = m[1].split('.');\n\n    if (modifiers.length > 0) throw new OreApiError(ORE_ERRORS.eventModifiersUnsupported(m[1]));\n\n    return { kind: SlotKind.EVENT, name, prefix };\n  }\n\n  if ((m = REF_RE.exec(str))) {\n    return { kind: SlotKind.REF, prefix: str.slice(0, -m[0].length) };\n  }\n\n  if ((m = BOOL_ATTR_RE.exec(str))) {\n    return { kind: SlotKind.BOOL_ATTR, name: m[1], prefix: str.slice(0, -m[0].length) };\n  }\n\n  if ((m = ATTR_RE.exec(str))) {\n    return { kind: SlotKind.ATTR, name: m[1], prefix: str.slice(0, -m[0].length) };\n  }\n\n  const lastOpen = str.lastIndexOf('<');\n  const lastClose = str.lastIndexOf('>');\n\n  if (lastOpen > lastClose && str[lastOpen + 1] !== '/') {\n    throw new OreApiError(ORE_ERRORS.templateInterpolationInTag);\n  }\n\n  return { kind: SlotKind.NODE, prefix: str };\n};\n\n// ─── Static template cache ────────────────────────────────────────────────────\n\nconst templateCache = new WeakMap<TemplateStringsArray, CompiledStaticTemplate>();\n\n/**\n * Matches a string that ends in an attribute-assignment context, e.g. `...attr=`,\n * `...@click=`, `...?disabled=`. Used together with tag-context\n * tracking (see below) to decide whether a quote immediately before an\n * interpolation is an attribute-value quote (strip it) or a literal text quote\n * (keep it) — previously every adjacent quote was stripped, so both\n * `` html`\"${value}\"` `` and prose like `area = \"${area}\"` lost their quotes.\n */\nconst ATTR_VALUE_CONTEXT_RE = /[@?]?[a-zA-Z_][-a-zA-Z0-9_.]*\\s*=\\s*$/;\n\n/**\n * Pre-process template strings to strip surrounding attribute quotes. This lets\n * the main loop operate on clean strings with no per-iteration state flags.\n *\n * Quote stripping requires BOTH an attr-assignment tail AND start-tag context\n * (tracked by replaying the raw strings): `class = \"${c}\"` inside a tag is\n * stripped; `area = \"${a}\"` in prose is not.\n */\nconst normalizeTemplateStrings = (strings: TemplateStringsArray): string[] => {\n  const out = Array.from(strings);\n  let insideTag = false;\n\n  for (let i = 0; i < out.length - 1; i++) {\n    const s = out[i];\n    const lastChar = s[s.length - 1];\n\n    // Tag context at the interpolation boundary is determined by all raw string\n    // content up to it — including this string's own text before its final quote\n    // (last angle bracket wins; attribute values containing '<'/'>' are outside\n    // the supported syntax either way).\n    for (const ch of s) {\n      if (ch === '<') insideTag = true;\n      else if (ch === '>') insideTag = false;\n    }\n\n    // Strip wrapping attribute quotes: attr=\"${value}\" → attr=${value}\n    if ((lastChar === '\"' || lastChar === \"'\") && insideTag && ATTR_VALUE_CONTEXT_RE.test(s.slice(0, -1))) {\n      out[i] = s.slice(0, -1);\n\n      const next = out[i + 1];\n\n      if (next.startsWith(lastChar)) out[i + 1] = next.slice(1);\n    }\n  }\n\n  return out;\n};\n\n/**\n * Attribute names that mark a binding target element, and the comment prefix for\n * node-slot anchors. Namespaced (`data-ore-*` / `ore:N`) so user-authored markup in\n * static template regions can never collide with them — a plain `u` attribute or a\n * numeric comment was previously hijacked as a binding marker and stripped.\n */\nconst ELEMENT_MARKER_ATTR = 'data-ore-b';\nconst COMMENT_MARKER_RE = /^ore:(\\d+)$/;\n\nconst walkNode = (\n  node: Node,\n  path: number[],\n  elementPaths: Map<number, NodePath>,\n  commentPaths: Map<number, NodePath>,\n): void => {\n  if (node.nodeType === Node.ELEMENT_NODE) {\n    const el = node as Element;\n    const marker = el.getAttribute(ELEMENT_MARKER_ATTR);\n\n    if (marker !== null) {\n      elementPaths.set(Number(marker), [...path]);\n      el.removeAttribute(ELEMENT_MARKER_ATTR);\n    }\n  } else if (node.nodeType === Node.COMMENT_NODE) {\n    const content = (node as Comment).nodeValue;\n    const m = content !== null ? COMMENT_MARKER_RE.exec(content) : null;\n\n    if (m) {\n      commentPaths.set(Number(m[1]), [...path]);\n    }\n  }\n\n  const children = node.childNodes;\n\n  for (let i = 0; i < children.length; i++) walkNode(children[i], [...path, i], elementPaths, commentPaths);\n};\n\nconst buildStaticTemplate = (strings: TemplateStringsArray): CompiledStaticTemplate => {\n  const normalized = normalizeTemplateStrings(strings);\n  let html = '';\n  let activeElementId: number | undefined;\n  let elementCounter = 0;\n  let commentCounter = 0;\n  const slots: SlotMeta[] = [];\n\n  for (let i = 0; i < normalized.length - 1; i++) {\n    const raw = normalized[i];\n    const slot = detectSlot(raw);\n\n    if (slot.kind === SlotKind.NODE) {\n      html += `${slot.prefix}<!--ore:${commentCounter}-->`;\n      slots.push({ commentId: commentCounter, kind: SlotKind.NODE });\n      commentCounter++;\n      activeElementId = undefined;\n    } else {\n      const needsNewMarker =\n        activeElementId === undefined || slot.prefix.lastIndexOf('<') > slot.prefix.lastIndexOf('>');\n\n      if (needsNewMarker) {\n        activeElementId = elementCounter++;\n        html += `${slot.prefix} ${ELEMENT_MARKER_ATTR}=\"${activeElementId}\"`;\n      } else {\n        html += slot.prefix;\n      }\n\n      const mode: 'attr' | 'bool' | undefined =\n        slot.kind === SlotKind.BOOL_ATTR ? 'bool' : slot.kind === SlotKind.ATTR ? 'attr' : undefined;\n\n      slots.push({ elementId: activeElementId, kind: slot.kind, mode, name: slot.name });\n    }\n  }\n\n  html += normalized[normalized.length - 1] ?? '';\n\n  const tpl = document.createElement('template');\n\n  tpl.innerHTML = html;\n\n  const elementPaths = new Map<number, NodePath>();\n  const commentPaths = new Map<number, NodePath>();\n  const topChildren = tpl.content.childNodes;\n\n  for (let i = 0; i < topChildren.length; i++) walkNode(topChildren[i], [i], elementPaths, commentPaths);\n\n  return { commentPaths, element: tpl, elementPaths, slots };\n};\n\nexport const getStaticTemplate = (strings: TemplateStringsArray): CompiledStaticTemplate => {\n  let tpl = templateCache.get(strings);\n\n  if (!tpl) {\n    tpl = buildStaticTemplate(strings);\n    templateCache.set(strings, tpl);\n  }\n\n  return tpl;\n};\n\n// ─── Path navigation (used by instantiator) ───────────────────────────────────\n\nexport const followPath = (root: Node, path: NodePath): Node => {\n  let node: Node = root;\n\n  for (const i of path) node = node.childNodes[i];\n\n  return node;\n};\n"],"mappings":"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"}