{"version":3,"sources":["../../src/client/head-manager.ts"],"sourcesContent":["import { setAttributesFromProps } from './set-attributes-from-props'\n\nimport type { JSX } from 'react'\n\nfunction reactElementToDOM({ type, props }: JSX.Element): HTMLElement {\n const el: HTMLElement = document.createElement(type)\n setAttributesFromProps(el, props)\n\n const { children, dangerouslySetInnerHTML } = props\n if (dangerouslySetInnerHTML) {\n el.innerHTML = dangerouslySetInnerHTML.__html || ''\n } else if (children) {\n el.textContent =\n typeof children === 'string'\n ? children\n : Array.isArray(children)\n ? children.join('')\n : ''\n }\n return el\n}\n\n/**\n * When a `nonce` is present on an element, browsers such as Chrome and Firefox strip it out of the\n * actual HTML attributes for security reasons *when the element is added to the document*. Thus,\n * given two equivalent elements that have nonces, `Element,isEqualNode()` will return false if one\n * of those elements gets added to the document. Although the `element.nonce` property will be the\n * same for both elements, the one that was added to the document will return an empty string for\n * its nonce HTML attribute value.\n *\n * This custom `isEqualNode()` function therefore removes the nonce value from the `newTag` before\n * comparing it to `oldTag`, restoring it afterwards.\n *\n * For more information, see:\n * https://bugs.chromium.org/p/chromium/issues/detail?id=1211471#c12\n */\nexport function isEqualNode(oldTag: Element, newTag: Element) {\n if (oldTag instanceof HTMLElement && newTag instanceof HTMLElement) {\n const nonce = newTag.getAttribute('nonce')\n // Only strip the nonce if `oldTag` has had it stripped. An element's nonce attribute will not\n // be stripped if there is no content security policy response header that includes a nonce.\n if (nonce && !oldTag.getAttribute('nonce')) {\n const cloneTag = newTag.cloneNode(true) as typeof newTag\n cloneTag.setAttribute('nonce', '')\n cloneTag.nonce = nonce\n return nonce === oldTag.nonce && oldTag.isEqualNode(cloneTag)\n }\n }\n\n return oldTag.isEqualNode(newTag)\n}\n\nfunction updateElements(type: string, components: JSX.Element[]) {\n const headEl = document.querySelector('head')\n if (!headEl) return\n\n const oldTags = new Set(headEl.querySelectorAll(`${type}[data-next-head]`))\n\n if (type === 'meta') {\n const metaCharset = headEl.querySelector('meta[charset]')\n if (metaCharset !== null) {\n oldTags.add(metaCharset)\n }\n }\n\n const newTags: Element[] = []\n for (let i = 0; i < components.length; i++) {\n const component = components[i]\n const newTag = reactElementToDOM(component)\n newTag.setAttribute('data-next-head', '')\n\n let isNew = true\n for (const oldTag of oldTags) {\n if (isEqualNode(oldTag, newTag)) {\n oldTags.delete(oldTag)\n isNew = false\n break\n }\n }\n\n if (isNew) {\n newTags.push(newTag)\n }\n }\n\n for (const oldTag of oldTags) {\n oldTag.parentNode?.removeChild(oldTag)\n }\n\n for (const newTag of newTags) {\n // meta[charset] must be first element so special case\n if (\n newTag.tagName.toLowerCase() === 'meta' &&\n newTag.getAttribute('charset') !== null\n ) {\n headEl.prepend(newTag)\n }\n headEl.appendChild(newTag)\n }\n}\n\nexport default function initHeadManager(): {\n mountedInstances: Set\n updateHead: (head: JSX.Element[]) => void\n} {\n return {\n mountedInstances: new Set(),\n updateHead: (head: JSX.Element[]) => {\n const tags: Record = {}\n\n head.forEach((h) => {\n if (\n // If the font tag is loaded only on client navigation\n // it won't be inlined. In this case revert to the original behavior\n h.type === 'link' &&\n h.props['data-optimized-fonts']\n ) {\n if (\n document.querySelector(`style[data-href=\"${h.props['data-href']}\"]`)\n ) {\n return\n } else {\n h.props.href = h.props['data-href']\n h.props['data-href'] = undefined\n }\n }\n\n const components = tags[h.type] || []\n components.push(h)\n tags[h.type] = components\n })\n\n const titleComponent = tags.title ? tags.title[0] : null\n let title = ''\n if (titleComponent) {\n const { children } = titleComponent.props\n title =\n typeof children === 'string'\n ? children\n : Array.isArray(children)\n ? children.join('')\n : ''\n }\n if (title !== document.title) document.title = title\n ;['meta', 'base', 'link', 'style', 'script'].forEach((type) => {\n updateElements(type, tags[type] || [])\n })\n },\n }\n}\n"],"names":["initHeadManager","isEqualNode","reactElementToDOM","type","props","el","document","createElement","setAttributesFromProps","children","dangerouslySetInnerHTML","innerHTML","__html","textContent","Array","isArray","join","oldTag","newTag","HTMLElement","nonce","getAttribute","cloneTag","cloneNode","setAttribute","updateElements","components","headEl","querySelector","oldTags","Set","querySelectorAll","metaCharset","add","newTags","i","length","component","isNew","delete","push","parentNode","removeChild","tagName","toLowerCase","prepend","appendChild","mountedInstances","updateHead","head","tags","forEach","h","href","undefined","titleComponent","title"],"mappings":"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","ignoreList":[0]}