{"version":3,"file":"getHTML.cjs","names":[],"sources":["../../../src/interpreter/getHTML.ts"],"sourcesContent":["/**\n * Component function that receives properly typed props\n * Props include:\n * - children?: any[] - Array of child elements (ReactNode[], VNode[], etc.)\n * - [key: string]: any - Any HTML attributes (className, style, href, etc.)\n */\ntype Component = (props: { children?: any[]; [key: string]: any }) => any;\n\n/**\n * Component object that delegates to another component\n */\ntype ComponentObject = {\n  tag: string;\n  props?: Record<string, any>;\n};\n\n/**\n * Components map:\n * - Function components receive typed props\n * - String components delegate to another tag\n * - Object components delegate with additional props\n */\ntype Components = Record<string, Component | string | ComponentObject>;\n\n/**\n * Helper to parse attributes from a tag string.\n */\nconst parseAttributes = (attributes: string): Record<string, string> => {\n  const props: Record<string, string> = {};\n  const attrRegex = /([a-zA-Z0-9-]+)=\"([^\"]*)\"/g;\n\n  let match = attrRegex.exec(attributes);\n  while (match !== null) {\n    props[match[1]] = match[2];\n    match = attrRegex.exec(attributes);\n  }\n  return props;\n};\n\ntype ASTNode =\n  | string\n  | {\n      tagName: string;\n      props: Record<string, string>;\n      children: ASTNode[];\n    };\n\nconst astCache = new Map<string, ASTNode[]>();\n\nconst parseHTML = (content: string): ASTNode[] => {\n  if (astCache.has(content)) {\n    return astCache.get(content)!;\n  }\n\n  if (typeof content !== 'string') {\n    return [];\n  }\n\n  const tagRegex = /<(\\/)?([a-zA-Z0-9.-]+)([\\s\\S]*?)(\\/?)>/g;\n  const elements: ASTNode[] = [];\n  const stack: {\n    tagName: string;\n    children: ASTNode[];\n    props: Record<string, string>;\n  }[] = [];\n\n  let lastIndex = 0;\n  let match = tagRegex.exec(content);\n\n  const appendChild = (child: ASTNode) => {\n    const target =\n      stack.length > 0 ? stack[stack.length - 1].children : elements;\n    target.push(child);\n  };\n\n  while (match !== null) {\n    const [fullMatch, isClosingRaw, tagName, attributesRaw, isSelfClosingRaw] =\n      match;\n    const matchIndex = match.index;\n\n    if (matchIndex > lastIndex) {\n      appendChild(content.slice(lastIndex, matchIndex));\n    }\n\n    const isClosing = isClosingRaw === '/';\n    const isSelfClosing =\n      isSelfClosingRaw === '/' ||\n      attributesRaw.trim().endsWith('/') ||\n      fullMatch.endsWith('/>');\n\n    const cleanedAttributes = attributesRaw.trim().replace(/\\/$/, '').trim();\n\n    if (isClosing) {\n      const last = stack[stack.length - 1];\n\n      // Only pop if the tag names match\n      if (last && last.tagName === tagName) {\n        const popped = stack.pop();\n        if (popped) {\n          appendChild({\n            tagName: popped.tagName,\n            props: popped.props,\n            children: popped.children,\n          });\n        }\n      }\n      // If tags don't match or no open tag, silently ignore the closing tag\n    } else if (isSelfClosing) {\n      const tagProps = parseAttributes(cleanedAttributes);\n      appendChild({\n        tagName,\n        props: tagProps,\n        children: [],\n      });\n    } else {\n      const tagProps = parseAttributes(cleanedAttributes);\n      stack.push({ tagName, children: [], props: tagProps });\n    }\n\n    lastIndex = matchIndex + fullMatch.length;\n    match = tagRegex.exec(content);\n  }\n\n  if (lastIndex < content.length) {\n    appendChild(content.slice(lastIndex));\n  }\n\n  // Handle unclosed tags by appending them to the root or parent\n  while (stack.length > 0) {\n    const last = stack.pop();\n    if (last) {\n      appendChild({\n        tagName: last.tagName,\n        props: last.props,\n        children: last.children,\n      });\n    }\n  }\n\n  astCache.set(content, elements);\n  return elements;\n};\n\n/**\n * Interprets a string containing HTML-like tags and replaces them with provided components or strings.\n */\nexport const getHTML = (content: string, values: Components): any => {\n  // Parse into AST (cached)\n  const ast = parseHTML(content);\n\n  // Render AST\n  let keyCounter = 0;\n\n  const renderASTNode = (node: ASTNode): any => {\n    if (typeof node === 'string') {\n      return node;\n    }\n\n    const { tagName, props, children } = node;\n    const renderedChildren = children.flatMap(renderASTNode);\n    const index = keyCounter++;\n\n    let override = values[tagName];\n\n    if (!override) {\n      const lowerTagName = tagName.toLowerCase();\n      const foundKey = Object.keys(values).find(\n        (key) => key.toLowerCase() === lowerTagName\n      );\n\n      if (foundKey) override = values[foundKey];\n    }\n\n    const key = `html-tag-${tagName}-${index}`;\n\n    if (typeof override === 'function') {\n      return override({ ...props, children: renderedChildren, key });\n    }\n\n    if (typeof override === 'string') {\n      const component = values[override];\n\n      if (typeof component === 'function') {\n        return component({ ...props, children: renderedChildren, key });\n      }\n      return renderedChildren;\n    }\n\n    if (\n      typeof override === 'object' &&\n      override !== null &&\n      'tag' in override\n    ) {\n      const { tag: targetTag, props: extraProps } = override as ComponentObject;\n      const component = values[targetTag];\n\n      if (typeof component === 'function') {\n        return component({\n          ...props,\n          ...extraProps,\n          children: renderedChildren,\n          key,\n        });\n      }\n      return renderedChildren;\n    }\n\n    // Default: Skip tag, render children\n    return renderedChildren;\n  };\n\n  const result = ast.flatMap(renderASTNode);\n  return result.length === 1 ? result[0] : result;\n};\n"],"mappings":"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"}