element.\n * const html = parse5.serializeOuter(document.childNodes[0]);\n *\n * console.log(str); //> '
Hello, world!
'\n * ```\n *\n * @param node Node to serialize.\n * @param options Serialization options.\n */\nexport function serializeOuter(node, options) {\n const opts = {\n ...defaultOpts,\n ...options\n };\n return serializeNode(node, opts);\n}\nfunction serializeChildNodes(parentNode, options) {\n let html = '';\n // Get container of the child nodes\n const container = options.treeAdapter.isElementNode(parentNode) && options.treeAdapter.getTagName(parentNode) === $.TEMPLATE && options.treeAdapter.getNamespaceURI(parentNode) === NS.HTML ? options.treeAdapter.getTemplateContent(parentNode) : parentNode;\n const childNodes = options.treeAdapter.getChildNodes(container);\n if (childNodes) {\n for (const currentNode of childNodes) {\n html += serializeNode(currentNode, options);\n }\n }\n return html;\n}\nfunction serializeNode(node, options) {\n if (options.treeAdapter.isElementNode(node)) {\n return serializeElement(node, options);\n }\n if (options.treeAdapter.isTextNode(node)) {\n return serializeTextNode(node, options);\n }\n if (options.treeAdapter.isCommentNode(node)) {\n return serializeCommentNode(node, options);\n }\n if (options.treeAdapter.isDocumentTypeNode(node)) {\n return serializeDocumentTypeNode(node, options);\n }\n // Return an empty string for unknown nodes\n return '';\n}\nfunction serializeElement(node, options) {\n const tn = options.treeAdapter.getTagName(node);\n return `<${tn}${serializeAttributes(node, options)}>${isVoidElement(node, options) ? '' : `${serializeChildNodes(node, options)}${tn}>`}`;\n}\nfunction serializeAttributes(node, _ref) {\n let {\n treeAdapter\n } = _ref;\n let html = '';\n for (const attr of treeAdapter.getAttrList(node)) {\n html += ' ';\n if (!attr.namespace) {\n html += attr.name;\n } else switch (attr.namespace) {\n case NS.XML:\n {\n html += `xml:${attr.name}`;\n break;\n }\n case NS.XMLNS:\n {\n if (attr.name !== 'xmlns') {\n html += 'xmlns:';\n }\n html += attr.name;\n break;\n }\n case NS.XLINK:\n {\n html += `xlink:${attr.name}`;\n break;\n }\n default:\n {\n html += `${attr.prefix}:${attr.name}`;\n }\n }\n html += `=\"${escapeAttribute(attr.value)}\"`;\n }\n return html;\n}\nfunction serializeTextNode(node, options) {\n const {\n treeAdapter\n } = options;\n const content = treeAdapter.getTextNodeContent(node);\n const parent = treeAdapter.getParentNode(node);\n const parentTn = parent && treeAdapter.isElementNode(parent) && treeAdapter.getTagName(parent);\n return parentTn && treeAdapter.getNamespaceURI(parent) === NS.HTML && hasUnescapedText(parentTn, options.scriptingEnabled) ? content : escapeText(content);\n}\nfunction serializeCommentNode(node, _ref2) {\n let {\n treeAdapter\n } = _ref2;\n return ``;\n}\nfunction serializeDocumentTypeNode(node, _ref3) {\n let {\n treeAdapter\n } = _ref3;\n return ``;\n}","/**\n * @typedef {import('unist').Node} Node\n * @typedef {import('unist').Point} Point\n * @typedef {import('unist').Position} Position\n */\n\n/**\n * @typedef NodeLike\n * @property {string} type\n * @property {PositionLike | null | undefined} [position]\n *\n * @typedef PositionLike\n * @property {PointLike | null | undefined} [start]\n * @property {PointLike | null | undefined} [end]\n *\n * @typedef PointLike\n * @property {number | null | undefined} [line]\n * @property {number | null | undefined} [column]\n * @property {number | null | undefined} [offset]\n */\n\n/**\n * Get the ending point of `node`.\n *\n * @param node\n * Node.\n * @returns\n * Point.\n */\nexport const pointEnd = point('end');\n\n/**\n * Get the starting point of `node`.\n *\n * @param node\n * Node.\n * @returns\n * Point.\n */\nexport const pointStart = point('start');\n\n/**\n * Get the positional info of `node`.\n *\n * @param {'end' | 'start'} type\n * Side.\n * @returns\n * Getter.\n */\nfunction point(type) {\n return point;\n\n /**\n * Get the point info of `node` at a bound side.\n *\n * @param {Node | NodeLike | null | undefined} [node]\n * @returns {Point | undefined}\n */\n function point(node) {\n const point = node && node.position && node.position[type] || {};\n if (typeof point.line === 'number' && point.line > 0 && typeof point.column === 'number' && point.column > 0) {\n return {\n line: point.line,\n column: point.column,\n offset: typeof point.offset === 'number' && point.offset > -1 ? point.offset : undefined\n };\n }\n }\n}\n\n/**\n * Get the positional info of `node`.\n *\n * @param {Node | NodeLike | null | undefined} [node]\n * Node.\n * @returns {Position | undefined}\n * Position.\n */\nexport function position(node) {\n const start = pointStart(node);\n const end = pointEnd(node);\n if (start && end) {\n return {\n start,\n end\n };\n }\n}","/**\n * @typedef {import('unist').Node} Node\n * @typedef {import('unist').Parent} Parent\n */\n\n/**\n * @template Fn\n * @template Fallback\n * @typedef {Fn extends (value: any) => value is infer Thing ? Thing : Fallback} Predicate\n */\n\n/**\n * @callback Check\n * Check that an arbitrary value is a node.\n * @param {unknown} this\n * The given context.\n * @param {unknown} [node]\n * Anything (typically a node).\n * @param {number | null | undefined} [index]\n * The node’s position in its parent.\n * @param {Parent | null | undefined} [parent]\n * The node’s parent.\n * @returns {boolean}\n * Whether this is a node and passes a test.\n *\n * @typedef {Record
| Node} Props\n * Object to check for equivalence.\n *\n * Note: `Node` is included as it is common but is not indexable.\n *\n * @typedef {Array | Props | TestFunction | string | null | undefined} Test\n * Check for an arbitrary node.\n *\n * @callback TestFunction\n * Check if a node passes a test.\n * @param {unknown} this\n * The given context.\n * @param {Node} node\n * A node.\n * @param {number | undefined} [index]\n * The node’s position in its parent.\n * @param {Parent | undefined} [parent]\n * The node’s parent.\n * @returns {boolean | undefined | void}\n * Whether this node passes the test.\n *\n * Note: `void` is included until TS sees no return as `undefined`.\n */\n\n/**\n * Check if `node` is a `Node` and whether it passes the given test.\n *\n * @param {unknown} node\n * Thing to check, typically `Node`.\n * @param {Test} test\n * A check for a specific node.\n * @param {number | null | undefined} index\n * The node’s position in its parent.\n * @param {Parent | null | undefined} parent\n * The node’s parent.\n * @param {unknown} context\n * Context object (`this`) to pass to `test` functions.\n * @returns {boolean}\n * Whether `node` is a node and passes a test.\n */\nexport const is =\n// Note: overloads in JSDoc can’t yet use different `@template`s.\n/**\n * @type {(\n * ((node: unknown, test: Condition, index?: number | null | undefined, parent?: Parent | null | undefined, context?: unknown) => node is Node & {type: Condition}) &\n * ((node: unknown, test: Condition, index?: number | null | undefined, parent?: Parent | null | undefined, context?: unknown) => node is Node & Condition) &\n * ((node: unknown, test: Condition, index?: number | null | undefined, parent?: Parent | null | undefined, context?: unknown) => node is Node & Predicate) &\n * ((node?: null | undefined) => false) &\n * ((node: unknown, test?: null | undefined, index?: number | null | undefined, parent?: Parent | null | undefined, context?: unknown) => node is Node) &\n * ((node: unknown, test?: Test, index?: number | null | undefined, parent?: Parent | null | undefined, context?: unknown) => boolean)\n * )}\n */\n\n/**\n * @param {unknown} [node]\n * @param {Test} [test]\n * @param {number | null | undefined} [index]\n * @param {Parent | null | undefined} [parent]\n * @param {unknown} [context]\n * @returns {boolean}\n */\n// eslint-disable-next-line max-params\nfunction (node, test, index, parent, context) {\n const check = convert(test);\n if (index !== undefined && index !== null && (typeof index !== 'number' || index < 0 || index === Number.POSITIVE_INFINITY)) {\n throw new Error('Expected positive finite index');\n }\n if (parent !== undefined && parent !== null && (!is(parent) || !parent.children)) {\n throw new Error('Expected parent node');\n }\n if ((parent === undefined || parent === null) !== (index === undefined || index === null)) {\n throw new Error('Expected both parent and index');\n }\n return looksLikeANode(node) ? check.call(context, node, index, parent) : false;\n};\n\n/**\n * Generate an assertion from a test.\n *\n * Useful if you’re going to test many nodes, for example when creating a\n * utility where something else passes a compatible test.\n *\n * The created function is a bit faster because it expects valid input only:\n * a `node`, `index`, and `parent`.\n *\n * @param {Test} test\n * * when nullish, checks if `node` is a `Node`.\n * * when `string`, works like passing `(node) => node.type === test`.\n * * when `function` checks if function passed the node is true.\n * * when `object`, checks that all keys in test are in node, and that they have (strictly) equal values.\n * * when `array`, checks if any one of the subtests pass.\n * @returns {Check}\n * An assertion.\n */\nexport const convert =\n// Note: overloads in JSDoc can’t yet use different `@template`s.\n/**\n * @type {(\n * ((test: Condition) => (node: unknown, index?: number | null | undefined, parent?: Parent | null | undefined, context?: unknown) => node is Node & {type: Condition}) &\n * ((test: Condition) => (node: unknown, index?: number | null | undefined, parent?: Parent | null | undefined, context?: unknown) => node is Node & Condition) &\n * ((test: Condition) => (node: unknown, index?: number | null | undefined, parent?: Parent | null | undefined, context?: unknown) => node is Node & Predicate) &\n * ((test?: null | undefined) => (node?: unknown, index?: number | null | undefined, parent?: Parent | null | undefined, context?: unknown) => node is Node) &\n * ((test?: Test) => Check)\n * )}\n */\n\n/**\n * @param {Test} [test]\n * @returns {Check}\n */\nfunction (test) {\n if (test === null || test === undefined) {\n return ok;\n }\n if (typeof test === 'function') {\n return castFactory(test);\n }\n if (typeof test === 'object') {\n return Array.isArray(test) ? anyFactory(test) : propsFactory(test);\n }\n if (typeof test === 'string') {\n return typeFactory(test);\n }\n throw new Error('Expected function, string, or object as test');\n};\n\n/**\n * @param {Array} tests\n * @returns {Check}\n */\nfunction anyFactory(tests) {\n /** @type {Array} */\n const checks = [];\n let index = -1;\n while (++index < tests.length) {\n checks[index] = convert(tests[index]);\n }\n return castFactory(any);\n\n /**\n * @this {unknown}\n * @type {TestFunction}\n */\n function any() {\n let index = -1;\n for (var _len = arguments.length, parameters = new Array(_len), _key = 0; _key < _len; _key++) {\n parameters[_key] = arguments[_key];\n }\n while (++index < checks.length) {\n if (checks[index].apply(this, parameters)) return true;\n }\n return false;\n }\n}\n\n/**\n * Turn an object into a test for a node with a certain fields.\n *\n * @param {Props} check\n * @returns {Check}\n */\nfunction propsFactory(check) {\n const checkAsRecord = /** @type {Record} */check;\n return castFactory(all);\n\n /**\n * @param {Node} node\n * @returns {boolean}\n */\n function all(node) {\n const nodeAsRecord = /** @type {Record} */\n /** @type {unknown} */node;\n\n /** @type {string} */\n let key;\n for (key in check) {\n if (nodeAsRecord[key] !== checkAsRecord[key]) return false;\n }\n return true;\n }\n}\n\n/**\n * Turn a string into a test for a node with a certain type.\n *\n * @param {string} check\n * @returns {Check}\n */\nfunction typeFactory(check) {\n return castFactory(type);\n\n /**\n * @param {Node} node\n */\n function type(node) {\n return node && node.type === check;\n }\n}\n\n/**\n * Turn a custom test into a test for a node that passes that test.\n *\n * @param {TestFunction} testFunction\n * @returns {Check}\n */\nfunction castFactory(testFunction) {\n return check;\n\n /**\n * @this {unknown}\n * @type {Check}\n */\n function check(value, index, parent) {\n return Boolean(looksLikeANode(value) && testFunction.call(this, value, typeof index === 'number' ? index : undefined, parent || undefined));\n }\n}\nfunction ok() {\n return true;\n}\n\n/**\n * @param {unknown} value\n * @returns {value is Node}\n */\nfunction looksLikeANode(value) {\n return value !== null && typeof value === 'object' && 'type' in value;\n}","/**\n * @typedef {import('unist').Node} UnistNode\n * @typedef {import('unist').Parent} UnistParent\n */\n\n/**\n * @typedef {Exclude | undefined} Test\n * Test from `unist-util-is`.\n *\n * Note: we have remove and add `undefined`, because otherwise when generating\n * automatic `.d.ts` files, TS tries to flatten paths from a local perspective,\n * which doesn’t work when publishing on npm.\n */\n\n/**\n * @typedef {(\n * Fn extends (value: any) => value is infer Thing\n * ? Thing\n * : Fallback\n * )} Predicate\n * Get the value of a type guard `Fn`.\n * @template Fn\n * Value; typically function that is a type guard (such as `(x): x is Y`).\n * @template Fallback\n * Value to yield if `Fn` is not a type guard.\n */\n\n/**\n * @typedef {(\n * Check extends null | undefined // No test.\n * ? Value\n * : Value extends {type: Check} // String (type) test.\n * ? Value\n * : Value extends Check // Partial test.\n * ? Value\n * : Check extends Function // Function test.\n * ? Predicate extends Value\n * ? Predicate\n * : never\n * : never // Some other test?\n * )} MatchesOne\n * Check whether a node matches a primitive check in the type system.\n * @template Value\n * Value; typically unist `Node`.\n * @template Check\n * Value; typically `unist-util-is`-compatible test, but not arrays.\n */\n\n/**\n * @typedef {(\n * Check extends Array\n * ? MatchesOne\n * : MatchesOne\n * )} Matches\n * Check whether a node matches a check in the type system.\n * @template Value\n * Value; typically unist `Node`.\n * @template Check\n * Value; typically `unist-util-is`-compatible test.\n */\n\n/**\n * @typedef {0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10} Uint\n * Number; capped reasonably.\n */\n\n/**\n * @typedef {I extends 0 ? 1 : I extends 1 ? 2 : I extends 2 ? 3 : I extends 3 ? 4 : I extends 4 ? 5 : I extends 5 ? 6 : I extends 6 ? 7 : I extends 7 ? 8 : I extends 8 ? 9 : 10} Increment\n * Increment a number in the type system.\n * @template {Uint} [I=0]\n * Index.\n */\n\n/**\n * @typedef {(\n * Node extends UnistParent\n * ? Node extends {children: Array}\n * ? Child extends Children ? Node : never\n * : never\n * : never\n * )} InternalParent\n * Collect nodes that can be parents of `Child`.\n * @template {UnistNode} Node\n * All node types in a tree.\n * @template {UnistNode} Child\n * Node to search for.\n */\n\n/**\n * @typedef {InternalParent, Child>} Parent\n * Collect nodes in `Tree` that can be parents of `Child`.\n * @template {UnistNode} Tree\n * All node types in a tree.\n * @template {UnistNode} Child\n * Node to search for.\n */\n\n/**\n * @typedef {(\n * Depth extends Max\n * ? never\n * :\n * | InternalParent\n * | InternalAncestor, Max, Increment>\n * )} InternalAncestor\n * Collect nodes in `Tree` that can be ancestors of `Child`.\n * @template {UnistNode} Node\n * All node types in a tree.\n * @template {UnistNode} Child\n * Node to search for.\n * @template {Uint} [Max=10]\n * Max; searches up to this depth.\n * @template {Uint} [Depth=0]\n * Current depth.\n */\n\n/**\n * @typedef {InternalAncestor, Child>} Ancestor\n * Collect nodes in `Tree` that can be ancestors of `Child`.\n * @template {UnistNode} Tree\n * All node types in a tree.\n * @template {UnistNode} Child\n * Node to search for.\n */\n\n/**\n * @typedef {(\n * Tree extends UnistParent\n * ? Depth extends Max\n * ? Tree\n * : Tree | InclusiveDescendant>\n * : Tree\n * )} InclusiveDescendant\n * Collect all (inclusive) descendants of `Tree`.\n *\n * > 👉 **Note**: for performance reasons, this seems to be the fastest way to\n * > recurse without actually running into an infinite loop, which the\n * > previous version did.\n * >\n * > Practically, a max of `2` is typically enough assuming a `Root` is\n * > passed, but it doesn’t improve performance.\n * > It gets higher with `List > ListItem > Table > TableRow > TableCell`.\n * > Using up to `10` doesn’t hurt or help either.\n * @template {UnistNode} Tree\n * Tree type.\n * @template {Uint} [Max=10]\n * Max; searches up to this depth.\n * @template {Uint} [Depth=0]\n * Current depth.\n */\n\n/**\n * @typedef {'skip' | boolean} Action\n * Union of the action types.\n *\n * @typedef {number} Index\n * Move to the sibling at `index` next (after node itself is completely\n * traversed).\n *\n * Useful if mutating the tree, such as removing the node the visitor is\n * currently on, or any of its previous siblings.\n * Results less than 0 or greater than or equal to `children.length` stop\n * traversing the parent.\n *\n * @typedef {[(Action | null | undefined | void)?, (Index | null | undefined)?]} ActionTuple\n * List with one or two values, the first an action, the second an index.\n *\n * @typedef {Action | ActionTuple | Index | null | undefined | void} VisitorResult\n * Any value that can be returned from a visitor.\n */\n\n/**\n * @callback Visitor\n * Handle a node (matching `test`, if given).\n *\n * Visitors are free to transform `node`.\n * They can also transform the parent of node (the last of `ancestors`).\n *\n * Replacing `node` itself, if `SKIP` is not returned, still causes its\n * descendants to be walked (which is a bug).\n *\n * When adding or removing previous siblings of `node` (or next siblings, in\n * case of reverse), the `Visitor` should return a new `Index` to specify the\n * sibling to traverse after `node` is traversed.\n * Adding or removing next siblings of `node` (or previous siblings, in case\n * of reverse) is handled as expected without needing to return a new `Index`.\n *\n * Removing the children property of an ancestor still results in them being\n * traversed.\n * @param {Visited} node\n * Found node.\n * @param {Array} ancestors\n * Ancestors of `node`.\n * @returns {VisitorResult}\n * What to do next.\n *\n * An `Index` is treated as a tuple of `[CONTINUE, Index]`.\n * An `Action` is treated as a tuple of `[Action]`.\n *\n * Passing a tuple back only makes sense if the `Action` is `SKIP`.\n * When the `Action` is `EXIT`, that action can be returned.\n * When the `Action` is `CONTINUE`, `Index` can be returned.\n * @template {UnistNode} [Visited=UnistNode]\n * Visited node type.\n * @template {UnistParent} [VisitedParents=UnistParent]\n * Ancestor type.\n */\n\n/**\n * @typedef {Visitor, Check>, Ancestor, Check>>>} BuildVisitor\n * Build a typed `Visitor` function from a tree and a test.\n *\n * It will infer which values are passed as `node` and which as `parents`.\n * @template {UnistNode} [Tree=UnistNode]\n * Tree type.\n * @template {Test} [Check=Test]\n * Test type.\n */\n\nimport { convert } from 'unist-util-is';\nimport { color } from 'unist-util-visit-parents/do-not-use-color';\n\n/** @type {Readonly} */\nconst empty = [];\n\n/**\n * Continue traversing as normal.\n */\nexport const CONTINUE = true;\n\n/**\n * Stop traversing immediately.\n */\nexport const EXIT = false;\n\n/**\n * Do not traverse this node’s children.\n */\nexport const SKIP = 'skip';\n\n/**\n * Visit nodes, with ancestral information.\n *\n * This algorithm performs *depth-first* *tree traversal* in *preorder*\n * (**NLR**) or if `reverse` is given, in *reverse preorder* (**NRL**).\n *\n * You can choose for which nodes `visitor` is called by passing a `test`.\n * For complex tests, you should test yourself in `visitor`, as it will be\n * faster and will have improved type information.\n *\n * Walking the tree is an intensive task.\n * Make use of the return values of the visitor when possible.\n * Instead of walking a tree multiple times, walk it once, use `unist-util-is`\n * to check if a node matches, and then perform different operations.\n *\n * You can change the tree.\n * See `Visitor` for more info.\n *\n * @overload\n * @param {Tree} tree\n * @param {Check} check\n * @param {BuildVisitor} visitor\n * @param {boolean | null | undefined} [reverse]\n * @returns {undefined}\n *\n * @overload\n * @param {Tree} tree\n * @param {BuildVisitor} visitor\n * @param {boolean | null | undefined} [reverse]\n * @returns {undefined}\n *\n * @param {UnistNode} tree\n * Tree to traverse.\n * @param {Visitor | Test} test\n * `unist-util-is`-compatible test\n * @param {Visitor | boolean | null | undefined} [visitor]\n * Handle each node.\n * @param {boolean | null | undefined} [reverse]\n * Traverse in reverse preorder (NRL) instead of the default preorder (NLR).\n * @returns {undefined}\n * Nothing.\n *\n * @template {UnistNode} Tree\n * Node type.\n * @template {Test} Check\n * `unist-util-is`-compatible test.\n */\nexport function visitParents(tree, test, visitor, reverse) {\n /** @type {Test} */\n let check;\n if (typeof test === 'function' && typeof visitor !== 'function') {\n reverse = visitor;\n // @ts-expect-error no visitor given, so `visitor` is test.\n visitor = test;\n } else {\n // @ts-expect-error visitor given, so `test` isn’t a visitor.\n check = test;\n }\n const is = convert(check);\n const step = reverse ? -1 : 1;\n factory(tree, undefined, [])();\n\n /**\n * @param {UnistNode} node\n * @param {number | undefined} index\n * @param {Array} parents\n */\n function factory(node, index, parents) {\n const value = /** @type {Record} */\n node && typeof node === 'object' ? node : {};\n if (typeof value.type === 'string') {\n const name =\n // `hast`\n typeof value.tagName === 'string' ? value.tagName :\n // `xast`\n typeof value.name === 'string' ? value.name : undefined;\n Object.defineProperty(visit, 'name', {\n value: 'node (' + color(node.type + (name ? '<' + name + '>' : '')) + ')'\n });\n }\n return visit;\n function visit() {\n /** @type {Readonly} */\n let result = empty;\n /** @type {Readonly} */\n let subresult;\n /** @type {number} */\n let offset;\n /** @type {Array} */\n let grandparents;\n if (!test || is(node, index, parents[parents.length - 1] || undefined)) {\n // @ts-expect-error: `visitor` is now a visitor.\n result = toResult(visitor(node, parents));\n if (result[0] === EXIT) {\n return result;\n }\n }\n if ('children' in node && node.children) {\n const nodeAsParent = /** @type {UnistParent} */node;\n if (nodeAsParent.children && result[0] !== SKIP) {\n offset = (reverse ? nodeAsParent.children.length : -1) + step;\n grandparents = parents.concat(nodeAsParent);\n while (offset > -1 && offset < nodeAsParent.children.length) {\n const child = nodeAsParent.children[offset];\n subresult = factory(child, offset, grandparents)();\n if (subresult[0] === EXIT) {\n return subresult;\n }\n offset = typeof subresult[1] === 'number' ? subresult[1] : offset + step;\n }\n }\n }\n return result;\n }\n }\n}\n\n/**\n * Turn a return value into a clean result.\n *\n * @param {VisitorResult} value\n * Valid return values from visitors.\n * @returns {Readonly}\n * Clean result.\n */\nfunction toResult(value) {\n if (Array.isArray(value)) {\n return value;\n }\n if (typeof value === 'number') {\n return [CONTINUE, value];\n }\n return value === null || value === undefined ? empty : [value];\n}","/**\n * @typedef {import('hast').Comment} Comment\n * @typedef {import('hast').Doctype} Doctype\n * @typedef {import('hast').Element} Element\n * @typedef {import('hast').Nodes} Nodes\n * @typedef {import('hast').Root} Root\n * @typedef {import('hast').RootContent} RootContent\n * @typedef {import('hast').Text} Text\n *\n * @typedef {import('mdast-util-to-hast').Raw} Raw\n *\n * @typedef {import('parse5').DefaultTreeAdapterMap} DefaultTreeAdapterMap\n * @typedef {import('parse5').ParserOptions} ParserOptions\n * @typedef {import('parse5').Token.CharacterToken} CharacterToken\n * @typedef {import('parse5').Token.CommentToken} CommentToken\n * @typedef {import('parse5').Token.DoctypeToken} DoctypeToken\n * @typedef {import('parse5').Token.Location} Location\n * @typedef {import('parse5').Token.TagToken} TagToken\n *\n * @typedef {import('unist').Point} Point\n *\n * @typedef {import('vfile').VFile} VFile\n */\n\n/**\n * @typedef Options\n * Configuration.\n * @property {VFile | null | undefined} [file]\n * Corresponding virtual file representing the input document (optional).\n * @property {Array | null | undefined} [passThrough]\n * List of custom hast node types to pass through (as in, keep) (optional).\n *\n * If the passed through nodes have children, those children are expected to\n * be hast again and will be handled.\n *\n * @typedef State\n * Info passed around about the current state.\n * @property {(node: Nodes) => undefined} handle\n * Add a hast node to the parser.\n * @property {Options} options\n * User configuration.\n * @property {Parser} parser\n * Current parser.\n * @property {boolean} stitches\n * Whether there are stitches.\n *\n * @typedef {{type: 'comment', value: {stitch: Nodes}}} Stitch\n * Custom comment-like value we pass through parse5, which contains a\n * replacement node that we’ll swap back in afterwards.\n */\n\nimport structuredClone from '@ungap/structured-clone';\nimport { fromParse5 } from 'hast-util-from-parse5';\nimport { toParse5 } from 'hast-util-to-parse5';\nimport { htmlVoidElements } from 'html-void-elements';\nimport { Parser, Token, TokenizerMode, html } from 'parse5';\nimport { pointEnd, pointStart } from 'unist-util-position';\nimport { visit } from 'unist-util-visit';\nimport { webNamespaces } from 'web-namespaces';\nimport { zwitch } from 'zwitch';\n\n// Node types associated with MDX.\n// \nconst knownMdxNames = new Set(['mdxFlowExpression', 'mdxJsxFlowElement', 'mdxJsxTextElement', 'mdxTextExpression', 'mdxjsEsm']);\n\n/** @type {ParserOptions} */\nconst parseOptions = {\n sourceCodeLocationInfo: true,\n scriptingEnabled: false\n};\n\n/**\n * Pass a hast tree through an HTML parser, which will fix nesting, and turn\n * raw nodes into actual nodes.\n *\n * @param {Nodes} tree\n * Original hast tree to transform.\n * @param {Options | null | undefined} [options]\n * Configuration (optional).\n * @returns {Nodes}\n * Parsed again tree.\n */\nexport function raw(tree, options) {\n const document = documentMode(tree);\n /** @type {(node: Nodes, state: State) => undefined} */\n const one = zwitch('type', {\n handlers: {\n root,\n element,\n text,\n comment,\n doctype,\n raw: handleRaw\n },\n unknown\n });\n\n /** @type {State} */\n const state = {\n parser: document ? new Parser(parseOptions) : Parser.getFragmentParser(undefined, parseOptions),\n handle(node) {\n one(node, state);\n },\n stitches: false,\n options: options || {}\n };\n one(tree, state);\n resetTokenizer(state, pointStart());\n const p5 = document ? state.parser.document : state.parser.getFragment();\n const result = fromParse5(p5, {\n // To do: support `space`?\n file: state.options.file\n });\n if (state.stitches) {\n visit(result, 'comment', function (node, index, parent) {\n const stitch = /** @type {Stitch} */ /** @type {unknown} */node;\n if (stitch.value.stitch && parent && index !== undefined) {\n /** @type {Array} */\n const siblings = parent.children;\n // @ts-expect-error: assume the stitch is allowed.\n siblings[index] = stitch.value.stitch;\n return index;\n }\n });\n }\n\n // Unpack if possible and when not given a `root`.\n if (result.type === 'root' && result.children.length === 1 && result.children[0].type === tree.type) {\n return result.children[0];\n }\n return result;\n}\n\n/**\n * Transform all nodes\n *\n * @param {Array} nodes\n * hast content.\n * @param {State} state\n * Info passed around about the current state.\n * @returns {undefined}\n * Nothing.\n */\nfunction all(nodes, state) {\n let index = -1;\n\n /* istanbul ignore else - invalid nodes, see rehypejs/rehype-raw#7. */\n if (nodes) {\n while (++index < nodes.length) {\n state.handle(nodes[index]);\n }\n }\n}\n\n/**\n * Transform a root.\n *\n * @param {Root} node\n * hast root node.\n * @param {State} state\n * Info passed around about the current state.\n * @returns {undefined}\n * Nothing.\n */\nfunction root(node, state) {\n all(node.children, state);\n}\n\n/**\n * Transform an element.\n *\n * @param {Element} node\n * hast element node.\n * @param {State} state\n * Info passed around about the current state.\n * @returns {undefined}\n * Nothing.\n */\nfunction element(node, state) {\n startTag(node, state);\n all(node.children, state);\n endTag(node, state);\n}\n\n/**\n * Transform a text.\n *\n * @param {Text} node\n * hast text node.\n * @param {State} state\n * Info passed around about the current state.\n * @returns {undefined}\n * Nothing.\n */\nfunction text(node, state) {\n /** @type {CharacterToken} */\n const token = {\n type: Token.TokenType.CHARACTER,\n chars: node.value,\n location: createParse5Location(node)\n };\n resetTokenizer(state, pointStart(node));\n // @ts-expect-error: private.\n // type-coverage:ignore-next-line\n state.parser.currentToken = token;\n // @ts-expect-error: private.\n // type-coverage:ignore-next-line\n state.parser._processToken(state.parser.currentToken);\n}\n\n/**\n * Transform a doctype.\n *\n * @param {Doctype} node\n * hast doctype node.\n * @param {State} state\n * Info passed around about the current state.\n * @returns {undefined}\n * Nothing.\n */\nfunction doctype(node, state) {\n /** @type {DoctypeToken} */\n const token = {\n type: Token.TokenType.DOCTYPE,\n name: 'html',\n forceQuirks: false,\n publicId: '',\n systemId: '',\n location: createParse5Location(node)\n };\n resetTokenizer(state, pointStart(node));\n // @ts-expect-error: private.\n // type-coverage:ignore-next-line\n state.parser.currentToken = token;\n // @ts-expect-error: private.\n // type-coverage:ignore-next-line\n state.parser._processToken(state.parser.currentToken);\n}\n\n/**\n * Transform a stitch.\n *\n * @param {Nodes} node\n * unknown node.\n * @param {State} state\n * Info passed around about the current state.\n * @returns {undefined}\n * Nothing.\n */\nfunction stitch(node, state) {\n // Mark that there are stitches, so we need to walk the tree and revert them.\n state.stitches = true;\n\n /** @type {Nodes} */\n const clone = cloneWithoutChildren(node);\n\n // Recurse, because to somewhat handle `[]` (where `[]` denotes the\n // passed through node).\n if ('children' in node && 'children' in clone) {\n // Root in root out.\n const fakeRoot = /** @type {Root} */\n raw({\n type: 'root',\n children: node.children\n }, state.options);\n clone.children = fakeRoot.children;\n }\n\n // Hack: `value` is supposed to be a string, but as none of the tools\n // (`parse5` or `hast-util-from-parse5`) looks at it, we can pass nodes\n // through.\n comment({\n type: 'comment',\n value: {\n stitch: clone\n }\n }, state);\n}\n\n/**\n * Transform a comment (or stitch).\n *\n * @param {Comment | Stitch} node\n * hast comment node.\n * @param {State} state\n * Info passed around about the current state.\n * @returns {undefined}\n * Nothing.\n */\nfunction comment(node, state) {\n /** @type {string} */\n // @ts-expect-error: we pass stitches through.\n const data = node.value;\n\n /** @type {CommentToken} */\n const token = {\n type: Token.TokenType.COMMENT,\n data,\n location: createParse5Location(node)\n };\n resetTokenizer(state, pointStart(node));\n // @ts-expect-error: private.\n // type-coverage:ignore-next-line\n state.parser.currentToken = token;\n // @ts-expect-error: private.\n // type-coverage:ignore-next-line\n state.parser._processToken(state.parser.currentToken);\n}\n\n/**\n * Transform a raw node.\n *\n * @param {Raw} node\n * hast raw node.\n * @param {State} state\n * Info passed around about the current state.\n * @returns {undefined}\n * Nothing.\n */\nfunction handleRaw(node, state) {\n // Reset preprocessor:\n // See: .\n state.parser.tokenizer.preprocessor.html = '';\n // @ts-expect-error: private.\n // type-coverage:ignore-next-line\n state.parser.tokenizer.preprocessor.pos = -1;\n // @ts-expect-error: private.\n // type-coverage:ignore-next-line\n state.parser.tokenizer.preprocessor.lastGapPos = -2;\n // @ts-expect-error: private.\n // type-coverage:ignore-next-line\n state.parser.tokenizer.preprocessor.gapStack = [];\n // @ts-expect-error: private.\n // type-coverage:ignore-next-line\n state.parser.tokenizer.preprocessor.skipNextNewLine = false;\n // @ts-expect-error: private.\n // type-coverage:ignore-next-line\n state.parser.tokenizer.preprocessor.lastChunkWritten = false;\n state.parser.tokenizer.preprocessor.endOfChunkHit = false;\n // @ts-expect-error: private.\n // type-coverage:ignore-next-line\n state.parser.tokenizer.preprocessor.isEol = false;\n\n // Now pass `node.value`.\n setPoint(state, pointStart(node));\n state.parser.tokenizer.write(node.value, false);\n // @ts-expect-error: private.\n // type-coverage:ignore-next-line\n state.parser.tokenizer._runParsingLoop();\n\n // Character references hang, so if we ended there, we need to flush\n // those too.\n // We reset the preprocessor as if the document ends here.\n // Then one single call to the relevant state does the trick, parse5\n // consumes the whole token.\n\n // Note: `State` is not exposed by `parse5`, so these numbers are fragile.\n // See: \n // Note: a change to `parse5`, which breaks this, was merged but not released.\n // Investigate when it is.\n if (state.parser.tokenizer.state === 72 /* NAMED_CHARACTER_REFERENCE */ || state.parser.tokenizer.state === 78 /* NUMERIC_CHARACTER_REFERENCE_END */) {\n // @ts-expect-error: private.\n // type-coverage:ignore-next-line\n state.parser.tokenizer.preprocessor.lastChunkWritten = true;\n /** @type {number} */\n // @ts-expect-error: private.\n // type-coverage:ignore-next-line\n const cp = state.parser.tokenizer._consume();\n // @ts-expect-error: private.\n // type-coverage:ignore-next-line\n state.parser.tokenizer._callState(cp);\n }\n}\n\n/**\n * Crash on an unknown node.\n *\n * @param {unknown} node_\n * unknown node.\n * @param {State} state\n * Info passed around about the current state.\n * @returns {undefined}\n * Never.\n */\nfunction unknown(node_, state) {\n const node = /** @type {Nodes} */node_;\n if (state.options.passThrough && state.options.passThrough.includes(node.type)) {\n stitch(node, state);\n } else {\n let extra = '';\n if (knownMdxNames.has(node.type)) {\n extra = \". It looks like you are using MDX nodes with `hast-util-raw` (or `rehype-raw`). If you use this because you are using remark or rehype plugins that inject `'html'` nodes, then please raise an issue with that plugin, as its a bad and slow idea. If you use this because you are using markdown syntax, then you have to configure this utility (or plugin) to pass through these nodes (see `passThrough` in docs), but you can also migrate to use the MDX syntax\";\n }\n throw new Error('Cannot compile `' + node.type + '` node' + extra);\n }\n}\n\n/**\n * Reset the tokenizer of a parser.\n *\n * @param {State} state\n * Info passed around about the current state.\n * @param {Point | undefined} point\n * Point.\n * @returns {undefined}\n * Nothing.\n */\nfunction resetTokenizer(state, point) {\n setPoint(state, point);\n\n // Process final characters if they’re still there after hibernating.\n /** @type {CharacterToken} */\n // @ts-expect-error: private.\n // type-coverage:ignore-next-line\n const token = state.parser.tokenizer.currentCharacterToken;\n if (token && token.location) {\n token.location.endLine = state.parser.tokenizer.preprocessor.line;\n token.location.endCol = state.parser.tokenizer.preprocessor.col + 1;\n token.location.endOffset = state.parser.tokenizer.preprocessor.offset + 1;\n // @ts-expect-error: private.\n // type-coverage:ignore-next-line\n state.parser.currentToken = token;\n // @ts-expect-error: private.\n // type-coverage:ignore-next-line\n state.parser._processToken(state.parser.currentToken);\n }\n\n // Reset tokenizer:\n // See: .\n // Especially putting it back in the `data` state is useful: some elements,\n // like textareas and iframes, change the state.\n // See GH-7.\n // But also if broken HTML is in `raw`, and then a correct element is given.\n // See GH-11.\n // @ts-expect-error: private.\n // type-coverage:ignore-next-line\n state.parser.tokenizer.paused = false;\n // @ts-expect-error: private.\n // type-coverage:ignore-next-line\n state.parser.tokenizer.inLoop = false;\n\n // Note: don’t reset `state`, `inForeignNode`, or `lastStartTagName`, we\n // manually update those when needed.\n state.parser.tokenizer.active = false;\n // @ts-expect-error: private.\n // type-coverage:ignore-next-line\n state.parser.tokenizer.returnState = TokenizerMode.DATA;\n // @ts-expect-error: private.\n // type-coverage:ignore-next-line\n state.parser.tokenizer.charRefCode = -1;\n // @ts-expect-error: private.\n // type-coverage:ignore-next-line\n state.parser.tokenizer.consumedAfterSnapshot = -1;\n // @ts-expect-error: private.\n // type-coverage:ignore-next-line\n state.parser.tokenizer.currentLocation = null;\n // @ts-expect-error: private.\n // type-coverage:ignore-next-line\n state.parser.tokenizer.currentCharacterToken = null;\n // @ts-expect-error: private.\n // type-coverage:ignore-next-line\n state.parser.tokenizer.currentToken = null;\n // @ts-expect-error: private.\n // type-coverage:ignore-next-line\n state.parser.tokenizer.currentAttr = {\n name: '',\n value: ''\n };\n}\n\n/**\n * Set current location.\n *\n * @param {State} state\n * Info passed around about the current state.\n * @param {Point | undefined} point\n * Point.\n * @returns {undefined}\n * Nothing.\n */\nfunction setPoint(state, point) {\n if (point && point.offset !== undefined) {\n /** @type {Location} */\n const location = {\n startLine: point.line,\n startCol: point.column,\n startOffset: point.offset,\n endLine: -1,\n endCol: -1,\n endOffset: -1\n };\n\n // @ts-expect-error: private.\n // type-coverage:ignore-next-line\n state.parser.tokenizer.preprocessor.lineStartPos = -point.column + 1; // Looks weird, but ensures we get correct positional info.\n state.parser.tokenizer.preprocessor.droppedBufferSize = point.offset;\n state.parser.tokenizer.preprocessor.line = point.line;\n // @ts-expect-error: private.\n // type-coverage:ignore-next-line\n state.parser.tokenizer.currentLocation = location;\n }\n}\n\n/**\n * Emit a start tag.\n *\n * @param {Element} node\n * Element.\n * @param {State} state\n * Info passed around about the current state.\n * @returns {undefined}\n * Nothing.\n */\nfunction startTag(node, state) {\n // Ignore tags if we’re in plain text.\n if (state.parser.tokenizer.state === TokenizerMode.PLAINTEXT) return;\n resetTokenizer(state, pointStart(node));\n const current = state.parser.openElements.current;\n let ns = 'namespaceURI' in current ? current.namespaceURI : webNamespaces.html;\n if (ns === webNamespaces.html && node.tagName === 'svg') {\n ns = webNamespaces.svg;\n }\n const result = toParse5(\n // Shallow clone to not delve into `children`: we only need the attributes.\n {\n ...node,\n children: []\n }, {\n space: ns === webNamespaces.svg ? 'svg' : 'html'\n });\n // Always element.\n /* c8 ignore next */\n const attrs = 'attrs' in result ? result.attrs : [];\n\n /** @type {TagToken} */\n const tag = {\n type: Token.TokenType.START_TAG,\n tagName: node.tagName,\n tagID: html.getTagID(node.tagName),\n // We always send start and end tags.\n selfClosing: false,\n ackSelfClosing: false,\n attrs,\n location: createParse5Location(node)\n };\n\n // The HTML parsing algorithm works by doing half of the state management in\n // the tokenizer and half in the parser.\n // We can’t use the tokenizer here, as we don’t have strings.\n // So we act *as if* the tokenizer emits tokens:\n\n // @ts-expect-error: private.\n // type-coverage:ignore-next-line\n state.parser.currentToken = tag;\n // @ts-expect-error: private.\n // type-coverage:ignore-next-line\n state.parser._processToken(state.parser.currentToken);\n\n // …but then we still need a bunch of work that the tokenizer would normally\n // do, such as:\n\n // Set a tag name, similar to how the tokenizer would do it.\n state.parser.tokenizer.lastStartTagName = node.tagName;\n\n // `inForeignNode` is correctly set by the parser.\n}\n\n/**\n * Emit an end tag.\n *\n * @param {Element} node\n * Element.\n * @param {State} state\n * Info passed around about the current state.\n * @returns {undefined}\n * Nothing.\n */\nfunction endTag(node, state) {\n // Do not emit closing tags for HTML void elements.\n if (!state.parser.tokenizer.inForeignNode && htmlVoidElements.includes(node.tagName)) {\n return;\n }\n\n // Ignore tags if we’re in plain text.\n if (state.parser.tokenizer.state === TokenizerMode.PLAINTEXT) return;\n resetTokenizer(state, pointEnd(node));\n\n /** @type {TagToken} */\n const tag = {\n type: Token.TokenType.END_TAG,\n tagName: node.tagName,\n tagID: html.getTagID(node.tagName),\n selfClosing: false,\n ackSelfClosing: false,\n attrs: [],\n location: createParse5Location(node)\n };\n\n // The HTML parsing algorithm works by doing half of the state management in\n // the tokenizer and half in the parser.\n // We can’t use the tokenizer here, as we don’t have strings.\n // So we act *as if* the tokenizer emits tokens:\n\n // @ts-expect-error: private.\n // type-coverage:ignore-next-line\n state.parser.currentToken = tag;\n // @ts-expect-error: private.\n // type-coverage:ignore-next-line\n state.parser._processToken(state.parser.currentToken);\n\n // …but then we still need a bunch of work that the tokenizer would normally\n // do, such as:\n\n // Switch back to the data state after alternative states that don’t accept\n // tags:\n if (\n // Current element is closed.\n tag.tagName === state.parser.tokenizer.lastStartTagName && (\n // `