{"version":3,"file":"domparser.mjs","names":[],"sources":["../../src/client/domparser.ts"],"sourcesContent":["import type { TrustedTypePolicy } from '../types';\nimport { escapeSpecialCharacters, hasOpenTag } from './utilities';\n\n// constants\nconst HTML = 'html';\nconst HEAD = 'head';\nconst BODY = 'body';\nconst FIRST_TAG_REGEX = /<([a-zA-Z]+[0-9]?)/; // e.g., <h1>\n\nexport function getHTMLForInnerHTML(\n  html: string,\n  trustedTypePolicy?: TrustedTypePolicy,\n) {\n  return trustedTypePolicy ? trustedTypePolicy.createHTML(html) : html;\n}\n\n// falls back to `parseFromString` if `createHTMLDocument` cannot be used\n/* eslint-disable @typescript-eslint/no-unused-vars */\n/* v8 ignore start */\nlet parseFromDocument = (\n  html: string,\n  tagName?: string,\n  trustedTypePolicy?: TrustedTypePolicy,\n): Document => {\n  throw new Error(\n    'This browser does not support `document.implementation.createHTMLDocument`',\n  );\n};\n\nlet parseFromString = (\n  html: string,\n  tagName?: string,\n  trustedTypePolicy?: TrustedTypePolicy,\n): Document => {\n  void trustedTypePolicy;\n  throw new Error(\n    'This browser does not support `DOMParser.prototype.parseFromString`',\n  );\n};\n\nconst DOMParser = typeof window === 'object' && window.DOMParser;\n\n/**\n * DOMParser (performance: slow).\n *\n * @see https://developer.mozilla.org/docs/Web/API/DOMParser#Parsing_an_SVG_or_HTML_document\n */\nif (typeof DOMParser === 'function') {\n  const domParser = new DOMParser();\n  const mimeType = 'text/html';\n\n  /**\n   * Creates an HTML document using `DOMParser.parseFromString`.\n   *\n   * @param html - The HTML string.\n   * @param tagName - The element to render the HTML (with 'body' as fallback).\n   * @returns - Document.\n   */\n  parseFromString = (\n    html: string,\n    tagName?: string,\n    trustedTypePolicy?: TrustedTypePolicy,\n  ): Document => {\n    void trustedTypePolicy;\n    if (tagName) {\n      html = `<${tagName}>${html}</${tagName}>`;\n    }\n\n    return domParser.parseFromString(html, mimeType);\n  };\n\n  parseFromDocument = parseFromString;\n}\n\n/**\n * DOMImplementation (performance: fair).\n *\n * @see https://developer.mozilla.org/docs/Web/API/DOMImplementation/createHTMLDocument\n */\n// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\nif (typeof document === 'object' && document.implementation) {\n  const htmlDocument = document.implementation.createHTMLDocument();\n\n  /**\n   * Use HTML document created by `document.implementation.createHTMLDocument`.\n   *\n   * @param html - The HTML string.\n   * @param tagName - The element to render the HTML (with 'body' as fallback).\n   * @returns - Document\n   */\n  parseFromDocument = function (\n    html: string,\n    tagName?: string,\n    trustedTypePolicy?: TrustedTypePolicy,\n  ): Document {\n    if (tagName) {\n      const element = htmlDocument.documentElement.querySelector(tagName);\n\n      if (element) {\n        element.innerHTML = getHTMLForInnerHTML(\n          html,\n          trustedTypePolicy,\n        ) as string;\n      }\n\n      return htmlDocument;\n    }\n\n    htmlDocument.documentElement.innerHTML = getHTMLForInnerHTML(\n      html,\n      trustedTypePolicy,\n    ) as string;\n    return htmlDocument;\n  };\n}\n\n/**\n * Template (performance: fast).\n *\n * @see https://developer.mozilla.org/docs/Web/HTML/Element/template\n */\nconst template =\n  typeof document === 'object' && document.createElement('template');\n\nlet parseFromTemplate: (\n  html: string,\n  trustedTypePolicy?: TrustedTypePolicy,\n) => NodeList;\n\n// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\nif (template && template.content) {\n  /**\n   * Uses a template element (content fragment) to parse HTML.\n   *\n   * @param html - HTML string.\n   * @returns - Nodes.\n   */\n  parseFromTemplate = (\n    html: string,\n    trustedTypePolicy?: TrustedTypePolicy,\n  ): NodeList => {\n    template.innerHTML = getHTMLForInnerHTML(html, trustedTypePolicy) as string;\n    return template.content.childNodes;\n  };\n}\n\nconst createNodeList = () => document.createDocumentFragment().childNodes;\n/* v8 ignore stop */\n\n/**\n * Parses HTML string to DOM nodes.\n *\n * @param html - HTML markup.\n * @param trustedTypePolicy - Trusted Types policy.\n * @returns - DOM nodes.\n */\nexport default function domparser(\n  html: string,\n  trustedTypePolicy?: TrustedTypePolicy,\n): NodeList {\n  // Escape special characters before parsing\n  html = escapeSpecialCharacters(html);\n\n  const match = FIRST_TAG_REGEX.exec(html);\n  const firstTagName = match?.[1]?.toLowerCase();\n\n  switch (firstTagName) {\n    case HTML: {\n      const doc = parseFromString(html);\n\n      // the created document may come with filler head/body elements,\n      // so make sure to remove them if they don't actually exist\n      if (!hasOpenTag(html, HEAD)) {\n        const element = doc.querySelector(HEAD);\n        element?.parentNode?.removeChild(element);\n      }\n\n      if (!hasOpenTag(html, BODY)) {\n        const element = doc.querySelector(BODY);\n        element?.parentNode?.removeChild(element);\n      }\n\n      return doc.querySelectorAll(HTML);\n    }\n\n    case HEAD:\n    case BODY: {\n      const elements = parseFromDocument(\n        html,\n        undefined,\n        trustedTypePolicy,\n      ).querySelectorAll(firstTagName);\n\n      // if there's a sibling element, then return both elements\n      /* v8 ignore next */\n      if (hasOpenTag(html, BODY) && hasOpenTag(html, HEAD)) {\n        return elements[0].parentNode?.childNodes ?? createNodeList();\n      }\n\n      return elements;\n    }\n\n    // low-level tag or text\n    /* v8 ignore start */\n    default: {\n      // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n      if (parseFromTemplate) {\n        return parseFromTemplate(html, trustedTypePolicy);\n      }\n\n      const element = parseFromDocument(\n        html,\n        BODY,\n        trustedTypePolicy,\n      ).querySelector(BODY);\n\n      return element?.childNodes ?? createNodeList();\n    }\n    /* v8 ignore stop */\n  }\n}\n"],"mappings":";;AAIA,MAAM,OAAO;AACb,MAAM,OAAO;AACb,MAAM,OAAO;AACb,MAAM,kBAAkB;AAExB,SAAgB,oBACd,MACA,mBACA;CACA,OAAO,oBAAoB,kBAAkB,WAAW,IAAI,IAAI;AAClE;;AAKA,IAAI,qBACF,MACA,SACA,sBACa;CACb,MAAM,IAAI,MACR,4EACF;AACF;AAEA,IAAI,mBACF,MACA,SACA,sBACa;CAEb,MAAM,IAAI,MACR,qEACF;AACF;AAEA,MAAM,YAAY,OAAO,WAAW,YAAY,OAAO;;;;;;AAOvD,IAAI,OAAO,cAAc,YAAY;CACnC,MAAM,YAAY,IAAI,UAAU;CAChC,MAAM,WAAW;;;;;;;;CASjB,mBACE,MACA,SACA,sBACa;EAEb,IAAI,SACF,OAAO,IAAI,QAAQ,GAAG,KAAK,IAAI,QAAQ;EAGzC,OAAO,UAAU,gBAAgB,MAAM,QAAQ;CACjD;CAEA,oBAAoB;AACtB;;;;;;AAQA,IAAI,OAAO,aAAa,YAAY,SAAS,gBAAgB;CAC3D,MAAM,eAAe,SAAS,eAAe,mBAAmB;;;;;;;;CAShE,oBAAoB,SAClB,MACA,SACA,mBACU;EACV,IAAI,SAAS;GACX,MAAM,UAAU,aAAa,gBAAgB,cAAc,OAAO;GAElE,IAAI,SACF,QAAQ,YAAY,oBAClB,MACA,iBACF;GAGF,OAAO;EACT;EAEA,aAAa,gBAAgB,YAAY,oBACvC,MACA,iBACF;EACA,OAAO;CACT;AACF;;;;;;AAOA,MAAM,WACJ,OAAO,aAAa,YAAY,SAAS,cAAc,UAAU;AAEnE,IAAI;AAMJ,IAAI,YAAY,SAAS;;;;;;;AAOvB,qBACE,MACA,sBACa;CACb,SAAS,YAAY,oBAAoB,MAAM,iBAAiB;CAChE,OAAO,SAAS,QAAQ;AAC1B;AAGF,MAAM,uBAAuB,SAAS,uBAAuB,EAAE;;;;;;;;;AAU/D,SAAwB,UACtB,MACA,mBACU;CAEV,OAAO,wBAAwB,IAAI;CAGnC,MAAM,eADQ,gBAAgB,KAAK,IACV,IAAI,IAAI,YAAY;CAE7C,QAAQ,cAAR;EACE,KAAK,MAAM;GACT,MAAM,MAAM,gBAAgB,IAAI;GAIhC,IAAI,CAAC,WAAW,MAAM,IAAI,GAAG;IAC3B,MAAM,UAAU,IAAI,cAAc,IAAI;IACtC,SAAS,YAAY,YAAY,OAAO;GAC1C;GAEA,IAAI,CAAC,WAAW,MAAM,IAAI,GAAG;IAC3B,MAAM,UAAU,IAAI,cAAc,IAAI;IACtC,SAAS,YAAY,YAAY,OAAO;GAC1C;GAEA,OAAO,IAAI,iBAAiB,IAAI;EAClC;EAEA,KAAK;EACL,KAAK,MAAM;GACT,MAAM,WAAW,kBACf,MACA,KAAA,GACA,iBACF,EAAE,iBAAiB,YAAY;;GAI/B,IAAI,WAAW,MAAM,IAAI,KAAK,WAAW,MAAM,IAAI,GACjD,OAAO,SAAS,GAAG,YAAY,cAAc,eAAe;GAG9D,OAAO;EACT;;EAIA;GAEE,IAAI,mBACF,OAAO,kBAAkB,MAAM,iBAAiB;GASlD,OANgB,kBACd,MACA,MACA,iBACF,EAAE,cAAc,IAEH,GAAG,cAAc,eAAe;CAGjD;AACF"}