head-manager.js 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. exports.default = initHeadManager;
  6. exports.isEqualNode = isEqualNode;
  7. exports.DOMAttributeNames = void 0;
  8. function initHeadManager() {
  9. return {
  10. mountedInstances: new Set(),
  11. updateHead: (head)=>{
  12. const tags = {};
  13. head.forEach((h)=>{
  14. if (// If the font tag is loaded only on client navigation
  15. // it won't be inlined. In this case revert to the original behavior
  16. h.type === 'link' && h.props['data-optimized-fonts']) {
  17. if (document.querySelector(`style[data-href="${h.props['data-href']}"]`)) {
  18. return;
  19. } else {
  20. h.props.href = h.props['data-href'];
  21. h.props['data-href'] = undefined;
  22. }
  23. }
  24. const components = tags[h.type] || [];
  25. components.push(h);
  26. tags[h.type] = components;
  27. });
  28. const titleComponent = tags.title ? tags.title[0] : null;
  29. let title = '';
  30. if (titleComponent) {
  31. const { children } = titleComponent.props;
  32. title = typeof children === 'string' ? children : Array.isArray(children) ? children.join('') : '';
  33. }
  34. if (title !== document.title) document.title = title;
  35. [
  36. 'meta',
  37. 'base',
  38. 'link',
  39. 'style',
  40. 'script'
  41. ].forEach((type)=>{
  42. updateElements(type, tags[type] || []);
  43. });
  44. }
  45. };
  46. }
  47. const DOMAttributeNames = {
  48. acceptCharset: 'accept-charset',
  49. className: 'class',
  50. htmlFor: 'for',
  51. httpEquiv: 'http-equiv',
  52. noModule: 'noModule'
  53. };
  54. exports.DOMAttributeNames = DOMAttributeNames;
  55. function reactElementToDOM({ type , props }) {
  56. const el = document.createElement(type);
  57. for(const p in props){
  58. if (!props.hasOwnProperty(p)) continue;
  59. if (p === 'children' || p === 'dangerouslySetInnerHTML') continue;
  60. // we don't render undefined props to the DOM
  61. if (props[p] === undefined) continue;
  62. const attr = DOMAttributeNames[p] || p.toLowerCase();
  63. if (type === 'script' && (attr === 'async' || attr === 'defer' || attr === 'noModule')) {
  64. el[attr] = !!props[p];
  65. } else {
  66. el.setAttribute(attr, props[p]);
  67. }
  68. }
  69. const { children , dangerouslySetInnerHTML } = props;
  70. if (dangerouslySetInnerHTML) {
  71. el.innerHTML = dangerouslySetInnerHTML.__html || '';
  72. } else if (children) {
  73. el.textContent = typeof children === 'string' ? children : Array.isArray(children) ? children.join('') : '';
  74. }
  75. return el;
  76. }
  77. function isEqualNode(oldTag, newTag) {
  78. if (oldTag instanceof HTMLElement && newTag instanceof HTMLElement) {
  79. const nonce = newTag.getAttribute('nonce');
  80. // Only strip the nonce if `oldTag` has had it stripped. An element's nonce attribute will not
  81. // be stripped if there is no content security policy response header that includes a nonce.
  82. if (nonce && !oldTag.getAttribute('nonce')) {
  83. const cloneTag = newTag.cloneNode(true);
  84. cloneTag.setAttribute('nonce', '');
  85. cloneTag.nonce = nonce;
  86. return nonce === oldTag.nonce && oldTag.isEqualNode(cloneTag);
  87. }
  88. }
  89. return oldTag.isEqualNode(newTag);
  90. }
  91. function updateElements(type, components) {
  92. const headEl = document.getElementsByTagName('head')[0];
  93. const headCountEl = headEl.querySelector('meta[name=next-head-count]');
  94. if (process.env.NODE_ENV !== 'production') {
  95. if (!headCountEl) {
  96. console.error('Warning: next-head-count is missing. https://nextjs.org/docs/messages/next-head-count-missing');
  97. return;
  98. }
  99. }
  100. const headCount = Number(headCountEl.content);
  101. const oldTags = [];
  102. for(let i = 0, j = headCountEl.previousElementSibling; i < headCount; i++, j = (j == null ? void 0 : j.previousElementSibling) || null){
  103. var ref;
  104. if ((j == null ? void 0 : (ref = j.tagName) == null ? void 0 : ref.toLowerCase()) === type) {
  105. oldTags.push(j);
  106. }
  107. }
  108. const newTags = components.map(reactElementToDOM).filter((newTag)=>{
  109. for(let k = 0, len = oldTags.length; k < len; k++){
  110. const oldTag = oldTags[k];
  111. if (isEqualNode(oldTag, newTag)) {
  112. oldTags.splice(k, 1);
  113. return false;
  114. }
  115. }
  116. return true;
  117. });
  118. oldTags.forEach((t)=>{
  119. var ref;
  120. return (ref = t.parentNode) == null ? void 0 : ref.removeChild(t);
  121. });
  122. newTags.forEach((t)=>headEl.insertBefore(t, headCountEl));
  123. headCountEl.content = (headCount - oldTags.length + newTags.length).toString();
  124. }
  125. if ((typeof exports.default === 'function' || (typeof exports.default === 'object' && exports.default !== null)) && typeof exports.default.__esModule === 'undefined') {
  126. Object.defineProperty(exports.default, '__esModule', { value: true });
  127. Object.assign(exports.default, exports);
  128. module.exports = exports.default;
  129. }
  130. //# sourceMappingURL=head-manager.js.map