From 10957036e674de630f5b4114d03ce6f5015d5b36 Mon Sep 17 00:00:00 2001 From: Vikalp Jain Date: Fri, 12 Nov 2021 18:37:14 +0530 Subject: [PATCH 1/5] fix: change append to prepend child --- src/HelmetUtils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/HelmetUtils.js b/src/HelmetUtils.js index 5d072220..434a8f04 100644 --- a/src/HelmetUtils.js +++ b/src/HelmetUtils.js @@ -474,7 +474,7 @@ const updateTags = (type, tags) => { } oldTags.forEach(tag => tag.parentNode.removeChild(tag)); - newTags.forEach(tag => headElement.appendChild(tag)); + newTags.forEach(tag => headElement.prepend(tag)); return { oldTags, From 412fdefabf4092ab9c3103a747e3be733945a572 Mon Sep 17 00:00:00 2001 From: Vikalp Jain Date: Fri, 12 Nov 2021 20:02:50 +0530 Subject: [PATCH 2/5] chore: add post install --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index c20d77a4..e1fdd5d9 100644 --- a/package.json +++ b/package.json @@ -93,6 +93,7 @@ "pretest": "npm run clean && npm run lint", "commit": "git-cz", "build": "rollup -c", + "postinstall": "npm run build", "prepublish": "npm run build" }, "config": { From 978429d34ab624e8f30fc4e71bff4eba738572ff Mon Sep 17 00:00:00 2001 From: Vikalp Jain Date: Fri, 12 Nov 2021 20:17:02 +0530 Subject: [PATCH 3/5] fix: change main file --- package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/package.json b/package.json index e1fdd5d9..3e2e3da2 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "description": "A document head manager for React", "version": "6.1.0", "main": "./lib/Helmet.js", - "module": "./es/Helmet.js", + "module": "./src/Helmet.js", "author": "NFL ", "contributors": [ "Chris Welch ", @@ -93,7 +93,6 @@ "pretest": "npm run clean && npm run lint", "commit": "git-cz", "build": "rollup -c", - "postinstall": "npm run build", "prepublish": "npm run build" }, "config": { From b4d25656d95ceb4312fccd4f7e08544635c2f8c4 Mon Sep 17 00:00:00 2001 From: Vikalp Jain Date: Fri, 12 Nov 2021 20:50:26 +0530 Subject: [PATCH 4/5] choer: added build --- .gitignore | 2 - es/Helmet.js | 913 +++++++++++++++++++++++++++++++++++++++++++++++++ lib/Helmet.js | 919 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1832 insertions(+), 2 deletions(-) create mode 100644 es/Helmet.js create mode 100644 lib/Helmet.js diff --git a/.gitignore b/.gitignore index 36e1057e..7361e321 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,5 @@ .eslintcache .idea coverage -es -lib node_modules npm-debug.log diff --git a/es/Helmet.js b/es/Helmet.js new file mode 100644 index 00000000..888b9ca7 --- /dev/null +++ b/es/Helmet.js @@ -0,0 +1,913 @@ +import PropTypes from 'prop-types'; +import withSideEffect from 'react-side-effect'; +import isEqual from 'react-fast-compare'; +import React from 'react'; +import objectAssign from 'object-assign'; + +var ATTRIBUTE_NAMES = { + BODY: "bodyAttributes", + HTML: "htmlAttributes", + TITLE: "titleAttributes" +}; + +var TAG_NAMES = { + BASE: "base", + BODY: "body", + HEAD: "head", + HTML: "html", + LINK: "link", + META: "meta", + NOSCRIPT: "noscript", + SCRIPT: "script", + STYLE: "style", + TITLE: "title" +}; + +var VALID_TAG_NAMES = Object.values(TAG_NAMES); + +var TAG_PROPERTIES = { + CHARSET: "charset", + CSS_TEXT: "cssText", + HREF: "href", + HTTPEQUIV: "http-equiv", + INNER_HTML: "innerHTML", + ITEM_PROP: "itemprop", + NAME: "name", + PROPERTY: "property", + REL: "rel", + SRC: "src", + TARGET: "target" +}; + +var REACT_TAG_MAP = { + accesskey: "accessKey", + charset: "charSet", + class: "className", + contenteditable: "contentEditable", + contextmenu: "contextMenu", + "http-equiv": "httpEquiv", + itemprop: "itemProp", + tabindex: "tabIndex" +}; + +var HELMET_PROPS = { + DEFAULT_TITLE: "defaultTitle", + DEFER: "defer", + ENCODE_SPECIAL_CHARACTERS: "encodeSpecialCharacters", + ON_CHANGE_CLIENT_STATE: "onChangeClientState", + TITLE_TEMPLATE: "titleTemplate" +}; + +var HTML_TAG_MAP = Object.keys(REACT_TAG_MAP).reduce(function (obj, key) { + obj[REACT_TAG_MAP[key]] = key; + return obj; +}, {}); + +var SELF_CLOSING_TAGS = [TAG_NAMES.NOSCRIPT, TAG_NAMES.SCRIPT, TAG_NAMES.STYLE]; + +var HELMET_ATTRIBUTE = "data-react-helmet"; + +var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { + return typeof obj; +} : function (obj) { + return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; +}; + +var classCallCheck = function (instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError("Cannot call a class as a function"); + } +}; + +var createClass = function () { + function defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i]; + descriptor.enumerable = descriptor.enumerable || false; + descriptor.configurable = true; + if ("value" in descriptor) descriptor.writable = true; + Object.defineProperty(target, descriptor.key, descriptor); + } + } + + return function (Constructor, protoProps, staticProps) { + if (protoProps) defineProperties(Constructor.prototype, protoProps); + if (staticProps) defineProperties(Constructor, staticProps); + return Constructor; + }; +}(); + +var _extends = Object.assign || function (target) { + for (var i = 1; i < arguments.length; i++) { + var source = arguments[i]; + + for (var key in source) { + if (Object.prototype.hasOwnProperty.call(source, key)) { + target[key] = source[key]; + } + } + } + + return target; +}; + +var inherits = function (subClass, superClass) { + if (typeof superClass !== "function" && superClass !== null) { + throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); + } + + subClass.prototype = Object.create(superClass && superClass.prototype, { + constructor: { + value: subClass, + enumerable: false, + writable: true, + configurable: true + } + }); + if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; +}; + +var objectWithoutProperties = function (obj, keys) { + var target = {}; + + for (var i in obj) { + if (keys.indexOf(i) >= 0) continue; + if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; + target[i] = obj[i]; + } + + return target; +}; + +var possibleConstructorReturn = function (self, call) { + if (!self) { + throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); + } + + return call && (typeof call === "object" || typeof call === "function") ? call : self; +}; + +var encodeSpecialCharacters = function encodeSpecialCharacters(str) { + var encode = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; + + if (encode === false) { + return String(str); + } + + return String(str).replace(/&/g, "&").replace(//g, ">").replace(/"/g, """).replace(/'/g, "'"); +}; + +var getTitleFromPropsList = function getTitleFromPropsList(propsList) { + var innermostTitle = getInnermostProperty(propsList, TAG_NAMES.TITLE); + var innermostTemplate = getInnermostProperty(propsList, HELMET_PROPS.TITLE_TEMPLATE); + + if (innermostTemplate && innermostTitle) { + // use function arg to avoid need to escape $ characters + return innermostTemplate.replace(/%s/g, function () { + return Array.isArray(innermostTitle) ? innermostTitle.join("") : innermostTitle; + }); + } + + var innermostDefaultTitle = getInnermostProperty(propsList, HELMET_PROPS.DEFAULT_TITLE); + + return innermostTitle || innermostDefaultTitle || undefined; +}; + +var getOnChangeClientState = function getOnChangeClientState(propsList) { + return getInnermostProperty(propsList, HELMET_PROPS.ON_CHANGE_CLIENT_STATE) || function () {}; +}; + +var getAttributesFromPropsList = function getAttributesFromPropsList(tagType, propsList) { + return propsList.filter(function (props) { + return typeof props[tagType] !== "undefined"; + }).map(function (props) { + return props[tagType]; + }).reduce(function (tagAttrs, current) { + return _extends({}, tagAttrs, current); + }, {}); +}; + +var getBaseTagFromPropsList = function getBaseTagFromPropsList(primaryAttributes, propsList) { + return propsList.filter(function (props) { + return typeof props[TAG_NAMES.BASE] !== "undefined"; + }).map(function (props) { + return props[TAG_NAMES.BASE]; + }).reverse().reduce(function (innermostBaseTag, tag) { + if (!innermostBaseTag.length) { + var keys = Object.keys(tag); + + for (var i = 0; i < keys.length; i++) { + var attributeKey = keys[i]; + var lowerCaseAttributeKey = attributeKey.toLowerCase(); + + if (primaryAttributes.indexOf(lowerCaseAttributeKey) !== -1 && tag[lowerCaseAttributeKey]) { + return innermostBaseTag.concat(tag); + } + } + } + + return innermostBaseTag; + }, []); +}; + +var getTagsFromPropsList = function getTagsFromPropsList(tagName, primaryAttributes, propsList) { + // Calculate list of tags, giving priority innermost component (end of the propslist) + var approvedSeenTags = {}; + + return propsList.filter(function (props) { + if (Array.isArray(props[tagName])) { + return true; + } + if (typeof props[tagName] !== "undefined") { + warn("Helmet: " + tagName + " should be of type \"Array\". Instead found type \"" + _typeof(props[tagName]) + "\""); + } + return false; + }).map(function (props) { + return props[tagName]; + }).reverse().reduce(function (approvedTags, instanceTags) { + var instanceSeenTags = {}; + + instanceTags.filter(function (tag) { + var primaryAttributeKey = void 0; + var keys = Object.keys(tag); + for (var i = 0; i < keys.length; i++) { + var attributeKey = keys[i]; + var lowerCaseAttributeKey = attributeKey.toLowerCase(); + + // Special rule with link tags, since rel and href are both primary tags, rel takes priority + if (primaryAttributes.indexOf(lowerCaseAttributeKey) !== -1 && !(primaryAttributeKey === TAG_PROPERTIES.REL && tag[primaryAttributeKey].toLowerCase() === "canonical") && !(lowerCaseAttributeKey === TAG_PROPERTIES.REL && tag[lowerCaseAttributeKey].toLowerCase() === "stylesheet")) { + primaryAttributeKey = lowerCaseAttributeKey; + } + // Special case for innerHTML which doesn't work lowercased + if (primaryAttributes.indexOf(attributeKey) !== -1 && (attributeKey === TAG_PROPERTIES.INNER_HTML || attributeKey === TAG_PROPERTIES.CSS_TEXT || attributeKey === TAG_PROPERTIES.ITEM_PROP)) { + primaryAttributeKey = attributeKey; + } + } + + if (!primaryAttributeKey || !tag[primaryAttributeKey]) { + return false; + } + + var value = tag[primaryAttributeKey].toLowerCase(); + + if (!approvedSeenTags[primaryAttributeKey]) { + approvedSeenTags[primaryAttributeKey] = {}; + } + + if (!instanceSeenTags[primaryAttributeKey]) { + instanceSeenTags[primaryAttributeKey] = {}; + } + + if (!approvedSeenTags[primaryAttributeKey][value]) { + instanceSeenTags[primaryAttributeKey][value] = true; + return true; + } + + return false; + }).reverse().forEach(function (tag) { + return approvedTags.push(tag); + }); + + // Update seen tags with tags from this instance + var keys = Object.keys(instanceSeenTags); + for (var i = 0; i < keys.length; i++) { + var attributeKey = keys[i]; + var tagUnion = objectAssign({}, approvedSeenTags[attributeKey], instanceSeenTags[attributeKey]); + + approvedSeenTags[attributeKey] = tagUnion; + } + + return approvedTags; + }, []).reverse(); +}; + +var getInnermostProperty = function getInnermostProperty(propsList, property) { + for (var i = propsList.length - 1; i >= 0; i--) { + var props = propsList[i]; + + if (props.hasOwnProperty(property)) { + return props[property]; + } + } + + return null; +}; + +var reducePropsToState = function reducePropsToState(propsList) { + return { + baseTag: getBaseTagFromPropsList([TAG_PROPERTIES.HREF, TAG_PROPERTIES.TARGET], propsList), + bodyAttributes: getAttributesFromPropsList(ATTRIBUTE_NAMES.BODY, propsList), + defer: getInnermostProperty(propsList, HELMET_PROPS.DEFER), + encode: getInnermostProperty(propsList, HELMET_PROPS.ENCODE_SPECIAL_CHARACTERS), + htmlAttributes: getAttributesFromPropsList(ATTRIBUTE_NAMES.HTML, propsList), + linkTags: getTagsFromPropsList(TAG_NAMES.LINK, [TAG_PROPERTIES.REL, TAG_PROPERTIES.HREF], propsList), + metaTags: getTagsFromPropsList(TAG_NAMES.META, [TAG_PROPERTIES.NAME, TAG_PROPERTIES.CHARSET, TAG_PROPERTIES.HTTPEQUIV, TAG_PROPERTIES.PROPERTY, TAG_PROPERTIES.ITEM_PROP], propsList), + noscriptTags: getTagsFromPropsList(TAG_NAMES.NOSCRIPT, [TAG_PROPERTIES.INNER_HTML], propsList), + onChangeClientState: getOnChangeClientState(propsList), + scriptTags: getTagsFromPropsList(TAG_NAMES.SCRIPT, [TAG_PROPERTIES.SRC, TAG_PROPERTIES.INNER_HTML], propsList), + styleTags: getTagsFromPropsList(TAG_NAMES.STYLE, [TAG_PROPERTIES.CSS_TEXT], propsList), + title: getTitleFromPropsList(propsList), + titleAttributes: getAttributesFromPropsList(ATTRIBUTE_NAMES.TITLE, propsList) + }; +}; + +var rafPolyfill = function () { + var clock = Date.now(); + + return function (callback) { + var currentTime = Date.now(); + + if (currentTime - clock > 16) { + clock = currentTime; + callback(currentTime); + } else { + setTimeout(function () { + rafPolyfill(callback); + }, 0); + } + }; +}(); + +var cafPolyfill = function cafPolyfill(id) { + return clearTimeout(id); +}; + +var requestAnimationFrame = typeof window !== "undefined" ? window.requestAnimationFrame && window.requestAnimationFrame.bind(window) || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || rafPolyfill : global.requestAnimationFrame || rafPolyfill; + +var cancelAnimationFrame = typeof window !== "undefined" ? window.cancelAnimationFrame || window.webkitCancelAnimationFrame || window.mozCancelAnimationFrame || cafPolyfill : global.cancelAnimationFrame || cafPolyfill; + +var warn = function warn(msg) { + return console && typeof console.warn === "function" && console.warn(msg); +}; + +var _helmetCallback = null; + +var handleClientStateChange = function handleClientStateChange(newState) { + if (_helmetCallback) { + cancelAnimationFrame(_helmetCallback); + } + + if (newState.defer) { + _helmetCallback = requestAnimationFrame(function () { + commitTagChanges(newState, function () { + _helmetCallback = null; + }); + }); + } else { + commitTagChanges(newState); + _helmetCallback = null; + } +}; + +var commitTagChanges = function commitTagChanges(newState, cb) { + var baseTag = newState.baseTag, + bodyAttributes = newState.bodyAttributes, + htmlAttributes = newState.htmlAttributes, + linkTags = newState.linkTags, + metaTags = newState.metaTags, + noscriptTags = newState.noscriptTags, + onChangeClientState = newState.onChangeClientState, + scriptTags = newState.scriptTags, + styleTags = newState.styleTags, + title = newState.title, + titleAttributes = newState.titleAttributes; + + updateAttributes(TAG_NAMES.BODY, bodyAttributes); + updateAttributes(TAG_NAMES.HTML, htmlAttributes); + + updateTitle(title, titleAttributes); + + var tagUpdates = { + baseTag: updateTags(TAG_NAMES.BASE, baseTag), + linkTags: updateTags(TAG_NAMES.LINK, linkTags), + metaTags: updateTags(TAG_NAMES.META, metaTags), + noscriptTags: updateTags(TAG_NAMES.NOSCRIPT, noscriptTags), + scriptTags: updateTags(TAG_NAMES.SCRIPT, scriptTags), + styleTags: updateTags(TAG_NAMES.STYLE, styleTags) + }; + + var addedTags = {}; + var removedTags = {}; + + Object.keys(tagUpdates).forEach(function (tagType) { + var _tagUpdates$tagType = tagUpdates[tagType], + newTags = _tagUpdates$tagType.newTags, + oldTags = _tagUpdates$tagType.oldTags; + + + if (newTags.length) { + addedTags[tagType] = newTags; + } + if (oldTags.length) { + removedTags[tagType] = tagUpdates[tagType].oldTags; + } + }); + + cb && cb(); + + onChangeClientState(newState, addedTags, removedTags); +}; + +var flattenArray = function flattenArray(possibleArray) { + return Array.isArray(possibleArray) ? possibleArray.join("") : possibleArray; +}; + +var updateTitle = function updateTitle(title, attributes) { + if (typeof title !== "undefined" && document.title !== title) { + document.title = flattenArray(title); + } + + updateAttributes(TAG_NAMES.TITLE, attributes); +}; + +var updateAttributes = function updateAttributes(tagName, attributes) { + var elementTag = document.getElementsByTagName(tagName)[0]; + + if (!elementTag) { + return; + } + + var helmetAttributeString = elementTag.getAttribute(HELMET_ATTRIBUTE); + var helmetAttributes = helmetAttributeString ? helmetAttributeString.split(",") : []; + var attributesToRemove = [].concat(helmetAttributes); + var attributeKeys = Object.keys(attributes); + + for (var i = 0; i < attributeKeys.length; i++) { + var attribute = attributeKeys[i]; + var value = attributes[attribute] || ""; + + if (elementTag.getAttribute(attribute) !== value) { + elementTag.setAttribute(attribute, value); + } + + if (helmetAttributes.indexOf(attribute) === -1) { + helmetAttributes.push(attribute); + } + + var indexToSave = attributesToRemove.indexOf(attribute); + if (indexToSave !== -1) { + attributesToRemove.splice(indexToSave, 1); + } + } + + for (var _i = attributesToRemove.length - 1; _i >= 0; _i--) { + elementTag.removeAttribute(attributesToRemove[_i]); + } + + if (helmetAttributes.length === attributesToRemove.length) { + elementTag.removeAttribute(HELMET_ATTRIBUTE); + } else if (elementTag.getAttribute(HELMET_ATTRIBUTE) !== attributeKeys.join(",")) { + elementTag.setAttribute(HELMET_ATTRIBUTE, attributeKeys.join(",")); + } +}; + +var updateTags = function updateTags(type, tags) { + var headElement = document.head || document.querySelector(TAG_NAMES.HEAD); + var tagNodes = headElement.querySelectorAll(type + "[" + HELMET_ATTRIBUTE + "]"); + var oldTags = Array.prototype.slice.call(tagNodes); + var newTags = []; + var indexToDelete = void 0; + + if (tags && tags.length) { + tags.forEach(function (tag) { + var newElement = document.createElement(type); + + for (var attribute in tag) { + if (tag.hasOwnProperty(attribute)) { + if (attribute === TAG_PROPERTIES.INNER_HTML) { + newElement.innerHTML = tag.innerHTML; + } else if (attribute === TAG_PROPERTIES.CSS_TEXT) { + if (newElement.styleSheet) { + newElement.styleSheet.cssText = tag.cssText; + } else { + newElement.appendChild(document.createTextNode(tag.cssText)); + } + } else { + var value = typeof tag[attribute] === "undefined" ? "" : tag[attribute]; + newElement.setAttribute(attribute, value); + } + } + } + + newElement.setAttribute(HELMET_ATTRIBUTE, "true"); + + // Remove a duplicate tag from domTagstoRemove, so it isn't cleared. + if (oldTags.some(function (existingTag, index) { + indexToDelete = index; + return newElement.isEqualNode(existingTag); + })) { + oldTags.splice(indexToDelete, 1); + } else { + newTags.push(newElement); + } + }); + } + + oldTags.forEach(function (tag) { + return tag.parentNode.removeChild(tag); + }); + newTags.forEach(function (tag) { + return headElement.prepend(tag); + }); + + return { + oldTags: oldTags, + newTags: newTags + }; +}; + +var generateElementAttributesAsString = function generateElementAttributesAsString(attributes) { + return Object.keys(attributes).reduce(function (str, key) { + var attr = typeof attributes[key] !== "undefined" ? key + "=\"" + attributes[key] + "\"" : "" + key; + return str ? str + " " + attr : attr; + }, ""); +}; + +var generateTitleAsString = function generateTitleAsString(type, title, attributes, encode) { + var attributeString = generateElementAttributesAsString(attributes); + var flattenedTitle = flattenArray(title); + return attributeString ? "<" + type + " " + HELMET_ATTRIBUTE + "=\"true\" " + attributeString + ">" + encodeSpecialCharacters(flattenedTitle, encode) + "" : "<" + type + " " + HELMET_ATTRIBUTE + "=\"true\">" + encodeSpecialCharacters(flattenedTitle, encode) + ""; +}; + +var generateTagsAsString = function generateTagsAsString(type, tags, encode) { + return tags.reduce(function (str, tag) { + var attributeHtml = Object.keys(tag).filter(function (attribute) { + return !(attribute === TAG_PROPERTIES.INNER_HTML || attribute === TAG_PROPERTIES.CSS_TEXT); + }).reduce(function (string, attribute) { + var attr = typeof tag[attribute] === "undefined" ? attribute : attribute + "=\"" + encodeSpecialCharacters(tag[attribute], encode) + "\""; + return string ? string + " " + attr : attr; + }, ""); + + var tagContent = tag.innerHTML || tag.cssText || ""; + + var isSelfClosing = SELF_CLOSING_TAGS.indexOf(type) === -1; + + return str + "<" + type + " " + HELMET_ATTRIBUTE + "=\"true\" " + attributeHtml + (isSelfClosing ? "/>" : ">" + tagContent + ""); + }, ""); +}; + +var convertElementAttributestoReactProps = function convertElementAttributestoReactProps(attributes) { + var initProps = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + + return Object.keys(attributes).reduce(function (obj, key) { + obj[REACT_TAG_MAP[key] || key] = attributes[key]; + return obj; + }, initProps); +}; + +var convertReactPropstoHtmlAttributes = function convertReactPropstoHtmlAttributes(props) { + var initAttributes = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + + return Object.keys(props).reduce(function (obj, key) { + obj[HTML_TAG_MAP[key] || key] = props[key]; + return obj; + }, initAttributes); +}; + +var generateTitleAsReactComponent = function generateTitleAsReactComponent(type, title, attributes) { + var _initProps; + + // assigning into an array to define toString function on it + var initProps = (_initProps = { + key: title + }, _initProps[HELMET_ATTRIBUTE] = true, _initProps); + var props = convertElementAttributestoReactProps(attributes, initProps); + + return [React.createElement(TAG_NAMES.TITLE, props, title)]; +}; + +var generateTagsAsReactComponent = function generateTagsAsReactComponent(type, tags) { + return tags.map(function (tag, i) { + var _mappedTag; + + var mappedTag = (_mappedTag = { + key: i + }, _mappedTag[HELMET_ATTRIBUTE] = true, _mappedTag); + + Object.keys(tag).forEach(function (attribute) { + var mappedAttribute = REACT_TAG_MAP[attribute] || attribute; + + if (mappedAttribute === TAG_PROPERTIES.INNER_HTML || mappedAttribute === TAG_PROPERTIES.CSS_TEXT) { + var content = tag.innerHTML || tag.cssText; + mappedTag.dangerouslySetInnerHTML = { __html: content }; + } else { + mappedTag[mappedAttribute] = tag[attribute]; + } + }); + + return React.createElement(type, mappedTag); + }); +}; + +var getMethodsForTag = function getMethodsForTag(type, tags, encode) { + switch (type) { + case TAG_NAMES.TITLE: + return { + toComponent: function toComponent() { + return generateTitleAsReactComponent(type, tags.title, tags.titleAttributes, encode); + }, + toString: function toString() { + return generateTitleAsString(type, tags.title, tags.titleAttributes, encode); + } + }; + case ATTRIBUTE_NAMES.BODY: + case ATTRIBUTE_NAMES.HTML: + return { + toComponent: function toComponent() { + return convertElementAttributestoReactProps(tags); + }, + toString: function toString() { + return generateElementAttributesAsString(tags); + } + }; + default: + return { + toComponent: function toComponent() { + return generateTagsAsReactComponent(type, tags); + }, + toString: function toString() { + return generateTagsAsString(type, tags, encode); + } + }; + } +}; + +var mapStateOnServer = function mapStateOnServer(_ref) { + var baseTag = _ref.baseTag, + bodyAttributes = _ref.bodyAttributes, + encode = _ref.encode, + htmlAttributes = _ref.htmlAttributes, + linkTags = _ref.linkTags, + metaTags = _ref.metaTags, + noscriptTags = _ref.noscriptTags, + scriptTags = _ref.scriptTags, + styleTags = _ref.styleTags, + _ref$title = _ref.title, + title = _ref$title === undefined ? "" : _ref$title, + titleAttributes = _ref.titleAttributes; + return { + base: getMethodsForTag(TAG_NAMES.BASE, baseTag, encode), + bodyAttributes: getMethodsForTag(ATTRIBUTE_NAMES.BODY, bodyAttributes, encode), + htmlAttributes: getMethodsForTag(ATTRIBUTE_NAMES.HTML, htmlAttributes, encode), + link: getMethodsForTag(TAG_NAMES.LINK, linkTags, encode), + meta: getMethodsForTag(TAG_NAMES.META, metaTags, encode), + noscript: getMethodsForTag(TAG_NAMES.NOSCRIPT, noscriptTags, encode), + script: getMethodsForTag(TAG_NAMES.SCRIPT, scriptTags, encode), + style: getMethodsForTag(TAG_NAMES.STYLE, styleTags, encode), + title: getMethodsForTag(TAG_NAMES.TITLE, { title: title, titleAttributes: titleAttributes }, encode) + }; +}; + +var Helmet = function Helmet(Component) { + var _class, _temp; + + return _temp = _class = function (_React$Component) { + inherits(HelmetWrapper, _React$Component); + + function HelmetWrapper() { + classCallCheck(this, HelmetWrapper); + return possibleConstructorReturn(this, _React$Component.apply(this, arguments)); + } + + HelmetWrapper.prototype.shouldComponentUpdate = function shouldComponentUpdate(nextProps) { + return !isEqual(this.props, nextProps); + }; + + HelmetWrapper.prototype.mapNestedChildrenToProps = function mapNestedChildrenToProps(child, nestedChildren) { + if (!nestedChildren) { + return null; + } + + switch (child.type) { + case TAG_NAMES.SCRIPT: + case TAG_NAMES.NOSCRIPT: + return { + innerHTML: nestedChildren + }; + + case TAG_NAMES.STYLE: + return { + cssText: nestedChildren + }; + } + + throw new Error("<" + child.type + " /> elements are self-closing and can not contain children. Refer to our API for more information."); + }; + + HelmetWrapper.prototype.flattenArrayTypeChildren = function flattenArrayTypeChildren(_ref) { + var _babelHelpers$extends; + + var child = _ref.child, + arrayTypeChildren = _ref.arrayTypeChildren, + newChildProps = _ref.newChildProps, + nestedChildren = _ref.nestedChildren; + + return _extends({}, arrayTypeChildren, (_babelHelpers$extends = {}, _babelHelpers$extends[child.type] = [].concat(arrayTypeChildren[child.type] || [], [_extends({}, newChildProps, this.mapNestedChildrenToProps(child, nestedChildren))]), _babelHelpers$extends)); + }; + + HelmetWrapper.prototype.mapObjectTypeChildren = function mapObjectTypeChildren(_ref2) { + var _babelHelpers$extends2, _babelHelpers$extends3; + + var child = _ref2.child, + newProps = _ref2.newProps, + newChildProps = _ref2.newChildProps, + nestedChildren = _ref2.nestedChildren; + + switch (child.type) { + case TAG_NAMES.TITLE: + return _extends({}, newProps, (_babelHelpers$extends2 = {}, _babelHelpers$extends2[child.type] = nestedChildren, _babelHelpers$extends2.titleAttributes = _extends({}, newChildProps), _babelHelpers$extends2)); + + case TAG_NAMES.BODY: + return _extends({}, newProps, { + bodyAttributes: _extends({}, newChildProps) + }); + + case TAG_NAMES.HTML: + return _extends({}, newProps, { + htmlAttributes: _extends({}, newChildProps) + }); + } + + return _extends({}, newProps, (_babelHelpers$extends3 = {}, _babelHelpers$extends3[child.type] = _extends({}, newChildProps), _babelHelpers$extends3)); + }; + + HelmetWrapper.prototype.mapArrayTypeChildrenToProps = function mapArrayTypeChildrenToProps(arrayTypeChildren, newProps) { + var newFlattenedProps = _extends({}, newProps); + + Object.keys(arrayTypeChildren).forEach(function (arrayChildName) { + var _babelHelpers$extends4; + + newFlattenedProps = _extends({}, newFlattenedProps, (_babelHelpers$extends4 = {}, _babelHelpers$extends4[arrayChildName] = arrayTypeChildren[arrayChildName], _babelHelpers$extends4)); + }); + + return newFlattenedProps; + }; + + HelmetWrapper.prototype.warnOnInvalidChildren = function warnOnInvalidChildren(child, nestedChildren) { + if (process.env.NODE_ENV !== "production") { + if (!VALID_TAG_NAMES.some(function (name) { + return child.type === name; + })) { + if (typeof child.type === "function") { + return warn("You may be attempting to nest components within each other, which is not allowed. Refer to our API for more information."); + } + + return warn("Only elements types " + VALID_TAG_NAMES.join(", ") + " are allowed. Helmet does not support rendering <" + child.type + "> elements. Refer to our API for more information."); + } + + if (nestedChildren && typeof nestedChildren !== "string" && (!Array.isArray(nestedChildren) || nestedChildren.some(function (nestedChild) { + return typeof nestedChild !== "string"; + }))) { + throw new Error("Helmet expects a string as a child of <" + child.type + ">. Did you forget to wrap your children in braces? ( <" + child.type + ">{``} ) Refer to our API for more information."); + } + } + + return true; + }; + + HelmetWrapper.prototype.mapChildrenToProps = function mapChildrenToProps(children, newProps) { + var _this2 = this; + + var arrayTypeChildren = {}; + + React.Children.forEach(children, function (child) { + if (!child || !child.props) { + return; + } + + var _child$props = child.props, + nestedChildren = _child$props.children, + childProps = objectWithoutProperties(_child$props, ["children"]); + + var newChildProps = convertReactPropstoHtmlAttributes(childProps); + + _this2.warnOnInvalidChildren(child, nestedChildren); + + switch (child.type) { + case TAG_NAMES.LINK: + case TAG_NAMES.META: + case TAG_NAMES.NOSCRIPT: + case TAG_NAMES.SCRIPT: + case TAG_NAMES.STYLE: + arrayTypeChildren = _this2.flattenArrayTypeChildren({ + child: child, + arrayTypeChildren: arrayTypeChildren, + newChildProps: newChildProps, + nestedChildren: nestedChildren + }); + break; + + default: + newProps = _this2.mapObjectTypeChildren({ + child: child, + newProps: newProps, + newChildProps: newChildProps, + nestedChildren: nestedChildren + }); + break; + } + }); + + newProps = this.mapArrayTypeChildrenToProps(arrayTypeChildren, newProps); + return newProps; + }; + + HelmetWrapper.prototype.render = function render() { + var _props = this.props, + children = _props.children, + props = objectWithoutProperties(_props, ["children"]); + + var newProps = _extends({}, props); + + if (children) { + newProps = this.mapChildrenToProps(children, newProps); + } + + return React.createElement(Component, newProps); + }; + + createClass(HelmetWrapper, null, [{ + key: "canUseDOM", + + + // Component.peek comes from react-side-effect: + // For testing, you may use a static peek() method available on the returned component. + // It lets you get the current state without resetting the mounted instance stack. + // Don’t use it for anything other than testing. + + /** + * @param {Object} base: {"target": "_blank", "href": "http://mysite.com/"} + * @param {Object} bodyAttributes: {"className": "root"} + * @param {String} defaultTitle: "Default Title" + * @param {Boolean} defer: true + * @param {Boolean} encodeSpecialCharacters: true + * @param {Object} htmlAttributes: {"lang": "en", "amp": undefined} + * @param {Array} link: [{"rel": "canonical", "href": "http://mysite.com/example"}] + * @param {Array} meta: [{"name": "description", "content": "Test description"}] + * @param {Array} noscript: [{"innerHTML": " console.log(newState)" + * @param {Array} script: [{"type": "text/javascript", "src": "http://mysite.com/js/test.js"}] + * @param {Array} style: [{"type": "text/css", "cssText": "div { display: block; color: blue; }"}] + * @param {String} title: "Title" + * @param {Object} titleAttributes: {"itemprop": "name"} + * @param {String} titleTemplate: "MySite.com - %s" + */ + set: function set$$1(canUseDOM) { + Component.canUseDOM = canUseDOM; + } + }]); + return HelmetWrapper; + }(React.Component), _class.propTypes = { + base: PropTypes.object, + bodyAttributes: PropTypes.object, + children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]), + defaultTitle: PropTypes.string, + defer: PropTypes.bool, + encodeSpecialCharacters: PropTypes.bool, + htmlAttributes: PropTypes.object, + link: PropTypes.arrayOf(PropTypes.object), + meta: PropTypes.arrayOf(PropTypes.object), + noscript: PropTypes.arrayOf(PropTypes.object), + onChangeClientState: PropTypes.func, + script: PropTypes.arrayOf(PropTypes.object), + style: PropTypes.arrayOf(PropTypes.object), + title: PropTypes.string, + titleAttributes: PropTypes.object, + titleTemplate: PropTypes.string + }, _class.defaultProps = { + defer: true, + encodeSpecialCharacters: true + }, _class.peek = Component.peek, _class.rewind = function () { + var mappedState = Component.rewind(); + if (!mappedState) { + // provide fallback if mappedState is undefined + mappedState = mapStateOnServer({ + baseTag: [], + bodyAttributes: {}, + encodeSpecialCharacters: true, + htmlAttributes: {}, + linkTags: [], + metaTags: [], + noscriptTags: [], + scriptTags: [], + styleTags: [], + title: "", + titleAttributes: {} + }); + } + + return mappedState; + }, _temp; +}; + +var NullComponent = function NullComponent() { + return null; +}; + +var HelmetSideEffects = withSideEffect(reducePropsToState, handleClientStateChange, mapStateOnServer)(NullComponent); + +var HelmetExport = Helmet(HelmetSideEffects); +HelmetExport.renderStatic = HelmetExport.rewind; + +export default HelmetExport; +export { HelmetExport as Helmet }; diff --git a/lib/Helmet.js b/lib/Helmet.js new file mode 100644 index 00000000..7270d266 --- /dev/null +++ b/lib/Helmet.js @@ -0,0 +1,919 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { value: true }); + +function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } + +var PropTypes = _interopDefault(require('prop-types')); +var withSideEffect = _interopDefault(require('react-side-effect')); +var isEqual = _interopDefault(require('react-fast-compare')); +var React = _interopDefault(require('react')); +var objectAssign = _interopDefault(require('object-assign')); + +var ATTRIBUTE_NAMES = { + BODY: "bodyAttributes", + HTML: "htmlAttributes", + TITLE: "titleAttributes" +}; + +var TAG_NAMES = { + BASE: "base", + BODY: "body", + HEAD: "head", + HTML: "html", + LINK: "link", + META: "meta", + NOSCRIPT: "noscript", + SCRIPT: "script", + STYLE: "style", + TITLE: "title" +}; + +var VALID_TAG_NAMES = Object.values(TAG_NAMES); + +var TAG_PROPERTIES = { + CHARSET: "charset", + CSS_TEXT: "cssText", + HREF: "href", + HTTPEQUIV: "http-equiv", + INNER_HTML: "innerHTML", + ITEM_PROP: "itemprop", + NAME: "name", + PROPERTY: "property", + REL: "rel", + SRC: "src", + TARGET: "target" +}; + +var REACT_TAG_MAP = { + accesskey: "accessKey", + charset: "charSet", + class: "className", + contenteditable: "contentEditable", + contextmenu: "contextMenu", + "http-equiv": "httpEquiv", + itemprop: "itemProp", + tabindex: "tabIndex" +}; + +var HELMET_PROPS = { + DEFAULT_TITLE: "defaultTitle", + DEFER: "defer", + ENCODE_SPECIAL_CHARACTERS: "encodeSpecialCharacters", + ON_CHANGE_CLIENT_STATE: "onChangeClientState", + TITLE_TEMPLATE: "titleTemplate" +}; + +var HTML_TAG_MAP = Object.keys(REACT_TAG_MAP).reduce(function (obj, key) { + obj[REACT_TAG_MAP[key]] = key; + return obj; +}, {}); + +var SELF_CLOSING_TAGS = [TAG_NAMES.NOSCRIPT, TAG_NAMES.SCRIPT, TAG_NAMES.STYLE]; + +var HELMET_ATTRIBUTE = "data-react-helmet"; + +var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { + return typeof obj; +} : function (obj) { + return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; +}; + +var classCallCheck = function (instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError("Cannot call a class as a function"); + } +}; + +var createClass = function () { + function defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i]; + descriptor.enumerable = descriptor.enumerable || false; + descriptor.configurable = true; + if ("value" in descriptor) descriptor.writable = true; + Object.defineProperty(target, descriptor.key, descriptor); + } + } + + return function (Constructor, protoProps, staticProps) { + if (protoProps) defineProperties(Constructor.prototype, protoProps); + if (staticProps) defineProperties(Constructor, staticProps); + return Constructor; + }; +}(); + +var _extends = Object.assign || function (target) { + for (var i = 1; i < arguments.length; i++) { + var source = arguments[i]; + + for (var key in source) { + if (Object.prototype.hasOwnProperty.call(source, key)) { + target[key] = source[key]; + } + } + } + + return target; +}; + +var inherits = function (subClass, superClass) { + if (typeof superClass !== "function" && superClass !== null) { + throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); + } + + subClass.prototype = Object.create(superClass && superClass.prototype, { + constructor: { + value: subClass, + enumerable: false, + writable: true, + configurable: true + } + }); + if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; +}; + +var objectWithoutProperties = function (obj, keys) { + var target = {}; + + for (var i in obj) { + if (keys.indexOf(i) >= 0) continue; + if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; + target[i] = obj[i]; + } + + return target; +}; + +var possibleConstructorReturn = function (self, call) { + if (!self) { + throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); + } + + return call && (typeof call === "object" || typeof call === "function") ? call : self; +}; + +var encodeSpecialCharacters = function encodeSpecialCharacters(str) { + var encode = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; + + if (encode === false) { + return String(str); + } + + return String(str).replace(/&/g, "&").replace(//g, ">").replace(/"/g, """).replace(/'/g, "'"); +}; + +var getTitleFromPropsList = function getTitleFromPropsList(propsList) { + var innermostTitle = getInnermostProperty(propsList, TAG_NAMES.TITLE); + var innermostTemplate = getInnermostProperty(propsList, HELMET_PROPS.TITLE_TEMPLATE); + + if (innermostTemplate && innermostTitle) { + // use function arg to avoid need to escape $ characters + return innermostTemplate.replace(/%s/g, function () { + return Array.isArray(innermostTitle) ? innermostTitle.join("") : innermostTitle; + }); + } + + var innermostDefaultTitle = getInnermostProperty(propsList, HELMET_PROPS.DEFAULT_TITLE); + + return innermostTitle || innermostDefaultTitle || undefined; +}; + +var getOnChangeClientState = function getOnChangeClientState(propsList) { + return getInnermostProperty(propsList, HELMET_PROPS.ON_CHANGE_CLIENT_STATE) || function () {}; +}; + +var getAttributesFromPropsList = function getAttributesFromPropsList(tagType, propsList) { + return propsList.filter(function (props) { + return typeof props[tagType] !== "undefined"; + }).map(function (props) { + return props[tagType]; + }).reduce(function (tagAttrs, current) { + return _extends({}, tagAttrs, current); + }, {}); +}; + +var getBaseTagFromPropsList = function getBaseTagFromPropsList(primaryAttributes, propsList) { + return propsList.filter(function (props) { + return typeof props[TAG_NAMES.BASE] !== "undefined"; + }).map(function (props) { + return props[TAG_NAMES.BASE]; + }).reverse().reduce(function (innermostBaseTag, tag) { + if (!innermostBaseTag.length) { + var keys = Object.keys(tag); + + for (var i = 0; i < keys.length; i++) { + var attributeKey = keys[i]; + var lowerCaseAttributeKey = attributeKey.toLowerCase(); + + if (primaryAttributes.indexOf(lowerCaseAttributeKey) !== -1 && tag[lowerCaseAttributeKey]) { + return innermostBaseTag.concat(tag); + } + } + } + + return innermostBaseTag; + }, []); +}; + +var getTagsFromPropsList = function getTagsFromPropsList(tagName, primaryAttributes, propsList) { + // Calculate list of tags, giving priority innermost component (end of the propslist) + var approvedSeenTags = {}; + + return propsList.filter(function (props) { + if (Array.isArray(props[tagName])) { + return true; + } + if (typeof props[tagName] !== "undefined") { + warn("Helmet: " + tagName + " should be of type \"Array\". Instead found type \"" + _typeof(props[tagName]) + "\""); + } + return false; + }).map(function (props) { + return props[tagName]; + }).reverse().reduce(function (approvedTags, instanceTags) { + var instanceSeenTags = {}; + + instanceTags.filter(function (tag) { + var primaryAttributeKey = void 0; + var keys = Object.keys(tag); + for (var i = 0; i < keys.length; i++) { + var attributeKey = keys[i]; + var lowerCaseAttributeKey = attributeKey.toLowerCase(); + + // Special rule with link tags, since rel and href are both primary tags, rel takes priority + if (primaryAttributes.indexOf(lowerCaseAttributeKey) !== -1 && !(primaryAttributeKey === TAG_PROPERTIES.REL && tag[primaryAttributeKey].toLowerCase() === "canonical") && !(lowerCaseAttributeKey === TAG_PROPERTIES.REL && tag[lowerCaseAttributeKey].toLowerCase() === "stylesheet")) { + primaryAttributeKey = lowerCaseAttributeKey; + } + // Special case for innerHTML which doesn't work lowercased + if (primaryAttributes.indexOf(attributeKey) !== -1 && (attributeKey === TAG_PROPERTIES.INNER_HTML || attributeKey === TAG_PROPERTIES.CSS_TEXT || attributeKey === TAG_PROPERTIES.ITEM_PROP)) { + primaryAttributeKey = attributeKey; + } + } + + if (!primaryAttributeKey || !tag[primaryAttributeKey]) { + return false; + } + + var value = tag[primaryAttributeKey].toLowerCase(); + + if (!approvedSeenTags[primaryAttributeKey]) { + approvedSeenTags[primaryAttributeKey] = {}; + } + + if (!instanceSeenTags[primaryAttributeKey]) { + instanceSeenTags[primaryAttributeKey] = {}; + } + + if (!approvedSeenTags[primaryAttributeKey][value]) { + instanceSeenTags[primaryAttributeKey][value] = true; + return true; + } + + return false; + }).reverse().forEach(function (tag) { + return approvedTags.push(tag); + }); + + // Update seen tags with tags from this instance + var keys = Object.keys(instanceSeenTags); + for (var i = 0; i < keys.length; i++) { + var attributeKey = keys[i]; + var tagUnion = objectAssign({}, approvedSeenTags[attributeKey], instanceSeenTags[attributeKey]); + + approvedSeenTags[attributeKey] = tagUnion; + } + + return approvedTags; + }, []).reverse(); +}; + +var getInnermostProperty = function getInnermostProperty(propsList, property) { + for (var i = propsList.length - 1; i >= 0; i--) { + var props = propsList[i]; + + if (props.hasOwnProperty(property)) { + return props[property]; + } + } + + return null; +}; + +var reducePropsToState = function reducePropsToState(propsList) { + return { + baseTag: getBaseTagFromPropsList([TAG_PROPERTIES.HREF, TAG_PROPERTIES.TARGET], propsList), + bodyAttributes: getAttributesFromPropsList(ATTRIBUTE_NAMES.BODY, propsList), + defer: getInnermostProperty(propsList, HELMET_PROPS.DEFER), + encode: getInnermostProperty(propsList, HELMET_PROPS.ENCODE_SPECIAL_CHARACTERS), + htmlAttributes: getAttributesFromPropsList(ATTRIBUTE_NAMES.HTML, propsList), + linkTags: getTagsFromPropsList(TAG_NAMES.LINK, [TAG_PROPERTIES.REL, TAG_PROPERTIES.HREF], propsList), + metaTags: getTagsFromPropsList(TAG_NAMES.META, [TAG_PROPERTIES.NAME, TAG_PROPERTIES.CHARSET, TAG_PROPERTIES.HTTPEQUIV, TAG_PROPERTIES.PROPERTY, TAG_PROPERTIES.ITEM_PROP], propsList), + noscriptTags: getTagsFromPropsList(TAG_NAMES.NOSCRIPT, [TAG_PROPERTIES.INNER_HTML], propsList), + onChangeClientState: getOnChangeClientState(propsList), + scriptTags: getTagsFromPropsList(TAG_NAMES.SCRIPT, [TAG_PROPERTIES.SRC, TAG_PROPERTIES.INNER_HTML], propsList), + styleTags: getTagsFromPropsList(TAG_NAMES.STYLE, [TAG_PROPERTIES.CSS_TEXT], propsList), + title: getTitleFromPropsList(propsList), + titleAttributes: getAttributesFromPropsList(ATTRIBUTE_NAMES.TITLE, propsList) + }; +}; + +var rafPolyfill = function () { + var clock = Date.now(); + + return function (callback) { + var currentTime = Date.now(); + + if (currentTime - clock > 16) { + clock = currentTime; + callback(currentTime); + } else { + setTimeout(function () { + rafPolyfill(callback); + }, 0); + } + }; +}(); + +var cafPolyfill = function cafPolyfill(id) { + return clearTimeout(id); +}; + +var requestAnimationFrame = typeof window !== "undefined" ? window.requestAnimationFrame && window.requestAnimationFrame.bind(window) || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || rafPolyfill : global.requestAnimationFrame || rafPolyfill; + +var cancelAnimationFrame = typeof window !== "undefined" ? window.cancelAnimationFrame || window.webkitCancelAnimationFrame || window.mozCancelAnimationFrame || cafPolyfill : global.cancelAnimationFrame || cafPolyfill; + +var warn = function warn(msg) { + return console && typeof console.warn === "function" && console.warn(msg); +}; + +var _helmetCallback = null; + +var handleClientStateChange = function handleClientStateChange(newState) { + if (_helmetCallback) { + cancelAnimationFrame(_helmetCallback); + } + + if (newState.defer) { + _helmetCallback = requestAnimationFrame(function () { + commitTagChanges(newState, function () { + _helmetCallback = null; + }); + }); + } else { + commitTagChanges(newState); + _helmetCallback = null; + } +}; + +var commitTagChanges = function commitTagChanges(newState, cb) { + var baseTag = newState.baseTag, + bodyAttributes = newState.bodyAttributes, + htmlAttributes = newState.htmlAttributes, + linkTags = newState.linkTags, + metaTags = newState.metaTags, + noscriptTags = newState.noscriptTags, + onChangeClientState = newState.onChangeClientState, + scriptTags = newState.scriptTags, + styleTags = newState.styleTags, + title = newState.title, + titleAttributes = newState.titleAttributes; + + updateAttributes(TAG_NAMES.BODY, bodyAttributes); + updateAttributes(TAG_NAMES.HTML, htmlAttributes); + + updateTitle(title, titleAttributes); + + var tagUpdates = { + baseTag: updateTags(TAG_NAMES.BASE, baseTag), + linkTags: updateTags(TAG_NAMES.LINK, linkTags), + metaTags: updateTags(TAG_NAMES.META, metaTags), + noscriptTags: updateTags(TAG_NAMES.NOSCRIPT, noscriptTags), + scriptTags: updateTags(TAG_NAMES.SCRIPT, scriptTags), + styleTags: updateTags(TAG_NAMES.STYLE, styleTags) + }; + + var addedTags = {}; + var removedTags = {}; + + Object.keys(tagUpdates).forEach(function (tagType) { + var _tagUpdates$tagType = tagUpdates[tagType], + newTags = _tagUpdates$tagType.newTags, + oldTags = _tagUpdates$tagType.oldTags; + + + if (newTags.length) { + addedTags[tagType] = newTags; + } + if (oldTags.length) { + removedTags[tagType] = tagUpdates[tagType].oldTags; + } + }); + + cb && cb(); + + onChangeClientState(newState, addedTags, removedTags); +}; + +var flattenArray = function flattenArray(possibleArray) { + return Array.isArray(possibleArray) ? possibleArray.join("") : possibleArray; +}; + +var updateTitle = function updateTitle(title, attributes) { + if (typeof title !== "undefined" && document.title !== title) { + document.title = flattenArray(title); + } + + updateAttributes(TAG_NAMES.TITLE, attributes); +}; + +var updateAttributes = function updateAttributes(tagName, attributes) { + var elementTag = document.getElementsByTagName(tagName)[0]; + + if (!elementTag) { + return; + } + + var helmetAttributeString = elementTag.getAttribute(HELMET_ATTRIBUTE); + var helmetAttributes = helmetAttributeString ? helmetAttributeString.split(",") : []; + var attributesToRemove = [].concat(helmetAttributes); + var attributeKeys = Object.keys(attributes); + + for (var i = 0; i < attributeKeys.length; i++) { + var attribute = attributeKeys[i]; + var value = attributes[attribute] || ""; + + if (elementTag.getAttribute(attribute) !== value) { + elementTag.setAttribute(attribute, value); + } + + if (helmetAttributes.indexOf(attribute) === -1) { + helmetAttributes.push(attribute); + } + + var indexToSave = attributesToRemove.indexOf(attribute); + if (indexToSave !== -1) { + attributesToRemove.splice(indexToSave, 1); + } + } + + for (var _i = attributesToRemove.length - 1; _i >= 0; _i--) { + elementTag.removeAttribute(attributesToRemove[_i]); + } + + if (helmetAttributes.length === attributesToRemove.length) { + elementTag.removeAttribute(HELMET_ATTRIBUTE); + } else if (elementTag.getAttribute(HELMET_ATTRIBUTE) !== attributeKeys.join(",")) { + elementTag.setAttribute(HELMET_ATTRIBUTE, attributeKeys.join(",")); + } +}; + +var updateTags = function updateTags(type, tags) { + var headElement = document.head || document.querySelector(TAG_NAMES.HEAD); + var tagNodes = headElement.querySelectorAll(type + "[" + HELMET_ATTRIBUTE + "]"); + var oldTags = Array.prototype.slice.call(tagNodes); + var newTags = []; + var indexToDelete = void 0; + + if (tags && tags.length) { + tags.forEach(function (tag) { + var newElement = document.createElement(type); + + for (var attribute in tag) { + if (tag.hasOwnProperty(attribute)) { + if (attribute === TAG_PROPERTIES.INNER_HTML) { + newElement.innerHTML = tag.innerHTML; + } else if (attribute === TAG_PROPERTIES.CSS_TEXT) { + if (newElement.styleSheet) { + newElement.styleSheet.cssText = tag.cssText; + } else { + newElement.appendChild(document.createTextNode(tag.cssText)); + } + } else { + var value = typeof tag[attribute] === "undefined" ? "" : tag[attribute]; + newElement.setAttribute(attribute, value); + } + } + } + + newElement.setAttribute(HELMET_ATTRIBUTE, "true"); + + // Remove a duplicate tag from domTagstoRemove, so it isn't cleared. + if (oldTags.some(function (existingTag, index) { + indexToDelete = index; + return newElement.isEqualNode(existingTag); + })) { + oldTags.splice(indexToDelete, 1); + } else { + newTags.push(newElement); + } + }); + } + + oldTags.forEach(function (tag) { + return tag.parentNode.removeChild(tag); + }); + newTags.forEach(function (tag) { + return headElement.prepend(tag); + }); + + return { + oldTags: oldTags, + newTags: newTags + }; +}; + +var generateElementAttributesAsString = function generateElementAttributesAsString(attributes) { + return Object.keys(attributes).reduce(function (str, key) { + var attr = typeof attributes[key] !== "undefined" ? key + "=\"" + attributes[key] + "\"" : "" + key; + return str ? str + " " + attr : attr; + }, ""); +}; + +var generateTitleAsString = function generateTitleAsString(type, title, attributes, encode) { + var attributeString = generateElementAttributesAsString(attributes); + var flattenedTitle = flattenArray(title); + return attributeString ? "<" + type + " " + HELMET_ATTRIBUTE + "=\"true\" " + attributeString + ">" + encodeSpecialCharacters(flattenedTitle, encode) + "" : "<" + type + " " + HELMET_ATTRIBUTE + "=\"true\">" + encodeSpecialCharacters(flattenedTitle, encode) + ""; +}; + +var generateTagsAsString = function generateTagsAsString(type, tags, encode) { + return tags.reduce(function (str, tag) { + var attributeHtml = Object.keys(tag).filter(function (attribute) { + return !(attribute === TAG_PROPERTIES.INNER_HTML || attribute === TAG_PROPERTIES.CSS_TEXT); + }).reduce(function (string, attribute) { + var attr = typeof tag[attribute] === "undefined" ? attribute : attribute + "=\"" + encodeSpecialCharacters(tag[attribute], encode) + "\""; + return string ? string + " " + attr : attr; + }, ""); + + var tagContent = tag.innerHTML || tag.cssText || ""; + + var isSelfClosing = SELF_CLOSING_TAGS.indexOf(type) === -1; + + return str + "<" + type + " " + HELMET_ATTRIBUTE + "=\"true\" " + attributeHtml + (isSelfClosing ? "/>" : ">" + tagContent + ""); + }, ""); +}; + +var convertElementAttributestoReactProps = function convertElementAttributestoReactProps(attributes) { + var initProps = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + + return Object.keys(attributes).reduce(function (obj, key) { + obj[REACT_TAG_MAP[key] || key] = attributes[key]; + return obj; + }, initProps); +}; + +var convertReactPropstoHtmlAttributes = function convertReactPropstoHtmlAttributes(props) { + var initAttributes = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + + return Object.keys(props).reduce(function (obj, key) { + obj[HTML_TAG_MAP[key] || key] = props[key]; + return obj; + }, initAttributes); +}; + +var generateTitleAsReactComponent = function generateTitleAsReactComponent(type, title, attributes) { + var _initProps; + + // assigning into an array to define toString function on it + var initProps = (_initProps = { + key: title + }, _initProps[HELMET_ATTRIBUTE] = true, _initProps); + var props = convertElementAttributestoReactProps(attributes, initProps); + + return [React.createElement(TAG_NAMES.TITLE, props, title)]; +}; + +var generateTagsAsReactComponent = function generateTagsAsReactComponent(type, tags) { + return tags.map(function (tag, i) { + var _mappedTag; + + var mappedTag = (_mappedTag = { + key: i + }, _mappedTag[HELMET_ATTRIBUTE] = true, _mappedTag); + + Object.keys(tag).forEach(function (attribute) { + var mappedAttribute = REACT_TAG_MAP[attribute] || attribute; + + if (mappedAttribute === TAG_PROPERTIES.INNER_HTML || mappedAttribute === TAG_PROPERTIES.CSS_TEXT) { + var content = tag.innerHTML || tag.cssText; + mappedTag.dangerouslySetInnerHTML = { __html: content }; + } else { + mappedTag[mappedAttribute] = tag[attribute]; + } + }); + + return React.createElement(type, mappedTag); + }); +}; + +var getMethodsForTag = function getMethodsForTag(type, tags, encode) { + switch (type) { + case TAG_NAMES.TITLE: + return { + toComponent: function toComponent() { + return generateTitleAsReactComponent(type, tags.title, tags.titleAttributes, encode); + }, + toString: function toString() { + return generateTitleAsString(type, tags.title, tags.titleAttributes, encode); + } + }; + case ATTRIBUTE_NAMES.BODY: + case ATTRIBUTE_NAMES.HTML: + return { + toComponent: function toComponent() { + return convertElementAttributestoReactProps(tags); + }, + toString: function toString() { + return generateElementAttributesAsString(tags); + } + }; + default: + return { + toComponent: function toComponent() { + return generateTagsAsReactComponent(type, tags); + }, + toString: function toString() { + return generateTagsAsString(type, tags, encode); + } + }; + } +}; + +var mapStateOnServer = function mapStateOnServer(_ref) { + var baseTag = _ref.baseTag, + bodyAttributes = _ref.bodyAttributes, + encode = _ref.encode, + htmlAttributes = _ref.htmlAttributes, + linkTags = _ref.linkTags, + metaTags = _ref.metaTags, + noscriptTags = _ref.noscriptTags, + scriptTags = _ref.scriptTags, + styleTags = _ref.styleTags, + _ref$title = _ref.title, + title = _ref$title === undefined ? "" : _ref$title, + titleAttributes = _ref.titleAttributes; + return { + base: getMethodsForTag(TAG_NAMES.BASE, baseTag, encode), + bodyAttributes: getMethodsForTag(ATTRIBUTE_NAMES.BODY, bodyAttributes, encode), + htmlAttributes: getMethodsForTag(ATTRIBUTE_NAMES.HTML, htmlAttributes, encode), + link: getMethodsForTag(TAG_NAMES.LINK, linkTags, encode), + meta: getMethodsForTag(TAG_NAMES.META, metaTags, encode), + noscript: getMethodsForTag(TAG_NAMES.NOSCRIPT, noscriptTags, encode), + script: getMethodsForTag(TAG_NAMES.SCRIPT, scriptTags, encode), + style: getMethodsForTag(TAG_NAMES.STYLE, styleTags, encode), + title: getMethodsForTag(TAG_NAMES.TITLE, { title: title, titleAttributes: titleAttributes }, encode) + }; +}; + +var Helmet = function Helmet(Component) { + var _class, _temp; + + return _temp = _class = function (_React$Component) { + inherits(HelmetWrapper, _React$Component); + + function HelmetWrapper() { + classCallCheck(this, HelmetWrapper); + return possibleConstructorReturn(this, _React$Component.apply(this, arguments)); + } + + HelmetWrapper.prototype.shouldComponentUpdate = function shouldComponentUpdate(nextProps) { + return !isEqual(this.props, nextProps); + }; + + HelmetWrapper.prototype.mapNestedChildrenToProps = function mapNestedChildrenToProps(child, nestedChildren) { + if (!nestedChildren) { + return null; + } + + switch (child.type) { + case TAG_NAMES.SCRIPT: + case TAG_NAMES.NOSCRIPT: + return { + innerHTML: nestedChildren + }; + + case TAG_NAMES.STYLE: + return { + cssText: nestedChildren + }; + } + + throw new Error("<" + child.type + " /> elements are self-closing and can not contain children. Refer to our API for more information."); + }; + + HelmetWrapper.prototype.flattenArrayTypeChildren = function flattenArrayTypeChildren(_ref) { + var _babelHelpers$extends; + + var child = _ref.child, + arrayTypeChildren = _ref.arrayTypeChildren, + newChildProps = _ref.newChildProps, + nestedChildren = _ref.nestedChildren; + + return _extends({}, arrayTypeChildren, (_babelHelpers$extends = {}, _babelHelpers$extends[child.type] = [].concat(arrayTypeChildren[child.type] || [], [_extends({}, newChildProps, this.mapNestedChildrenToProps(child, nestedChildren))]), _babelHelpers$extends)); + }; + + HelmetWrapper.prototype.mapObjectTypeChildren = function mapObjectTypeChildren(_ref2) { + var _babelHelpers$extends2, _babelHelpers$extends3; + + var child = _ref2.child, + newProps = _ref2.newProps, + newChildProps = _ref2.newChildProps, + nestedChildren = _ref2.nestedChildren; + + switch (child.type) { + case TAG_NAMES.TITLE: + return _extends({}, newProps, (_babelHelpers$extends2 = {}, _babelHelpers$extends2[child.type] = nestedChildren, _babelHelpers$extends2.titleAttributes = _extends({}, newChildProps), _babelHelpers$extends2)); + + case TAG_NAMES.BODY: + return _extends({}, newProps, { + bodyAttributes: _extends({}, newChildProps) + }); + + case TAG_NAMES.HTML: + return _extends({}, newProps, { + htmlAttributes: _extends({}, newChildProps) + }); + } + + return _extends({}, newProps, (_babelHelpers$extends3 = {}, _babelHelpers$extends3[child.type] = _extends({}, newChildProps), _babelHelpers$extends3)); + }; + + HelmetWrapper.prototype.mapArrayTypeChildrenToProps = function mapArrayTypeChildrenToProps(arrayTypeChildren, newProps) { + var newFlattenedProps = _extends({}, newProps); + + Object.keys(arrayTypeChildren).forEach(function (arrayChildName) { + var _babelHelpers$extends4; + + newFlattenedProps = _extends({}, newFlattenedProps, (_babelHelpers$extends4 = {}, _babelHelpers$extends4[arrayChildName] = arrayTypeChildren[arrayChildName], _babelHelpers$extends4)); + }); + + return newFlattenedProps; + }; + + HelmetWrapper.prototype.warnOnInvalidChildren = function warnOnInvalidChildren(child, nestedChildren) { + if (process.env.NODE_ENV !== "production") { + if (!VALID_TAG_NAMES.some(function (name) { + return child.type === name; + })) { + if (typeof child.type === "function") { + return warn("You may be attempting to nest components within each other, which is not allowed. Refer to our API for more information."); + } + + return warn("Only elements types " + VALID_TAG_NAMES.join(", ") + " are allowed. Helmet does not support rendering <" + child.type + "> elements. Refer to our API for more information."); + } + + if (nestedChildren && typeof nestedChildren !== "string" && (!Array.isArray(nestedChildren) || nestedChildren.some(function (nestedChild) { + return typeof nestedChild !== "string"; + }))) { + throw new Error("Helmet expects a string as a child of <" + child.type + ">. Did you forget to wrap your children in braces? ( <" + child.type + ">{``} ) Refer to our API for more information."); + } + } + + return true; + }; + + HelmetWrapper.prototype.mapChildrenToProps = function mapChildrenToProps(children, newProps) { + var _this2 = this; + + var arrayTypeChildren = {}; + + React.Children.forEach(children, function (child) { + if (!child || !child.props) { + return; + } + + var _child$props = child.props, + nestedChildren = _child$props.children, + childProps = objectWithoutProperties(_child$props, ["children"]); + + var newChildProps = convertReactPropstoHtmlAttributes(childProps); + + _this2.warnOnInvalidChildren(child, nestedChildren); + + switch (child.type) { + case TAG_NAMES.LINK: + case TAG_NAMES.META: + case TAG_NAMES.NOSCRIPT: + case TAG_NAMES.SCRIPT: + case TAG_NAMES.STYLE: + arrayTypeChildren = _this2.flattenArrayTypeChildren({ + child: child, + arrayTypeChildren: arrayTypeChildren, + newChildProps: newChildProps, + nestedChildren: nestedChildren + }); + break; + + default: + newProps = _this2.mapObjectTypeChildren({ + child: child, + newProps: newProps, + newChildProps: newChildProps, + nestedChildren: nestedChildren + }); + break; + } + }); + + newProps = this.mapArrayTypeChildrenToProps(arrayTypeChildren, newProps); + return newProps; + }; + + HelmetWrapper.prototype.render = function render() { + var _props = this.props, + children = _props.children, + props = objectWithoutProperties(_props, ["children"]); + + var newProps = _extends({}, props); + + if (children) { + newProps = this.mapChildrenToProps(children, newProps); + } + + return React.createElement(Component, newProps); + }; + + createClass(HelmetWrapper, null, [{ + key: "canUseDOM", + + + // Component.peek comes from react-side-effect: + // For testing, you may use a static peek() method available on the returned component. + // It lets you get the current state without resetting the mounted instance stack. + // Don’t use it for anything other than testing. + + /** + * @param {Object} base: {"target": "_blank", "href": "http://mysite.com/"} + * @param {Object} bodyAttributes: {"className": "root"} + * @param {String} defaultTitle: "Default Title" + * @param {Boolean} defer: true + * @param {Boolean} encodeSpecialCharacters: true + * @param {Object} htmlAttributes: {"lang": "en", "amp": undefined} + * @param {Array} link: [{"rel": "canonical", "href": "http://mysite.com/example"}] + * @param {Array} meta: [{"name": "description", "content": "Test description"}] + * @param {Array} noscript: [{"innerHTML": " console.log(newState)" + * @param {Array} script: [{"type": "text/javascript", "src": "http://mysite.com/js/test.js"}] + * @param {Array} style: [{"type": "text/css", "cssText": "div { display: block; color: blue; }"}] + * @param {String} title: "Title" + * @param {Object} titleAttributes: {"itemprop": "name"} + * @param {String} titleTemplate: "MySite.com - %s" + */ + set: function set$$1(canUseDOM) { + Component.canUseDOM = canUseDOM; + } + }]); + return HelmetWrapper; + }(React.Component), _class.propTypes = { + base: PropTypes.object, + bodyAttributes: PropTypes.object, + children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]), + defaultTitle: PropTypes.string, + defer: PropTypes.bool, + encodeSpecialCharacters: PropTypes.bool, + htmlAttributes: PropTypes.object, + link: PropTypes.arrayOf(PropTypes.object), + meta: PropTypes.arrayOf(PropTypes.object), + noscript: PropTypes.arrayOf(PropTypes.object), + onChangeClientState: PropTypes.func, + script: PropTypes.arrayOf(PropTypes.object), + style: PropTypes.arrayOf(PropTypes.object), + title: PropTypes.string, + titleAttributes: PropTypes.object, + titleTemplate: PropTypes.string + }, _class.defaultProps = { + defer: true, + encodeSpecialCharacters: true + }, _class.peek = Component.peek, _class.rewind = function () { + var mappedState = Component.rewind(); + if (!mappedState) { + // provide fallback if mappedState is undefined + mappedState = mapStateOnServer({ + baseTag: [], + bodyAttributes: {}, + encodeSpecialCharacters: true, + htmlAttributes: {}, + linkTags: [], + metaTags: [], + noscriptTags: [], + scriptTags: [], + styleTags: [], + title: "", + titleAttributes: {} + }); + } + + return mappedState; + }, _temp; +}; + +var NullComponent = function NullComponent() { + return null; +}; + +var HelmetSideEffects = withSideEffect(reducePropsToState, handleClientStateChange, mapStateOnServer)(NullComponent); + +var HelmetExport = Helmet(HelmetSideEffects); +HelmetExport.renderStatic = HelmetExport.rewind; + +exports.Helmet = HelmetExport; +exports.default = HelmetExport; From ef79f28e05bfc3bbc17c5941486ded8a798b4930 Mon Sep 17 00:00:00 2001 From: Vikalp Jain Date: Fri, 12 Nov 2021 20:51:28 +0530 Subject: [PATCH 5/5] revert: package.json changes --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3e2e3da2..c20d77a4 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "description": "A document head manager for React", "version": "6.1.0", "main": "./lib/Helmet.js", - "module": "./src/Helmet.js", + "module": "./es/Helmet.js", "author": "NFL ", "contributors": [ "Chris Welch ",