UNPKG

gatsby

Version:
117 lines (97 loc) 3.5 kB
"use strict"; exports.__esModule = true; exports.diffNodes = diffNodes; exports.filterHeadProps = filterHeadProps; exports.headExportValidator = headExportValidator; exports.isEqualNode = isEqualNode; exports.warnForInvalidTags = warnForInvalidTags; var _constants = require("./constants"); /** * Filter the props coming from a page down to just the ones that are relevant for head. * This e.g. filters out properties that are undefined during SSR. */ function filterHeadProps(input) { return { location: { pathname: input.location.pathname }, params: input.params, data: input.data || {}, pageContext: input.pageContext }; } /** * Throw error if Head export is not a valid */ function headExportValidator(head) { if (typeof head !== `function`) throw new Error(`Expected "Head" export to be a function got "${typeof head}".`); } /** * Warn once for same messsage */ let warnOnce = _ => {}; if (process.env.NODE_ENV !== `production`) { const warnings = new Set(); warnOnce = msg => { if (!warnings.has(msg)) { console.warn(msg); } warnings.add(msg); }; } /** * Warn for invalid tags in head. * @param {string} tagName */ function warnForInvalidTags(tagName) { if (process.env.NODE_ENV !== `production`) { const warning = `<${tagName}> is not a valid head element. Please use one of the following: ${_constants.VALID_NODE_NAMES.join(`, `)}`; warnOnce(warning); } } /** * When a `nonce` is present on an element, browsers such as Chrome and Firefox strip it out of the * actual HTML attributes for security reasons *when the element is added to the document*. Thus, * given two equivalent elements that have nonces, `Element,isEqualNode()` will return false if one * of those elements gets added to the document. Although the `element.nonce` property will be the * same for both elements, the one that was added to the document will return an empty string for * its nonce HTML attribute value. * * This custom `isEqualNode()` function therefore removes the nonce value from the `newTag` before * comparing it to `oldTag`, restoring it afterwards. * * For more information, see: * https://bugs.chromium.org/p/chromium/issues/detail?id=1211471#c12 */ function isEqualNode(oldTag, newTag) { if (oldTag instanceof HTMLElement && newTag instanceof HTMLElement) { const nonce = newTag.getAttribute(`nonce`); // Only strip the nonce if `oldTag` has had it stripped. An element's nonce attribute will not // be stripped if there is no content security policy response header that includes a nonce. if (nonce && !oldTag.getAttribute(`nonce`)) { const cloneTag = newTag.cloneNode(true); cloneTag.setAttribute(`nonce`, ``); cloneTag.nonce = nonce; return nonce === oldTag.nonce && oldTag.isEqualNode(cloneTag); } } return oldTag.isEqualNode(newTag); } function diffNodes({ oldNodes, newNodes, onStale, onNew }) { for (const existingHeadElement of oldNodes) { const indexInNewNodes = newNodes.findIndex(e => isEqualNode(e, existingHeadElement)); if (indexInNewNodes === -1) { onStale(existingHeadElement); } else { // this node is re-created as-is, so we keep old node, and remove it from list of new nodes (as we handled it already here) newNodes.splice(indexInNewNodes, 1); } } // remaing new nodes didn't have matching old node, so need to be added for (const newNode of newNodes) { onNew(newNode); } }