Latest Threat Research:SANDWORM_MODE: Shai-Hulud-Style npm Worm Hijacks CI Workflows and Poisons AI Toolchains.Details
Socket
Book a DemoInstallSign in
Socket

@dr.pogodin/react-helmet

Package Overview
Dependencies
Maintainers
1
Versions
8
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@dr.pogodin/react-helmet - npm Package Compare versions

Comparing version
2.0.4
to
3.0.0
+13
babel.config.js
module.exports = {
plugins: [
'@babel/transform-runtime',
],
presets: [
['@babel/env', {
modules: 'cjs',
targets: 'defaults',
}],
['@babel/react', { runtime: 'automatic' }],
'@babel/typescript',
],
};
module.exports = {
plugins: [
'@babel/transform-runtime',
],
presets: [
['@babel/env', {
modules: false,
targets: 'defaults',
}],
['@babel/react', { runtime: 'automatic' }],
'@babel/typescript',
],
};
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.commitTagChanges = commitTagChanges;
var _constants = require("./constants");
var _utils = require("./utils");
/**
* Replaces HTML elements previously added to the DOM's head by React Helmet
* by the set of given elements (tags). For any given element that matches
* exactly an element already present in the head no actual DOM modification
* happens, it just keeps already present element. Returns arrays of newly
* added (newTags) and removed (oldTags) elements.
*/
function updateTags(type, tags) {
// TODO: Do we really need the fallback here? document.head is supposed to be
// always defined.
const headElement = document.head || document.querySelector(_constants.TAG_NAMES.HEAD);
const tagNodes = headElement.querySelectorAll(`${type}[${_constants.HELMET_ATTRIBUTE}]`);
const allTags = [];
const oldTags = [...tagNodes];
const newTags = [];
for (const tag of tags) {
const newElement = document.createElement(type);
// TODO: Well, the typing within this block is bad, and should be improved.
for (const [key, value] of Object.entries(tag)) {
if (Object.prototype.hasOwnProperty.call(tag, key)) {
const name = _constants.HTML_TAG_MAP[key] ?? key;
if (name === _constants.TAG_PROPERTIES.INNER_HTML) {
newElement.innerHTML = value;
} else if (name === _constants.TAG_PROPERTIES.CSS_TEXT) {
// TODO: Not sure when this is true?
// @ts-expect-error "pre-existing"
if (newElement.styleSheet) {
// @ts-expect-error "pre-existing"
newElement.styleSheet.cssText = tag.cssText;
} else {
newElement.appendChild(document.createTextNode(tag.cssText));
}
} else {
newElement.setAttribute(name, value ?? '');
}
}
}
newElement.setAttribute(_constants.HELMET_ATTRIBUTE, 'true');
const attrs = {};
for (const {
name,
value
} of newElement.attributes) {
attrs[name] = value;
}
allTags.push(attrs);
// Remove a duplicate tag from domTagstoRemove, so it isn't cleared.
for (let i = 0;; ++i) {
if (newElement.isEqualNode(oldTags[i])) {
oldTags.splice(i, 1);
break;
}
if (i >= oldTags.length) {
newTags.push(newElement);
break;
}
}
}
oldTags.forEach(tag => tag.parentNode?.removeChild(tag));
newTags.forEach(tag => headElement.appendChild(tag));
// TODO: Do we really need this return value anywhere? Especially `oldTags`
// that have been removed from DOM already?
return {
allTags,
oldTags,
newTags
};
}
function updateAttributes(tagName, props) {
const elementTag = document.getElementsByTagName(tagName)[0];
if (!elementTag) {
return;
}
const helmetAttributeString = elementTag.getAttribute(_constants.HELMET_ATTRIBUTE);
const helmetAttributes = helmetAttributeString ? helmetAttributeString.split(',') : [];
const attributesToRemove = [...helmetAttributes];
const attributeKeys = [];
for (const prop of Object.keys(props)) {
// TODO: See a comment below.
attributeKeys.push(_constants.HTML_TAG_MAP[prop] ?? prop);
}
for (const [key, value] of Object.entries(props)) {
// TODO: Get rid of the mapping later. It is not really needed, as HTML
// attribute names are case-insensitive. However, our related logic may
// still be case dependent - we should be careful about it.
const attr = _constants.HTML_TAG_MAP[key] ?? key;
if (elementTag.getAttribute(attr) !== value) {
// TODO: That ?? '' piece is here to keep the legacy behavior for now,
// I guess later we should prefer to consider attrbiutes with "undefined"
// value as not set.
elementTag.setAttribute(attr, value ?? '');
}
if (!helmetAttributes.includes(attr)) {
helmetAttributes.push(attr);
}
const indexToSave = attributesToRemove.indexOf(attr);
if (indexToSave !== -1) {
attributesToRemove.splice(indexToSave, 1);
}
}
for (let i = attributesToRemove.length - 1; i >= 0; i -= 1) {
elementTag.removeAttribute(attributesToRemove[i]);
}
if (helmetAttributes.length === attributesToRemove.length) {
elementTag.removeAttribute(_constants.HELMET_ATTRIBUTE);
} else if (elementTag.getAttribute(_constants.HELMET_ATTRIBUTE) !== attributeKeys.join(',')) {
elementTag.setAttribute(_constants.HELMET_ATTRIBUTE, attributeKeys.join(','));
}
}
function updateTitle(title, attributes) {
if (title !== undefined && document.title !== title) {
document.title = (0, _utils.flattenArray)(title);
}
updateAttributes(_constants.TAG_NAMES.TITLE, attributes);
}
function commitTagChanges(newState, firstRender) {
const {
base,
bodyAttributes,
defer,
htmlAttributes,
links,
meta,
noscript,
onChangeClientState,
script,
style,
title,
titleAttributes
} = newState;
updateAttributes(_constants.TAG_NAMES.BODY, bodyAttributes ?? {});
updateAttributes(_constants.TAG_NAMES.HTML, htmlAttributes ?? {});
updateTitle(title, titleAttributes);
const tagUpdates = {
baseTag: updateTags(_constants.TAG_NAMES.BASE, base ? [base] : []),
linkTags: updateTags(_constants.TAG_NAMES.LINK, links ?? []),
metaTags: updateTags(_constants.TAG_NAMES.META, meta ?? []),
noscriptTags: updateTags(_constants.TAG_NAMES.NOSCRIPT, noscript ?? []),
scriptTags: updateTags(_constants.TAG_NAMES.SCRIPT, script ?? []),
styleTags: updateTags(_constants.TAG_NAMES.STYLE, style ?? [])
};
const resultTags = {
baseTag: [],
bodyAttributes: {},
defer: defer ?? false,
htmlAttributes: {},
linkTags: [],
metaTags: [],
noscriptTags: [],
onChangeClientState: onChangeClientState ?? (() => undefined),
scriptTags: [],
styleTags: [],
title: title ?? '',
titleAttributes: {}
};
const addedTags = {};
const removedTags = {};
Object.keys(tagUpdates).forEach(tagType => {
const {
allTags,
newTags,
oldTags
} = tagUpdates[tagType];
resultTags[tagType] = allTags;
if (newTags.length) {
addedTags[tagType] = newTags;
}
if (oldTags.length) {
removedTags[tagType] = tagUpdates[tagType].oldTags;
}
});
if (firstRender || Object.keys(addedTags).length || Object.keys(removedTags).length) {
onChangeClientState?.(resultTags, addedTags, removedTags);
}
}
//# sourceMappingURL=client.js.map
{"version":3,"file":"client.js","names":["_constants","require","_utils","updateTags","type","tags","headElement","document","head","querySelector","TAG_NAMES","HEAD","tagNodes","querySelectorAll","HELMET_ATTRIBUTE","allTags","oldTags","newTags","tag","newElement","createElement","key","value","Object","entries","prototype","hasOwnProperty","call","name","HTML_TAG_MAP","TAG_PROPERTIES","INNER_HTML","innerHTML","CSS_TEXT","styleSheet","cssText","appendChild","createTextNode","setAttribute","attrs","attributes","push","i","isEqualNode","splice","length","forEach","parentNode","removeChild","updateAttributes","tagName","props","elementTag","getElementsByTagName","helmetAttributeString","getAttribute","helmetAttributes","split","attributesToRemove","attributeKeys","prop","keys","attr","includes","indexToSave","indexOf","removeAttribute","join","updateTitle","title","undefined","flattenArray","TITLE","commitTagChanges","newState","firstRender","base","bodyAttributes","defer","htmlAttributes","links","meta","noscript","onChangeClientState","script","style","titleAttributes","BODY","HTML","tagUpdates","baseTag","BASE","linkTags","LINK","metaTags","META","noscriptTags","NOSCRIPT","scriptTags","SCRIPT","styleTags","STYLE","resultTags","addedTags","removedTags","tagType"],"sources":["../../src/client.ts"],"sourcesContent":["import {\n HELMET_ATTRIBUTE,\n HTML_TAG_MAP,\n TAG_NAMES,\n TAG_PROPERTIES,\n} from './constants';\n\nimport type {\n AggregatedState,\n BodyProps,\n HelmetChildProps,\n HelmetTags,\n HtmlProps,\n StateUpdate,\n} from './types';\n\nimport { flattenArray } from './utils';\n\ntype TagUpdates = {\n allTags: HTMLElement[];\n oldTags: HTMLElement[];\n newTags: HTMLElement[];\n};\n\ntype TagUpdateList = Record<string, TagUpdates>;\n\n/**\n * Replaces HTML elements previously added to the DOM's head by React Helmet\n * by the set of given elements (tags). For any given element that matches\n * exactly an element already present in the head no actual DOM modification\n * happens, it just keeps already present element. Returns arrays of newly\n * added (newTags) and removed (oldTags) elements.\n */\nfunction updateTags(type: string, tags: HelmetChildProps[]) {\n // TODO: Do we really need the fallback here? document.head is supposed to be\n // always defined.\n const headElement = document.head || document.querySelector(TAG_NAMES.HEAD);\n\n const tagNodes = headElement.querySelectorAll<HTMLElement>(`${type}[${HELMET_ATTRIBUTE}]`);\n const allTags: HTMLElement[] = [];\n const oldTags: HTMLElement[] = [...tagNodes];\n const newTags: HTMLElement[] = [];\n\n for (const tag of tags) {\n const newElement = document.createElement(type);\n\n // TODO: Well, the typing within this block is bad, and should be improved.\n for (const [key, value] of Object.entries(tag)) {\n if (Object.prototype.hasOwnProperty.call(tag, key)) {\n const name = HTML_TAG_MAP[key] ?? key;\n if (name as TAG_PROPERTIES === TAG_PROPERTIES.INNER_HTML) {\n newElement.innerHTML = value as string;\n } else if (name as TAG_PROPERTIES === TAG_PROPERTIES.CSS_TEXT) {\n // TODO: Not sure when this is true?\n // @ts-expect-error \"pre-existing\"\n if (newElement.styleSheet) {\n // @ts-expect-error \"pre-existing\"\n (newElement.styleSheet as CSSStyleDeclaration).cssText = (\n tag as CSSStyleDeclaration).cssText;\n } else {\n newElement.appendChild(document.createTextNode(\n (tag as CSSStyleDeclaration).cssText,\n ));\n }\n } else {\n newElement.setAttribute(name, (value as string) ?? '');\n }\n }\n }\n\n newElement.setAttribute(HELMET_ATTRIBUTE, 'true');\n\n const attrs = {} as HTMLElement;\n for (const { name, value } of newElement.attributes) {\n (attrs[name as keyof HTMLElement] as unknown) = value;\n }\n allTags.push(attrs);\n\n // Remove a duplicate tag from domTagstoRemove, so it isn't cleared.\n for (let i = 0; ; ++i) {\n if (newElement.isEqualNode(oldTags[i]!)) {\n oldTags.splice(i, 1);\n break;\n }\n if (i >= oldTags.length) {\n newTags.push(newElement);\n break;\n }\n }\n }\n\n oldTags.forEach((tag: Node) => tag.parentNode?.removeChild(tag));\n newTags.forEach((tag) => headElement.appendChild(tag));\n\n // TODO: Do we really need this return value anywhere? Especially `oldTags`\n // that have been removed from DOM already?\n return {\n allTags,\n oldTags,\n newTags,\n };\n}\n\nfunction updateAttributes(tagName: string, props: BodyProps | HtmlProps) {\n const elementTag = document.getElementsByTagName(tagName)[0];\n\n if (!elementTag) {\n return;\n }\n\n const helmetAttributeString = elementTag.getAttribute(HELMET_ATTRIBUTE);\n const helmetAttributes = helmetAttributeString ? helmetAttributeString.split(',') : [];\n const attributesToRemove = [...helmetAttributes];\n\n const attributeKeys: string[] = [];\n for (const prop of Object.keys(props)) {\n // TODO: See a comment below.\n attributeKeys.push(HTML_TAG_MAP[prop] ?? prop);\n }\n\n for (const [key, value] of Object.entries(props)) {\n // TODO: Get rid of the mapping later. It is not really needed, as HTML\n // attribute names are case-insensitive. However, our related logic may\n // still be case dependent - we should be careful about it.\n const attr = HTML_TAG_MAP[key] ?? key;\n if (elementTag.getAttribute(attr) !== value) {\n // TODO: That ?? '' piece is here to keep the legacy behavior for now,\n // I guess later we should prefer to consider attrbiutes with \"undefined\"\n // value as not set.\n elementTag.setAttribute(attr, value as string ?? '');\n }\n\n if (!helmetAttributes.includes(attr)) {\n helmetAttributes.push(attr);\n }\n\n const indexToSave = attributesToRemove.indexOf(attr);\n if (indexToSave !== -1) {\n attributesToRemove.splice(indexToSave, 1);\n }\n }\n\n for (let i = attributesToRemove.length - 1; i >= 0; i -= 1) {\n elementTag.removeAttribute(attributesToRemove[i]!);\n }\n\n if (helmetAttributes.length === attributesToRemove.length) {\n elementTag.removeAttribute(HELMET_ATTRIBUTE);\n } else if (elementTag.getAttribute(HELMET_ATTRIBUTE) !== attributeKeys.join(',')) {\n elementTag.setAttribute(HELMET_ATTRIBUTE, attributeKeys.join(','));\n }\n}\n\nfunction updateTitle(\n title: string | undefined,\n attributes: BodyProps | HtmlProps,\n) {\n if (title !== undefined && document.title !== title) {\n document.title = flattenArray(title);\n }\n\n updateAttributes(TAG_NAMES.TITLE, attributes);\n}\n\nexport function commitTagChanges(\n newState: AggregatedState,\n firstRender: boolean,\n) {\n const {\n base,\n bodyAttributes,\n defer,\n htmlAttributes,\n links,\n meta,\n noscript,\n onChangeClientState,\n script,\n style,\n title,\n titleAttributes,\n } = newState;\n updateAttributes(TAG_NAMES.BODY, bodyAttributes ?? {});\n updateAttributes(TAG_NAMES.HTML, htmlAttributes ?? {});\n\n updateTitle(title, titleAttributes!);\n\n const tagUpdates: TagUpdateList = {\n baseTag: updateTags(TAG_NAMES.BASE, base ? [base] : []),\n linkTags: updateTags(TAG_NAMES.LINK, links ?? []),\n metaTags: updateTags(TAG_NAMES.META, meta ?? []),\n noscriptTags: updateTags(TAG_NAMES.NOSCRIPT, noscript ?? []),\n scriptTags: updateTags(TAG_NAMES.SCRIPT, script ?? []),\n styleTags: updateTags(TAG_NAMES.STYLE, style ?? []),\n };\n\n const resultTags: StateUpdate = {\n baseTag: [],\n bodyAttributes: {},\n defer: defer ?? false,\n htmlAttributes: {},\n linkTags: [],\n metaTags: [],\n noscriptTags: [],\n onChangeClientState: onChangeClientState ?? (() => undefined),\n scriptTags: [],\n styleTags: [],\n title: title ?? '',\n titleAttributes: {},\n };\n\n const addedTags: Partial<HelmetTags> = {};\n const removedTags: Partial<HelmetTags> = {};\n\n Object.keys(tagUpdates).forEach((tagType) => {\n const { allTags, newTags, oldTags } = tagUpdates[tagType]!;\n\n (resultTags[tagType as keyof HelmetTags] as HTMLElement[]) = allTags;\n\n if (newTags.length) {\n (addedTags[tagType as keyof HelmetTags] as HTMLElement[]) = newTags;\n }\n if (oldTags.length) {\n (removedTags[tagType as keyof HelmetTags] as HTMLElement[])\n = tagUpdates[tagType]!.oldTags;\n }\n });\n\n if (firstRender\n || Object.keys(addedTags).length\n || Object.keys(removedTags).length\n ) {\n onChangeClientState?.(resultTags, addedTags, removedTags);\n }\n}\n"],"mappings":"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","ignoreList":[]}
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.VALID_TAG_NAMES = exports.TAG_PROPERTIES = exports.TAG_NAMES = exports.SEO_PRIORITY_TAGS = exports.REACT_TAG_MAP = exports.IS_DOM_ENVIRONMENT = exports.HTML_TAG_MAP = exports.HELMET_ATTRIBUTE = exports.ATTRIBUTE_NAMES = void 0;
let TAG_PROPERTIES = exports.TAG_PROPERTIES = /*#__PURE__*/function (TAG_PROPERTIES) {
TAG_PROPERTIES["CHARSET"] = "charset";
TAG_PROPERTIES["CSS_TEXT"] = "cssText";
TAG_PROPERTIES["HREF"] = "href";
TAG_PROPERTIES["HTTPEQUIV"] = "http-equiv";
TAG_PROPERTIES["INNER_HTML"] = "innerHTML";
TAG_PROPERTIES["ITEM_PROP"] = "itemprop";
TAG_PROPERTIES["NAME"] = "name";
TAG_PROPERTIES["PROPERTY"] = "property";
TAG_PROPERTIES["REL"] = "rel";
TAG_PROPERTIES["SRC"] = "src";
return TAG_PROPERTIES;
}({});
let ATTRIBUTE_NAMES = exports.ATTRIBUTE_NAMES = /*#__PURE__*/function (ATTRIBUTE_NAMES) {
ATTRIBUTE_NAMES["BODY"] = "bodyAttributes";
ATTRIBUTE_NAMES["HTML"] = "htmlAttributes";
ATTRIBUTE_NAMES["TITLE"] = "titleAttributes";
return ATTRIBUTE_NAMES;
}({});
let TAG_NAMES = exports.TAG_NAMES = /*#__PURE__*/function (TAG_NAMES) {
TAG_NAMES["BASE"] = "base";
TAG_NAMES["BODY"] = "body";
TAG_NAMES["HEAD"] = "head";
TAG_NAMES["HTML"] = "html";
TAG_NAMES["LINK"] = "link";
TAG_NAMES["META"] = "meta";
TAG_NAMES["NOSCRIPT"] = "noscript";
TAG_NAMES["SCRIPT"] = "script";
TAG_NAMES["STYLE"] = "style";
TAG_NAMES["TITLE"] = "title";
TAG_NAMES["FRAGMENT"] = "Symbol(react.fragment)";
return TAG_NAMES;
}({});
const SEO_PRIORITY_TAGS = exports.SEO_PRIORITY_TAGS = {
link: {
rel: ['amphtml', 'canonical', 'alternate']
},
script: {
type: ['application/ld+json']
},
meta: {
charset: '',
name: ['generator', 'robots', 'description'],
property: ['og:type', 'og:title', 'og:url', 'og:image', 'og:image:alt', 'og:description', 'twitter:url', 'twitter:title', 'twitter:description', 'twitter:image', 'twitter:image:alt', 'twitter:card', 'twitter:site']
}
};
const VALID_TAG_NAMES = exports.VALID_TAG_NAMES = Object.values(TAG_NAMES);
/**
* The mapping of HTML attribute names to the corresponding element properties,
* for the names that do not match their corresponding properties.
*/
const REACT_TAG_MAP = exports.REACT_TAG_MAP = {
accesskey: 'accessKey',
charset: 'charSet',
class: 'className',
contenteditable: 'contentEditable',
contextmenu: 'contextMenu',
'http-equiv': 'httpEquiv',
itemprop: 'itemProp',
tabindex: 'tabIndex'
};
/**
* The mapping reverse of REACT_TAG_MAP.
*/
const HTML_TAG_MAP = exports.HTML_TAG_MAP = (() => {
const res = {};
for (const [key, value] of Object.entries(REACT_TAG_MAP)) {
res[value] = key;
}
return res;
})();
const HELMET_ATTRIBUTE = exports.HELMET_ATTRIBUTE = 'data-rh';
const IS_DOM_ENVIRONMENT = exports.IS_DOM_ENVIRONMENT = !!(typeof window !== 'undefined' && window.document.createElement);
//# sourceMappingURL=constants.js.map
{"version":3,"file":"constants.js","names":["TAG_PROPERTIES","exports","ATTRIBUTE_NAMES","TAG_NAMES","SEO_PRIORITY_TAGS","link","rel","script","type","meta","charset","name","property","VALID_TAG_NAMES","Object","values","REACT_TAG_MAP","accesskey","class","contenteditable","contextmenu","itemprop","tabindex","HTML_TAG_MAP","res","key","value","entries","HELMET_ATTRIBUTE","IS_DOM_ENVIRONMENT","window","document","createElement"],"sources":["../../src/constants.ts"],"sourcesContent":["export enum TAG_PROPERTIES {\n CHARSET = 'charset',\n CSS_TEXT = 'cssText',\n HREF = 'href',\n HTTPEQUIV = 'http-equiv',\n INNER_HTML = 'innerHTML',\n ITEM_PROP = 'itemprop',\n NAME = 'name',\n PROPERTY = 'property',\n REL = 'rel',\n SRC = 'src',\n}\n\nexport enum ATTRIBUTE_NAMES {\n BODY = 'bodyAttributes',\n HTML = 'htmlAttributes',\n TITLE = 'titleAttributes',\n}\n\nexport enum TAG_NAMES {\n BASE = 'base',\n BODY = 'body',\n HEAD = 'head',\n HTML = 'html',\n LINK = 'link',\n META = 'meta',\n NOSCRIPT = 'noscript',\n SCRIPT = 'script',\n STYLE = 'style',\n TITLE = 'title',\n FRAGMENT = 'Symbol(react.fragment)',\n}\n\nexport const SEO_PRIORITY_TAGS = {\n link: { rel: ['amphtml', 'canonical', 'alternate'] },\n script: { type: ['application/ld+json'] },\n meta: {\n charset: '',\n name: ['generator', 'robots', 'description'],\n property: [\n 'og:type',\n 'og:title',\n 'og:url',\n 'og:image',\n 'og:image:alt',\n 'og:description',\n 'twitter:url',\n 'twitter:title',\n 'twitter:description',\n 'twitter:image',\n 'twitter:image:alt',\n 'twitter:card',\n 'twitter:site',\n ],\n },\n};\n\nexport const VALID_TAG_NAMES = Object.values(TAG_NAMES);\n\n/**\n * The mapping of HTML attribute names to the corresponding element properties,\n * for the names that do not match their corresponding properties.\n */\nexport const REACT_TAG_MAP: Record<string, string> = {\n accesskey: 'accessKey',\n charset: 'charSet',\n class: 'className',\n contenteditable: 'contentEditable',\n contextmenu: 'contextMenu',\n 'http-equiv': 'httpEquiv',\n itemprop: 'itemProp',\n tabindex: 'tabIndex',\n};\n\n/**\n * The mapping reverse of REACT_TAG_MAP.\n */\nexport const HTML_TAG_MAP = (() => {\n const res: Record<string, string> = {};\n for (const [key, value] of Object.entries(REACT_TAG_MAP)) {\n res[value] = key;\n }\n return res;\n})();\n\nexport const HELMET_ATTRIBUTE = 'data-rh';\n\nexport const IS_DOM_ENVIRONMENT = !!(\n typeof window !== 'undefined'\n && window.document.createElement\n);\n"],"mappings":";;;;;;IAAYA,cAAc,GAAAC,OAAA,CAAAD,cAAA,0BAAdA,cAAc;EAAdA,cAAc;EAAdA,cAAc;EAAdA,cAAc;EAAdA,cAAc;EAAdA,cAAc;EAAdA,cAAc;EAAdA,cAAc;EAAdA,cAAc;EAAdA,cAAc;EAAdA,cAAc;EAAA,OAAdA,cAAc;AAAA;AAAA,IAadE,eAAe,GAAAD,OAAA,CAAAC,eAAA,0BAAfA,eAAe;EAAfA,eAAe;EAAfA,eAAe;EAAfA,eAAe;EAAA,OAAfA,eAAe;AAAA;AAAA,IAMfC,SAAS,GAAAF,OAAA,CAAAE,SAAA,0BAATA,SAAS;EAATA,SAAS;EAATA,SAAS;EAATA,SAAS;EAATA,SAAS;EAATA,SAAS;EAATA,SAAS;EAATA,SAAS;EAATA,SAAS;EAATA,SAAS;EAATA,SAAS;EAATA,SAAS;EAAA,OAATA,SAAS;AAAA;AAcd,MAAMC,iBAAiB,GAAAH,OAAA,CAAAG,iBAAA,GAAG;EAC/BC,IAAI,EAAE;IAAEC,GAAG,EAAE,CAAC,SAAS,EAAE,WAAW,EAAE,WAAW;EAAE,CAAC;EACpDC,MAAM,EAAE;IAAEC,IAAI,EAAE,CAAC,qBAAqB;EAAE,CAAC;EACzCC,IAAI,EAAE;IACJC,OAAO,EAAE,EAAE;IACXC,IAAI,EAAE,CAAC,WAAW,EAAE,QAAQ,EAAE,aAAa,CAAC;IAC5CC,QAAQ,EAAE,CACR,SAAS,EACT,UAAU,EACV,QAAQ,EACR,UAAU,EACV,cAAc,EACd,gBAAgB,EAChB,aAAa,EACb,eAAe,EACf,qBAAqB,EACrB,eAAe,EACf,mBAAmB,EACnB,cAAc,EACd,cAAc;EAElB;AACF,CAAC;AAEM,MAAMC,eAAe,GAAAZ,OAAA,CAAAY,eAAA,GAAGC,MAAM,CAACC,MAAM,CAACZ,SAAS,CAAC;;AAEvD;AACA;AACA;AACA;AACO,MAAMa,aAAqC,GAAAf,OAAA,CAAAe,aAAA,GAAG;EACnDC,SAAS,EAAE,WAAW;EACtBP,OAAO,EAAE,SAAS;EAClBQ,KAAK,EAAE,WAAW;EAClBC,eAAe,EAAE,iBAAiB;EAClCC,WAAW,EAAE,aAAa;EAC1B,YAAY,EAAE,WAAW;EACzBC,QAAQ,EAAE,UAAU;EACpBC,QAAQ,EAAE;AACZ,CAAC;;AAED;AACA;AACA;AACO,MAAMC,YAAY,GAAAtB,OAAA,CAAAsB,YAAA,GAAG,CAAC,MAAM;EACjC,MAAMC,GAA2B,GAAG,CAAC,CAAC;EACtC,KAAK,MAAM,CAACC,GAAG,EAAEC,KAAK,CAAC,IAAIZ,MAAM,CAACa,OAAO,CAACX,aAAa,CAAC,EAAE;IACxDQ,GAAG,CAACE,KAAK,CAAC,GAAGD,GAAG;EAClB;EACA,OAAOD,GAAG;AACZ,CAAC,EAAE,CAAC;AAEG,MAAMI,gBAAgB,GAAA3B,OAAA,CAAA2B,gBAAA,GAAG,SAAS;AAElC,MAAMC,kBAAkB,GAAA5B,OAAA,CAAA4B,kBAAA,GAAG,CAAC,EACjC,OAAOC,MAAM,KAAK,WAAW,IAC1BA,MAAM,CAACC,QAAQ,CAACC,aAAa,CACjC","ignoreList":[]}
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _react = require("react");
var _constants = require("./constants");
var _Provider = require("./Provider");
var _utils = require("./utils");
function assertChildType(childType, nestedChildren) {
if (typeof childType !== 'string') throw Error('You may be attempting to nest <Helmet> components within each other, which is not allowed. Refer to our API for more information.');
if (!_constants.VALID_TAG_NAMES.includes(childType)) throw Error(`Only elements types ${_constants.VALID_TAG_NAMES.join(', ')} are allowed. Helmet does not support rendering <${childType}> elements. Refer to our API for more information.`);
if (!nestedChildren || typeof nestedChildren === 'string' || Array.isArray(nestedChildren)
// TODO: This piece of the check is wrong when parent is a fragment,
// and thus children may not be an array of strings.
// && nestedChildren.every((item) => typeof item === 'string')
) return;
throw Error(`Helmet expects a string as a child of <${childType}>. Did you forget to wrap your children in braces? ( <${childType}>{\`\`}</${childType}> ) Refer to our API for more information.`);
}
/**
* Given a string key, it checks it against the legacy mapping between supported
* HTML attribute names and their corresponding React prop names (for the names
* that are different). If found in the mapping, it prints a warning to console
* and returns the mapped prop name. Otherwise, it just returns the key as is,
* assuming it is already a valid React prop name.
*/
function getPropName(key) {
const res = _constants.REACT_TAG_MAP[key];
if (res) console.warn(`"${key}" is not a valid JSX prop, replace it by "${res}"`);
return res ?? key;
}
/**
* Given children and props of a <Helmet> component, it reduces them to a single
* props object.
*
* TODO: I guess, it should be further refactored, to make it cleaner...
* though, it should perfectly work as is, so not a huge priority for now.
*/
function reduceChildrenAndProps(props) {
// NOTE: `props` are clonned, thus it is safe to push additional items to
// array values of `res`, and to re-assign non-array values of `res`, without
// the risk to mutate the original `props` object.
const res = (0, _utils.cloneProps)(props);
// TODO: This is a temporary block, for compatibility with legacy library.
for (const item of Object.values(props)) {
if (Array.isArray(item)) {
for (const it of item) {
if (it) {
for (const key of Object.keys(it)) {
const p = getPropName(key);
if (p !== key) {
it[p] = it[key];
delete it[key];
}
}
}
}
} else if (item && typeof item === 'object') {
const it = item;
for (const key of Object.keys(it)) {
const p = getPropName(key);
if (p !== key) {
it[p] = it[key];
delete it[key];
}
}
}
}
_react.Children.forEach(props.children, child => {
if (child === undefined || child === null) return;
if (typeof child !== 'object' || !('props' in child)) throw Error(`"${typeof child}" is not a valid <Helmet> descendant`);
let nestedChildren;
const childProps = {};
if (child.props) {
for (const [key, value] of Object.entries(child.props)) {
if (key === 'children') nestedChildren = value;else childProps[getPropName(key)] = value;
}
}
let {
type
} = child;
if (typeof type === 'symbol') type = type.toString();
assertChildType(type, nestedChildren);
function assertStringChild(child) {
if (typeof child !== 'string') {
// TODO: We want to throw, but the legacy code did not, so we won't for
// now.
console.error(`child of ${type} element should be a string`);
/*
throw Error(
// NOTE: assertChildType() above guarantees that `type` is a string,
// although it is not expressed in a way TypeScript can automatically
// pick up.
);
*/
}
}
switch (type) {
case _constants.TAG_NAMES.BASE:
res.base = childProps;
break;
case _constants.TAG_NAMES.BODY:
res.bodyAttributes = childProps;
break;
case _constants.TAG_NAMES.FRAGMENT:
(0, _utils.mergeProps)(res, reduceChildrenAndProps({
children: nestedChildren
}));
break;
case _constants.TAG_NAMES.HTML:
res.htmlAttributes = childProps;
break;
case _constants.TAG_NAMES.LINK:
case _constants.TAG_NAMES.META:
if (nestedChildren) throw Error(`<${type} /> elements are self-closing and can not contain children. Refer to our API for more information.`);
(0, _utils.pushToPropArray)(res, type, childProps);
break;
case _constants.TAG_NAMES.NOSCRIPT:
case _constants.TAG_NAMES.SCRIPT:
if (nestedChildren !== undefined) {
assertStringChild(nestedChildren);
childProps.innerHTML = nestedChildren;
}
(0, _utils.pushToPropArray)(res, type, childProps);
break;
case _constants.TAG_NAMES.STYLE:
assertStringChild(nestedChildren);
childProps.cssText = nestedChildren;
(0, _utils.pushToPropArray)(res, type, childProps);
break;
case _constants.TAG_NAMES.TITLE:
res.titleAttributes = childProps;
if (typeof nestedChildren === 'string') res.title = nestedChildren;
// When title contains {} expressions the children are an array of
// strings, and other values.
else if (Array.isArray(nestedChildren)) res.title = nestedChildren.join('');
break;
default:
{
// TODO: Perhaps, we should remove HEAD entry from TAG_NAMES?
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const bad = type;
}
}
});
delete res.children;
return res;
}
const Helmet = props => {
const context = (0, _react.use)(_Provider.Context);
if (!context) throw Error('<Helmet> component must be within a <HelmetProvider> children tree');
const id = (0, _react.useId)();
// TODO: Agh... we need it here to ensure that it works server-side,
// and we need the same in the useEffect() below to ensure it works
// client-side in strict mode (and, thus completely correctly from React's
// pure component / side effect logic). It clearly should be optimized,
// but let's care about it later.
context.update(id, reduceChildrenAndProps(props));
// TODO: I guess, these two useEffect() can be merged together, which should
// also allow to simplify and optimize the client-side management of attrs and
// elements managed by these. Though, keeping them separate is an easier way
// for now to ensure backward compatibility.
(0, _react.useEffect)(() => {
context.update(id, reduceChildrenAndProps(props));
context.clientApply();
});
(0, _react.useEffect)(() => () => context.update(id, undefined), [context, id]);
return null;
};
var _default = exports.default = Helmet;
//# sourceMappingURL=Helmet.js.map
{"version":3,"file":"Helmet.js","names":["_react","require","_constants","_Provider","_utils","assertChildType","childType","nestedChildren","Error","VALID_TAG_NAMES","includes","join","Array","isArray","getPropName","key","res","REACT_TAG_MAP","console","warn","reduceChildrenAndProps","props","cloneProps","item","Object","values","it","keys","p","Children","forEach","children","child","undefined","childProps","value","entries","type","toString","assertStringChild","error","TAG_NAMES","BASE","base","BODY","bodyAttributes","FRAGMENT","mergeProps","HTML","htmlAttributes","LINK","META","pushToPropArray","NOSCRIPT","SCRIPT","innerHTML","STYLE","cssText","TITLE","titleAttributes","title","bad","Helmet","context","use","Context","id","useId","update","useEffect","clientApply","_default","exports","default"],"sources":["../../src/Helmet.tsx"],"sourcesContent":["import {\n type FunctionComponent,\n type ReactElement,\n type ReactNode,\n Children,\n use,\n useEffect,\n useId,\n} from 'react';\n\nimport { REACT_TAG_MAP, TAG_NAMES, VALID_TAG_NAMES } from './constants';\n\nimport { Context } from './Provider';\n\nimport type {\n BaseProps,\n BodyProps,\n HelmetChildProps,\n HelmetProps,\n HtmlProps,\n LinkProps,\n MetaProps,\n NoscriptProps,\n ScriptProps,\n StyleProps,\n TitleProps,\n} from './types';\nimport { cloneProps, mergeProps, pushToPropArray } from './utils';\n\nfunction assertChildType(\n childType: ReactElement['type'],\n nestedChildren: ReactNode,\n): asserts childType is TAG_NAMES {\n if (typeof childType !== 'string') throw Error(\n 'You may be attempting to nest <Helmet> components within each other, which is not allowed. Refer to our API for more information.',\n );\n\n if (!(VALID_TAG_NAMES as string[]).includes(childType)) throw Error(\n `Only elements types ${VALID_TAG_NAMES.join(', ')} are allowed. Helmet does not support rendering <${childType}> elements. Refer to our API for more information.`,\n );\n\n if (\n !nestedChildren\n || typeof nestedChildren === 'string'\n || Array.isArray(nestedChildren)\n // TODO: This piece of the check is wrong when parent is a fragment,\n // and thus children may not be an array of strings.\n // && nestedChildren.every((item) => typeof item === 'string')\n ) return;\n\n throw Error(\n `Helmet expects a string as a child of <${childType}>. Did you forget to wrap your children in braces? ( <${childType}>{\\`\\`}</${childType}> ) Refer to our API for more information.`,\n );\n}\n\n/**\n * Given a string key, it checks it against the legacy mapping between supported\n * HTML attribute names and their corresponding React prop names (for the names\n * that are different). If found in the mapping, it prints a warning to console\n * and returns the mapped prop name. Otherwise, it just returns the key as is,\n * assuming it is already a valid React prop name.\n */\nfunction getPropName(key: string): keyof HelmetChildProps {\n const res = REACT_TAG_MAP[key];\n if (res) console.warn(\n `\"${key}\" is not a valid JSX prop, replace it by \"${res}\"`,\n );\n return (res ?? key) as keyof HelmetChildProps;\n}\n\n/**\n * Given children and props of a <Helmet> component, it reduces them to a single\n * props object.\n *\n * TODO: I guess, it should be further refactored, to make it cleaner...\n * though, it should perfectly work as is, so not a huge priority for now.\n */\nfunction reduceChildrenAndProps(props: HelmetProps): Omit<HelmetProps, 'children'> {\n // NOTE: `props` are clonned, thus it is safe to push additional items to\n // array values of `res`, and to re-assign non-array values of `res`, without\n // the risk to mutate the original `props` object.\n const res: HelmetProps = cloneProps(props);\n\n // TODO: This is a temporary block, for compatibility with legacy library.\n for (const item of Object.values(props)) {\n if (Array.isArray(item)) {\n for (const it of item) {\n if (it) {\n for (const key of Object.keys(it)) {\n const p = getPropName(key);\n if (p !== key) {\n it[p] = it[key as keyof HelmetChildProps] as unknown;\n delete it[key as keyof HelmetChildProps];\n }\n }\n }\n }\n } else if (item && typeof item === 'object') {\n const it = item as HelmetChildProps;\n for (const key of Object.keys(it)) {\n const p = getPropName(key);\n if (p !== key) {\n it[p] = it[key as keyof HelmetChildProps] as unknown;\n delete it[key as keyof HelmetChildProps];\n }\n }\n }\n }\n\n Children.forEach(props.children, (child) => {\n if (child === undefined || child === null) return;\n\n if (typeof child !== 'object' || !('props' in child)) throw Error(\n `\"${typeof child}\" is not a valid <Helmet> descendant`,\n );\n\n let nestedChildren: ReactNode;\n const childProps: HelmetChildProps = {};\n if (child.props) {\n for (const [key, value] of Object.entries(child.props)) {\n if (key === 'children') nestedChildren = value as ReactNode;\n else childProps[getPropName(key)] = value as unknown;\n }\n }\n\n let { type } = child;\n if (typeof type === 'symbol') type = (type as 'symbol').toString();\n assertChildType(type, nestedChildren);\n\n function assertStringChild(child: ReactNode): asserts child is string {\n if (typeof child !== 'string') {\n // TODO: We want to throw, but the legacy code did not, so we won't for\n // now.\n console.error(`child of ${type as string} element should be a string`);\n\n /*\n throw Error(\n // NOTE: assertChildType() above guarantees that `type` is a string,\n // although it is not expressed in a way TypeScript can automatically\n // pick up.\n );\n */\n }\n }\n\n switch (type) {\n case TAG_NAMES.BASE:\n res.base = childProps as BaseProps;\n break;\n\n case TAG_NAMES.BODY:\n res.bodyAttributes = childProps as BodyProps;\n break;\n\n case TAG_NAMES.FRAGMENT:\n mergeProps(res, reduceChildrenAndProps({ children: nestedChildren }));\n break;\n\n case TAG_NAMES.HTML:\n res.htmlAttributes = childProps as HtmlProps;\n break;\n\n case TAG_NAMES.LINK:\n case TAG_NAMES.META:\n if (nestedChildren) throw Error(\n `<${type} /> elements are self-closing and can not contain children. Refer to our API for more information.`,\n );\n pushToPropArray(res, type, childProps as LinkProps | MetaProps);\n break;\n\n case TAG_NAMES.NOSCRIPT:\n case TAG_NAMES.SCRIPT:\n if (nestedChildren !== undefined) {\n assertStringChild(nestedChildren);\n (childProps as NoscriptProps | ScriptProps)\n .innerHTML = nestedChildren;\n }\n pushToPropArray(res, type, childProps);\n break;\n\n case TAG_NAMES.STYLE:\n assertStringChild(nestedChildren);\n (childProps as StyleProps).cssText = nestedChildren;\n pushToPropArray(res, type, childProps as StyleProps);\n break;\n\n case TAG_NAMES.TITLE:\n res.titleAttributes = childProps as TitleProps;\n\n if (typeof nestedChildren === 'string') res.title = nestedChildren;\n\n // When title contains {} expressions the children are an array of\n // strings, and other values.\n else if (Array.isArray(nestedChildren)) res.title = nestedChildren.join('');\n break;\n\n default: {\n // TODO: Perhaps, we should remove HEAD entry from TAG_NAMES?\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const bad: TAG_NAMES.HEAD = type;\n }\n }\n });\n\n delete res.children;\n return res;\n}\n\nconst Helmet: FunctionComponent<HelmetProps> = (props) => {\n const context = use(Context);\n\n if (!context) throw Error(\n '<Helmet> component must be within a <HelmetProvider> children tree',\n );\n\n const id = useId();\n\n // TODO: Agh... we need it here to ensure that it works server-side,\n // and we need the same in the useEffect() below to ensure it works\n // client-side in strict mode (and, thus completely correctly from React's\n // pure component / side effect logic). It clearly should be optimized,\n // but let's care about it later.\n context.update(id, reduceChildrenAndProps(props));\n\n // TODO: I guess, these two useEffect() can be merged together, which should\n // also allow to simplify and optimize the client-side management of attrs and\n // elements managed by these. Though, keeping them separate is an easier way\n // for now to ensure backward compatibility.\n useEffect(() => {\n context.update(id, reduceChildrenAndProps(props));\n context.clientApply();\n });\n\n useEffect(() => () => context.update(id, undefined), [context, id]);\n\n return null;\n};\n\nexport default Helmet;\n"],"mappings":"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","ignoreList":[]}
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
var _exportNames = {
Helmet: true,
HelmetProvider: true
};
Object.defineProperty(exports, "Helmet", {
enumerable: true,
get: function () {
return _Helmet.default;
}
});
Object.defineProperty(exports, "HelmetProvider", {
enumerable: true,
get: function () {
return _Provider.default;
}
});
var _types = require("./types");
Object.keys(_types).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
if (key in exports && exports[key] === _types[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _types[key];
}
});
});
var _Helmet = _interopRequireDefault(require("./Helmet"));
var _Provider = _interopRequireDefault(require("./Provider"));
//# sourceMappingURL=index.js.map
{"version":3,"file":"index.js","names":["_types","require","Object","keys","forEach","key","prototype","hasOwnProperty","call","_exportNames","exports","defineProperty","enumerable","get","_Helmet","_interopRequireDefault","_Provider"],"sources":["../../src/index.tsx"],"sourcesContent":["export * from './types';\n\nexport { default as Helmet } from './Helmet';\nexport { default as HelmetProvider } from './Provider';\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAAA,IAAAA,MAAA,GAAAC,OAAA;AAAAC,MAAA,CAAAC,IAAA,CAAAH,MAAA,EAAAI,OAAA,WAAAC,GAAA;EAAA,IAAAA,GAAA,kBAAAA,GAAA;EAAA,IAAAH,MAAA,CAAAI,SAAA,CAAAC,cAAA,CAAAC,IAAA,CAAAC,YAAA,EAAAJ,GAAA;EAAA,IAAAA,GAAA,IAAAK,OAAA,IAAAA,OAAA,CAAAL,GAAA,MAAAL,MAAA,CAAAK,GAAA;EAAAH,MAAA,CAAAS,cAAA,CAAAD,OAAA,EAAAL,GAAA;IAAAO,UAAA;IAAAC,GAAA,WAAAA,CAAA;MAAA,OAAAb,MAAA,CAAAK,GAAA;IAAA;EAAA;AAAA;AAEA,IAAAS,OAAA,GAAAC,sBAAA,CAAAd,OAAA;AACA,IAAAe,SAAA,GAAAD,sBAAA,CAAAd,OAAA","ignoreList":[]}
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = exports.Context = void 0;
var _react = require("react");
var _server = require("./server");
var _constants = require("./constants");
var _utils = require("./utils");
var _client = require("./client");
var _jsxRuntime = require("react/jsx-runtime");
const Context = exports.Context = /*#__PURE__*/(0, _react.createContext)(undefined);
const HelmetProvider = _ref => {
let {
children,
context
} = _ref;
const {
current: heap
} = (0, _react.useRef)({
firstRender: true,
helmets: [],
state: undefined
});
const contextValueRef = (0, _react.useRef)(null);
if (!contextValueRef.current) {
contextValueRef.current = {
clientApply() {
if (_constants.IS_DOM_ENVIRONMENT && !heap.state) {
heap.state = (0, _utils.calcAggregatedState)(heap.helmets);
if (heap.state.defer) {
if (heap.nextAnimFrameId === undefined) {
heap.nextAnimFrameId = requestAnimationFrame(() => {
heap.state ??= (0, _utils.calcAggregatedState)(heap.helmets);
(0, _client.commitTagChanges)(heap.state, heap.firstRender);
heap.firstRender = false;
delete heap.nextAnimFrameId;
});
}
} else {
if (heap.nextAnimFrameId !== undefined) {
cancelAnimationFrame(heap.nextAnimFrameId);
delete heap.nextAnimFrameId;
}
(0, _client.commitTagChanges)(heap.state, heap.firstRender);
heap.firstRender = false;
}
}
},
update(id, props) {
const idx = heap.helmets.findIndex(item => item[0] === id);
if (idx >= 0) {
delete heap.state;
if (props) heap.helmets[idx][1] = props;else heap.helmets.splice(idx, 1);
} else if (props) {
delete heap.state;
heap.helmets.push([id, props]);
}
}
};
}
if (context && (!context.helmet || context.helmet !== heap.serverState)) {
heap.serverState ??= (0, _server.newServerState)(heap);
context.helmet = heap.serverState;
}
return /*#__PURE__*/(0, _jsxRuntime.jsx)(Context, {
value: contextValueRef.current,
children: children
});
};
var _default = exports.default = HelmetProvider;
//# sourceMappingURL=Provider.js.map
{"version":3,"file":"Provider.js","names":["_react","require","_server","_constants","_utils","_client","_jsxRuntime","Context","exports","createContext","undefined","HelmetProvider","_ref","children","context","current","heap","useRef","firstRender","helmets","state","contextValueRef","clientApply","IS_DOM_ENVIRONMENT","calcAggregatedState","defer","nextAnimFrameId","requestAnimationFrame","commitTagChanges","cancelAnimationFrame","update","id","props","idx","findIndex","item","splice","push","helmet","serverState","newServerState","jsx","value","_default","default"],"sources":["../../src/Provider.tsx"],"sourcesContent":["import {\n type FunctionComponent,\n type ReactNode,\n createContext,\n useRef,\n} from 'react';\n\nimport type {\n ContextValue,\n HelmetDataContext,\n HelmetProps,\n HelmetProviderHeap,\n} from './types';\n\nimport { newServerState } from './server';\nimport { IS_DOM_ENVIRONMENT } from './constants';\nimport { calcAggregatedState } from './utils';\nimport { commitTagChanges } from './client';\n\nexport const Context = createContext<ContextValue | undefined>(undefined);\n\ntype ProviderProps = {\n children?: ReactNode;\n context?: HelmetDataContext;\n};\n\nconst HelmetProvider: FunctionComponent<ProviderProps> = ({\n children,\n context,\n}) => {\n const { current: heap } = useRef<HelmetProviderHeap>({\n firstRender: true,\n helmets: [],\n state: undefined,\n });\n\n const contextValueRef = useRef<ContextValue>(null);\n\n if (!contextValueRef.current) {\n contextValueRef.current = {\n clientApply() {\n if (IS_DOM_ENVIRONMENT && !heap.state) {\n heap.state = calcAggregatedState(heap.helmets);\n if (heap.state.defer) {\n if (heap.nextAnimFrameId === undefined) {\n heap.nextAnimFrameId = requestAnimationFrame(() => {\n heap.state ??= calcAggregatedState(heap.helmets);\n commitTagChanges(heap.state, heap.firstRender);\n heap.firstRender = false;\n delete heap.nextAnimFrameId;\n });\n }\n } else {\n if (heap.nextAnimFrameId !== undefined) {\n cancelAnimationFrame(heap.nextAnimFrameId);\n delete heap.nextAnimFrameId;\n }\n commitTagChanges(heap.state, heap.firstRender);\n heap.firstRender = false;\n }\n }\n },\n update(id: string, props: HelmetProps | undefined) {\n const idx = heap.helmets.findIndex((item) => item[0] === id);\n if (idx >= 0) {\n delete heap.state;\n if (props) heap.helmets[idx]![1] = props;\n else heap.helmets.splice(idx, 1);\n } else if (props) {\n delete heap.state;\n heap.helmets.push([id, props]);\n }\n },\n };\n }\n\n if (context && (!context.helmet || context.helmet !== heap.serverState)) {\n heap.serverState ??= newServerState(heap);\n context.helmet = heap.serverState;\n }\n\n return <Context value={contextValueRef.current}>{children}</Context>;\n};\n\nexport default HelmetProvider;\n"],"mappings":";;;;;;AAAA,IAAAA,MAAA,GAAAC,OAAA;AAcA,IAAAC,OAAA,GAAAD,OAAA;AACA,IAAAE,UAAA,GAAAF,OAAA;AACA,IAAAG,MAAA,GAAAH,OAAA;AACA,IAAAI,OAAA,GAAAJ,OAAA;AAA4C,IAAAK,WAAA,GAAAL,OAAA;AAErC,MAAMM,OAAO,GAAAC,OAAA,CAAAD,OAAA,gBAAG,IAAAE,oBAAa,EAA2BC,SAAS,CAAC;AAOzE,MAAMC,cAAgD,GAAGC,IAAA,IAGnD;EAAA,IAHoD;IACxDC,QAAQ;IACRC;EACF,CAAC,GAAAF,IAAA;EACC,MAAM;IAAEG,OAAO,EAAEC;EAAK,CAAC,GAAG,IAAAC,aAAM,EAAqB;IACnDC,WAAW,EAAE,IAAI;IACjBC,OAAO,EAAE,EAAE;IACXC,KAAK,EAAEV;EACT,CAAC,CAAC;EAEF,MAAMW,eAAe,GAAG,IAAAJ,aAAM,EAAe,IAAI,CAAC;EAElD,IAAI,CAACI,eAAe,CAACN,OAAO,EAAE;IAC5BM,eAAe,CAACN,OAAO,GAAG;MACxBO,WAAWA,CAAA,EAAG;QACZ,IAAIC,6BAAkB,IAAI,CAACP,IAAI,CAACI,KAAK,EAAE;UACrCJ,IAAI,CAACI,KAAK,GAAG,IAAAI,0BAAmB,EAACR,IAAI,CAACG,OAAO,CAAC;UAC9C,IAAIH,IAAI,CAACI,KAAK,CAACK,KAAK,EAAE;YACpB,IAAIT,IAAI,CAACU,eAAe,KAAKhB,SAAS,EAAE;cACtCM,IAAI,CAACU,eAAe,GAAGC,qBAAqB,CAAC,MAAM;gBACjDX,IAAI,CAACI,KAAK,KAAK,IAAAI,0BAAmB,EAACR,IAAI,CAACG,OAAO,CAAC;gBAChD,IAAAS,wBAAgB,EAACZ,IAAI,CAACI,KAAK,EAAEJ,IAAI,CAACE,WAAW,CAAC;gBAC9CF,IAAI,CAACE,WAAW,GAAG,KAAK;gBACxB,OAAOF,IAAI,CAACU,eAAe;cAC7B,CAAC,CAAC;YACJ;UACF,CAAC,MAAM;YACL,IAAIV,IAAI,CAACU,eAAe,KAAKhB,SAAS,EAAE;cACtCmB,oBAAoB,CAACb,IAAI,CAACU,eAAe,CAAC;cAC1C,OAAOV,IAAI,CAACU,eAAe;YAC7B;YACA,IAAAE,wBAAgB,EAACZ,IAAI,CAACI,KAAK,EAAEJ,IAAI,CAACE,WAAW,CAAC;YAC9CF,IAAI,CAACE,WAAW,GAAG,KAAK;UAC1B;QACF;MACF,CAAC;MACDY,MAAMA,CAACC,EAAU,EAAEC,KAA8B,EAAE;QACjD,MAAMC,GAAG,GAAGjB,IAAI,CAACG,OAAO,CAACe,SAAS,CAAEC,IAAI,IAAKA,IAAI,CAAC,CAAC,CAAC,KAAKJ,EAAE,CAAC;QAC5D,IAAIE,GAAG,IAAI,CAAC,EAAE;UACZ,OAAOjB,IAAI,CAACI,KAAK;UACjB,IAAIY,KAAK,EAAEhB,IAAI,CAACG,OAAO,CAACc,GAAG,CAAC,CAAE,CAAC,CAAC,GAAGD,KAAK,CAAC,KACpChB,IAAI,CAACG,OAAO,CAACiB,MAAM,CAACH,GAAG,EAAE,CAAC,CAAC;QAClC,CAAC,MAAM,IAAID,KAAK,EAAE;UAChB,OAAOhB,IAAI,CAACI,KAAK;UACjBJ,IAAI,CAACG,OAAO,CAACkB,IAAI,CAAC,CAACN,EAAE,EAAEC,KAAK,CAAC,CAAC;QAChC;MACF;IACF,CAAC;EACH;EAEA,IAAIlB,OAAO,KAAK,CAACA,OAAO,CAACwB,MAAM,IAAIxB,OAAO,CAACwB,MAAM,KAAKtB,IAAI,CAACuB,WAAW,CAAC,EAAE;IACvEvB,IAAI,CAACuB,WAAW,KAAK,IAAAC,sBAAc,EAACxB,IAAI,CAAC;IACzCF,OAAO,CAACwB,MAAM,GAAGtB,IAAI,CAACuB,WAAW;EACnC;EAEA,oBAAO,IAAAjC,WAAA,CAAAmC,GAAA,EAAClC,OAAO;IAACmC,KAAK,EAAErB,eAAe,CAACN,OAAQ;IAAAF,QAAA,EAAEA;EAAQ,CAAU,CAAC;AACtE,CAAC;AAAC,IAAA8B,QAAA,GAAAnC,OAAA,CAAAoC,OAAA,GAEajC,cAAc","ignoreList":[]}
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.newServerState = newServerState;
var _react = require("react");
var _constants = require("./constants");
var _utils = require("./utils");
var _jsxRuntime = require("react/jsx-runtime");
const SELF_CLOSING_TAGS = [_constants.TAG_NAMES.NOSCRIPT, _constants.TAG_NAMES.SCRIPT, _constants.TAG_NAMES.STYLE];
const encodeSpecialCharacters = function (str) {
let encode = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
if (encode === false) {
return String(str);
}
return String(str).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#x27;');
};
function generateElementAttributesAsString(attrs) {
let res = '';
for (const [name, value] of Object.entries(attrs)) {
const attr = (0, _utils.propToAttr)(name);
const neu = value === undefined ? attr : `${attr}="${value}"`;
if (neu && res) res += ' ';
res += neu;
}
return res;
}
const generateTitleAsString = (title, attrs, encode) => {
let attrsStr = generateElementAttributesAsString(attrs);
if (attrsStr) attrsStr = ` ${attrsStr}`;
const flattenedTitle = (0, _utils.flattenArray)(title);
return `<title ${_constants.HELMET_ATTRIBUTE}="true"${attrsStr}>${encodeSpecialCharacters(flattenedTitle, encode)}</title>`;
};
function generateTagsAsString(type, tags, encode) {
let res = '';
for (const tag of tags) {
let attributeHtml = '';
const entries = Object.entries(tag);
for (const [name, value] of entries) {
if (!(name === _constants.TAG_PROPERTIES.INNER_HTML || name === _constants.TAG_PROPERTIES.CSS_TEXT)) {
const attrName = _constants.HTML_TAG_MAP[name] ?? name;
const attr = value === undefined ? attrName : `${attrName}="${encodeSpecialCharacters(value, encode)}"`;
if (attributeHtml) attributeHtml += ` ${attr}`;else attributeHtml = attr;
}
}
const tagContent = tag.innerHTML ?? tag.cssText ?? '';
const isSelfClosing = !SELF_CLOSING_TAGS.includes(type);
res += `<${type} ${_constants.HELMET_ATTRIBUTE}="true" ${attributeHtml}${isSelfClosing ? '/>' : `>${tagContent}</${type}>`}`;
}
return res;
}
/**
* Given a map of element attribute names & values it returns the corresponding
* map of element properties & values (i.e. replacing some attribute names by
* their corresponding property names).
*/
function mapElementAttributesToProps(attributes) {
let ops = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
const res = {};
if (ops.addHelmetDataAttr) res[_constants.HELMET_ATTRIBUTE] = true;
if (ops.addKey !== undefined) res.key = ops.addKey;
for (const [attrName, value] of Object.entries(attributes)) {
const propName = _constants.REACT_TAG_MAP[attrName] ?? attrName;
switch (propName) {
// cssText and innerHTML props get a special treatment to avoid that React
// escapes their values.
case 'cssText':
case 'innerHTML':
res.dangerouslySetInnerHTML = {
__html: value
};
break;
default:
res[propName] = value;
}
}
return res;
}
function renderTitle(title, attrs) {
// NOTE: Rendered as array to match legacy behavior.
return [/*#__PURE__*/(0, _jsxRuntime.jsx)("title", {
...mapElementAttributesToProps(attrs, {
addHelmetDataAttr: true
}),
children: title
}, title)];
}
function renderElement(type, attrs, key) {
return /*#__PURE__*/(0, _react.createElement)(type, mapElementAttributesToProps(attrs, {
addHelmetDataAttr: true,
addKey: key
}));
}
function renderElements(type, attrs) {
const res = [];
for (let i = 0; i < attrs.length; ++i) {
res.push(renderElement(type, attrs[i], i));
}
return res;
}
/*
function newHelmetDatumForTitle(
title: string,
attrs: Attributes = {},
encode: boolean,
): HelmetDatum {
return {
};
}
const getPriorityMethods = ({
metaTags,
linkTags,
scriptTags,
encode,
}: MappedServerState) => {
const meta = prioritizer(metaTags, SEO_PRIORITY_TAGS.meta);
const link = prioritizer(linkTags, SEO_PRIORITY_TAGS.link);
const script = prioritizer(scriptTags, SEO_PRIORITY_TAGS.script);
// need to have toComponent() and toString()
const priorityMethods = {
toComponent: () => [
...renderElements(TAG_NAMES.META, meta.priority),
...renderElements(TAG_NAMES.LINK, link.priority),
...renderElements(TAG_NAMES.SCRIPT, script.priority),
],
// generate all the tags as strings and concatenate them
toString: () => `${
generateTagsAsString(TAG_NAMES.META, meta.priority, encode)} ${
generateTagsAsString(TAG_NAMES.LINK, link.priority, encode)} ${
generateTagsAsString(TAG_NAMES.SCRIPT, script.priority, encode)}`,
};
return {
priorityMethods,
metaTags: meta.default as HTMLMetaElement[],
linkTags: link.default as HTMLLinkElement[],
scriptTags: script.default as HTMLScriptElement[],
};
};
/*
function mapStateOnServer(props: MappedServerState): HelmetServerState {
const {
baseTag,
bodyAttributes,
encode = true,
htmlAttributes,
noscriptTags,
prioritizeSeoTags,
styleTags,
title = '',
titleAttributes,
} = props;
let { linkTags, metaTags, scriptTags } = props;
let priorityMethods: HelmetDatum = {
toComponent: () => null,
toString: () => '',
};
if (prioritizeSeoTags) {
({
priorityMethods,
linkTags,
metaTags,
scriptTags,
} = getPriorityMethods(props));
}
return {
priority: priorityMethods,
};
}
*/
// export default mapStateOnServer;
// TODO: So... refactor it, it should be based on the Helmet provider
// heap, and it should calculate its state into that, and then use it.
function newServerState(heap) {
// TODO: Should this function to be attached to the heap itself?
const getState = () => {
heap.state ??= (0, _utils.calcAggregatedState)(heap.helmets);
return heap.state;
};
return {
base: {
toComponent() {
const props = getState().base;
return props ? renderElements('base', [props]) : [];
},
toString() {
const s = getState();
return s.base ? generateTagsAsString('base', [s.base], s.encodeSpecialCharacters) : '';
}
},
bodyAttributes: {
toComponent() {
const props = getState().bodyAttributes;
return mapElementAttributesToProps(props ?? {});
},
toString() {
const props = getState().bodyAttributes;
return generateElementAttributesAsString(props ?? {});
}
},
htmlAttributes: {
toComponent() {
const props = getState().htmlAttributes;
return mapElementAttributesToProps(props ?? {});
},
toString() {
const props = getState().htmlAttributes;
return generateElementAttributesAsString(props ?? {});
}
},
link: {
toComponent() {
return renderElements('link', getState().links ?? []);
},
toString() {
const s = getState();
return generateTagsAsString('link', s.links ?? [], s.encodeSpecialCharacters);
}
},
meta: {
toComponent() {
return renderElements('meta', getState().meta ?? []);
},
toString() {
const s = getState();
return generateTagsAsString('meta', s.meta ?? [], s.encodeSpecialCharacters);
}
},
noscript: {
toComponent() {
return renderElements('noscript', getState().noscript ?? []);
},
toString() {
const s = getState();
return generateTagsAsString('noscript', s.noscript ?? [], s.encodeSpecialCharacters);
}
},
priority: {
toComponent() {
const s = getState();
return [...renderElements('meta', s.priority?.meta ?? []), ...renderElements('link', s.priority?.links ?? []), ...renderElements('script', s.priority?.script ?? [])];
},
toString() {
const s = getState();
const meta = generateTagsAsString('meta', s.priority?.meta ?? [], s.encodeSpecialCharacters);
const link = generateTagsAsString('link', s.priority?.links ?? [], s.encodeSpecialCharacters);
const script = generateTagsAsString('script', s.priority?.script ?? [], s.encodeSpecialCharacters);
let res = meta;
if (link) {
if (res) res += ' ';
res += link;
}
if (script) {
if (res) res += ' ';
res += script;
}
return res;
}
},
script: {
toComponent() {
return renderElements('script', getState().script ?? []);
},
toString() {
const s = getState();
return generateTagsAsString('script', s.script ?? [], s.encodeSpecialCharacters);
}
},
style: {
toComponent() {
return renderElements('style', getState().style ?? []);
},
toString() {
const s = getState();
return generateTagsAsString('style', s.style ?? [], s.encodeSpecialCharacters);
}
},
title: {
toComponent() {
const s = getState();
return renderTitle(s.title ?? '', s.titleAttributes ?? {});
},
toString() {
const s = getState();
return generateTitleAsString(s.title ?? '', s.titleAttributes ?? {}, s.encodeSpecialCharacters);
}
}
};
}
//# sourceMappingURL=server.js.map
{"version":3,"file":"server.js","names":["_react","require","_constants","_utils","_jsxRuntime","SELF_CLOSING_TAGS","TAG_NAMES","NOSCRIPT","SCRIPT","STYLE","encodeSpecialCharacters","str","encode","arguments","length","undefined","String","replace","generateElementAttributesAsString","attrs","res","name","value","Object","entries","attr","propToAttr","neu","generateTitleAsString","title","attrsStr","flattenedTitle","flattenArray","HELMET_ATTRIBUTE","generateTagsAsString","type","tags","tag","attributeHtml","TAG_PROPERTIES","INNER_HTML","CSS_TEXT","attrName","HTML_TAG_MAP","tagContent","innerHTML","cssText","isSelfClosing","includes","mapElementAttributesToProps","attributes","ops","addHelmetDataAttr","addKey","key","propName","REACT_TAG_MAP","dangerouslySetInnerHTML","__html","renderTitle","jsx","children","renderElement","createElement","renderElements","i","push","newServerState","heap","getState","state","calcAggregatedState","helmets","base","toComponent","props","toString","s","bodyAttributes","htmlAttributes","link","links","meta","noscript","priority","script","style","titleAttributes"],"sources":["../../src/server.tsx"],"sourcesContent":["import {\n type HTMLAttributes,\n type Key,\n type ReactNode,\n createElement,\n} from 'react';\n\nimport {\n HELMET_ATTRIBUTE,\n TAG_NAMES,\n REACT_TAG_MAP,\n TAG_PROPERTIES,\n HTML_TAG_MAP,\n} from './constants';\n\nimport type {\n HelmetProviderHeap,\n HelmetServerState,\n ScriptProps,\n StyleProps,\n TitleProps,\n} from './types';\n\nimport {\n calcAggregatedState,\n flattenArray,\n propToAttr,\n} from './utils';\n\nconst SELF_CLOSING_TAGS: string[] = [\n TAG_NAMES.NOSCRIPT,\n TAG_NAMES.SCRIPT,\n TAG_NAMES.STYLE,\n];\n\nconst encodeSpecialCharacters = (str: string, encode = true) => {\n if (encode === false) {\n return String(str);\n }\n\n return String(str)\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&#x27;');\n};\n\nfunction generateElementAttributesAsString<T>(\n attrs: HTMLAttributes<T>,\n): string {\n let res: string = '';\n\n for (const [name, value] of Object.entries(attrs)) {\n const attr = propToAttr(name);\n const neu = value === undefined ? attr : `${attr}=\"${value}\"`;\n if (neu && res) res += ' ';\n res += neu;\n }\n\n return res;\n}\n\nconst generateTitleAsString = (\n title: string,\n attrs: TitleProps,\n encode: boolean,\n) => {\n let attrsStr = generateElementAttributesAsString(attrs);\n if (attrsStr) attrsStr = ` ${attrsStr}`;\n\n const flattenedTitle = flattenArray(title);\n\n return `<title ${HELMET_ATTRIBUTE}=\"true\"${attrsStr}>${\n encodeSpecialCharacters(flattenedTitle, encode)\n }</title>`;\n};\n\nfunction generateTagsAsString<T>(\n type: string,\n tags: HTMLAttributes<T>[],\n encode: boolean,\n): string {\n let res = '';\n\n for (const tag of tags) {\n let attributeHtml = '';\n\n const entries = Object.entries(tag);\n for (const [name, value] of entries) {\n if (!(name === TAG_PROPERTIES.INNER_HTML as string\n || name === TAG_PROPERTIES.CSS_TEXT as string)) {\n const attrName = HTML_TAG_MAP[name] ?? name;\n const attr = value === undefined ? attrName : `${attrName}=\"${encodeSpecialCharacters(value as string, encode)}\"`;\n if (attributeHtml) attributeHtml += ` ${attr}`;\n else attributeHtml = attr;\n }\n }\n\n const tagContent = (tag as ScriptProps).innerHTML ?? (tag as StyleProps).cssText ?? '';\n\n const isSelfClosing = !SELF_CLOSING_TAGS.includes(type);\n\n res += `<${type} ${HELMET_ATTRIBUTE}=\"true\" ${attributeHtml}${\n isSelfClosing ? '/>' : `>${tagContent}</${type}>`\n }`;\n }\n\n return res;\n}\n\n/**\n * Given a map of element attribute names & values it returns the corresponding\n * map of element properties & values (i.e. replacing some attribute names by\n * their corresponding property names).\n */\n\nfunction mapElementAttributesToProps<T>(\n attributes: HTMLAttributes<T>,\n ops: { addHelmetDataAttr?: boolean; addKey?: Key } = {},\n): Record<string, unknown> {\n const res: Record<string, unknown> = {};\n if (ops.addHelmetDataAttr) res[HELMET_ATTRIBUTE] = true;\n if (ops.addKey !== undefined) res.key = ops.addKey;\n for (const [attrName, value] of Object.entries(attributes)) {\n const propName = REACT_TAG_MAP[attrName] ?? attrName;\n\n switch (propName) {\n // cssText and innerHTML props get a special treatment to avoid that React\n // escapes their values.\n case 'cssText':\n case 'innerHTML':\n res.dangerouslySetInnerHTML = { __html: value as unknown };\n break;\n default:\n res[propName] = value;\n }\n }\n return res;\n}\n\nfunction renderTitle(title: string, attrs: TitleProps): ReactNode {\n // NOTE: Rendered as array to match legacy behavior.\n return [\n <title\n key={title}\n {...mapElementAttributesToProps(attrs, { addHelmetDataAttr: true })}\n >\n {title}\n </title>,\n ];\n}\n\nfunction renderElement<T>(\n type: string,\n attrs: HTMLAttributes<T>,\n key?: Key,\n): ReactNode {\n return createElement(type, mapElementAttributesToProps(attrs, {\n addHelmetDataAttr: true,\n addKey: key,\n }));\n}\n\nfunction renderElements<T>(\n type: string,\n attrs: HTMLAttributes<T>[],\n): ReactNode[] {\n const res: ReactNode[] = [];\n for (let i = 0; i < attrs.length; ++i) {\n res.push(renderElement(type, attrs[i]!, i));\n }\n return res;\n}\n\n/*\n\nfunction newHelmetDatumForTitle(\n title: string,\n attrs: Attributes = {},\n encode: boolean,\n): HelmetDatum {\n return {\n\n };\n}\n\nconst getPriorityMethods = ({\n metaTags,\n linkTags,\n scriptTags,\n encode,\n}: MappedServerState) => {\n const meta = prioritizer(metaTags, SEO_PRIORITY_TAGS.meta);\n const link = prioritizer(linkTags, SEO_PRIORITY_TAGS.link);\n const script = prioritizer(scriptTags, SEO_PRIORITY_TAGS.script);\n\n // need to have toComponent() and toString()\n const priorityMethods = {\n toComponent: () => [\n ...renderElements(TAG_NAMES.META, meta.priority),\n ...renderElements(TAG_NAMES.LINK, link.priority),\n ...renderElements(TAG_NAMES.SCRIPT, script.priority),\n ],\n\n // generate all the tags as strings and concatenate them\n toString: () => `${\n generateTagsAsString(TAG_NAMES.META, meta.priority, encode)} ${\n generateTagsAsString(TAG_NAMES.LINK, link.priority, encode)} ${\n generateTagsAsString(TAG_NAMES.SCRIPT, script.priority, encode)}`,\n };\n\n return {\n priorityMethods,\n metaTags: meta.default as HTMLMetaElement[],\n linkTags: link.default as HTMLLinkElement[],\n scriptTags: script.default as HTMLScriptElement[],\n };\n};\n\n/*\nfunction mapStateOnServer(props: MappedServerState): HelmetServerState {\n const {\n baseTag,\n bodyAttributes,\n encode = true,\n htmlAttributes,\n noscriptTags,\n prioritizeSeoTags,\n styleTags,\n title = '',\n titleAttributes,\n } = props;\n let { linkTags, metaTags, scriptTags } = props;\n let priorityMethods: HelmetDatum = {\n toComponent: () => null,\n toString: () => '',\n };\n if (prioritizeSeoTags) {\n ({\n priorityMethods,\n linkTags,\n metaTags,\n scriptTags,\n } = getPriorityMethods(props));\n }\n return {\n priority: priorityMethods,\n };\n}\n*/\n\n// export default mapStateOnServer;\n\n// TODO: So... refactor it, it should be based on the Helmet provider\n// heap, and it should calculate its state into that, and then use it.\n\nexport function newServerState(heap: HelmetProviderHeap): HelmetServerState {\n // TODO: Should this function to be attached to the heap itself?\n const getState = () => {\n heap.state ??= calcAggregatedState(heap.helmets);\n return heap.state;\n };\n\n return {\n base: {\n toComponent() {\n const props = getState().base;\n return props ? renderElements('base', [props]) : [];\n },\n toString() {\n const s = getState();\n return s.base ? generateTagsAsString('base', [s.base], s.encodeSpecialCharacters) : '';\n },\n },\n bodyAttributes: {\n toComponent() {\n const props = getState().bodyAttributes;\n return mapElementAttributesToProps(props ?? {});\n },\n toString() {\n const props = getState().bodyAttributes;\n return generateElementAttributesAsString(props ?? {});\n },\n },\n htmlAttributes: {\n toComponent() {\n const props = getState().htmlAttributes;\n return mapElementAttributesToProps(props ?? {});\n },\n toString() {\n const props = getState().htmlAttributes;\n return generateElementAttributesAsString(props ?? {});\n },\n },\n link: {\n toComponent() {\n return renderElements('link', getState().links ?? []);\n },\n toString() {\n const s = getState();\n return generateTagsAsString('link', s.links ?? [], s.encodeSpecialCharacters);\n },\n },\n meta: {\n toComponent() {\n return renderElements('meta', getState().meta ?? []);\n },\n toString() {\n const s = getState();\n return generateTagsAsString('meta', s.meta ?? [], s.encodeSpecialCharacters);\n },\n },\n noscript: {\n toComponent() {\n return renderElements('noscript', getState().noscript ?? []);\n },\n toString() {\n const s = getState();\n return generateTagsAsString('noscript', s.noscript ?? [], s.encodeSpecialCharacters);\n },\n },\n priority: {\n toComponent() {\n const s = getState();\n return [\n ...renderElements('meta', s.priority?.meta ?? []),\n ...renderElements('link', s.priority?.links ?? []),\n ...renderElements('script', s.priority?.script ?? []),\n ];\n },\n toString() {\n const s = getState();\n const meta = generateTagsAsString('meta', s.priority?.meta ?? [], s.encodeSpecialCharacters);\n const link = generateTagsAsString('link', s.priority?.links ?? [], s.encodeSpecialCharacters);\n const script = generateTagsAsString('script', s.priority?.script ?? [], s.encodeSpecialCharacters);\n\n let res = meta;\n if (link) {\n if (res) res += ' ';\n res += link;\n }\n if (script) {\n if (res) res += ' ';\n res += script;\n }\n\n return res;\n },\n },\n script: {\n toComponent() {\n return renderElements('script', getState().script ?? []);\n },\n toString() {\n const s = getState();\n return generateTagsAsString('script', s.script ?? [], s.encodeSpecialCharacters);\n },\n },\n style: {\n toComponent() {\n return renderElements('style', getState().style ?? []);\n },\n toString() {\n const s = getState();\n return generateTagsAsString('style', s.style ?? [], s.encodeSpecialCharacters);\n },\n },\n title: {\n toComponent() {\n const s = getState();\n return renderTitle(s.title ?? '', s.titleAttributes ?? {});\n },\n toString() {\n const s = getState();\n return generateTitleAsString(s.title ?? '', s.titleAttributes ?? {}, s.encodeSpecialCharacters);\n },\n },\n };\n}\n"],"mappings":"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","ignoreList":[]}
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
//# sourceMappingURL=types.js.map
{"version":3,"file":"types.js","names":[],"sources":["../../src/types.ts"],"sourcesContent":["import type {\n BaseHTMLAttributes,\n HtmlHTMLAttributes,\n HTMLAttributes,\n LinkHTMLAttributes,\n MetaHTMLAttributes,\n ReactNode,\n ScriptHTMLAttributes,\n StyleHTMLAttributes,\n} from 'react';\n\nexport type BaseProps = BaseHTMLAttributes<HTMLBaseElement>;\nexport type BodyProps = HTMLAttributes<HTMLBodyElement>;\nexport type HtmlProps = HtmlHTMLAttributes<HTMLHtmlElement>;\nexport type LinkProps = LinkHTMLAttributes<HTMLLinkElement>;\nexport type MetaProps = MetaHTMLAttributes<HTMLMetaElement>;\n\nexport type NoscriptProps = HTMLAttributes<HTMLElement> & {\n innerHTML?: string;\n};\n\nexport type ScriptProps = ScriptHTMLAttributes<HTMLScriptElement> & {\n innerHTML?: string;\n};\n\nexport type StyleProps = StyleHTMLAttributes<HTMLStyleElement> & {\n cssText?: string;\n};\n\nexport type TitleProps = HTMLAttributes<HTMLTitleElement>;\n\nexport type HelmetChildProps =\n | BaseProps | BodyProps | HtmlProps | LinkProps | MetaProps | NoscriptProps\n | ScriptProps | StyleProps | TitleProps;\n\n/**\n * String data for title.\n */\nexport type StringData = {\n title?: string;\n titleTemplate?: string;\n};\n\nexport type HelmetTags = {\n baseTag: HTMLBaseElement[];\n linkTags: HTMLLinkElement[];\n metaTags: HTMLMetaElement[];\n noscriptTags: HTMLElement[];\n scriptTags: HTMLScriptElement[];\n styleTags: HTMLStyleElement[];\n};\n\nexport type HelmetDatum<T = ReactNode> = {\n toString(): string;\n toComponent(): T;\n};\n\nexport type HelmetHTMLBodyDatum = HelmetDatum<HTMLAttributes<HTMLBodyElement>>;\n\nexport type HelmetHTMLElementDatum =\n HelmetDatum<HTMLAttributes<HTMLHtmlElement>>;\n\nexport type HelmetServerState = {\n base: HelmetDatum;\n bodyAttributes: HelmetHTMLBodyDatum;\n htmlAttributes: HelmetHTMLElementDatum;\n link: HelmetDatum;\n meta: HelmetDatum;\n noscript: HelmetDatum;\n script: HelmetDatum;\n style: HelmetDatum;\n title: HelmetDatum;\n\n // TODO: Why is it needed? Can't it be a part of `title` value?\n titleAttributes?: HelmetDatum;\n\n priority: HelmetDatum;\n};\n\nexport type StateUpdate = HelmetTags & {\n bodyAttributes: BodyProps;\n defer: boolean;\n htmlAttributes: HtmlProps;\n\n // TODO: The signature of this callback is essentially the same as\n // OnChangeClientState, declared inside Helmet module; and there is\n // a circular dependency between that declaration and this StateUpdate type.\n // Also, not sure this field is really necessary inside StateUpdate?\n onChangeClientState: (\n newState: StateUpdate,\n addedTags: Partial<HelmetTags>,\n removedTags: Partial<HelmetTags>,\n ) => void;\n\n title: string;\n titleAttributes: TitleProps;\n};\n\n// TODO: Rewise the typing!\nexport type OnChangeClientState = (\n // TODO: So... the new state should be a map of attribute/value maps\n // for all children elements.\n newState: StateUpdate,\n addedTags: Partial<HelmetTags>,\n removedTags: Partial<HelmetTags>,\n) => void;\n\n/**\n * A subset of <Helmet> properties, corresponding to prop arrays for\n * elements that may be present in DOM multiple times.\n */\nexport type HelmetPropArrays = {\n link?: LinkProps[];\n meta?: MetaProps[];\n noscript?: NoscriptProps[];\n script?: ScriptProps[];\n style?: StyleProps[];\n};\n\nexport type PropArrayItem<T extends keyof HelmetPropArrays>\n = Exclude<HelmetPropArrays[T], undefined>[number];\n\n/**\n * A subset of <Helmet> properties, corresponding to props for elements\n * that may be present in DOM a single time at most.\n */\nexport type HelmetPropObjects = {\n bodyAttributes?: BodyProps;\n htmlAttributes?: HtmlProps;\n titleAttributes?: TitleProps;\n};\n\nexport type HelmetPropBooleans = {\n prioritizeSeoTags?: boolean;\n};\n\n/**\n * Properties accepted by <Helmet> components.\n */\nexport type HelmetProps = HelmetPropArrays\n & HelmetPropObjects\n & HelmetPropBooleans\n & {\n base?: BaseProps;\n children?: ReactNode;\n defaultTitle?: string;\n defer?: boolean;\n encodeSpecialCharacters?: boolean;\n onChangeClientState?: OnChangeClientState;\n title?: string;\n titleTemplate?: string;\n };\n\nexport type RegisteredHelmetPropsArray\n = Array<[id: string, props: HelmetProps]>;\n\n/**\n * The overall Helmet state, aggregated from props of all <Helmet> instances\n * registered with the Helmet context provider.\n */\nexport type AggregatedState = {\n base: BaseProps | undefined;\n bodyAttributes: BodyProps | undefined;\n defer: boolean | undefined;\n encodeSpecialCharacters: boolean;\n htmlAttributes: HtmlProps | undefined;\n links: LinkProps[] | undefined;\n meta: MetaProps[] | undefined;\n noscript: NoscriptProps[] | undefined;\n onChangeClientState: OnChangeClientState | undefined;\n priority: {\n links: LinkProps[] | undefined;\n meta: MetaProps[] | undefined;\n script: ScriptProps[] | undefined;\n } | undefined;\n script: ScriptProps[] | undefined;\n style: StyleProps[] | undefined;\n title: string | undefined;\n titleAttributes: TitleProps | undefined;\n};\n\nexport type MappedServerState = HelmetTags & { encode?: boolean };\n\n/**\n * Server-side rendering context.\n */\nexport type HelmetDataContext = {\n helmet?: HelmetServerState;\n};\n\n/**\n * The value of internal context used by Helmet to communicate between its\n * context provider and <Helmet> components within its children tree.\n */\nexport type ContextValue = {\n clientApply: () => void;\n\n /** One function to register, update, and un-register <Helmet> instances\n * (or, more precisely their current aggregated props, aggregated between\n * the actual props of <Helmet> instance and its children). */\n update: (id: string, props: HelmetProps | undefined) => void;\n};\n\nexport type HelmetProviderHeap = {\n // TODO: Temporary, to keep legacy behavior to call onChange client-side\n // callback on the first render.\n firstRender: boolean;\n\n helmets: RegisteredHelmetPropsArray;\n nextAnimFrameId?: number;\n serverState?: HelmetServerState;\n state: AggregatedState | undefined;\n};\n"],"mappings":"","ignoreList":[]}
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.aggregateBaseProps = aggregateBaseProps;
exports.calcAggregatedState = calcAggregatedState;
exports.cloneProps = cloneProps;
exports.flattenArray = flattenArray;
exports.getTagsFromPropsList = getTagsFromPropsList;
exports.getTitleFromPropsList = getTitleFromPropsList;
exports.mergeAttributes = mergeAttributes;
exports.mergeProps = mergeProps;
exports.prioritizer = prioritizer;
exports.propToAttr = propToAttr;
exports.pushToPropArray = pushToPropArray;
exports.without = void 0;
var _constants = require("./constants");
// eslint-disable-next-line @typescript-eslint/no-explicit-any
/**
* Finds the last object in the given array of registered props,
* that has the specified prop defined, and returns the value of
* that prop in that object. Returns `undefined` if no prop object
* has that prop defined.
*/
function getInnermostProperty(props, propName) {
for (let i = props.length - 1; i >= 0; --i) {
const value = props[i][1][propName];
if (value !== undefined) return value;
}
}
function getTitleFromPropsList(props) {
let innermostTitle = getInnermostProperty(props, _constants.TAG_NAMES.TITLE);
const innermostTemplate = getInnermostProperty(props, 'titleTemplate');
if (Array.isArray(innermostTitle)) {
innermostTitle = innermostTitle.join('');
}
if (innermostTemplate && innermostTitle) {
// use function arg to avoid need to escape $ characters
return innermostTemplate.replace(/%s/g, () => innermostTitle);
}
const innermostDefaultTitle = getInnermostProperty(props, 'defaultTitle');
// NOTE: We really want || here to match legacy behavior, where default title
// was applied also when the given title was an empty string.
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
return (innermostTitle || innermostDefaultTitle) ?? undefined;
}
/**
* Merges together attributes provided for the same element by different Helmet
* instances. Attributes provided by later registered Helmet instances overwrite
* the same attributes provided by the earlier registered instances.
*/
function mergeAttributes(element, props) {
const res = {};
for (const item of props) {
const attrs = item[1][element];
if (attrs) Object.assign(res, attrs);
}
return res;
}
/**
* Finds the latest registered Helmet instance with `base` props provided,
* and with its `href` value set, and returns those `base` props.
* NOTE: Based on the legacy getBaseTagFromPropsList().
*/
function aggregateBaseProps(props) {
for (let i = props.length - 1; i >= 0; --i) {
const res = props[i][1].base;
if (res?.href) return res;
}
}
const warn = msg => console && typeof console.warn === 'function' && console.warn(msg);
/**
* Determines the primary key in the given `props` object, accoding to the given
* array of valid primary keys for the kind of props object.
* TODO: Rather than passing an array of primaryProps around, it might be more
* simple to just have a dedicated function for each possible kind of that
* object.
*/
function getPrimaryProp(props, primaryProps) {
// Looks up for the "primary attribute" key.
let primaryAttributeKey;
// TODO: Perhaps also check that the value of attribute being selected
// as primary is actually defined? Right now, it implicitly assumes that
// in such case the attribute is just not present as a key in `props`.
for (const keyString of Object.keys(props)) {
const key = keyString;
// Special rule with link tags, since rel and href are both primary tags,
// rel takes priority
if (primaryProps.includes(key) && !(primaryAttributeKey === _constants.TAG_PROPERTIES.REL && props[primaryAttributeKey].toLowerCase() === 'canonical') && !(key === _constants.TAG_PROPERTIES.REL && props[key].toLowerCase() === 'stylesheet')) primaryAttributeKey = key;
// Special case for innerHTML which doesn't work lowercased
if (primaryProps.includes(key) && (key === _constants.TAG_PROPERTIES.INNER_HTML || key === _constants.TAG_PROPERTIES.CSS_TEXT || key === _constants.TAG_PROPERTIES.ITEM_PROP)) primaryAttributeKey = key;
}
return primaryAttributeKey ?? null;
}
function getTagsFromPropsList(tagName, primaryAttributes, propsArray) {
// Calculate list of tags, giving priority innermost component
// (end of the propslist)
const approvedSeenTags = {};
// TODO: Well, this is a touch one to refactor, while ensuring it does not
// change any behavior aspect... let's stick to the legacy implementation,
// with minimal updates, for now, then refactor it later.
return propsArray.map(_ref => {
let [, props] = _ref;
return props;
}).filter(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(props => props[tagName]).reverse()
// From last to first.
.reduce((approvedTags, instanceTags) => {
const instanceSeenTags = {};
instanceTags.filter(tag => {
const primaryAttributeKey = getPrimaryProp(tag, primaryAttributes);
if (!primaryAttributeKey || !tag[primaryAttributeKey]) {
return false;
}
const value = tag[primaryAttributeKey].toLowerCase();
if (!approvedSeenTags[primaryAttributeKey]) {
approvedSeenTags[primaryAttributeKey] = {};
}
if (!instanceSeenTags[primaryAttributeKey]) {
instanceSeenTags[primaryAttributeKey] = {};
}
// essentially we collect every item that haven't been seen so far?
if (!approvedSeenTags[primaryAttributeKey][value]) {
instanceSeenTags[primaryAttributeKey][value] = true;
return true;
}
return false;
}).reverse()
// so approved tags are accumulated from last to first
.forEach(tag => approvedTags.push(tag));
// Update seen tags with tags from this instance
const keys = Object.keys(instanceSeenTags);
for (const attributeKey of keys) {
const tagUnion = {
...approvedSeenTags[attributeKey],
...instanceSeenTags[attributeKey]
};
approvedSeenTags[attributeKey] = tagUnion;
}
return approvedTags;
}, [])
// then reversed back to the from first-to-last order.
.reverse();
}
function getAnyTrueFromPropsArray(propsArray, propName) {
for (const [, props] of propsArray) {
if (props[propName]) return true;
}
return false;
}
function flattenArray(possibleArray) {
return Array.isArray(possibleArray) ? possibleArray.join('') : possibleArray;
}
function checkIfPropsMatch(props, toMatch) {
for (const key of Object.keys(props)) {
// e.g. if rel exists in the list of allowed props [amphtml, alternate, etc]
// TODO: Do a better typing job here.
if (toMatch[key]?.includes(props[key])) return true;
}
return false;
}
function prioritizer(propsArray, propsToMatch) {
const res = {
default: Array(),
priority: Array()
};
if (propsArray) {
for (const props of propsArray) {
if (checkIfPropsMatch(props, propsToMatch)) {
res.priority.push(props);
} else {
res.default.push(props);
}
}
}
return res;
}
const without = (obj, key) => {
return {
...obj,
[key]: undefined
};
};
exports.without = without;
/**
* Clones given props object deep enough to make it safe to push new items
* to its array values, and re-assign its non-array values, without a risk
* to mutate any externally owned objects.
*/
function cloneProps(props) {
const res = {};
for (const [key, value] of Object.entries(props)) {
res[key] = Array.isArray(value) ? value.slice() : value;
}
return res;
}
/**
* Merges `source` props into `target`, mutating the `target` object.
*/
function mergeProps(target, source) {
const tgt = target;
for (const [key, srcValue] of Object.entries(source)) {
if (Array.isArray(srcValue)) {
const tgtValue = tgt[key];
tgt[key] = tgtValue ? tgtValue.concat(srcValue) : srcValue;
} else tgt[key] = srcValue;
}
}
/**
* Adds given item to the specified prop array inside `target`.
* It mutates the target.
*/
function pushToPropArray(target, array, item) {
const tgt = target[array];
if (tgt) tgt.push(item);else target[array] = [item];
}
function calcAggregatedState(props) {
let links = getTagsFromPropsList(_constants.TAG_NAMES.LINK, [_constants.TAG_PROPERTIES.REL, _constants.TAG_PROPERTIES.HREF], props);
let meta = getTagsFromPropsList('meta', [
// NOTE: In the legacy version "charSet", "httpEquiv", and "itemProp"
// were given as HTML attributes: charset, http-equiv, itemprop.
// I believe, it is already fine to replace them here now, but
// let's be vigilant.
_constants.TAG_PROPERTIES.NAME, 'charSet', 'httpEquiv', _constants.TAG_PROPERTIES.PROPERTY, 'itemProp'], props);
let script = getTagsFromPropsList('script', [_constants.TAG_PROPERTIES.SRC, _constants.TAG_PROPERTIES.INNER_HTML], props);
const prioritizeSeoTags = getAnyTrueFromPropsArray(props, 'prioritizeSeoTags');
let priority;
if (prioritizeSeoTags) {
const linkP = prioritizer(links, _constants.SEO_PRIORITY_TAGS.link);
links = linkP.default;
const metaP = prioritizer(meta, _constants.SEO_PRIORITY_TAGS.meta);
meta = metaP.default;
const scriptP = prioritizer(script, _constants.SEO_PRIORITY_TAGS.script);
script = scriptP.default;
priority = {
links: linkP.priority,
meta: metaP.priority,
script: scriptP.priority
};
}
return {
base: aggregateBaseProps(props),
bodyAttributes: mergeAttributes('bodyAttributes', props),
defer: getInnermostProperty(props, 'defer'),
encodeSpecialCharacters: getInnermostProperty(props, 'encodeSpecialCharacters') ?? true,
htmlAttributes: mergeAttributes('htmlAttributes', props),
links,
meta,
noscript: getTagsFromPropsList('noscript', [_constants.TAG_PROPERTIES.INNER_HTML], props),
onChangeClientState: getInnermostProperty(props, 'onChangeClientState'),
priority,
script,
style: getTagsFromPropsList('style', [_constants.TAG_PROPERTIES.CSS_TEXT], props),
title: getTitleFromPropsList(props),
titleAttributes: mergeAttributes('titleAttributes', props)
};
}
function propToAttr(prop) {
return _constants.HTML_TAG_MAP[prop] ?? prop;
}
//# sourceMappingURL=utils.js.map
{"version":3,"file":"utils.js","names":["_constants","require","getInnermostProperty","props","propName","i","length","value","undefined","getTitleFromPropsList","innermostTitle","TAG_NAMES","TITLE","innermostTemplate","Array","isArray","join","replace","innermostDefaultTitle","mergeAttributes","element","res","item","attrs","Object","assign","aggregateBaseProps","base","href","warn","msg","console","getPrimaryProp","primaryProps","primaryAttributeKey","keyString","keys","key","includes","TAG_PROPERTIES","REL","toLowerCase","INNER_HTML","CSS_TEXT","ITEM_PROP","getTagsFromPropsList","tagName","primaryAttributes","propsArray","approvedSeenTags","map","_ref","filter","reverse","reduce","approvedTags","instanceTags","instanceSeenTags","tag","forEach","push","attributeKey","tagUnion","getAnyTrueFromPropsArray","flattenArray","possibleArray","checkIfPropsMatch","toMatch","prioritizer","propsToMatch","default","priority","without","obj","exports","cloneProps","entries","slice","mergeProps","target","source","tgt","srcValue","tgtValue","concat","pushToPropArray","array","calcAggregatedState","links","LINK","HREF","meta","NAME","PROPERTY","script","SRC","prioritizeSeoTags","linkP","SEO_PRIORITY_TAGS","link","metaP","scriptP","bodyAttributes","defer","encodeSpecialCharacters","htmlAttributes","noscript","onChangeClientState","style","title","titleAttributes","propToAttr","prop","HTML_TAG_MAP"],"sources":["../../src/utils.ts"],"sourcesContent":["import {\n HTML_TAG_MAP,\n TAG_NAMES,\n TAG_PROPERTIES,\n SEO_PRIORITY_TAGS,\n} from './constants';\n\nimport type {\n AggregatedState,\n BaseProps,\n HelmetPropArrays,\n HelmetPropBooleans,\n HelmetPropObjects,\n HelmetProps,\n LinkProps,\n MetaProps,\n PropArrayItem,\n RegisteredHelmetPropsArray,\n ScriptProps,\n} from './types';\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype PropList = Record<string, any>;\n\ntype AttributeList = string[];\n\ntype SeenTags<T extends keyof HelmetPropArrays> = {\n [key in keyof PropArrayItem<T>]?: Record<string, boolean>\n};\n\ntype MatchProps = Record<string, string | AttributeList>;\n\n/**\n * Finds the last object in the given array of registered props,\n * that has the specified prop defined, and returns the value of\n * that prop in that object. Returns `undefined` if no prop object\n * has that prop defined.\n */\nfunction getInnermostProperty<T extends keyof HelmetProps>(\n props: RegisteredHelmetPropsArray,\n propName: T,\n): HelmetProps[T] | undefined {\n for (let i = props.length - 1; i >= 0; --i) {\n const value = props[i]![1][propName];\n if (value !== undefined) return value;\n }\n}\n\nexport function getTitleFromPropsList(\n props: RegisteredHelmetPropsArray,\n): string | undefined {\n let innermostTitle = getInnermostProperty(props, TAG_NAMES.TITLE);\n\n const innermostTemplate = getInnermostProperty(\n props,\n 'titleTemplate',\n );\n\n if (Array.isArray(innermostTitle)) {\n innermostTitle = innermostTitle.join('');\n }\n if (innermostTemplate && innermostTitle) {\n // use function arg to avoid need to escape $ characters\n return innermostTemplate.replace(/%s/g, () => innermostTitle);\n }\n\n const innermostDefaultTitle = getInnermostProperty(\n props,\n 'defaultTitle',\n );\n\n // NOTE: We really want || here to match legacy behavior, where default title\n // was applied also when the given title was an empty string.\n // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing\n return (innermostTitle || innermostDefaultTitle) ?? undefined;\n}\n\n/**\n * Merges together attributes provided for the same element by different Helmet\n * instances. Attributes provided by later registered Helmet instances overwrite\n * the same attributes provided by the earlier registered instances.\n */\nexport function mergeAttributes<T extends keyof HelmetPropObjects>(\n element: T,\n props: RegisteredHelmetPropsArray,\n): HelmetProps[T] {\n const res: HelmetProps[T] = {};\n for (const item of props) {\n const attrs = item[1][element];\n if (attrs) Object.assign(res, attrs);\n }\n return res;\n}\n\n/**\n * Finds the latest registered Helmet instance with `base` props provided,\n * and with its `href` value set, and returns those `base` props.\n * NOTE: Based on the legacy getBaseTagFromPropsList().\n */\nexport function aggregateBaseProps(\n props: RegisteredHelmetPropsArray,\n): BaseProps | undefined {\n for (let i = props.length - 1; i >= 0; --i) {\n const res = props[i]![1].base;\n if (res?.href) return res;\n }\n}\n\nconst warn = (msg: string) => console && typeof console.warn === 'function' && console.warn(msg);\n\n/**\n * Determines the primary key in the given `props` object, accoding to the given\n * array of valid primary keys for the kind of props object.\n * TODO: Rather than passing an array of primaryProps around, it might be more\n * simple to just have a dedicated function for each possible kind of that\n * object.\n */\nfunction getPrimaryProp<T extends keyof HelmetPropArrays>(\n props: PropArrayItem<T>,\n primaryProps: Array<keyof PropArrayItem<T>>,\n): keyof PropArrayItem<T> | null {\n // Looks up for the \"primary attribute\" key.\n let primaryAttributeKey: keyof PropArrayItem<T> | undefined;\n\n // TODO: Perhaps also check that the value of attribute being selected\n // as primary is actually defined? Right now, it implicitly assumes that\n // in such case the attribute is just not present as a key in `props`.\n for (const keyString of Object.keys(props)) {\n const key = keyString as keyof PropArrayItem<T>;\n\n // Special rule with link tags, since rel and href are both primary tags,\n // rel takes priority\n if (primaryProps.includes(key)\n && !(\n primaryAttributeKey === TAG_PROPERTIES.REL\n && (props[primaryAttributeKey] as string).toLowerCase() === 'canonical'\n )\n && !(\n key === TAG_PROPERTIES.REL\n && (props[key] as string).toLowerCase() === 'stylesheet'\n )\n ) primaryAttributeKey = key;\n\n // Special case for innerHTML which doesn't work lowercased\n if (\n primaryProps.includes(key)\n && (key === TAG_PROPERTIES.INNER_HTML\n || key === TAG_PROPERTIES.CSS_TEXT\n || key === TAG_PROPERTIES.ITEM_PROP)\n ) primaryAttributeKey = key;\n }\n\n return primaryAttributeKey ?? null;\n}\n\nexport function getTagsFromPropsList<T extends keyof HelmetPropArrays>(\n tagName: T,\n primaryAttributes: Array<keyof PropArrayItem<T>>,\n propsArray: RegisteredHelmetPropsArray,\n): HelmetPropArrays[T] {\n // Calculate list of tags, giving priority innermost component\n // (end of the propslist)\n const approvedSeenTags: SeenTags<T> = {};\n\n // TODO: Well, this is a touch one to refactor, while ensuring it does not\n // change any behavior aspect... let's stick to the legacy implementation,\n // with minimal updates, for now, then refactor it later.\n return propsArray.map(([, props]) => props)\n .filter((props) => {\n if (Array.isArray(props[tagName])) {\n return true;\n }\n if (typeof props[tagName] !== 'undefined') {\n warn(\n `Helmet: ${tagName} should be of type \"Array\". Instead found type \"${typeof props[\n tagName\n ]}\"`,\n );\n }\n return false;\n })\n .map((props) => props[tagName])\n .reverse()\n\n // From last to first.\n .reduce<PropArrayItem<T>[]>((approvedTags, instanceTags) => {\n const instanceSeenTags: SeenTags<T> = {};\n\n instanceTags!.filter((tag: PropArrayItem<T>) => {\n const primaryAttributeKey = getPrimaryProp(tag, primaryAttributes);\n\n if (!primaryAttributeKey || !tag[primaryAttributeKey]) {\n return false;\n }\n\n const value = (tag[primaryAttributeKey] as string).toLowerCase();\n\n if (!approvedSeenTags[primaryAttributeKey]) {\n approvedSeenTags[primaryAttributeKey] = {};\n }\n\n if (!instanceSeenTags[primaryAttributeKey]) {\n instanceSeenTags[primaryAttributeKey] = {};\n }\n\n // essentially we collect every item that haven't been seen so far?\n\n if (!approvedSeenTags[primaryAttributeKey][value]) {\n instanceSeenTags[primaryAttributeKey][value] = true;\n return true;\n }\n\n return false;\n }).reverse()\n\n // so approved tags are accumulated from last to first\n .forEach((tag: PropArrayItem<T>) => approvedTags.push(tag));\n\n // Update seen tags with tags from this instance\n const keys = Object.keys(instanceSeenTags) as\n Array<keyof PropArrayItem<T>>;\n\n for (const attributeKey of keys) {\n const tagUnion = {\n ...approvedSeenTags[attributeKey],\n ...instanceSeenTags[attributeKey],\n };\n\n approvedSeenTags[attributeKey] = tagUnion;\n }\n\n return approvedTags;\n }, [])\n\n // then reversed back to the from first-to-last order.\n .reverse() as HelmetPropArrays[T];\n}\n\nfunction getAnyTrueFromPropsArray<T extends keyof HelmetPropBooleans>(\n propsArray: RegisteredHelmetPropsArray,\n propName: T,\n): boolean {\n for (const [, props] of propsArray) {\n if (props[propName]) return true;\n }\n return false;\n}\n\nexport function flattenArray(possibleArray: string[] | string) {\n return Array.isArray(possibleArray) ? possibleArray.join('') : possibleArray;\n}\n\nfunction checkIfPropsMatch<T extends keyof HelmetPropArrays>(\n props: PropArrayItem<T>,\n toMatch: MatchProps,\n) {\n for (const key of Object.keys(props)) {\n // e.g. if rel exists in the list of allowed props [amphtml, alternate, etc]\n // TODO: Do a better typing job here.\n if (toMatch[key]?.includes(\n props[key as keyof PropArrayItem<T>] as unknown as string,\n )) return true;\n }\n return false;\n}\n\nexport function prioritizer<T extends keyof HelmetPropArrays>(\n propsArray: HelmetPropArrays[T],\n propsToMatch: MatchProps,\n): {\n default: PropArrayItem<T>[];\n priority: PropArrayItem<T>[];\n } {\n const res = {\n default: Array<PropArrayItem<T>>(),\n priority: Array<PropArrayItem<T>>(),\n };\n\n if (propsArray) {\n for (const props of propsArray) {\n if (checkIfPropsMatch(props, propsToMatch)) {\n res.priority.push(props);\n } else {\n res.default.push(props);\n }\n }\n }\n\n return res;\n}\n\nexport const without = (obj: PropList, key: string) => {\n return {\n ...obj,\n [key]: undefined,\n };\n};\n\ntype UnknownObject = Record<number | string | symbol, unknown>;\n\n/**\n * Clones given props object deep enough to make it safe to push new items\n * to its array values, and re-assign its non-array values, without a risk\n * to mutate any externally owned objects.\n */\nexport function cloneProps(props: HelmetProps): HelmetProps {\n const res: UnknownObject = {};\n for (const [key, value] of Object.entries(props)) {\n res[key] = Array.isArray(value) ? value.slice() : value;\n }\n return res;\n}\n\n/**\n * Merges `source` props into `target`, mutating the `target` object.\n */\nexport function mergeProps(target: HelmetProps, source: HelmetProps) {\n const tgt = target as UnknownObject;\n for (const [key, srcValue] of Object.entries(source)) {\n if (Array.isArray(srcValue)) {\n const tgtValue = tgt[key] as unknown[];\n tgt[key] = tgtValue ? tgtValue.concat(srcValue) : srcValue;\n } else tgt[key] = srcValue;\n }\n}\n\n/**\n * Adds given item to the specified prop array inside `target`.\n * It mutates the target.\n */\nexport function pushToPropArray<K extends keyof HelmetPropArrays>(\n target: HelmetProps,\n array: K,\n item: Exclude<HelmetPropArrays[K], undefined>[number],\n) {\n type A = Array<typeof item>;\n const tgt = target[array] as A;\n if (tgt) tgt.push(item);\n else (target[array] as A) = [item];\n}\n\nexport function calcAggregatedState(\n props: RegisteredHelmetPropsArray,\n): AggregatedState {\n let links = getTagsFromPropsList(\n TAG_NAMES.LINK,\n [TAG_PROPERTIES.REL, TAG_PROPERTIES.HREF],\n props,\n );\n let meta = getTagsFromPropsList(\n 'meta',\n [\n // NOTE: In the legacy version \"charSet\", \"httpEquiv\", and \"itemProp\"\n // were given as HTML attributes: charset, http-equiv, itemprop.\n // I believe, it is already fine to replace them here now, but\n // let's be vigilant.\n TAG_PROPERTIES.NAME,\n 'charSet',\n 'httpEquiv',\n TAG_PROPERTIES.PROPERTY,\n 'itemProp',\n ],\n props,\n );\n let script = getTagsFromPropsList(\n 'script',\n [TAG_PROPERTIES.SRC, TAG_PROPERTIES.INNER_HTML],\n props,\n );\n\n const prioritizeSeoTags = getAnyTrueFromPropsArray(props, 'prioritizeSeoTags');\n\n let priority: {\n links: LinkProps[] | undefined;\n meta: MetaProps[] | undefined;\n script: ScriptProps[] | undefined;\n } | undefined;\n\n if (prioritizeSeoTags) {\n const linkP = prioritizer<'link'>(links, SEO_PRIORITY_TAGS.link);\n links = linkP.default;\n\n const metaP = prioritizer<'meta'>(meta, SEO_PRIORITY_TAGS.meta);\n meta = metaP.default;\n\n const scriptP = prioritizer<'script'>(script, SEO_PRIORITY_TAGS.script);\n script = scriptP.default;\n\n priority = {\n links: linkP.priority,\n meta: metaP.priority,\n script: scriptP.priority,\n };\n }\n\n return {\n base: aggregateBaseProps(props),\n bodyAttributes: mergeAttributes('bodyAttributes', props),\n defer: getInnermostProperty(props, 'defer'),\n encodeSpecialCharacters: getInnermostProperty(props, 'encodeSpecialCharacters') ?? true,\n htmlAttributes: mergeAttributes('htmlAttributes', props),\n links,\n meta,\n noscript: getTagsFromPropsList(\n 'noscript',\n [TAG_PROPERTIES.INNER_HTML],\n props,\n ),\n onChangeClientState: getInnermostProperty(props, 'onChangeClientState'),\n priority,\n script,\n style: getTagsFromPropsList(\n 'style',\n [TAG_PROPERTIES.CSS_TEXT],\n props,\n ),\n title: getTitleFromPropsList(props),\n titleAttributes: mergeAttributes('titleAttributes', props),\n };\n}\n\nexport function propToAttr(prop: string): string {\n return HTML_TAG_MAP[prop] ?? prop;\n}\n"],"mappings":"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","ignoreList":[]}
import { HELMET_ATTRIBUTE, HTML_TAG_MAP, TAG_NAMES, TAG_PROPERTIES } from './constants';
import { flattenArray } from './utils';
/**
* Replaces HTML elements previously added to the DOM's head by React Helmet
* by the set of given elements (tags). For any given element that matches
* exactly an element already present in the head no actual DOM modification
* happens, it just keeps already present element. Returns arrays of newly
* added (newTags) and removed (oldTags) elements.
*/
function updateTags(type, tags) {
// TODO: Do we really need the fallback here? document.head is supposed to be
// always defined.
const headElement = document.head || document.querySelector(TAG_NAMES.HEAD);
const tagNodes = headElement.querySelectorAll(`${type}[${HELMET_ATTRIBUTE}]`);
const allTags = [];
const oldTags = [...tagNodes];
const newTags = [];
for (const tag of tags) {
const newElement = document.createElement(type);
// TODO: Well, the typing within this block is bad, and should be improved.
for (const [key, value] of Object.entries(tag)) {
if (Object.prototype.hasOwnProperty.call(tag, key)) {
const name = HTML_TAG_MAP[key] ?? key;
if (name === TAG_PROPERTIES.INNER_HTML) {
newElement.innerHTML = value;
} else if (name === TAG_PROPERTIES.CSS_TEXT) {
// TODO: Not sure when this is true?
// @ts-expect-error "pre-existing"
if (newElement.styleSheet) {
// @ts-expect-error "pre-existing"
newElement.styleSheet.cssText = tag.cssText;
} else {
newElement.appendChild(document.createTextNode(tag.cssText));
}
} else {
newElement.setAttribute(name, value ?? '');
}
}
}
newElement.setAttribute(HELMET_ATTRIBUTE, 'true');
const attrs = {};
for (const {
name,
value
} of newElement.attributes) {
attrs[name] = value;
}
allTags.push(attrs);
// Remove a duplicate tag from domTagstoRemove, so it isn't cleared.
for (let i = 0;; ++i) {
if (newElement.isEqualNode(oldTags[i])) {
oldTags.splice(i, 1);
break;
}
if (i >= oldTags.length) {
newTags.push(newElement);
break;
}
}
}
oldTags.forEach(tag => tag.parentNode?.removeChild(tag));
newTags.forEach(tag => headElement.appendChild(tag));
// TODO: Do we really need this return value anywhere? Especially `oldTags`
// that have been removed from DOM already?
return {
allTags,
oldTags,
newTags
};
}
function updateAttributes(tagName, props) {
const elementTag = document.getElementsByTagName(tagName)[0];
if (!elementTag) {
return;
}
const helmetAttributeString = elementTag.getAttribute(HELMET_ATTRIBUTE);
const helmetAttributes = helmetAttributeString ? helmetAttributeString.split(',') : [];
const attributesToRemove = [...helmetAttributes];
const attributeKeys = [];
for (const prop of Object.keys(props)) {
// TODO: See a comment below.
attributeKeys.push(HTML_TAG_MAP[prop] ?? prop);
}
for (const [key, value] of Object.entries(props)) {
// TODO: Get rid of the mapping later. It is not really needed, as HTML
// attribute names are case-insensitive. However, our related logic may
// still be case dependent - we should be careful about it.
const attr = HTML_TAG_MAP[key] ?? key;
if (elementTag.getAttribute(attr) !== value) {
// TODO: That ?? '' piece is here to keep the legacy behavior for now,
// I guess later we should prefer to consider attrbiutes with "undefined"
// value as not set.
elementTag.setAttribute(attr, value ?? '');
}
if (!helmetAttributes.includes(attr)) {
helmetAttributes.push(attr);
}
const indexToSave = attributesToRemove.indexOf(attr);
if (indexToSave !== -1) {
attributesToRemove.splice(indexToSave, 1);
}
}
for (let i = attributesToRemove.length - 1; i >= 0; i -= 1) {
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(','));
}
}
function updateTitle(title, attributes) {
if (title !== undefined && document.title !== title) {
document.title = flattenArray(title);
}
updateAttributes(TAG_NAMES.TITLE, attributes);
}
export function commitTagChanges(newState, firstRender) {
const {
base,
bodyAttributes,
defer,
htmlAttributes,
links,
meta,
noscript,
onChangeClientState,
script,
style,
title,
titleAttributes
} = newState;
updateAttributes(TAG_NAMES.BODY, bodyAttributes ?? {});
updateAttributes(TAG_NAMES.HTML, htmlAttributes ?? {});
updateTitle(title, titleAttributes);
const tagUpdates = {
baseTag: updateTags(TAG_NAMES.BASE, base ? [base] : []),
linkTags: updateTags(TAG_NAMES.LINK, links ?? []),
metaTags: updateTags(TAG_NAMES.META, meta ?? []),
noscriptTags: updateTags(TAG_NAMES.NOSCRIPT, noscript ?? []),
scriptTags: updateTags(TAG_NAMES.SCRIPT, script ?? []),
styleTags: updateTags(TAG_NAMES.STYLE, style ?? [])
};
const resultTags = {
baseTag: [],
bodyAttributes: {},
defer: defer ?? false,
htmlAttributes: {},
linkTags: [],
metaTags: [],
noscriptTags: [],
onChangeClientState: onChangeClientState ?? (() => undefined),
scriptTags: [],
styleTags: [],
title: title ?? '',
titleAttributes: {}
};
const addedTags = {};
const removedTags = {};
Object.keys(tagUpdates).forEach(tagType => {
const {
allTags,
newTags,
oldTags
} = tagUpdates[tagType];
resultTags[tagType] = allTags;
if (newTags.length) {
addedTags[tagType] = newTags;
}
if (oldTags.length) {
removedTags[tagType] = tagUpdates[tagType].oldTags;
}
});
if (firstRender || Object.keys(addedTags).length || Object.keys(removedTags).length) {
onChangeClientState?.(resultTags, addedTags, removedTags);
}
}
//# sourceMappingURL=client.js.map
{"version":3,"file":"client.js","names":["HELMET_ATTRIBUTE","HTML_TAG_MAP","TAG_NAMES","TAG_PROPERTIES","flattenArray","updateTags","type","tags","headElement","document","head","querySelector","HEAD","tagNodes","querySelectorAll","allTags","oldTags","newTags","tag","newElement","createElement","key","value","Object","entries","prototype","hasOwnProperty","call","name","INNER_HTML","innerHTML","CSS_TEXT","styleSheet","cssText","appendChild","createTextNode","setAttribute","attrs","attributes","push","i","isEqualNode","splice","length","forEach","parentNode","removeChild","updateAttributes","tagName","props","elementTag","getElementsByTagName","helmetAttributeString","getAttribute","helmetAttributes","split","attributesToRemove","attributeKeys","prop","keys","attr","includes","indexToSave","indexOf","removeAttribute","join","updateTitle","title","undefined","TITLE","commitTagChanges","newState","firstRender","base","bodyAttributes","defer","htmlAttributes","links","meta","noscript","onChangeClientState","script","style","titleAttributes","BODY","HTML","tagUpdates","baseTag","BASE","linkTags","LINK","metaTags","META","noscriptTags","NOSCRIPT","scriptTags","SCRIPT","styleTags","STYLE","resultTags","addedTags","removedTags","tagType"],"sources":["../../src/client.ts"],"sourcesContent":["import {\n HELMET_ATTRIBUTE,\n HTML_TAG_MAP,\n TAG_NAMES,\n TAG_PROPERTIES,\n} from './constants';\n\nimport type {\n AggregatedState,\n BodyProps,\n HelmetChildProps,\n HelmetTags,\n HtmlProps,\n StateUpdate,\n} from './types';\n\nimport { flattenArray } from './utils';\n\ntype TagUpdates = {\n allTags: HTMLElement[];\n oldTags: HTMLElement[];\n newTags: HTMLElement[];\n};\n\ntype TagUpdateList = Record<string, TagUpdates>;\n\n/**\n * Replaces HTML elements previously added to the DOM's head by React Helmet\n * by the set of given elements (tags). For any given element that matches\n * exactly an element already present in the head no actual DOM modification\n * happens, it just keeps already present element. Returns arrays of newly\n * added (newTags) and removed (oldTags) elements.\n */\nfunction updateTags(type: string, tags: HelmetChildProps[]) {\n // TODO: Do we really need the fallback here? document.head is supposed to be\n // always defined.\n const headElement = document.head || document.querySelector(TAG_NAMES.HEAD);\n\n const tagNodes = headElement.querySelectorAll<HTMLElement>(`${type}[${HELMET_ATTRIBUTE}]`);\n const allTags: HTMLElement[] = [];\n const oldTags: HTMLElement[] = [...tagNodes];\n const newTags: HTMLElement[] = [];\n\n for (const tag of tags) {\n const newElement = document.createElement(type);\n\n // TODO: Well, the typing within this block is bad, and should be improved.\n for (const [key, value] of Object.entries(tag)) {\n if (Object.prototype.hasOwnProperty.call(tag, key)) {\n const name = HTML_TAG_MAP[key] ?? key;\n if (name as TAG_PROPERTIES === TAG_PROPERTIES.INNER_HTML) {\n newElement.innerHTML = value as string;\n } else if (name as TAG_PROPERTIES === TAG_PROPERTIES.CSS_TEXT) {\n // TODO: Not sure when this is true?\n // @ts-expect-error \"pre-existing\"\n if (newElement.styleSheet) {\n // @ts-expect-error \"pre-existing\"\n (newElement.styleSheet as CSSStyleDeclaration).cssText = (\n tag as CSSStyleDeclaration).cssText;\n } else {\n newElement.appendChild(document.createTextNode(\n (tag as CSSStyleDeclaration).cssText,\n ));\n }\n } else {\n newElement.setAttribute(name, (value as string) ?? '');\n }\n }\n }\n\n newElement.setAttribute(HELMET_ATTRIBUTE, 'true');\n\n const attrs = {} as HTMLElement;\n for (const { name, value } of newElement.attributes) {\n (attrs[name as keyof HTMLElement] as unknown) = value;\n }\n allTags.push(attrs);\n\n // Remove a duplicate tag from domTagstoRemove, so it isn't cleared.\n for (let i = 0; ; ++i) {\n if (newElement.isEqualNode(oldTags[i]!)) {\n oldTags.splice(i, 1);\n break;\n }\n if (i >= oldTags.length) {\n newTags.push(newElement);\n break;\n }\n }\n }\n\n oldTags.forEach((tag: Node) => tag.parentNode?.removeChild(tag));\n newTags.forEach((tag) => headElement.appendChild(tag));\n\n // TODO: Do we really need this return value anywhere? Especially `oldTags`\n // that have been removed from DOM already?\n return {\n allTags,\n oldTags,\n newTags,\n };\n}\n\nfunction updateAttributes(tagName: string, props: BodyProps | HtmlProps) {\n const elementTag = document.getElementsByTagName(tagName)[0];\n\n if (!elementTag) {\n return;\n }\n\n const helmetAttributeString = elementTag.getAttribute(HELMET_ATTRIBUTE);\n const helmetAttributes = helmetAttributeString ? helmetAttributeString.split(',') : [];\n const attributesToRemove = [...helmetAttributes];\n\n const attributeKeys: string[] = [];\n for (const prop of Object.keys(props)) {\n // TODO: See a comment below.\n attributeKeys.push(HTML_TAG_MAP[prop] ?? prop);\n }\n\n for (const [key, value] of Object.entries(props)) {\n // TODO: Get rid of the mapping later. It is not really needed, as HTML\n // attribute names are case-insensitive. However, our related logic may\n // still be case dependent - we should be careful about it.\n const attr = HTML_TAG_MAP[key] ?? key;\n if (elementTag.getAttribute(attr) !== value) {\n // TODO: That ?? '' piece is here to keep the legacy behavior for now,\n // I guess later we should prefer to consider attrbiutes with \"undefined\"\n // value as not set.\n elementTag.setAttribute(attr, value as string ?? '');\n }\n\n if (!helmetAttributes.includes(attr)) {\n helmetAttributes.push(attr);\n }\n\n const indexToSave = attributesToRemove.indexOf(attr);\n if (indexToSave !== -1) {\n attributesToRemove.splice(indexToSave, 1);\n }\n }\n\n for (let i = attributesToRemove.length - 1; i >= 0; i -= 1) {\n elementTag.removeAttribute(attributesToRemove[i]!);\n }\n\n if (helmetAttributes.length === attributesToRemove.length) {\n elementTag.removeAttribute(HELMET_ATTRIBUTE);\n } else if (elementTag.getAttribute(HELMET_ATTRIBUTE) !== attributeKeys.join(',')) {\n elementTag.setAttribute(HELMET_ATTRIBUTE, attributeKeys.join(','));\n }\n}\n\nfunction updateTitle(\n title: string | undefined,\n attributes: BodyProps | HtmlProps,\n) {\n if (title !== undefined && document.title !== title) {\n document.title = flattenArray(title);\n }\n\n updateAttributes(TAG_NAMES.TITLE, attributes);\n}\n\nexport function commitTagChanges(\n newState: AggregatedState,\n firstRender: boolean,\n) {\n const {\n base,\n bodyAttributes,\n defer,\n htmlAttributes,\n links,\n meta,\n noscript,\n onChangeClientState,\n script,\n style,\n title,\n titleAttributes,\n } = newState;\n updateAttributes(TAG_NAMES.BODY, bodyAttributes ?? {});\n updateAttributes(TAG_NAMES.HTML, htmlAttributes ?? {});\n\n updateTitle(title, titleAttributes!);\n\n const tagUpdates: TagUpdateList = {\n baseTag: updateTags(TAG_NAMES.BASE, base ? [base] : []),\n linkTags: updateTags(TAG_NAMES.LINK, links ?? []),\n metaTags: updateTags(TAG_NAMES.META, meta ?? []),\n noscriptTags: updateTags(TAG_NAMES.NOSCRIPT, noscript ?? []),\n scriptTags: updateTags(TAG_NAMES.SCRIPT, script ?? []),\n styleTags: updateTags(TAG_NAMES.STYLE, style ?? []),\n };\n\n const resultTags: StateUpdate = {\n baseTag: [],\n bodyAttributes: {},\n defer: defer ?? false,\n htmlAttributes: {},\n linkTags: [],\n metaTags: [],\n noscriptTags: [],\n onChangeClientState: onChangeClientState ?? (() => undefined),\n scriptTags: [],\n styleTags: [],\n title: title ?? '',\n titleAttributes: {},\n };\n\n const addedTags: Partial<HelmetTags> = {};\n const removedTags: Partial<HelmetTags> = {};\n\n Object.keys(tagUpdates).forEach((tagType) => {\n const { allTags, newTags, oldTags } = tagUpdates[tagType]!;\n\n (resultTags[tagType as keyof HelmetTags] as HTMLElement[]) = allTags;\n\n if (newTags.length) {\n (addedTags[tagType as keyof HelmetTags] as HTMLElement[]) = newTags;\n }\n if (oldTags.length) {\n (removedTags[tagType as keyof HelmetTags] as HTMLElement[])\n = tagUpdates[tagType]!.oldTags;\n }\n });\n\n if (firstRender\n || Object.keys(addedTags).length\n || Object.keys(removedTags).length\n ) {\n onChangeClientState?.(resultTags, addedTags, removedTags);\n }\n}\n"],"mappings":"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","ignoreList":[]}
export let TAG_PROPERTIES = /*#__PURE__*/function (TAG_PROPERTIES) {
TAG_PROPERTIES["CHARSET"] = "charset";
TAG_PROPERTIES["CSS_TEXT"] = "cssText";
TAG_PROPERTIES["HREF"] = "href";
TAG_PROPERTIES["HTTPEQUIV"] = "http-equiv";
TAG_PROPERTIES["INNER_HTML"] = "innerHTML";
TAG_PROPERTIES["ITEM_PROP"] = "itemprop";
TAG_PROPERTIES["NAME"] = "name";
TAG_PROPERTIES["PROPERTY"] = "property";
TAG_PROPERTIES["REL"] = "rel";
TAG_PROPERTIES["SRC"] = "src";
return TAG_PROPERTIES;
}({});
export let ATTRIBUTE_NAMES = /*#__PURE__*/function (ATTRIBUTE_NAMES) {
ATTRIBUTE_NAMES["BODY"] = "bodyAttributes";
ATTRIBUTE_NAMES["HTML"] = "htmlAttributes";
ATTRIBUTE_NAMES["TITLE"] = "titleAttributes";
return ATTRIBUTE_NAMES;
}({});
export let TAG_NAMES = /*#__PURE__*/function (TAG_NAMES) {
TAG_NAMES["BASE"] = "base";
TAG_NAMES["BODY"] = "body";
TAG_NAMES["HEAD"] = "head";
TAG_NAMES["HTML"] = "html";
TAG_NAMES["LINK"] = "link";
TAG_NAMES["META"] = "meta";
TAG_NAMES["NOSCRIPT"] = "noscript";
TAG_NAMES["SCRIPT"] = "script";
TAG_NAMES["STYLE"] = "style";
TAG_NAMES["TITLE"] = "title";
TAG_NAMES["FRAGMENT"] = "Symbol(react.fragment)";
return TAG_NAMES;
}({});
export const SEO_PRIORITY_TAGS = {
link: {
rel: ['amphtml', 'canonical', 'alternate']
},
script: {
type: ['application/ld+json']
},
meta: {
charset: '',
name: ['generator', 'robots', 'description'],
property: ['og:type', 'og:title', 'og:url', 'og:image', 'og:image:alt', 'og:description', 'twitter:url', 'twitter:title', 'twitter:description', 'twitter:image', 'twitter:image:alt', 'twitter:card', 'twitter:site']
}
};
export const VALID_TAG_NAMES = Object.values(TAG_NAMES);
/**
* The mapping of HTML attribute names to the corresponding element properties,
* for the names that do not match their corresponding properties.
*/
export const REACT_TAG_MAP = {
accesskey: 'accessKey',
charset: 'charSet',
class: 'className',
contenteditable: 'contentEditable',
contextmenu: 'contextMenu',
'http-equiv': 'httpEquiv',
itemprop: 'itemProp',
tabindex: 'tabIndex'
};
/**
* The mapping reverse of REACT_TAG_MAP.
*/
export const HTML_TAG_MAP = (() => {
const res = {};
for (const [key, value] of Object.entries(REACT_TAG_MAP)) {
res[value] = key;
}
return res;
})();
export const HELMET_ATTRIBUTE = 'data-rh';
export const IS_DOM_ENVIRONMENT = !!(typeof window !== 'undefined' && window.document.createElement);
//# sourceMappingURL=constants.js.map
{"version":3,"file":"constants.js","names":["TAG_PROPERTIES","ATTRIBUTE_NAMES","TAG_NAMES","SEO_PRIORITY_TAGS","link","rel","script","type","meta","charset","name","property","VALID_TAG_NAMES","Object","values","REACT_TAG_MAP","accesskey","class","contenteditable","contextmenu","itemprop","tabindex","HTML_TAG_MAP","res","key","value","entries","HELMET_ATTRIBUTE","IS_DOM_ENVIRONMENT","window","document","createElement"],"sources":["../../src/constants.ts"],"sourcesContent":["export enum TAG_PROPERTIES {\n CHARSET = 'charset',\n CSS_TEXT = 'cssText',\n HREF = 'href',\n HTTPEQUIV = 'http-equiv',\n INNER_HTML = 'innerHTML',\n ITEM_PROP = 'itemprop',\n NAME = 'name',\n PROPERTY = 'property',\n REL = 'rel',\n SRC = 'src',\n}\n\nexport enum ATTRIBUTE_NAMES {\n BODY = 'bodyAttributes',\n HTML = 'htmlAttributes',\n TITLE = 'titleAttributes',\n}\n\nexport enum TAG_NAMES {\n BASE = 'base',\n BODY = 'body',\n HEAD = 'head',\n HTML = 'html',\n LINK = 'link',\n META = 'meta',\n NOSCRIPT = 'noscript',\n SCRIPT = 'script',\n STYLE = 'style',\n TITLE = 'title',\n FRAGMENT = 'Symbol(react.fragment)',\n}\n\nexport const SEO_PRIORITY_TAGS = {\n link: { rel: ['amphtml', 'canonical', 'alternate'] },\n script: { type: ['application/ld+json'] },\n meta: {\n charset: '',\n name: ['generator', 'robots', 'description'],\n property: [\n 'og:type',\n 'og:title',\n 'og:url',\n 'og:image',\n 'og:image:alt',\n 'og:description',\n 'twitter:url',\n 'twitter:title',\n 'twitter:description',\n 'twitter:image',\n 'twitter:image:alt',\n 'twitter:card',\n 'twitter:site',\n ],\n },\n};\n\nexport const VALID_TAG_NAMES = Object.values(TAG_NAMES);\n\n/**\n * The mapping of HTML attribute names to the corresponding element properties,\n * for the names that do not match their corresponding properties.\n */\nexport const REACT_TAG_MAP: Record<string, string> = {\n accesskey: 'accessKey',\n charset: 'charSet',\n class: 'className',\n contenteditable: 'contentEditable',\n contextmenu: 'contextMenu',\n 'http-equiv': 'httpEquiv',\n itemprop: 'itemProp',\n tabindex: 'tabIndex',\n};\n\n/**\n * The mapping reverse of REACT_TAG_MAP.\n */\nexport const HTML_TAG_MAP = (() => {\n const res: Record<string, string> = {};\n for (const [key, value] of Object.entries(REACT_TAG_MAP)) {\n res[value] = key;\n }\n return res;\n})();\n\nexport const HELMET_ATTRIBUTE = 'data-rh';\n\nexport const IS_DOM_ENVIRONMENT = !!(\n typeof window !== 'undefined'\n && window.document.createElement\n);\n"],"mappings":"AAAA,WAAYA,cAAc,0BAAdA,cAAc;EAAdA,cAAc;EAAdA,cAAc;EAAdA,cAAc;EAAdA,cAAc;EAAdA,cAAc;EAAdA,cAAc;EAAdA,cAAc;EAAdA,cAAc;EAAdA,cAAc;EAAdA,cAAc;EAAA,OAAdA,cAAc;AAAA;AAa1B,WAAYC,eAAe,0BAAfA,eAAe;EAAfA,eAAe;EAAfA,eAAe;EAAfA,eAAe;EAAA,OAAfA,eAAe;AAAA;AAM3B,WAAYC,SAAS,0BAATA,SAAS;EAATA,SAAS;EAATA,SAAS;EAATA,SAAS;EAATA,SAAS;EAATA,SAAS;EAATA,SAAS;EAATA,SAAS;EAATA,SAAS;EAATA,SAAS;EAATA,SAAS;EAATA,SAAS;EAAA,OAATA,SAAS;AAAA;AAcrB,OAAO,MAAMC,iBAAiB,GAAG;EAC/BC,IAAI,EAAE;IAAEC,GAAG,EAAE,CAAC,SAAS,EAAE,WAAW,EAAE,WAAW;EAAE,CAAC;EACpDC,MAAM,EAAE;IAAEC,IAAI,EAAE,CAAC,qBAAqB;EAAE,CAAC;EACzCC,IAAI,EAAE;IACJC,OAAO,EAAE,EAAE;IACXC,IAAI,EAAE,CAAC,WAAW,EAAE,QAAQ,EAAE,aAAa,CAAC;IAC5CC,QAAQ,EAAE,CACR,SAAS,EACT,UAAU,EACV,QAAQ,EACR,UAAU,EACV,cAAc,EACd,gBAAgB,EAChB,aAAa,EACb,eAAe,EACf,qBAAqB,EACrB,eAAe,EACf,mBAAmB,EACnB,cAAc,EACd,cAAc;EAElB;AACF,CAAC;AAED,OAAO,MAAMC,eAAe,GAAGC,MAAM,CAACC,MAAM,CAACZ,SAAS,CAAC;;AAEvD;AACA;AACA;AACA;AACA,OAAO,MAAMa,aAAqC,GAAG;EACnDC,SAAS,EAAE,WAAW;EACtBP,OAAO,EAAE,SAAS;EAClBQ,KAAK,EAAE,WAAW;EAClBC,eAAe,EAAE,iBAAiB;EAClCC,WAAW,EAAE,aAAa;EAC1B,YAAY,EAAE,WAAW;EACzBC,QAAQ,EAAE,UAAU;EACpBC,QAAQ,EAAE;AACZ,CAAC;;AAED;AACA;AACA;AACA,OAAO,MAAMC,YAAY,GAAG,CAAC,MAAM;EACjC,MAAMC,GAA2B,GAAG,CAAC,CAAC;EACtC,KAAK,MAAM,CAACC,GAAG,EAAEC,KAAK,CAAC,IAAIZ,MAAM,CAACa,OAAO,CAACX,aAAa,CAAC,EAAE;IACxDQ,GAAG,CAACE,KAAK,CAAC,GAAGD,GAAG;EAClB;EACA,OAAOD,GAAG;AACZ,CAAC,EAAE,CAAC;AAEJ,OAAO,MAAMI,gBAAgB,GAAG,SAAS;AAEzC,OAAO,MAAMC,kBAAkB,GAAG,CAAC,EACjC,OAAOC,MAAM,KAAK,WAAW,IAC1BA,MAAM,CAACC,QAAQ,CAACC,aAAa,CACjC","ignoreList":[]}
import { Children, use, useEffect, useId } from 'react';
import { REACT_TAG_MAP, TAG_NAMES, VALID_TAG_NAMES } from './constants';
import { Context } from './Provider';
import { cloneProps, mergeProps, pushToPropArray } from './utils';
function assertChildType(childType, nestedChildren) {
if (typeof childType !== 'string') throw Error('You may be attempting to nest <Helmet> components within each other, which is not allowed. Refer to our API for more information.');
if (!VALID_TAG_NAMES.includes(childType)) throw Error(`Only elements types ${VALID_TAG_NAMES.join(', ')} are allowed. Helmet does not support rendering <${childType}> elements. Refer to our API for more information.`);
if (!nestedChildren || typeof nestedChildren === 'string' || Array.isArray(nestedChildren)
// TODO: This piece of the check is wrong when parent is a fragment,
// and thus children may not be an array of strings.
// && nestedChildren.every((item) => typeof item === 'string')
) return;
throw Error(`Helmet expects a string as a child of <${childType}>. Did you forget to wrap your children in braces? ( <${childType}>{\`\`}</${childType}> ) Refer to our API for more information.`);
}
/**
* Given a string key, it checks it against the legacy mapping between supported
* HTML attribute names and their corresponding React prop names (for the names
* that are different). If found in the mapping, it prints a warning to console
* and returns the mapped prop name. Otherwise, it just returns the key as is,
* assuming it is already a valid React prop name.
*/
function getPropName(key) {
const res = REACT_TAG_MAP[key];
if (res) console.warn(`"${key}" is not a valid JSX prop, replace it by "${res}"`);
return res ?? key;
}
/**
* Given children and props of a <Helmet> component, it reduces them to a single
* props object.
*
* TODO: I guess, it should be further refactored, to make it cleaner...
* though, it should perfectly work as is, so not a huge priority for now.
*/
function reduceChildrenAndProps(props) {
// NOTE: `props` are clonned, thus it is safe to push additional items to
// array values of `res`, and to re-assign non-array values of `res`, without
// the risk to mutate the original `props` object.
const res = cloneProps(props);
// TODO: This is a temporary block, for compatibility with legacy library.
for (const item of Object.values(props)) {
if (Array.isArray(item)) {
for (const it of item) {
if (it) {
for (const key of Object.keys(it)) {
const p = getPropName(key);
if (p !== key) {
it[p] = it[key];
delete it[key];
}
}
}
}
} else if (item && typeof item === 'object') {
const it = item;
for (const key of Object.keys(it)) {
const p = getPropName(key);
if (p !== key) {
it[p] = it[key];
delete it[key];
}
}
}
}
Children.forEach(props.children, child => {
if (child === undefined || child === null) return;
if (typeof child !== 'object' || !('props' in child)) throw Error(`"${typeof child}" is not a valid <Helmet> descendant`);
let nestedChildren;
const childProps = {};
if (child.props) {
for (const [key, value] of Object.entries(child.props)) {
if (key === 'children') nestedChildren = value;else childProps[getPropName(key)] = value;
}
}
let {
type
} = child;
if (typeof type === 'symbol') type = type.toString();
assertChildType(type, nestedChildren);
function assertStringChild(child) {
if (typeof child !== 'string') {
// TODO: We want to throw, but the legacy code did not, so we won't for
// now.
console.error(`child of ${type} element should be a string`);
/*
throw Error(
// NOTE: assertChildType() above guarantees that `type` is a string,
// although it is not expressed in a way TypeScript can automatically
// pick up.
);
*/
}
}
switch (type) {
case TAG_NAMES.BASE:
res.base = childProps;
break;
case TAG_NAMES.BODY:
res.bodyAttributes = childProps;
break;
case TAG_NAMES.FRAGMENT:
mergeProps(res, reduceChildrenAndProps({
children: nestedChildren
}));
break;
case TAG_NAMES.HTML:
res.htmlAttributes = childProps;
break;
case TAG_NAMES.LINK:
case TAG_NAMES.META:
if (nestedChildren) throw Error(`<${type} /> elements are self-closing and can not contain children. Refer to our API for more information.`);
pushToPropArray(res, type, childProps);
break;
case TAG_NAMES.NOSCRIPT:
case TAG_NAMES.SCRIPT:
if (nestedChildren !== undefined) {
assertStringChild(nestedChildren);
childProps.innerHTML = nestedChildren;
}
pushToPropArray(res, type, childProps);
break;
case TAG_NAMES.STYLE:
assertStringChild(nestedChildren);
childProps.cssText = nestedChildren;
pushToPropArray(res, type, childProps);
break;
case TAG_NAMES.TITLE:
res.titleAttributes = childProps;
if (typeof nestedChildren === 'string') res.title = nestedChildren;
// When title contains {} expressions the children are an array of
// strings, and other values.
else if (Array.isArray(nestedChildren)) res.title = nestedChildren.join('');
break;
default:
{
// TODO: Perhaps, we should remove HEAD entry from TAG_NAMES?
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const bad = type;
}
}
});
delete res.children;
return res;
}
const Helmet = props => {
const context = use(Context);
if (!context) throw Error('<Helmet> component must be within a <HelmetProvider> children tree');
const id = useId();
// TODO: Agh... we need it here to ensure that it works server-side,
// and we need the same in the useEffect() below to ensure it works
// client-side in strict mode (and, thus completely correctly from React's
// pure component / side effect logic). It clearly should be optimized,
// but let's care about it later.
context.update(id, reduceChildrenAndProps(props));
// TODO: I guess, these two useEffect() can be merged together, which should
// also allow to simplify and optimize the client-side management of attrs and
// elements managed by these. Though, keeping them separate is an easier way
// for now to ensure backward compatibility.
useEffect(() => {
context.update(id, reduceChildrenAndProps(props));
context.clientApply();
});
useEffect(() => () => context.update(id, undefined), [context, id]);
return null;
};
export default Helmet;
//# sourceMappingURL=Helmet.js.map
{"version":3,"file":"Helmet.js","names":["Children","use","useEffect","useId","REACT_TAG_MAP","TAG_NAMES","VALID_TAG_NAMES","Context","cloneProps","mergeProps","pushToPropArray","assertChildType","childType","nestedChildren","Error","includes","join","Array","isArray","getPropName","key","res","console","warn","reduceChildrenAndProps","props","item","Object","values","it","keys","p","forEach","children","child","undefined","childProps","value","entries","type","toString","assertStringChild","error","BASE","base","BODY","bodyAttributes","FRAGMENT","HTML","htmlAttributes","LINK","META","NOSCRIPT","SCRIPT","innerHTML","STYLE","cssText","TITLE","titleAttributes","title","bad","Helmet","context","id","update","clientApply"],"sources":["../../src/Helmet.tsx"],"sourcesContent":["import {\n type FunctionComponent,\n type ReactElement,\n type ReactNode,\n Children,\n use,\n useEffect,\n useId,\n} from 'react';\n\nimport { REACT_TAG_MAP, TAG_NAMES, VALID_TAG_NAMES } from './constants';\n\nimport { Context } from './Provider';\n\nimport type {\n BaseProps,\n BodyProps,\n HelmetChildProps,\n HelmetProps,\n HtmlProps,\n LinkProps,\n MetaProps,\n NoscriptProps,\n ScriptProps,\n StyleProps,\n TitleProps,\n} from './types';\nimport { cloneProps, mergeProps, pushToPropArray } from './utils';\n\nfunction assertChildType(\n childType: ReactElement['type'],\n nestedChildren: ReactNode,\n): asserts childType is TAG_NAMES {\n if (typeof childType !== 'string') throw Error(\n 'You may be attempting to nest <Helmet> components within each other, which is not allowed. Refer to our API for more information.',\n );\n\n if (!(VALID_TAG_NAMES as string[]).includes(childType)) throw Error(\n `Only elements types ${VALID_TAG_NAMES.join(', ')} are allowed. Helmet does not support rendering <${childType}> elements. Refer to our API for more information.`,\n );\n\n if (\n !nestedChildren\n || typeof nestedChildren === 'string'\n || Array.isArray(nestedChildren)\n // TODO: This piece of the check is wrong when parent is a fragment,\n // and thus children may not be an array of strings.\n // && nestedChildren.every((item) => typeof item === 'string')\n ) return;\n\n throw Error(\n `Helmet expects a string as a child of <${childType}>. Did you forget to wrap your children in braces? ( <${childType}>{\\`\\`}</${childType}> ) Refer to our API for more information.`,\n );\n}\n\n/**\n * Given a string key, it checks it against the legacy mapping between supported\n * HTML attribute names and their corresponding React prop names (for the names\n * that are different). If found in the mapping, it prints a warning to console\n * and returns the mapped prop name. Otherwise, it just returns the key as is,\n * assuming it is already a valid React prop name.\n */\nfunction getPropName(key: string): keyof HelmetChildProps {\n const res = REACT_TAG_MAP[key];\n if (res) console.warn(\n `\"${key}\" is not a valid JSX prop, replace it by \"${res}\"`,\n );\n return (res ?? key) as keyof HelmetChildProps;\n}\n\n/**\n * Given children and props of a <Helmet> component, it reduces them to a single\n * props object.\n *\n * TODO: I guess, it should be further refactored, to make it cleaner...\n * though, it should perfectly work as is, so not a huge priority for now.\n */\nfunction reduceChildrenAndProps(props: HelmetProps): Omit<HelmetProps, 'children'> {\n // NOTE: `props` are clonned, thus it is safe to push additional items to\n // array values of `res`, and to re-assign non-array values of `res`, without\n // the risk to mutate the original `props` object.\n const res: HelmetProps = cloneProps(props);\n\n // TODO: This is a temporary block, for compatibility with legacy library.\n for (const item of Object.values(props)) {\n if (Array.isArray(item)) {\n for (const it of item) {\n if (it) {\n for (const key of Object.keys(it)) {\n const p = getPropName(key);\n if (p !== key) {\n it[p] = it[key as keyof HelmetChildProps] as unknown;\n delete it[key as keyof HelmetChildProps];\n }\n }\n }\n }\n } else if (item && typeof item === 'object') {\n const it = item as HelmetChildProps;\n for (const key of Object.keys(it)) {\n const p = getPropName(key);\n if (p !== key) {\n it[p] = it[key as keyof HelmetChildProps] as unknown;\n delete it[key as keyof HelmetChildProps];\n }\n }\n }\n }\n\n Children.forEach(props.children, (child) => {\n if (child === undefined || child === null) return;\n\n if (typeof child !== 'object' || !('props' in child)) throw Error(\n `\"${typeof child}\" is not a valid <Helmet> descendant`,\n );\n\n let nestedChildren: ReactNode;\n const childProps: HelmetChildProps = {};\n if (child.props) {\n for (const [key, value] of Object.entries(child.props)) {\n if (key === 'children') nestedChildren = value as ReactNode;\n else childProps[getPropName(key)] = value as unknown;\n }\n }\n\n let { type } = child;\n if (typeof type === 'symbol') type = (type as 'symbol').toString();\n assertChildType(type, nestedChildren);\n\n function assertStringChild(child: ReactNode): asserts child is string {\n if (typeof child !== 'string') {\n // TODO: We want to throw, but the legacy code did not, so we won't for\n // now.\n console.error(`child of ${type as string} element should be a string`);\n\n /*\n throw Error(\n // NOTE: assertChildType() above guarantees that `type` is a string,\n // although it is not expressed in a way TypeScript can automatically\n // pick up.\n );\n */\n }\n }\n\n switch (type) {\n case TAG_NAMES.BASE:\n res.base = childProps as BaseProps;\n break;\n\n case TAG_NAMES.BODY:\n res.bodyAttributes = childProps as BodyProps;\n break;\n\n case TAG_NAMES.FRAGMENT:\n mergeProps(res, reduceChildrenAndProps({ children: nestedChildren }));\n break;\n\n case TAG_NAMES.HTML:\n res.htmlAttributes = childProps as HtmlProps;\n break;\n\n case TAG_NAMES.LINK:\n case TAG_NAMES.META:\n if (nestedChildren) throw Error(\n `<${type} /> elements are self-closing and can not contain children. Refer to our API for more information.`,\n );\n pushToPropArray(res, type, childProps as LinkProps | MetaProps);\n break;\n\n case TAG_NAMES.NOSCRIPT:\n case TAG_NAMES.SCRIPT:\n if (nestedChildren !== undefined) {\n assertStringChild(nestedChildren);\n (childProps as NoscriptProps | ScriptProps)\n .innerHTML = nestedChildren;\n }\n pushToPropArray(res, type, childProps);\n break;\n\n case TAG_NAMES.STYLE:\n assertStringChild(nestedChildren);\n (childProps as StyleProps).cssText = nestedChildren;\n pushToPropArray(res, type, childProps as StyleProps);\n break;\n\n case TAG_NAMES.TITLE:\n res.titleAttributes = childProps as TitleProps;\n\n if (typeof nestedChildren === 'string') res.title = nestedChildren;\n\n // When title contains {} expressions the children are an array of\n // strings, and other values.\n else if (Array.isArray(nestedChildren)) res.title = nestedChildren.join('');\n break;\n\n default: {\n // TODO: Perhaps, we should remove HEAD entry from TAG_NAMES?\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const bad: TAG_NAMES.HEAD = type;\n }\n }\n });\n\n delete res.children;\n return res;\n}\n\nconst Helmet: FunctionComponent<HelmetProps> = (props) => {\n const context = use(Context);\n\n if (!context) throw Error(\n '<Helmet> component must be within a <HelmetProvider> children tree',\n );\n\n const id = useId();\n\n // TODO: Agh... we need it here to ensure that it works server-side,\n // and we need the same in the useEffect() below to ensure it works\n // client-side in strict mode (and, thus completely correctly from React's\n // pure component / side effect logic). It clearly should be optimized,\n // but let's care about it later.\n context.update(id, reduceChildrenAndProps(props));\n\n // TODO: I guess, these two useEffect() can be merged together, which should\n // also allow to simplify and optimize the client-side management of attrs and\n // elements managed by these. Though, keeping them separate is an easier way\n // for now to ensure backward compatibility.\n useEffect(() => {\n context.update(id, reduceChildrenAndProps(props));\n context.clientApply();\n });\n\n useEffect(() => () => context.update(id, undefined), [context, id]);\n\n return null;\n};\n\nexport default Helmet;\n"],"mappings":"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","ignoreList":[]}
export * from './types';
export { default as Helmet } from './Helmet';
export { default as HelmetProvider } from './Provider';
//# sourceMappingURL=index.js.map
{"version":3,"file":"index.js","names":["default","Helmet","HelmetProvider"],"sources":["../../src/index.tsx"],"sourcesContent":["export * from './types';\n\nexport { default as Helmet } from './Helmet';\nexport { default as HelmetProvider } from './Provider';\n"],"mappings":"AAAA,cAAc,SAAS;AAEvB,SAASA,OAAO,IAAIC,MAAM,QAAQ,UAAU;AAC5C,SAASD,OAAO,IAAIE,cAAc,QAAQ,YAAY","ignoreList":[]}
import { createContext, useRef } from 'react';
import { newServerState } from './server';
import { IS_DOM_ENVIRONMENT } from './constants';
import { calcAggregatedState } from './utils';
import { commitTagChanges } from './client';
import { jsx as _jsx } from "react/jsx-runtime";
export const Context = /*#__PURE__*/createContext(undefined);
const HelmetProvider = _ref => {
let {
children,
context
} = _ref;
const {
current: heap
} = useRef({
firstRender: true,
helmets: [],
state: undefined
});
const contextValueRef = useRef(null);
if (!contextValueRef.current) {
contextValueRef.current = {
clientApply() {
if (IS_DOM_ENVIRONMENT && !heap.state) {
heap.state = calcAggregatedState(heap.helmets);
if (heap.state.defer) {
if (heap.nextAnimFrameId === undefined) {
heap.nextAnimFrameId = requestAnimationFrame(() => {
heap.state ??= calcAggregatedState(heap.helmets);
commitTagChanges(heap.state, heap.firstRender);
heap.firstRender = false;
delete heap.nextAnimFrameId;
});
}
} else {
if (heap.nextAnimFrameId !== undefined) {
cancelAnimationFrame(heap.nextAnimFrameId);
delete heap.nextAnimFrameId;
}
commitTagChanges(heap.state, heap.firstRender);
heap.firstRender = false;
}
}
},
update(id, props) {
const idx = heap.helmets.findIndex(item => item[0] === id);
if (idx >= 0) {
delete heap.state;
if (props) heap.helmets[idx][1] = props;else heap.helmets.splice(idx, 1);
} else if (props) {
delete heap.state;
heap.helmets.push([id, props]);
}
}
};
}
if (context && (!context.helmet || context.helmet !== heap.serverState)) {
heap.serverState ??= newServerState(heap);
context.helmet = heap.serverState;
}
return /*#__PURE__*/_jsx(Context, {
value: contextValueRef.current,
children: children
});
};
export default HelmetProvider;
//# sourceMappingURL=Provider.js.map
{"version":3,"file":"Provider.js","names":["createContext","useRef","newServerState","IS_DOM_ENVIRONMENT","calcAggregatedState","commitTagChanges","jsx","_jsx","Context","undefined","HelmetProvider","_ref","children","context","current","heap","firstRender","helmets","state","contextValueRef","clientApply","defer","nextAnimFrameId","requestAnimationFrame","cancelAnimationFrame","update","id","props","idx","findIndex","item","splice","push","helmet","serverState","value"],"sources":["../../src/Provider.tsx"],"sourcesContent":["import {\n type FunctionComponent,\n type ReactNode,\n createContext,\n useRef,\n} from 'react';\n\nimport type {\n ContextValue,\n HelmetDataContext,\n HelmetProps,\n HelmetProviderHeap,\n} from './types';\n\nimport { newServerState } from './server';\nimport { IS_DOM_ENVIRONMENT } from './constants';\nimport { calcAggregatedState } from './utils';\nimport { commitTagChanges } from './client';\n\nexport const Context = createContext<ContextValue | undefined>(undefined);\n\ntype ProviderProps = {\n children?: ReactNode;\n context?: HelmetDataContext;\n};\n\nconst HelmetProvider: FunctionComponent<ProviderProps> = ({\n children,\n context,\n}) => {\n const { current: heap } = useRef<HelmetProviderHeap>({\n firstRender: true,\n helmets: [],\n state: undefined,\n });\n\n const contextValueRef = useRef<ContextValue>(null);\n\n if (!contextValueRef.current) {\n contextValueRef.current = {\n clientApply() {\n if (IS_DOM_ENVIRONMENT && !heap.state) {\n heap.state = calcAggregatedState(heap.helmets);\n if (heap.state.defer) {\n if (heap.nextAnimFrameId === undefined) {\n heap.nextAnimFrameId = requestAnimationFrame(() => {\n heap.state ??= calcAggregatedState(heap.helmets);\n commitTagChanges(heap.state, heap.firstRender);\n heap.firstRender = false;\n delete heap.nextAnimFrameId;\n });\n }\n } else {\n if (heap.nextAnimFrameId !== undefined) {\n cancelAnimationFrame(heap.nextAnimFrameId);\n delete heap.nextAnimFrameId;\n }\n commitTagChanges(heap.state, heap.firstRender);\n heap.firstRender = false;\n }\n }\n },\n update(id: string, props: HelmetProps | undefined) {\n const idx = heap.helmets.findIndex((item) => item[0] === id);\n if (idx >= 0) {\n delete heap.state;\n if (props) heap.helmets[idx]![1] = props;\n else heap.helmets.splice(idx, 1);\n } else if (props) {\n delete heap.state;\n heap.helmets.push([id, props]);\n }\n },\n };\n }\n\n if (context && (!context.helmet || context.helmet !== heap.serverState)) {\n heap.serverState ??= newServerState(heap);\n context.helmet = heap.serverState;\n }\n\n return <Context value={contextValueRef.current}>{children}</Context>;\n};\n\nexport default HelmetProvider;\n"],"mappings":"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","ignoreList":[]}
import { createElement } from 'react';
import { HELMET_ATTRIBUTE, TAG_NAMES, REACT_TAG_MAP, TAG_PROPERTIES, HTML_TAG_MAP } from './constants';
import { calcAggregatedState, flattenArray, propToAttr } from './utils';
import { jsx as _jsx } from "react/jsx-runtime";
const SELF_CLOSING_TAGS = [TAG_NAMES.NOSCRIPT, TAG_NAMES.SCRIPT, TAG_NAMES.STYLE];
const encodeSpecialCharacters = function (str) {
let encode = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
if (encode === false) {
return String(str);
}
return String(str).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#x27;');
};
function generateElementAttributesAsString(attrs) {
let res = '';
for (const [name, value] of Object.entries(attrs)) {
const attr = propToAttr(name);
const neu = value === undefined ? attr : `${attr}="${value}"`;
if (neu && res) res += ' ';
res += neu;
}
return res;
}
const generateTitleAsString = (title, attrs, encode) => {
let attrsStr = generateElementAttributesAsString(attrs);
if (attrsStr) attrsStr = ` ${attrsStr}`;
const flattenedTitle = flattenArray(title);
return `<title ${HELMET_ATTRIBUTE}="true"${attrsStr}>${encodeSpecialCharacters(flattenedTitle, encode)}</title>`;
};
function generateTagsAsString(type, tags, encode) {
let res = '';
for (const tag of tags) {
let attributeHtml = '';
const entries = Object.entries(tag);
for (const [name, value] of entries) {
if (!(name === TAG_PROPERTIES.INNER_HTML || name === TAG_PROPERTIES.CSS_TEXT)) {
const attrName = HTML_TAG_MAP[name] ?? name;
const attr = value === undefined ? attrName : `${attrName}="${encodeSpecialCharacters(value, encode)}"`;
if (attributeHtml) attributeHtml += ` ${attr}`;else attributeHtml = attr;
}
}
const tagContent = tag.innerHTML ?? tag.cssText ?? '';
const isSelfClosing = !SELF_CLOSING_TAGS.includes(type);
res += `<${type} ${HELMET_ATTRIBUTE}="true" ${attributeHtml}${isSelfClosing ? '/>' : `>${tagContent}</${type}>`}`;
}
return res;
}
/**
* Given a map of element attribute names & values it returns the corresponding
* map of element properties & values (i.e. replacing some attribute names by
* their corresponding property names).
*/
function mapElementAttributesToProps(attributes) {
let ops = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
const res = {};
if (ops.addHelmetDataAttr) res[HELMET_ATTRIBUTE] = true;
if (ops.addKey !== undefined) res.key = ops.addKey;
for (const [attrName, value] of Object.entries(attributes)) {
const propName = REACT_TAG_MAP[attrName] ?? attrName;
switch (propName) {
// cssText and innerHTML props get a special treatment to avoid that React
// escapes their values.
case 'cssText':
case 'innerHTML':
res.dangerouslySetInnerHTML = {
__html: value
};
break;
default:
res[propName] = value;
}
}
return res;
}
function renderTitle(title, attrs) {
// NOTE: Rendered as array to match legacy behavior.
return [/*#__PURE__*/_jsx("title", {
...mapElementAttributesToProps(attrs, {
addHelmetDataAttr: true
}),
children: title
}, title)];
}
function renderElement(type, attrs, key) {
return /*#__PURE__*/createElement(type, mapElementAttributesToProps(attrs, {
addHelmetDataAttr: true,
addKey: key
}));
}
function renderElements(type, attrs) {
const res = [];
for (let i = 0; i < attrs.length; ++i) {
res.push(renderElement(type, attrs[i], i));
}
return res;
}
/*
function newHelmetDatumForTitle(
title: string,
attrs: Attributes = {},
encode: boolean,
): HelmetDatum {
return {
};
}
const getPriorityMethods = ({
metaTags,
linkTags,
scriptTags,
encode,
}: MappedServerState) => {
const meta = prioritizer(metaTags, SEO_PRIORITY_TAGS.meta);
const link = prioritizer(linkTags, SEO_PRIORITY_TAGS.link);
const script = prioritizer(scriptTags, SEO_PRIORITY_TAGS.script);
// need to have toComponent() and toString()
const priorityMethods = {
toComponent: () => [
...renderElements(TAG_NAMES.META, meta.priority),
...renderElements(TAG_NAMES.LINK, link.priority),
...renderElements(TAG_NAMES.SCRIPT, script.priority),
],
// generate all the tags as strings and concatenate them
toString: () => `${
generateTagsAsString(TAG_NAMES.META, meta.priority, encode)} ${
generateTagsAsString(TAG_NAMES.LINK, link.priority, encode)} ${
generateTagsAsString(TAG_NAMES.SCRIPT, script.priority, encode)}`,
};
return {
priorityMethods,
metaTags: meta.default as HTMLMetaElement[],
linkTags: link.default as HTMLLinkElement[],
scriptTags: script.default as HTMLScriptElement[],
};
};
/*
function mapStateOnServer(props: MappedServerState): HelmetServerState {
const {
baseTag,
bodyAttributes,
encode = true,
htmlAttributes,
noscriptTags,
prioritizeSeoTags,
styleTags,
title = '',
titleAttributes,
} = props;
let { linkTags, metaTags, scriptTags } = props;
let priorityMethods: HelmetDatum = {
toComponent: () => null,
toString: () => '',
};
if (prioritizeSeoTags) {
({
priorityMethods,
linkTags,
metaTags,
scriptTags,
} = getPriorityMethods(props));
}
return {
priority: priorityMethods,
};
}
*/
// export default mapStateOnServer;
// TODO: So... refactor it, it should be based on the Helmet provider
// heap, and it should calculate its state into that, and then use it.
export function newServerState(heap) {
// TODO: Should this function to be attached to the heap itself?
const getState = () => {
heap.state ??= calcAggregatedState(heap.helmets);
return heap.state;
};
return {
base: {
toComponent() {
const props = getState().base;
return props ? renderElements('base', [props]) : [];
},
toString() {
const s = getState();
return s.base ? generateTagsAsString('base', [s.base], s.encodeSpecialCharacters) : '';
}
},
bodyAttributes: {
toComponent() {
const props = getState().bodyAttributes;
return mapElementAttributesToProps(props ?? {});
},
toString() {
const props = getState().bodyAttributes;
return generateElementAttributesAsString(props ?? {});
}
},
htmlAttributes: {
toComponent() {
const props = getState().htmlAttributes;
return mapElementAttributesToProps(props ?? {});
},
toString() {
const props = getState().htmlAttributes;
return generateElementAttributesAsString(props ?? {});
}
},
link: {
toComponent() {
return renderElements('link', getState().links ?? []);
},
toString() {
const s = getState();
return generateTagsAsString('link', s.links ?? [], s.encodeSpecialCharacters);
}
},
meta: {
toComponent() {
return renderElements('meta', getState().meta ?? []);
},
toString() {
const s = getState();
return generateTagsAsString('meta', s.meta ?? [], s.encodeSpecialCharacters);
}
},
noscript: {
toComponent() {
return renderElements('noscript', getState().noscript ?? []);
},
toString() {
const s = getState();
return generateTagsAsString('noscript', s.noscript ?? [], s.encodeSpecialCharacters);
}
},
priority: {
toComponent() {
const s = getState();
return [...renderElements('meta', s.priority?.meta ?? []), ...renderElements('link', s.priority?.links ?? []), ...renderElements('script', s.priority?.script ?? [])];
},
toString() {
const s = getState();
const meta = generateTagsAsString('meta', s.priority?.meta ?? [], s.encodeSpecialCharacters);
const link = generateTagsAsString('link', s.priority?.links ?? [], s.encodeSpecialCharacters);
const script = generateTagsAsString('script', s.priority?.script ?? [], s.encodeSpecialCharacters);
let res = meta;
if (link) {
if (res) res += ' ';
res += link;
}
if (script) {
if (res) res += ' ';
res += script;
}
return res;
}
},
script: {
toComponent() {
return renderElements('script', getState().script ?? []);
},
toString() {
const s = getState();
return generateTagsAsString('script', s.script ?? [], s.encodeSpecialCharacters);
}
},
style: {
toComponent() {
return renderElements('style', getState().style ?? []);
},
toString() {
const s = getState();
return generateTagsAsString('style', s.style ?? [], s.encodeSpecialCharacters);
}
},
title: {
toComponent() {
const s = getState();
return renderTitle(s.title ?? '', s.titleAttributes ?? {});
},
toString() {
const s = getState();
return generateTitleAsString(s.title ?? '', s.titleAttributes ?? {}, s.encodeSpecialCharacters);
}
}
};
}
//# sourceMappingURL=server.js.map
{"version":3,"file":"server.js","names":["createElement","HELMET_ATTRIBUTE","TAG_NAMES","REACT_TAG_MAP","TAG_PROPERTIES","HTML_TAG_MAP","calcAggregatedState","flattenArray","propToAttr","jsx","_jsx","SELF_CLOSING_TAGS","NOSCRIPT","SCRIPT","STYLE","encodeSpecialCharacters","str","encode","arguments","length","undefined","String","replace","generateElementAttributesAsString","attrs","res","name","value","Object","entries","attr","neu","generateTitleAsString","title","attrsStr","flattenedTitle","generateTagsAsString","type","tags","tag","attributeHtml","INNER_HTML","CSS_TEXT","attrName","tagContent","innerHTML","cssText","isSelfClosing","includes","mapElementAttributesToProps","attributes","ops","addHelmetDataAttr","addKey","key","propName","dangerouslySetInnerHTML","__html","renderTitle","children","renderElement","renderElements","i","push","newServerState","heap","getState","state","helmets","base","toComponent","props","toString","s","bodyAttributes","htmlAttributes","link","links","meta","noscript","priority","script","style","titleAttributes"],"sources":["../../src/server.tsx"],"sourcesContent":["import {\n type HTMLAttributes,\n type Key,\n type ReactNode,\n createElement,\n} from 'react';\n\nimport {\n HELMET_ATTRIBUTE,\n TAG_NAMES,\n REACT_TAG_MAP,\n TAG_PROPERTIES,\n HTML_TAG_MAP,\n} from './constants';\n\nimport type {\n HelmetProviderHeap,\n HelmetServerState,\n ScriptProps,\n StyleProps,\n TitleProps,\n} from './types';\n\nimport {\n calcAggregatedState,\n flattenArray,\n propToAttr,\n} from './utils';\n\nconst SELF_CLOSING_TAGS: string[] = [\n TAG_NAMES.NOSCRIPT,\n TAG_NAMES.SCRIPT,\n TAG_NAMES.STYLE,\n];\n\nconst encodeSpecialCharacters = (str: string, encode = true) => {\n if (encode === false) {\n return String(str);\n }\n\n return String(str)\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&#x27;');\n};\n\nfunction generateElementAttributesAsString<T>(\n attrs: HTMLAttributes<T>,\n): string {\n let res: string = '';\n\n for (const [name, value] of Object.entries(attrs)) {\n const attr = propToAttr(name);\n const neu = value === undefined ? attr : `${attr}=\"${value}\"`;\n if (neu && res) res += ' ';\n res += neu;\n }\n\n return res;\n}\n\nconst generateTitleAsString = (\n title: string,\n attrs: TitleProps,\n encode: boolean,\n) => {\n let attrsStr = generateElementAttributesAsString(attrs);\n if (attrsStr) attrsStr = ` ${attrsStr}`;\n\n const flattenedTitle = flattenArray(title);\n\n return `<title ${HELMET_ATTRIBUTE}=\"true\"${attrsStr}>${\n encodeSpecialCharacters(flattenedTitle, encode)\n }</title>`;\n};\n\nfunction generateTagsAsString<T>(\n type: string,\n tags: HTMLAttributes<T>[],\n encode: boolean,\n): string {\n let res = '';\n\n for (const tag of tags) {\n let attributeHtml = '';\n\n const entries = Object.entries(tag);\n for (const [name, value] of entries) {\n if (!(name === TAG_PROPERTIES.INNER_HTML as string\n || name === TAG_PROPERTIES.CSS_TEXT as string)) {\n const attrName = HTML_TAG_MAP[name] ?? name;\n const attr = value === undefined ? attrName : `${attrName}=\"${encodeSpecialCharacters(value as string, encode)}\"`;\n if (attributeHtml) attributeHtml += ` ${attr}`;\n else attributeHtml = attr;\n }\n }\n\n const tagContent = (tag as ScriptProps).innerHTML ?? (tag as StyleProps).cssText ?? '';\n\n const isSelfClosing = !SELF_CLOSING_TAGS.includes(type);\n\n res += `<${type} ${HELMET_ATTRIBUTE}=\"true\" ${attributeHtml}${\n isSelfClosing ? '/>' : `>${tagContent}</${type}>`\n }`;\n }\n\n return res;\n}\n\n/**\n * Given a map of element attribute names & values it returns the corresponding\n * map of element properties & values (i.e. replacing some attribute names by\n * their corresponding property names).\n */\n\nfunction mapElementAttributesToProps<T>(\n attributes: HTMLAttributes<T>,\n ops: { addHelmetDataAttr?: boolean; addKey?: Key } = {},\n): Record<string, unknown> {\n const res: Record<string, unknown> = {};\n if (ops.addHelmetDataAttr) res[HELMET_ATTRIBUTE] = true;\n if (ops.addKey !== undefined) res.key = ops.addKey;\n for (const [attrName, value] of Object.entries(attributes)) {\n const propName = REACT_TAG_MAP[attrName] ?? attrName;\n\n switch (propName) {\n // cssText and innerHTML props get a special treatment to avoid that React\n // escapes their values.\n case 'cssText':\n case 'innerHTML':\n res.dangerouslySetInnerHTML = { __html: value as unknown };\n break;\n default:\n res[propName] = value;\n }\n }\n return res;\n}\n\nfunction renderTitle(title: string, attrs: TitleProps): ReactNode {\n // NOTE: Rendered as array to match legacy behavior.\n return [\n <title\n key={title}\n {...mapElementAttributesToProps(attrs, { addHelmetDataAttr: true })}\n >\n {title}\n </title>,\n ];\n}\n\nfunction renderElement<T>(\n type: string,\n attrs: HTMLAttributes<T>,\n key?: Key,\n): ReactNode {\n return createElement(type, mapElementAttributesToProps(attrs, {\n addHelmetDataAttr: true,\n addKey: key,\n }));\n}\n\nfunction renderElements<T>(\n type: string,\n attrs: HTMLAttributes<T>[],\n): ReactNode[] {\n const res: ReactNode[] = [];\n for (let i = 0; i < attrs.length; ++i) {\n res.push(renderElement(type, attrs[i]!, i));\n }\n return res;\n}\n\n/*\n\nfunction newHelmetDatumForTitle(\n title: string,\n attrs: Attributes = {},\n encode: boolean,\n): HelmetDatum {\n return {\n\n };\n}\n\nconst getPriorityMethods = ({\n metaTags,\n linkTags,\n scriptTags,\n encode,\n}: MappedServerState) => {\n const meta = prioritizer(metaTags, SEO_PRIORITY_TAGS.meta);\n const link = prioritizer(linkTags, SEO_PRIORITY_TAGS.link);\n const script = prioritizer(scriptTags, SEO_PRIORITY_TAGS.script);\n\n // need to have toComponent() and toString()\n const priorityMethods = {\n toComponent: () => [\n ...renderElements(TAG_NAMES.META, meta.priority),\n ...renderElements(TAG_NAMES.LINK, link.priority),\n ...renderElements(TAG_NAMES.SCRIPT, script.priority),\n ],\n\n // generate all the tags as strings and concatenate them\n toString: () => `${\n generateTagsAsString(TAG_NAMES.META, meta.priority, encode)} ${\n generateTagsAsString(TAG_NAMES.LINK, link.priority, encode)} ${\n generateTagsAsString(TAG_NAMES.SCRIPT, script.priority, encode)}`,\n };\n\n return {\n priorityMethods,\n metaTags: meta.default as HTMLMetaElement[],\n linkTags: link.default as HTMLLinkElement[],\n scriptTags: script.default as HTMLScriptElement[],\n };\n};\n\n/*\nfunction mapStateOnServer(props: MappedServerState): HelmetServerState {\n const {\n baseTag,\n bodyAttributes,\n encode = true,\n htmlAttributes,\n noscriptTags,\n prioritizeSeoTags,\n styleTags,\n title = '',\n titleAttributes,\n } = props;\n let { linkTags, metaTags, scriptTags } = props;\n let priorityMethods: HelmetDatum = {\n toComponent: () => null,\n toString: () => '',\n };\n if (prioritizeSeoTags) {\n ({\n priorityMethods,\n linkTags,\n metaTags,\n scriptTags,\n } = getPriorityMethods(props));\n }\n return {\n priority: priorityMethods,\n };\n}\n*/\n\n// export default mapStateOnServer;\n\n// TODO: So... refactor it, it should be based on the Helmet provider\n// heap, and it should calculate its state into that, and then use it.\n\nexport function newServerState(heap: HelmetProviderHeap): HelmetServerState {\n // TODO: Should this function to be attached to the heap itself?\n const getState = () => {\n heap.state ??= calcAggregatedState(heap.helmets);\n return heap.state;\n };\n\n return {\n base: {\n toComponent() {\n const props = getState().base;\n return props ? renderElements('base', [props]) : [];\n },\n toString() {\n const s = getState();\n return s.base ? generateTagsAsString('base', [s.base], s.encodeSpecialCharacters) : '';\n },\n },\n bodyAttributes: {\n toComponent() {\n const props = getState().bodyAttributes;\n return mapElementAttributesToProps(props ?? {});\n },\n toString() {\n const props = getState().bodyAttributes;\n return generateElementAttributesAsString(props ?? {});\n },\n },\n htmlAttributes: {\n toComponent() {\n const props = getState().htmlAttributes;\n return mapElementAttributesToProps(props ?? {});\n },\n toString() {\n const props = getState().htmlAttributes;\n return generateElementAttributesAsString(props ?? {});\n },\n },\n link: {\n toComponent() {\n return renderElements('link', getState().links ?? []);\n },\n toString() {\n const s = getState();\n return generateTagsAsString('link', s.links ?? [], s.encodeSpecialCharacters);\n },\n },\n meta: {\n toComponent() {\n return renderElements('meta', getState().meta ?? []);\n },\n toString() {\n const s = getState();\n return generateTagsAsString('meta', s.meta ?? [], s.encodeSpecialCharacters);\n },\n },\n noscript: {\n toComponent() {\n return renderElements('noscript', getState().noscript ?? []);\n },\n toString() {\n const s = getState();\n return generateTagsAsString('noscript', s.noscript ?? [], s.encodeSpecialCharacters);\n },\n },\n priority: {\n toComponent() {\n const s = getState();\n return [\n ...renderElements('meta', s.priority?.meta ?? []),\n ...renderElements('link', s.priority?.links ?? []),\n ...renderElements('script', s.priority?.script ?? []),\n ];\n },\n toString() {\n const s = getState();\n const meta = generateTagsAsString('meta', s.priority?.meta ?? [], s.encodeSpecialCharacters);\n const link = generateTagsAsString('link', s.priority?.links ?? [], s.encodeSpecialCharacters);\n const script = generateTagsAsString('script', s.priority?.script ?? [], s.encodeSpecialCharacters);\n\n let res = meta;\n if (link) {\n if (res) res += ' ';\n res += link;\n }\n if (script) {\n if (res) res += ' ';\n res += script;\n }\n\n return res;\n },\n },\n script: {\n toComponent() {\n return renderElements('script', getState().script ?? []);\n },\n toString() {\n const s = getState();\n return generateTagsAsString('script', s.script ?? [], s.encodeSpecialCharacters);\n },\n },\n style: {\n toComponent() {\n return renderElements('style', getState().style ?? []);\n },\n toString() {\n const s = getState();\n return generateTagsAsString('style', s.style ?? [], s.encodeSpecialCharacters);\n },\n },\n title: {\n toComponent() {\n const s = getState();\n return renderTitle(s.title ?? '', s.titleAttributes ?? {});\n },\n toString() {\n const s = getState();\n return generateTitleAsString(s.title ?? '', s.titleAttributes ?? {}, s.encodeSpecialCharacters);\n },\n },\n };\n}\n"],"mappings":"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","ignoreList":[]}
export {};
//# sourceMappingURL=types.js.map
{"version":3,"file":"types.js","names":[],"sources":["../../src/types.ts"],"sourcesContent":["import type {\n BaseHTMLAttributes,\n HtmlHTMLAttributes,\n HTMLAttributes,\n LinkHTMLAttributes,\n MetaHTMLAttributes,\n ReactNode,\n ScriptHTMLAttributes,\n StyleHTMLAttributes,\n} from 'react';\n\nexport type BaseProps = BaseHTMLAttributes<HTMLBaseElement>;\nexport type BodyProps = HTMLAttributes<HTMLBodyElement>;\nexport type HtmlProps = HtmlHTMLAttributes<HTMLHtmlElement>;\nexport type LinkProps = LinkHTMLAttributes<HTMLLinkElement>;\nexport type MetaProps = MetaHTMLAttributes<HTMLMetaElement>;\n\nexport type NoscriptProps = HTMLAttributes<HTMLElement> & {\n innerHTML?: string;\n};\n\nexport type ScriptProps = ScriptHTMLAttributes<HTMLScriptElement> & {\n innerHTML?: string;\n};\n\nexport type StyleProps = StyleHTMLAttributes<HTMLStyleElement> & {\n cssText?: string;\n};\n\nexport type TitleProps = HTMLAttributes<HTMLTitleElement>;\n\nexport type HelmetChildProps =\n | BaseProps | BodyProps | HtmlProps | LinkProps | MetaProps | NoscriptProps\n | ScriptProps | StyleProps | TitleProps;\n\n/**\n * String data for title.\n */\nexport type StringData = {\n title?: string;\n titleTemplate?: string;\n};\n\nexport type HelmetTags = {\n baseTag: HTMLBaseElement[];\n linkTags: HTMLLinkElement[];\n metaTags: HTMLMetaElement[];\n noscriptTags: HTMLElement[];\n scriptTags: HTMLScriptElement[];\n styleTags: HTMLStyleElement[];\n};\n\nexport type HelmetDatum<T = ReactNode> = {\n toString(): string;\n toComponent(): T;\n};\n\nexport type HelmetHTMLBodyDatum = HelmetDatum<HTMLAttributes<HTMLBodyElement>>;\n\nexport type HelmetHTMLElementDatum =\n HelmetDatum<HTMLAttributes<HTMLHtmlElement>>;\n\nexport type HelmetServerState = {\n base: HelmetDatum;\n bodyAttributes: HelmetHTMLBodyDatum;\n htmlAttributes: HelmetHTMLElementDatum;\n link: HelmetDatum;\n meta: HelmetDatum;\n noscript: HelmetDatum;\n script: HelmetDatum;\n style: HelmetDatum;\n title: HelmetDatum;\n\n // TODO: Why is it needed? Can't it be a part of `title` value?\n titleAttributes?: HelmetDatum;\n\n priority: HelmetDatum;\n};\n\nexport type StateUpdate = HelmetTags & {\n bodyAttributes: BodyProps;\n defer: boolean;\n htmlAttributes: HtmlProps;\n\n // TODO: The signature of this callback is essentially the same as\n // OnChangeClientState, declared inside Helmet module; and there is\n // a circular dependency between that declaration and this StateUpdate type.\n // Also, not sure this field is really necessary inside StateUpdate?\n onChangeClientState: (\n newState: StateUpdate,\n addedTags: Partial<HelmetTags>,\n removedTags: Partial<HelmetTags>,\n ) => void;\n\n title: string;\n titleAttributes: TitleProps;\n};\n\n// TODO: Rewise the typing!\nexport type OnChangeClientState = (\n // TODO: So... the new state should be a map of attribute/value maps\n // for all children elements.\n newState: StateUpdate,\n addedTags: Partial<HelmetTags>,\n removedTags: Partial<HelmetTags>,\n) => void;\n\n/**\n * A subset of <Helmet> properties, corresponding to prop arrays for\n * elements that may be present in DOM multiple times.\n */\nexport type HelmetPropArrays = {\n link?: LinkProps[];\n meta?: MetaProps[];\n noscript?: NoscriptProps[];\n script?: ScriptProps[];\n style?: StyleProps[];\n};\n\nexport type PropArrayItem<T extends keyof HelmetPropArrays>\n = Exclude<HelmetPropArrays[T], undefined>[number];\n\n/**\n * A subset of <Helmet> properties, corresponding to props for elements\n * that may be present in DOM a single time at most.\n */\nexport type HelmetPropObjects = {\n bodyAttributes?: BodyProps;\n htmlAttributes?: HtmlProps;\n titleAttributes?: TitleProps;\n};\n\nexport type HelmetPropBooleans = {\n prioritizeSeoTags?: boolean;\n};\n\n/**\n * Properties accepted by <Helmet> components.\n */\nexport type HelmetProps = HelmetPropArrays\n & HelmetPropObjects\n & HelmetPropBooleans\n & {\n base?: BaseProps;\n children?: ReactNode;\n defaultTitle?: string;\n defer?: boolean;\n encodeSpecialCharacters?: boolean;\n onChangeClientState?: OnChangeClientState;\n title?: string;\n titleTemplate?: string;\n };\n\nexport type RegisteredHelmetPropsArray\n = Array<[id: string, props: HelmetProps]>;\n\n/**\n * The overall Helmet state, aggregated from props of all <Helmet> instances\n * registered with the Helmet context provider.\n */\nexport type AggregatedState = {\n base: BaseProps | undefined;\n bodyAttributes: BodyProps | undefined;\n defer: boolean | undefined;\n encodeSpecialCharacters: boolean;\n htmlAttributes: HtmlProps | undefined;\n links: LinkProps[] | undefined;\n meta: MetaProps[] | undefined;\n noscript: NoscriptProps[] | undefined;\n onChangeClientState: OnChangeClientState | undefined;\n priority: {\n links: LinkProps[] | undefined;\n meta: MetaProps[] | undefined;\n script: ScriptProps[] | undefined;\n } | undefined;\n script: ScriptProps[] | undefined;\n style: StyleProps[] | undefined;\n title: string | undefined;\n titleAttributes: TitleProps | undefined;\n};\n\nexport type MappedServerState = HelmetTags & { encode?: boolean };\n\n/**\n * Server-side rendering context.\n */\nexport type HelmetDataContext = {\n helmet?: HelmetServerState;\n};\n\n/**\n * The value of internal context used by Helmet to communicate between its\n * context provider and <Helmet> components within its children tree.\n */\nexport type ContextValue = {\n clientApply: () => void;\n\n /** One function to register, update, and un-register <Helmet> instances\n * (or, more precisely their current aggregated props, aggregated between\n * the actual props of <Helmet> instance and its children). */\n update: (id: string, props: HelmetProps | undefined) => void;\n};\n\nexport type HelmetProviderHeap = {\n // TODO: Temporary, to keep legacy behavior to call onChange client-side\n // callback on the first render.\n firstRender: boolean;\n\n helmets: RegisteredHelmetPropsArray;\n nextAnimFrameId?: number;\n serverState?: HelmetServerState;\n state: AggregatedState | undefined;\n};\n"],"mappings":"","ignoreList":[]}
import { HTML_TAG_MAP, TAG_NAMES, TAG_PROPERTIES, SEO_PRIORITY_TAGS } from './constants';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
/**
* Finds the last object in the given array of registered props,
* that has the specified prop defined, and returns the value of
* that prop in that object. Returns `undefined` if no prop object
* has that prop defined.
*/
function getInnermostProperty(props, propName) {
for (let i = props.length - 1; i >= 0; --i) {
const value = props[i][1][propName];
if (value !== undefined) return value;
}
}
export function getTitleFromPropsList(props) {
let innermostTitle = getInnermostProperty(props, TAG_NAMES.TITLE);
const innermostTemplate = getInnermostProperty(props, 'titleTemplate');
if (Array.isArray(innermostTitle)) {
innermostTitle = innermostTitle.join('');
}
if (innermostTemplate && innermostTitle) {
// use function arg to avoid need to escape $ characters
return innermostTemplate.replace(/%s/g, () => innermostTitle);
}
const innermostDefaultTitle = getInnermostProperty(props, 'defaultTitle');
// NOTE: We really want || here to match legacy behavior, where default title
// was applied also when the given title was an empty string.
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
return (innermostTitle || innermostDefaultTitle) ?? undefined;
}
/**
* Merges together attributes provided for the same element by different Helmet
* instances. Attributes provided by later registered Helmet instances overwrite
* the same attributes provided by the earlier registered instances.
*/
export function mergeAttributes(element, props) {
const res = {};
for (const item of props) {
const attrs = item[1][element];
if (attrs) Object.assign(res, attrs);
}
return res;
}
/**
* Finds the latest registered Helmet instance with `base` props provided,
* and with its `href` value set, and returns those `base` props.
* NOTE: Based on the legacy getBaseTagFromPropsList().
*/
export function aggregateBaseProps(props) {
for (let i = props.length - 1; i >= 0; --i) {
const res = props[i][1].base;
if (res?.href) return res;
}
}
const warn = msg => console && typeof console.warn === 'function' && console.warn(msg);
/**
* Determines the primary key in the given `props` object, accoding to the given
* array of valid primary keys for the kind of props object.
* TODO: Rather than passing an array of primaryProps around, it might be more
* simple to just have a dedicated function for each possible kind of that
* object.
*/
function getPrimaryProp(props, primaryProps) {
// Looks up for the "primary attribute" key.
let primaryAttributeKey;
// TODO: Perhaps also check that the value of attribute being selected
// as primary is actually defined? Right now, it implicitly assumes that
// in such case the attribute is just not present as a key in `props`.
for (const keyString of Object.keys(props)) {
const key = keyString;
// Special rule with link tags, since rel and href are both primary tags,
// rel takes priority
if (primaryProps.includes(key) && !(primaryAttributeKey === TAG_PROPERTIES.REL && props[primaryAttributeKey].toLowerCase() === 'canonical') && !(key === TAG_PROPERTIES.REL && props[key].toLowerCase() === 'stylesheet')) primaryAttributeKey = key;
// Special case for innerHTML which doesn't work lowercased
if (primaryProps.includes(key) && (key === TAG_PROPERTIES.INNER_HTML || key === TAG_PROPERTIES.CSS_TEXT || key === TAG_PROPERTIES.ITEM_PROP)) primaryAttributeKey = key;
}
return primaryAttributeKey ?? null;
}
export function getTagsFromPropsList(tagName, primaryAttributes, propsArray) {
// Calculate list of tags, giving priority innermost component
// (end of the propslist)
const approvedSeenTags = {};
// TODO: Well, this is a touch one to refactor, while ensuring it does not
// change any behavior aspect... let's stick to the legacy implementation,
// with minimal updates, for now, then refactor it later.
return propsArray.map(_ref => {
let [, props] = _ref;
return props;
}).filter(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(props => props[tagName]).reverse()
// From last to first.
.reduce((approvedTags, instanceTags) => {
const instanceSeenTags = {};
instanceTags.filter(tag => {
const primaryAttributeKey = getPrimaryProp(tag, primaryAttributes);
if (!primaryAttributeKey || !tag[primaryAttributeKey]) {
return false;
}
const value = tag[primaryAttributeKey].toLowerCase();
if (!approvedSeenTags[primaryAttributeKey]) {
approvedSeenTags[primaryAttributeKey] = {};
}
if (!instanceSeenTags[primaryAttributeKey]) {
instanceSeenTags[primaryAttributeKey] = {};
}
// essentially we collect every item that haven't been seen so far?
if (!approvedSeenTags[primaryAttributeKey][value]) {
instanceSeenTags[primaryAttributeKey][value] = true;
return true;
}
return false;
}).reverse()
// so approved tags are accumulated from last to first
.forEach(tag => approvedTags.push(tag));
// Update seen tags with tags from this instance
const keys = Object.keys(instanceSeenTags);
for (const attributeKey of keys) {
const tagUnion = {
...approvedSeenTags[attributeKey],
...instanceSeenTags[attributeKey]
};
approvedSeenTags[attributeKey] = tagUnion;
}
return approvedTags;
}, [])
// then reversed back to the from first-to-last order.
.reverse();
}
function getAnyTrueFromPropsArray(propsArray, propName) {
for (const [, props] of propsArray) {
if (props[propName]) return true;
}
return false;
}
export function flattenArray(possibleArray) {
return Array.isArray(possibleArray) ? possibleArray.join('') : possibleArray;
}
function checkIfPropsMatch(props, toMatch) {
for (const key of Object.keys(props)) {
// e.g. if rel exists in the list of allowed props [amphtml, alternate, etc]
// TODO: Do a better typing job here.
if (toMatch[key]?.includes(props[key])) return true;
}
return false;
}
export function prioritizer(propsArray, propsToMatch) {
const res = {
default: Array(),
priority: Array()
};
if (propsArray) {
for (const props of propsArray) {
if (checkIfPropsMatch(props, propsToMatch)) {
res.priority.push(props);
} else {
res.default.push(props);
}
}
}
return res;
}
export const without = (obj, key) => {
return {
...obj,
[key]: undefined
};
};
/**
* Clones given props object deep enough to make it safe to push new items
* to its array values, and re-assign its non-array values, without a risk
* to mutate any externally owned objects.
*/
export function cloneProps(props) {
const res = {};
for (const [key, value] of Object.entries(props)) {
res[key] = Array.isArray(value) ? value.slice() : value;
}
return res;
}
/**
* Merges `source` props into `target`, mutating the `target` object.
*/
export function mergeProps(target, source) {
const tgt = target;
for (const [key, srcValue] of Object.entries(source)) {
if (Array.isArray(srcValue)) {
const tgtValue = tgt[key];
tgt[key] = tgtValue ? tgtValue.concat(srcValue) : srcValue;
} else tgt[key] = srcValue;
}
}
/**
* Adds given item to the specified prop array inside `target`.
* It mutates the target.
*/
export function pushToPropArray(target, array, item) {
const tgt = target[array];
if (tgt) tgt.push(item);else target[array] = [item];
}
export function calcAggregatedState(props) {
let links = getTagsFromPropsList(TAG_NAMES.LINK, [TAG_PROPERTIES.REL, TAG_PROPERTIES.HREF], props);
let meta = getTagsFromPropsList('meta', [
// NOTE: In the legacy version "charSet", "httpEquiv", and "itemProp"
// were given as HTML attributes: charset, http-equiv, itemprop.
// I believe, it is already fine to replace them here now, but
// let's be vigilant.
TAG_PROPERTIES.NAME, 'charSet', 'httpEquiv', TAG_PROPERTIES.PROPERTY, 'itemProp'], props);
let script = getTagsFromPropsList('script', [TAG_PROPERTIES.SRC, TAG_PROPERTIES.INNER_HTML], props);
const prioritizeSeoTags = getAnyTrueFromPropsArray(props, 'prioritizeSeoTags');
let priority;
if (prioritizeSeoTags) {
const linkP = prioritizer(links, SEO_PRIORITY_TAGS.link);
links = linkP.default;
const metaP = prioritizer(meta, SEO_PRIORITY_TAGS.meta);
meta = metaP.default;
const scriptP = prioritizer(script, SEO_PRIORITY_TAGS.script);
script = scriptP.default;
priority = {
links: linkP.priority,
meta: metaP.priority,
script: scriptP.priority
};
}
return {
base: aggregateBaseProps(props),
bodyAttributes: mergeAttributes('bodyAttributes', props),
defer: getInnermostProperty(props, 'defer'),
encodeSpecialCharacters: getInnermostProperty(props, 'encodeSpecialCharacters') ?? true,
htmlAttributes: mergeAttributes('htmlAttributes', props),
links,
meta,
noscript: getTagsFromPropsList('noscript', [TAG_PROPERTIES.INNER_HTML], props),
onChangeClientState: getInnermostProperty(props, 'onChangeClientState'),
priority,
script,
style: getTagsFromPropsList('style', [TAG_PROPERTIES.CSS_TEXT], props),
title: getTitleFromPropsList(props),
titleAttributes: mergeAttributes('titleAttributes', props)
};
}
export function propToAttr(prop) {
return HTML_TAG_MAP[prop] ?? prop;
}
//# sourceMappingURL=utils.js.map
{"version":3,"file":"utils.js","names":["HTML_TAG_MAP","TAG_NAMES","TAG_PROPERTIES","SEO_PRIORITY_TAGS","getInnermostProperty","props","propName","i","length","value","undefined","getTitleFromPropsList","innermostTitle","TITLE","innermostTemplate","Array","isArray","join","replace","innermostDefaultTitle","mergeAttributes","element","res","item","attrs","Object","assign","aggregateBaseProps","base","href","warn","msg","console","getPrimaryProp","primaryProps","primaryAttributeKey","keyString","keys","key","includes","REL","toLowerCase","INNER_HTML","CSS_TEXT","ITEM_PROP","getTagsFromPropsList","tagName","primaryAttributes","propsArray","approvedSeenTags","map","_ref","filter","reverse","reduce","approvedTags","instanceTags","instanceSeenTags","tag","forEach","push","attributeKey","tagUnion","getAnyTrueFromPropsArray","flattenArray","possibleArray","checkIfPropsMatch","toMatch","prioritizer","propsToMatch","default","priority","without","obj","cloneProps","entries","slice","mergeProps","target","source","tgt","srcValue","tgtValue","concat","pushToPropArray","array","calcAggregatedState","links","LINK","HREF","meta","NAME","PROPERTY","script","SRC","prioritizeSeoTags","linkP","link","metaP","scriptP","bodyAttributes","defer","encodeSpecialCharacters","htmlAttributes","noscript","onChangeClientState","style","title","titleAttributes","propToAttr","prop"],"sources":["../../src/utils.ts"],"sourcesContent":["import {\n HTML_TAG_MAP,\n TAG_NAMES,\n TAG_PROPERTIES,\n SEO_PRIORITY_TAGS,\n} from './constants';\n\nimport type {\n AggregatedState,\n BaseProps,\n HelmetPropArrays,\n HelmetPropBooleans,\n HelmetPropObjects,\n HelmetProps,\n LinkProps,\n MetaProps,\n PropArrayItem,\n RegisteredHelmetPropsArray,\n ScriptProps,\n} from './types';\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype PropList = Record<string, any>;\n\ntype AttributeList = string[];\n\ntype SeenTags<T extends keyof HelmetPropArrays> = {\n [key in keyof PropArrayItem<T>]?: Record<string, boolean>\n};\n\ntype MatchProps = Record<string, string | AttributeList>;\n\n/**\n * Finds the last object in the given array of registered props,\n * that has the specified prop defined, and returns the value of\n * that prop in that object. Returns `undefined` if no prop object\n * has that prop defined.\n */\nfunction getInnermostProperty<T extends keyof HelmetProps>(\n props: RegisteredHelmetPropsArray,\n propName: T,\n): HelmetProps[T] | undefined {\n for (let i = props.length - 1; i >= 0; --i) {\n const value = props[i]![1][propName];\n if (value !== undefined) return value;\n }\n}\n\nexport function getTitleFromPropsList(\n props: RegisteredHelmetPropsArray,\n): string | undefined {\n let innermostTitle = getInnermostProperty(props, TAG_NAMES.TITLE);\n\n const innermostTemplate = getInnermostProperty(\n props,\n 'titleTemplate',\n );\n\n if (Array.isArray(innermostTitle)) {\n innermostTitle = innermostTitle.join('');\n }\n if (innermostTemplate && innermostTitle) {\n // use function arg to avoid need to escape $ characters\n return innermostTemplate.replace(/%s/g, () => innermostTitle);\n }\n\n const innermostDefaultTitle = getInnermostProperty(\n props,\n 'defaultTitle',\n );\n\n // NOTE: We really want || here to match legacy behavior, where default title\n // was applied also when the given title was an empty string.\n // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing\n return (innermostTitle || innermostDefaultTitle) ?? undefined;\n}\n\n/**\n * Merges together attributes provided for the same element by different Helmet\n * instances. Attributes provided by later registered Helmet instances overwrite\n * the same attributes provided by the earlier registered instances.\n */\nexport function mergeAttributes<T extends keyof HelmetPropObjects>(\n element: T,\n props: RegisteredHelmetPropsArray,\n): HelmetProps[T] {\n const res: HelmetProps[T] = {};\n for (const item of props) {\n const attrs = item[1][element];\n if (attrs) Object.assign(res, attrs);\n }\n return res;\n}\n\n/**\n * Finds the latest registered Helmet instance with `base` props provided,\n * and with its `href` value set, and returns those `base` props.\n * NOTE: Based on the legacy getBaseTagFromPropsList().\n */\nexport function aggregateBaseProps(\n props: RegisteredHelmetPropsArray,\n): BaseProps | undefined {\n for (let i = props.length - 1; i >= 0; --i) {\n const res = props[i]![1].base;\n if (res?.href) return res;\n }\n}\n\nconst warn = (msg: string) => console && typeof console.warn === 'function' && console.warn(msg);\n\n/**\n * Determines the primary key in the given `props` object, accoding to the given\n * array of valid primary keys for the kind of props object.\n * TODO: Rather than passing an array of primaryProps around, it might be more\n * simple to just have a dedicated function for each possible kind of that\n * object.\n */\nfunction getPrimaryProp<T extends keyof HelmetPropArrays>(\n props: PropArrayItem<T>,\n primaryProps: Array<keyof PropArrayItem<T>>,\n): keyof PropArrayItem<T> | null {\n // Looks up for the \"primary attribute\" key.\n let primaryAttributeKey: keyof PropArrayItem<T> | undefined;\n\n // TODO: Perhaps also check that the value of attribute being selected\n // as primary is actually defined? Right now, it implicitly assumes that\n // in such case the attribute is just not present as a key in `props`.\n for (const keyString of Object.keys(props)) {\n const key = keyString as keyof PropArrayItem<T>;\n\n // Special rule with link tags, since rel and href are both primary tags,\n // rel takes priority\n if (primaryProps.includes(key)\n && !(\n primaryAttributeKey === TAG_PROPERTIES.REL\n && (props[primaryAttributeKey] as string).toLowerCase() === 'canonical'\n )\n && !(\n key === TAG_PROPERTIES.REL\n && (props[key] as string).toLowerCase() === 'stylesheet'\n )\n ) primaryAttributeKey = key;\n\n // Special case for innerHTML which doesn't work lowercased\n if (\n primaryProps.includes(key)\n && (key === TAG_PROPERTIES.INNER_HTML\n || key === TAG_PROPERTIES.CSS_TEXT\n || key === TAG_PROPERTIES.ITEM_PROP)\n ) primaryAttributeKey = key;\n }\n\n return primaryAttributeKey ?? null;\n}\n\nexport function getTagsFromPropsList<T extends keyof HelmetPropArrays>(\n tagName: T,\n primaryAttributes: Array<keyof PropArrayItem<T>>,\n propsArray: RegisteredHelmetPropsArray,\n): HelmetPropArrays[T] {\n // Calculate list of tags, giving priority innermost component\n // (end of the propslist)\n const approvedSeenTags: SeenTags<T> = {};\n\n // TODO: Well, this is a touch one to refactor, while ensuring it does not\n // change any behavior aspect... let's stick to the legacy implementation,\n // with minimal updates, for now, then refactor it later.\n return propsArray.map(([, props]) => props)\n .filter((props) => {\n if (Array.isArray(props[tagName])) {\n return true;\n }\n if (typeof props[tagName] !== 'undefined') {\n warn(\n `Helmet: ${tagName} should be of type \"Array\". Instead found type \"${typeof props[\n tagName\n ]}\"`,\n );\n }\n return false;\n })\n .map((props) => props[tagName])\n .reverse()\n\n // From last to first.\n .reduce<PropArrayItem<T>[]>((approvedTags, instanceTags) => {\n const instanceSeenTags: SeenTags<T> = {};\n\n instanceTags!.filter((tag: PropArrayItem<T>) => {\n const primaryAttributeKey = getPrimaryProp(tag, primaryAttributes);\n\n if (!primaryAttributeKey || !tag[primaryAttributeKey]) {\n return false;\n }\n\n const value = (tag[primaryAttributeKey] as string).toLowerCase();\n\n if (!approvedSeenTags[primaryAttributeKey]) {\n approvedSeenTags[primaryAttributeKey] = {};\n }\n\n if (!instanceSeenTags[primaryAttributeKey]) {\n instanceSeenTags[primaryAttributeKey] = {};\n }\n\n // essentially we collect every item that haven't been seen so far?\n\n if (!approvedSeenTags[primaryAttributeKey][value]) {\n instanceSeenTags[primaryAttributeKey][value] = true;\n return true;\n }\n\n return false;\n }).reverse()\n\n // so approved tags are accumulated from last to first\n .forEach((tag: PropArrayItem<T>) => approvedTags.push(tag));\n\n // Update seen tags with tags from this instance\n const keys = Object.keys(instanceSeenTags) as\n Array<keyof PropArrayItem<T>>;\n\n for (const attributeKey of keys) {\n const tagUnion = {\n ...approvedSeenTags[attributeKey],\n ...instanceSeenTags[attributeKey],\n };\n\n approvedSeenTags[attributeKey] = tagUnion;\n }\n\n return approvedTags;\n }, [])\n\n // then reversed back to the from first-to-last order.\n .reverse() as HelmetPropArrays[T];\n}\n\nfunction getAnyTrueFromPropsArray<T extends keyof HelmetPropBooleans>(\n propsArray: RegisteredHelmetPropsArray,\n propName: T,\n): boolean {\n for (const [, props] of propsArray) {\n if (props[propName]) return true;\n }\n return false;\n}\n\nexport function flattenArray(possibleArray: string[] | string) {\n return Array.isArray(possibleArray) ? possibleArray.join('') : possibleArray;\n}\n\nfunction checkIfPropsMatch<T extends keyof HelmetPropArrays>(\n props: PropArrayItem<T>,\n toMatch: MatchProps,\n) {\n for (const key of Object.keys(props)) {\n // e.g. if rel exists in the list of allowed props [amphtml, alternate, etc]\n // TODO: Do a better typing job here.\n if (toMatch[key]?.includes(\n props[key as keyof PropArrayItem<T>] as unknown as string,\n )) return true;\n }\n return false;\n}\n\nexport function prioritizer<T extends keyof HelmetPropArrays>(\n propsArray: HelmetPropArrays[T],\n propsToMatch: MatchProps,\n): {\n default: PropArrayItem<T>[];\n priority: PropArrayItem<T>[];\n } {\n const res = {\n default: Array<PropArrayItem<T>>(),\n priority: Array<PropArrayItem<T>>(),\n };\n\n if (propsArray) {\n for (const props of propsArray) {\n if (checkIfPropsMatch(props, propsToMatch)) {\n res.priority.push(props);\n } else {\n res.default.push(props);\n }\n }\n }\n\n return res;\n}\n\nexport const without = (obj: PropList, key: string) => {\n return {\n ...obj,\n [key]: undefined,\n };\n};\n\ntype UnknownObject = Record<number | string | symbol, unknown>;\n\n/**\n * Clones given props object deep enough to make it safe to push new items\n * to its array values, and re-assign its non-array values, without a risk\n * to mutate any externally owned objects.\n */\nexport function cloneProps(props: HelmetProps): HelmetProps {\n const res: UnknownObject = {};\n for (const [key, value] of Object.entries(props)) {\n res[key] = Array.isArray(value) ? value.slice() : value;\n }\n return res;\n}\n\n/**\n * Merges `source` props into `target`, mutating the `target` object.\n */\nexport function mergeProps(target: HelmetProps, source: HelmetProps) {\n const tgt = target as UnknownObject;\n for (const [key, srcValue] of Object.entries(source)) {\n if (Array.isArray(srcValue)) {\n const tgtValue = tgt[key] as unknown[];\n tgt[key] = tgtValue ? tgtValue.concat(srcValue) : srcValue;\n } else tgt[key] = srcValue;\n }\n}\n\n/**\n * Adds given item to the specified prop array inside `target`.\n * It mutates the target.\n */\nexport function pushToPropArray<K extends keyof HelmetPropArrays>(\n target: HelmetProps,\n array: K,\n item: Exclude<HelmetPropArrays[K], undefined>[number],\n) {\n type A = Array<typeof item>;\n const tgt = target[array] as A;\n if (tgt) tgt.push(item);\n else (target[array] as A) = [item];\n}\n\nexport function calcAggregatedState(\n props: RegisteredHelmetPropsArray,\n): AggregatedState {\n let links = getTagsFromPropsList(\n TAG_NAMES.LINK,\n [TAG_PROPERTIES.REL, TAG_PROPERTIES.HREF],\n props,\n );\n let meta = getTagsFromPropsList(\n 'meta',\n [\n // NOTE: In the legacy version \"charSet\", \"httpEquiv\", and \"itemProp\"\n // were given as HTML attributes: charset, http-equiv, itemprop.\n // I believe, it is already fine to replace them here now, but\n // let's be vigilant.\n TAG_PROPERTIES.NAME,\n 'charSet',\n 'httpEquiv',\n TAG_PROPERTIES.PROPERTY,\n 'itemProp',\n ],\n props,\n );\n let script = getTagsFromPropsList(\n 'script',\n [TAG_PROPERTIES.SRC, TAG_PROPERTIES.INNER_HTML],\n props,\n );\n\n const prioritizeSeoTags = getAnyTrueFromPropsArray(props, 'prioritizeSeoTags');\n\n let priority: {\n links: LinkProps[] | undefined;\n meta: MetaProps[] | undefined;\n script: ScriptProps[] | undefined;\n } | undefined;\n\n if (prioritizeSeoTags) {\n const linkP = prioritizer<'link'>(links, SEO_PRIORITY_TAGS.link);\n links = linkP.default;\n\n const metaP = prioritizer<'meta'>(meta, SEO_PRIORITY_TAGS.meta);\n meta = metaP.default;\n\n const scriptP = prioritizer<'script'>(script, SEO_PRIORITY_TAGS.script);\n script = scriptP.default;\n\n priority = {\n links: linkP.priority,\n meta: metaP.priority,\n script: scriptP.priority,\n };\n }\n\n return {\n base: aggregateBaseProps(props),\n bodyAttributes: mergeAttributes('bodyAttributes', props),\n defer: getInnermostProperty(props, 'defer'),\n encodeSpecialCharacters: getInnermostProperty(props, 'encodeSpecialCharacters') ?? true,\n htmlAttributes: mergeAttributes('htmlAttributes', props),\n links,\n meta,\n noscript: getTagsFromPropsList(\n 'noscript',\n [TAG_PROPERTIES.INNER_HTML],\n props,\n ),\n onChangeClientState: getInnermostProperty(props, 'onChangeClientState'),\n priority,\n script,\n style: getTagsFromPropsList(\n 'style',\n [TAG_PROPERTIES.CSS_TEXT],\n props,\n ),\n title: getTitleFromPropsList(props),\n titleAttributes: mergeAttributes('titleAttributes', props),\n };\n}\n\nexport function propToAttr(prop: string): string {\n return HTML_TAG_MAP[prop] ?? prop;\n}\n"],"mappings":"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","ignoreList":[]}
import type { AggregatedState } from './types';
export declare function commitTagChanges(newState: AggregatedState, firstRender: boolean): void;
export declare enum 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"
}
export declare enum ATTRIBUTE_NAMES {
BODY = "bodyAttributes",
HTML = "htmlAttributes",
TITLE = "titleAttributes"
}
export declare enum TAG_NAMES {
BASE = "base",
BODY = "body",
HEAD = "head",
HTML = "html",
LINK = "link",
META = "meta",
NOSCRIPT = "noscript",
SCRIPT = "script",
STYLE = "style",
TITLE = "title",
FRAGMENT = "Symbol(react.fragment)"
}
export declare const SEO_PRIORITY_TAGS: {
link: {
rel: string[];
};
script: {
type: string[];
};
meta: {
charset: string;
name: string[];
property: string[];
};
};
export declare const VALID_TAG_NAMES: TAG_NAMES[];
/**
* The mapping of HTML attribute names to the corresponding element properties,
* for the names that do not match their corresponding properties.
*/
export declare const REACT_TAG_MAP: Record<string, string>;
/**
* The mapping reverse of REACT_TAG_MAP.
*/
export declare const HTML_TAG_MAP: Record<string, string>;
export declare const HELMET_ATTRIBUTE = "data-rh";
export declare const IS_DOM_ENVIRONMENT: boolean;
import { type FunctionComponent } from 'react';
import type { HelmetProps } from './types';
declare const Helmet: FunctionComponent<HelmetProps>;
export default Helmet;
export * from './types';
export { default as Helmet } from './Helmet';
export { default as HelmetProvider } from './Provider';
import { type FunctionComponent, type ReactNode } from 'react';
import type { ContextValue, HelmetDataContext } from './types';
export declare const Context: import("react").Context<ContextValue | undefined>;
type ProviderProps = {
children?: ReactNode;
context?: HelmetDataContext;
};
declare const HelmetProvider: FunctionComponent<ProviderProps>;
export default HelmetProvider;
import type { HelmetProviderHeap, HelmetServerState } from './types';
export declare function newServerState(heap: HelmetProviderHeap): HelmetServerState;
import type { BaseHTMLAttributes, HtmlHTMLAttributes, HTMLAttributes, LinkHTMLAttributes, MetaHTMLAttributes, ReactNode, ScriptHTMLAttributes, StyleHTMLAttributes } from 'react';
export type BaseProps = BaseHTMLAttributes<HTMLBaseElement>;
export type BodyProps = HTMLAttributes<HTMLBodyElement>;
export type HtmlProps = HtmlHTMLAttributes<HTMLHtmlElement>;
export type LinkProps = LinkHTMLAttributes<HTMLLinkElement>;
export type MetaProps = MetaHTMLAttributes<HTMLMetaElement>;
export type NoscriptProps = HTMLAttributes<HTMLElement> & {
innerHTML?: string;
};
export type ScriptProps = ScriptHTMLAttributes<HTMLScriptElement> & {
innerHTML?: string;
};
export type StyleProps = StyleHTMLAttributes<HTMLStyleElement> & {
cssText?: string;
};
export type TitleProps = HTMLAttributes<HTMLTitleElement>;
export type HelmetChildProps = BaseProps | BodyProps | HtmlProps | LinkProps | MetaProps | NoscriptProps | ScriptProps | StyleProps | TitleProps;
/**
* String data for title.
*/
export type StringData = {
title?: string;
titleTemplate?: string;
};
export type HelmetTags = {
baseTag: HTMLBaseElement[];
linkTags: HTMLLinkElement[];
metaTags: HTMLMetaElement[];
noscriptTags: HTMLElement[];
scriptTags: HTMLScriptElement[];
styleTags: HTMLStyleElement[];
};
export type HelmetDatum<T = ReactNode> = {
toString(): string;
toComponent(): T;
};
export type HelmetHTMLBodyDatum = HelmetDatum<HTMLAttributes<HTMLBodyElement>>;
export type HelmetHTMLElementDatum = HelmetDatum<HTMLAttributes<HTMLHtmlElement>>;
export type HelmetServerState = {
base: HelmetDatum;
bodyAttributes: HelmetHTMLBodyDatum;
htmlAttributes: HelmetHTMLElementDatum;
link: HelmetDatum;
meta: HelmetDatum;
noscript: HelmetDatum;
script: HelmetDatum;
style: HelmetDatum;
title: HelmetDatum;
titleAttributes?: HelmetDatum;
priority: HelmetDatum;
};
export type StateUpdate = HelmetTags & {
bodyAttributes: BodyProps;
defer: boolean;
htmlAttributes: HtmlProps;
onChangeClientState: (newState: StateUpdate, addedTags: Partial<HelmetTags>, removedTags: Partial<HelmetTags>) => void;
title: string;
titleAttributes: TitleProps;
};
export type OnChangeClientState = (newState: StateUpdate, addedTags: Partial<HelmetTags>, removedTags: Partial<HelmetTags>) => void;
/**
* A subset of <Helmet> properties, corresponding to prop arrays for
* elements that may be present in DOM multiple times.
*/
export type HelmetPropArrays = {
link?: LinkProps[];
meta?: MetaProps[];
noscript?: NoscriptProps[];
script?: ScriptProps[];
style?: StyleProps[];
};
export type PropArrayItem<T extends keyof HelmetPropArrays> = Exclude<HelmetPropArrays[T], undefined>[number];
/**
* A subset of <Helmet> properties, corresponding to props for elements
* that may be present in DOM a single time at most.
*/
export type HelmetPropObjects = {
bodyAttributes?: BodyProps;
htmlAttributes?: HtmlProps;
titleAttributes?: TitleProps;
};
export type HelmetPropBooleans = {
prioritizeSeoTags?: boolean;
};
/**
* Properties accepted by <Helmet> components.
*/
export type HelmetProps = HelmetPropArrays & HelmetPropObjects & HelmetPropBooleans & {
base?: BaseProps;
children?: ReactNode;
defaultTitle?: string;
defer?: boolean;
encodeSpecialCharacters?: boolean;
onChangeClientState?: OnChangeClientState;
title?: string;
titleTemplate?: string;
};
export type RegisteredHelmetPropsArray = Array<[id: string, props: HelmetProps]>;
/**
* The overall Helmet state, aggregated from props of all <Helmet> instances
* registered with the Helmet context provider.
*/
export type AggregatedState = {
base: BaseProps | undefined;
bodyAttributes: BodyProps | undefined;
defer: boolean | undefined;
encodeSpecialCharacters: boolean;
htmlAttributes: HtmlProps | undefined;
links: LinkProps[] | undefined;
meta: MetaProps[] | undefined;
noscript: NoscriptProps[] | undefined;
onChangeClientState: OnChangeClientState | undefined;
priority: {
links: LinkProps[] | undefined;
meta: MetaProps[] | undefined;
script: ScriptProps[] | undefined;
} | undefined;
script: ScriptProps[] | undefined;
style: StyleProps[] | undefined;
title: string | undefined;
titleAttributes: TitleProps | undefined;
};
export type MappedServerState = HelmetTags & {
encode?: boolean;
};
/**
* Server-side rendering context.
*/
export type HelmetDataContext = {
helmet?: HelmetServerState;
};
/**
* The value of internal context used by Helmet to communicate between its
* context provider and <Helmet> components within its children tree.
*/
export type ContextValue = {
clientApply: () => void;
/** One function to register, update, and un-register <Helmet> instances
* (or, more precisely their current aggregated props, aggregated between
* the actual props of <Helmet> instance and its children). */
update: (id: string, props: HelmetProps | undefined) => void;
};
export type HelmetProviderHeap = {
firstRender: boolean;
helmets: RegisteredHelmetPropsArray;
nextAnimFrameId?: number;
serverState?: HelmetServerState;
state: AggregatedState | undefined;
};
import type { AggregatedState, BaseProps, HelmetPropArrays, HelmetPropObjects, HelmetProps, PropArrayItem, RegisteredHelmetPropsArray } from './types';
type PropList = Record<string, any>;
type AttributeList = string[];
type MatchProps = Record<string, string | AttributeList>;
export declare function getTitleFromPropsList(props: RegisteredHelmetPropsArray): string | undefined;
/**
* Merges together attributes provided for the same element by different Helmet
* instances. Attributes provided by later registered Helmet instances overwrite
* the same attributes provided by the earlier registered instances.
*/
export declare function mergeAttributes<T extends keyof HelmetPropObjects>(element: T, props: RegisteredHelmetPropsArray): HelmetProps[T];
/**
* Finds the latest registered Helmet instance with `base` props provided,
* and with its `href` value set, and returns those `base` props.
* NOTE: Based on the legacy getBaseTagFromPropsList().
*/
export declare function aggregateBaseProps(props: RegisteredHelmetPropsArray): BaseProps | undefined;
export declare function getTagsFromPropsList<T extends keyof HelmetPropArrays>(tagName: T, primaryAttributes: Array<keyof PropArrayItem<T>>, propsArray: RegisteredHelmetPropsArray): HelmetPropArrays[T];
export declare function flattenArray(possibleArray: string[] | string): string;
export declare function prioritizer<T extends keyof HelmetPropArrays>(propsArray: HelmetPropArrays[T], propsToMatch: MatchProps): {
default: PropArrayItem<T>[];
priority: PropArrayItem<T>[];
};
export declare const without: (obj: PropList, key: string) => {
[x: string]: any;
};
/**
* Clones given props object deep enough to make it safe to push new items
* to its array values, and re-assign its non-array values, without a risk
* to mutate any externally owned objects.
*/
export declare function cloneProps(props: HelmetProps): HelmetProps;
/**
* Merges `source` props into `target`, mutating the `target` object.
*/
export declare function mergeProps(target: HelmetProps, source: HelmetProps): void;
/**
* Adds given item to the specified prop array inside `target`.
* It mutates the target.
*/
export declare function pushToPropArray<K extends keyof HelmetPropArrays>(target: HelmetProps, array: K, item: Exclude<HelmetPropArrays[K], undefined>[number]): void;
export declare function calcAggregatedState(props: RegisteredHelmetPropsArray): AggregatedState;
export declare function propToAttr(prop: string): string;
export {};
# Changelog
See [GitHub Releases Page](https://github.com/birdofpreyru/react-helmet/releases)
for changes in the released library versions.
module.exports = {
collectCoverage: true,
collectCoverageFrom: [
'src/**/*.{js,jsx,mjs,ts,tsx}',
],
coverageDirectory: '__coverage__',
rootDir: '../..',
testMatch: ['**/__tests__/**/*.{js,jsx,mjs,ts,tsx}'],
testPathIgnorePatterns: [
'/__assets__/',
'/node_modules/',
],
testEnvironmentOptions: {
url: 'http://localhost',
},
transform: {
'\\.(m?(j|t)sx?)$': 'babel-jest',
},
setupFilesAfterEnv: [
'<rootDir>/config/jest/setup.ts',
],
};
// Polyfills requestAnimationFrame() in the test environment.
import 'raf/polyfill';
import { afterEach, beforeEach } from '@jest/globals';
import '@testing-library/jest-dom';
import { unmount } from '../../jest/browser-utils';
// @ts-expect-error "that's fine"
global.IS_REACT_ACT_ENVIRONMENT = true;
let headElement: HTMLHeadElement;
beforeEach(() => {
if (typeof document !== 'undefined') {
headElement ||= document.head || document.querySelector('head');
headElement.innerHTML = '';
document.body.innerHTML = '<div id="mount"></div>';
}
});
afterEach(() => {
if (typeof document !== 'undefined') {
unmount();
}
});
// TODO: Move to a dedicated repo.
import babelParser from '@babel/eslint-parser';
import babelPlugin from '@babel/eslint-plugin';
import pluginJest from 'eslint-plugin-jest';
import reactPlugin from 'eslint-plugin-react';
import importPlugin from 'eslint-plugin-import';
import reactHooks from 'eslint-plugin-react-hooks';
import globals from 'globals';
import tseslint from 'typescript-eslint';
import pluginJs from '@eslint/js';
import stylisticPlugin from '@stylistic/eslint-plugin';
export default tseslint.config(
{ ignores: ['__coverage__', 'build/'] },
{
files: ['**/*.{js,mjs,cjs,ts,jsx,tsx}'],
languageOptions: {
globals: { ...globals.browser, ...globals.node },
parser: babelParser,
},
linterOptions: {
reportUnusedDisableDirectives: 'error',
reportUnusedInlineConfigs: 'error',
},
plugins: {
'@babel': babelPlugin,
},
settings: {
'import/resolver': {
node: true,
typescript: true,
},
react: { version: '19' },
},
},
importPlugin.flatConfigs.recommended,
pluginJs.configs.recommended,
reactPlugin.configs.flat.recommended,
reactPlugin.configs.flat['jsx-runtime'],
stylisticPlugin.configs.all,
// TODO: This does not work because a bug in eslint-plugin-react-hooks:
// https://github.com/facebook/react/issues/32431
// thus, the workaround below.
// reactHooks.configs.recommended
{
plugins: { 'react-hooks': reactHooks },
rules: { ...reactHooks.configs.recommended.rules },
},
{
rules: {
'@babel/new-cap': 'error',
'@babel/no-invalid-this': 'error',
'@babel/no-undef': 'error',
'@babel/no-unused-expressions': 'error',
'@stylistic/object-curly-spacing': 'off',
'@babel/object-curly-spacing': ['error', 'always'],
'@babel/semi': 'error',
'@stylistic/array-bracket-newline': ['error', 'consistent'],
'@stylistic/array-element-newline': ['error', 'consistent'],
'@stylistic/comma-dangle': ['error', 'always-multiline'],
'@stylistic/dot-location': ['error', 'property'],
'@stylistic/function-call-argument-newline': ['error', 'consistent'],
'@stylistic/function-paren-newline': ['error', 'multiline-arguments'],
'@stylistic/indent': ['error', 2, {
SwitchCase: 1,
}],
'@stylistic/lines-around-comment': ['error', {
allowBlockStart: true,
allowClassStart: true,
allowObjectStart: true,
allowTypeStart: true,
}],
'@stylistic/max-len': ['error', {
ignoreStrings: true,
ignoreTemplateLiterals: true,
}],
'@stylistic/multiline-comment-style': 'off',
'@stylistic/multiline-ternary': ['error', 'always-multiline'],
'@stylistic/no-extra-parens': ['error', 'all', {
enforceForArrowConditionals: false,
ignoreJSX: 'multi-line',
nestedBinaryExpressions: false,
returnAssign: false,
}],
'@stylistic/no-multiple-empty-lines': ['error', { max: 1 }],
'@stylistic/object-curly-newline': ['error', {
consistent: true,
minProperties: 4,
}],
'@stylistic/object-property-newline': ['error', {
allowAllPropertiesOnSameLine: true,
}],
'@stylistic/operator-linebreak': ['error', 'before'],
'@stylistic/padded-blocks': ['error', 'never'],
'@stylistic/quote-props': ['error', 'as-needed'],
'@stylistic/quotes': ['error', 'single'],
'@stylistic/space-before-function-paren': ['error', {
named: 'never',
}],
'import/no-cycle': 'error',
'import/no-extraneous-dependencies': 'error',
'no-use-before-define': 'error',
'react/function-component-definition': ['error', {
namedComponents: 'arrow-function',
unnamedComponents: 'arrow-function',
}],
'react-hooks/exhaustive-deps': 'error',
'react/prop-types': 'off',
},
},
{
// TypeScript-specific configuration.
files: ['**/*.{ts,tsx}'],
languageOptions: {
parserOptions: {
projectService: true,
tsconfigRootDir: import.meta.dirname,
},
},
extends: [
...tseslint.configs.recommendedTypeChecked,
...tseslint.configs.stylisticTypeChecked,
],
rules: {
'@typescript-eslint/array-type': 'off',
'@typescript-eslint/consistent-type-definitions': ['error', 'type'],
'@typescript-eslint/no-inferrable-types': 'off',
// TODO: Its current implementation seems to give false positive.
'@typescript-eslint/no-invalid-void-type': 'off',
// NOTE: According to its documentation
// "@typescript-eslint/no-unused-vars"
// requires to disable "no-unused-vars".
'no-unused-vars': 'off',
'@typescript-eslint/no-unused-vars': 'error',
'@typescript-eslint/prefer-nullish-coalescing': 'error',
'@typescript-eslint/unbound-method': 'off',
},
},
{
files: ['__tests__/**/*.{js,mjs,cjs,ts,jsx,tsx}'],
extends: [pluginJest.configs['flat/recommended']],
rules: {
'jest/unbound-method': 'error',
},
},
);
import { type ReactNode, act, StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import type { Root } from 'react-dom/client';
import Provider from '../src/Provider';
import type { HelmetDataContext } from '../src/types';
let root: Root | null = null;
export const unmount = () => {
act(() => {
root?.unmount();
root = null;
});
};
export const renderClient = (node: ReactNode, context = {}) => {
if (!root) {
const elem = document.getElementById('mount');
if (!elem) throw Error('Internal error');
root = createRoot(elem);
}
act(() => {
root?.render(
<StrictMode>
<Provider context={context}>{node}</Provider>
</StrictMode>,
);
});
};
export const renderContextClient = (node: ReactNode) => {
const context: HelmetDataContext = {};
renderClient(node, context);
return context.helmet;
};
// TODO: Get rid of this method.
export const isArray = {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
asymmetricMatch: (actual: any) => Array.isArray(actual),
};
// TODO: Remove client-side utils from this module, they belong to browser-utils
// module.
import { type ReactNode, StrictMode } from 'react';
import { renderToStaticMarkup } from 'react-dom/server';
import Provider from '../src/Provider';
import type { HelmetDataContext } from '../src/types';
/**
* Renders the given `node` within the provided `context` into HTML markup,
* using server-side rendering API.
*/
export function renderServer(node: ReactNode, context = {}) {
return renderToStaticMarkup(
<StrictMode>
<Provider context={context}>{node}</Provider>
</StrictMode>,
);
}
export function renderContextServer(node: ReactNode) {
const context: HelmetDataContext = {};
renderServer(node, context);
return context.helmet;
}
// TODO: Get rid of this method.
export const isArray = {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
asymmetricMatch: (actual: any) => Array.isArray(actual),
};
{
"extends": "@tsconfig/recommended",
"compilerOptions": {
"declaration": true,
"emitDeclarationOnly": true,
"jsx": "react-jsx",
"module": "Preserve",
"noUncheckedIndexedAccess": true,
"outDir": "build/types",
"verbatimModuleSyntax": true
},
"include": ["src"]
}
+49
-40
{
"name": "@dr.pogodin/react-helmet",
"version": "2.0.4",
"description": "Thread-safe Helmet for React 16+ and friends",
"main": "./lib/index.js",
"module": "./lib/index.esm.js",
"types": "./lib/index.d.ts",
"version": "3.0.0",
"description": "Thread-safe Helmet for React 19+ and friends",
"main": "./build/common/index.js",
"module": "./build/module/index.js",
"types": "./build/types/index.d.ts",
"exports": {
"types": "./lib/index.d.ts",
"import": "./lib/index.esm.js",
"default": "./lib/index.js"
"types": "./build/types/index.d.ts",
"module": "./build/module/index.js",
"default": "./build/common/index.js"
},
"scripts": {
"clean": "rimraf lib",
"build": "yarn run clean && NODE_ENV=production tsx build.ts && yarn types",
"lint": "eslint --cache --cache-location ./node_modules/.cache/eslint --report-unused-disable-directives .",
"test": "yarn lint && yarn typecheck && yarn vitest",
"typecheck": "yarn tsc",
"types": "tsc src/index.tsx --jsx react --declaration --esModuleInterop --allowJs --emitDeclarationOnly --outDir lib",
"vitest": "vitest run"
"build": "rimraf build && npm run build:types && npm run build:common && npm run build:module",
"build:common": "rimraf build/common && babel src -x .ts,.tsx --out-dir build/common --source-maps",
"build:module": "rimraf build/module && babel src -x .ts,.tsx --out-dir build/module --source-maps --config-file ./babel.module.config.js",
"build:types": "rimraf build/types && tsc --project tsconfig.types.json",
"lint": "eslint",
"test": "npm run lint && npm run typecheck && npm run jest",
"typecheck": "tsc",
"jest": "jest --config config/jest/config.js"
},

@@ -39,32 +40,40 @@ "repository": {

"dependencies": {
"invariant": "^2.2.4",
"react-fast-compare": "^3.2.2",
"shallowequal": "^1.1.0"
"@babel/runtime": "^7.26.9"
},
"devDependencies": {
"@remix-run/eslint-config": "2.3.1",
"@testing-library/jest-dom": "6.1.5",
"@testing-library/react": "14.1.2",
"@types/eslint": "8.44.8",
"@types/invariant": "2.2.37",
"@types/jsdom": "21.1.6",
"@types/react": "18.2.39",
"@types/shallowequal": "1.1.5",
"@vitejs/plugin-react": "4.2.0",
"esbuild": "0.19.8",
"eslint": "8.54.0",
"jsdom": "22.1.0",
"raf": "3.4.1",
"react": "18.2.0",
"react-dom": "18.2.0",
"rimraf": "5.0.5",
"tsx": "4.6.1",
"typescript": "5.2.2",
"vite": "4.5.0",
"vitest": "0.34.6"
"@babel/cli": "^7.27.0",
"@babel/eslint-parser": "^7.26.8",
"@babel/eslint-plugin": "^7.25.9",
"@babel/plugin-transform-runtime": "^7.26.9",
"@babel/preset-env": "^7.26.9",
"@babel/preset-react": "^7.26.3",
"@babel/preset-typescript": "^7.26.0",
"@eslint/js": "^9.21.0",
"@jest/globals": "^29.7.0",
"@stylistic/eslint-plugin": "^4.1.0",
"@testing-library/jest-dom": "6.6.3",
"@testing-library/react": "16.2.0",
"@tsconfig/recommended": "^1.0.8",
"@types/jest": "^29.5.14",
"@types/react": "^19.0.10",
"@types/react-dom": "^19.0.4",
"eslint": "^9.21.0",
"eslint-import-resolver-typescript": "^3.8.3",
"eslint-plugin-import": "^2.31.0",
"eslint-plugin-jest": "^28.11.0",
"eslint-plugin-react": "^7.37.4",
"eslint-plugin-react-hooks": "^5.2.0",
"globals": "^16.0.0",
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
"raf": "^3.4.1",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"rimraf": "6.0.1",
"typescript": "5.7",
"typescript-eslint": "^8.25.0"
},
"peerDependencies": {
"react": "19"
},
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
}
}
+305
-130

@@ -9,140 +9,153 @@ # React Helmet

---
_This is a fork of https://github.com/staylor/react-helmet-async library, which in turns is a fork & upgrade of https://github.com/nfl/react-helmet. The purpose of this fork is to ensure proper maintenance and further development of the library._
Advanced management of document head's elements (`<base>`, `<link>`, `<meta>`,
`<noscript>`, `<script>`, `<style>`, `<title>`), and of attributes of `<body>`
and `<html>` elements in React 19+ applications. This library is a proud
successor of now unmaintained and stale
[react-helmet-async](https://github.com/staylor/react-helmet-async) and
[react-helmet](https://github.com/nfl/react-helmet) libraries.
_This fork is published to NPM as https://www.npmjs.com/package/@dr.pogodin/react-helmet, its version 2.0.4 exactly matches the same version of the forked library, but with React peer dependency version set to 19. Future versions will take care of an update of dependencies, and code upgrades to the latest React best practices._
[![Sponsor](https://raw.githubusercontent.com/birdofpreyru/js-utils/master/.README/sponsor.svg)](https://github.com/sponsors/birdofpreyru)
---
[Announcement post on Times Open blog](https://open.nytimes.com/the-future-of-meta-tag-management-for-modern-react-development-ec26a7dc9183)
### Sponsored By
[<img width=36 src="https://avatars.githubusercontent.com/u/17030877?v=4&s=36" />](https://github.com/RigoOnRails)
This package is a fork of [React Helmet](https://github.com/nfl/react-helmet).
`<Helmet>` usage is synonymous, but server and client now requires `<HelmetProvider>` to encapsulate state per request.
### [Contributors](https://github.com/birdofpreyru/react-helmet/graphs/contributors)
[<img width=36 src="https://avatars.githubusercontent.com/u/20144632?s=36" />](https://github.com/birdofpreyru)
`react-helmet` relies on `react-side-effect`, which is not thread-safe. If you are doing anything asynchronous on the server, you need Helmet to encapsulate data on a per-request basis, this package does just that.
## Table of Contents
- [Getting Started]
- [Prioritizing Tags for SEO]
- [Reference]
- [Helmet] &mdash; specifies elements and attributes to be created / set /
overriden.
- [HelmetDataContext] &mdash; context object for server-side rendering (SSR)
purposes.
- [HelmetProvider] &mdash; provides [React Context] to [Helmet] components
## Usage
## Getting Started
[Getting Started]: #getting-started
**New is 1.0.0:** No more default export! `import { Helmet } from '@dr.pogodin/react-helmet'`
To install the library:
```sh
npm install --save @dr.pogodin/react-helmet
```
The main way that this package differs from `react-helmet` is that it requires using a Provider to encapsulate Helmet state for your React tree. If you use libraries like Redux or Apollo, you are already familiar with this paradigm:
At a high level, wrap the main application tree into [HelmetProvider]:
```tsx
import type { FunctionComponent } from 'react';
import { HelmetProvider } from '@dr.pogodin/react-helmet';
```javascript
import React from 'react';
import ReactDOM from 'react-dom';
import { Helmet, HelmetProvider } from '@dr.pogodin/react-helmet';
const YourApp: FunctionComponent = () => {
/* Whatever code you need. */
return (
<HelmetProvider>
{ /* Your application tree. */ }
</HelmetProvider>
);
}
```
const app = (
<HelmetProvider>
<App>
Anywhere within the [HelmetProvider]'s children tree use [Helmet] component
to set / modify document head's elements, or supported attributes of `<body>`
and `<html>` elements. Instances of [Helmet] component within the application
tree add or override elements and attributes in the order these [Helmet]
instances are rendered.
```tsx
import type { FunctionComponent } from 'react';
import { Helmet } from '@dr.pogodin/react-helmet';
const SomeComponent: FunctionComponent = () => {
/* Whatever code you need. */
return (
<div>
<Helmet>
<title>Hello World</title>
<link rel="canonical" href="https://www.tacobell.com/" />
<title>My Title</title>
<link rel="canonical" href="http://mysite.com/example" />
<meta charSet="utf-8" />
<meta name="description" content="Some Component" />
</Helmet>
<h1>Hello World</h1>
</App>
</HelmetProvider>
);
{ /* Whatever other stuff you need. */ }
ReactDOM.hydrate(
app,
document.getElementById(‘app’)
);
```
On the server, we will no longer use static methods to extract state. `react-side-effect`
exposed a `.rewind()` method, which Helmet used when calling `Helmet.renderStatic()`. Instead, we are going
to pass a `context` prop to `HelmetProvider`, which will hold our state specific to each request.
```javascript
import React from 'react';
import { renderToString } from 'react-dom/server';
import { Helmet, HelmetProvider } from '@dr.pogodin/react-helmet';
const helmetContext = {};
const app = (
<HelmetProvider context={helmetContext}>
<App>
{ /* For example, this other <Helmet> component will override the title
and description set earlier in the render tree. */ }
<Helmet>
<title>Hello World</title>
<link rel="canonical" href="https://www.tacobell.com/" />
<title>Overriden Title</title>
<meta name="description" content="Overriden Component Description" />
</Helmet>
<h1>Hello World</h1>
</App>
</HelmetProvider>
);
const html = renderToString(app);
const { helmet } = helmetContext;
// helmet.title.toString() etc…
</div>
)
};
```
## Streams
Alternatively, all elements and attributes specified by [Helmet] components may
be provided _via_ props instead of children.
```tsx
import type { FunctionComponent } from 'react';
import { Helmet } from '@dr.pogodin/react-helmet';
This package only works with streaming if your `<head>` data is output outside of `renderToNodeStream()`.
This is possible if your data hydration method already parses your React tree. Example:
const SomeComponent: FunctionComponent = () => {
/* Whatever code you need. */
return (
<div>
<Helmet
title="My Title"
link={[{
href: 'http://mysite.com/example',
rel: 'canonical',
}]}
meta={[{
charSet: 'utf-8',
}, {
content: 'Some Component',
name: 'description',
}]}
/>
{ /* Whatever other stuff you need. */ }
```javascript
import through from 'through';
import { renderToNodeStream } from 'react-dom/server';
import { getDataFromTree } from 'react-apollo';
import { Helmet, HelmetProvider } from '@dr.pogodin/react-helmet';
import template from 'server/template';
const helmetContext = {};
const app = (
<HelmetProvider context={helmetContext}>
<App>
<Helmet>
<title>Hello World</title>
<link rel="canonical" href="https://www.tacobell.com/" />
{ /* For example, this other <Helmet> component will override the title
and description set earlier in the render tree. It is also fine to
use a mix of props and children. */ }
<Helmet title="Overriden Title">
<meta name="description" content="Overriden Component Description" />
</Helmet>
<h1>Hello World</h1>
</App>
</HelmetProvider>
);
await getDataFromTree(app);
const [header, footer] = template({
helmet: helmetContext.helmet,
});
res.status(200);
res.write(header);
renderToNodeStream(app)
.pipe(
through(
function write(data) {
this.queue(data);
},
function end() {
this.queue(footer);
this.queue(null);
}
)
</div>
)
.pipe(res);
};
```
## Usage in Jest
While testing in using jest, if there is a need to emulate SSR, the following string is required to have the test behave the way they are expected to.
For the server-side rendering purposes you pass in a `context` object to
the [HelmetProvider], and after the render you use that object to retrieve
the string, or component representation of the elements and attributes to be
injected into the document head (if you use streaming for server side rendering
you should output your `<head>` data outside `renderToNodeStream()`):
```tsx
import type { FunctionComponent } from 'react';
import { type HelmetDataContext, HelmetProvider } from '@dr.pogodin/react-helmet';
```javascript
import { HelmetProvider } from '@dr.pogodin/react-helmet';
async function yourServerSideRenderingFunction() {
// ...
const context: HelmetDataContext = {};
const { prelude } = await prerenderToNodeStream(
<HelmetProvider context={context}>
{ /* Your application tree. */ }
</HelmetProvider>
);
// ...
HelmetProvider.canUseDOM = false;
// For example, this is how you get the string representation of <meta> tags
// to be injected into your document head.
const metaElements = context.helmet.meta?.toString();
}
```
## Prioritizing tags for SEO
## Prioritizing Tags for SEO
[Prioritizing Tags for SEO]: #prioritizing-tags-for-seo
It is understood that in some cases for SEO, certain tags should appear earlier in the HEAD. Using the `prioritizeSeoTags` flag on any `<Helmet>` component allows the server render of @dr.pogodin/react-helmet to expose a method for prioritizing relevant SEO tags.
It is understood that in some cases for SEO, certain tags should appear earlier
in the HEAD. Using the `prioritizeSeoTags` flag on any `<Helmet>` component
allows the server render of @dr.pogodin/react-helmet to expose a method for
prioritizing relevant SEO tags.
In the component:
```javascript
```tsx
<Helmet prioritizeSeoTags>

@@ -158,4 +171,3 @@ <title>A fancy webpage</title>

In your server template:
```javascript
```tsx
<html>

@@ -174,3 +186,2 @@ <head>

Will result in:
```html

@@ -189,28 +200,192 @@ <html>

A list of prioritized tags and attributes can be found in [constants.ts](./src/constants.ts).
A list of prioritized tags and attributes (`SEO_PRIORITY_TAGS`) can be found in
[constants.ts](./src/constants.ts).
## Usage without Context
You can optionally use `<Helmet>` outside a context by manually creating a stateful `HelmetData` instance, and passing that stateful object to each `<Helmet>` instance:
## Reference
[Reference]: #reference
### Helmet
[Helmet]: #helmet
```js
import React from 'react';
import { renderToString } from 'react-dom/server';
import { Helmet, HelmetProvider, HelmetData } from '@dr.pogodin/react-helmet';
The [Helmet] component specifies (document head) elements and attributes to be
created / set / overriden by the library. Different [Helmet] instances within
the application tree (within the single [HelmetProvider]) have effect in the
order they are encountered (mounted to DOM) during the render.
const helmetData = new HelmetData({});
The [Helmet] component exposes two equivalent APIs &mdash; the elements /
attributes may be provided either as component's children (_i.e._ written as
regular JSX component children), or as [Helmet] component props. Both these
APIs can also be used at the same time, with values provided as props handled
as additional JSX children, appearing prior the explicitly provided JSX children.
const app = (
<App>
<Helmet helmetData={helmetData}>
<title>Hello World</title>
<link rel="canonical" href="https://www.tacobell.com/" />
</Helmet>
<h1>Hello World</h1>
</App>
);
**Props**
- `base` &mdash; _To be documented_
const html = renderToString(app);
- `bodyAttributes` &mdash; _To be documented_
const { helmet } = helmetData.context;
- `defaultTitle` &mdash; **string** | **undefined** &mdash; Optional.
The fallback to use when `titleTemplate` (below) prop is provided,
but no title was specified:
```tsx
// JSX code:
<Helmet
defaultTitle="My Site"
titleTemplate"My Site - %s"
/>
// DOM output:
<head>
<title>My Site</title>
</head>
```
- `defer` &mdash; **boolean** | **undefined** &mdash; Optional. Defaults _true_.
set to _false_ to not use `requestAnimationFrame` and instead update the DOM
as soon as possible. Useful if you want to update the title when the tab is
out of focus.
- `encodeSpecialCharacters` &mdash; **boolean** | **undefined** &mdash;
Optional. Defaults _true_. Set _false_ to disable string encoding by
`.toString()` methods of [HelmetDataContext].
- `htmlAttributes` &mdash; _To be documented_
- `link` &mdash; _To be documented_
- `meta` &mdash; _To be documented_
- `noscript` &mdash; _To be documented_
- `onChangeClientState` &mdash; **function** | **undefined** &mdash; Optional.
A callback to trigger on client-side each time the head elements / attributes
are updated. It will be called with three arguments:
- `newState` &mdash; _To be documented_
- `addedTags` &mdash; _To be documented_
- `removedTags` &mdash; _To be documented_
- `prioritizeSeoTags` &mdash; _To be documented_
- `script` &mdash; _To be documented_
- `style` &mdash; _To be documented_
- `title` &mdash; _To be documented_
- `titleAttributes` &mdash; _To be documented_
- `titleTemplate` &mdash; **string** | **undefined** &mdash; Optional.
Allows to inherit title from a template, _e.g._
```tsx
// JSX code:
<Helmet titleTemplate="%s | MyAwesomeWebsite.com">
<title>Nested Title</title>
</Helmet>
// DOM output:
<head>
<title>Nested Title | MyAwesomeWebsite.com</title>
</head>
```
**Children**
```tsx
<Helmet>
{/* html attributes */}
<html lang="en" amp />
{/* body attributes */}
<body className="root" />
{/* title attributes and value */}
<title itemProp="name" lang="en">My Plain Title or {`dynamic`} title</title>
{/* base element */}
<base target="_blank" href="http://mysite.com/" />
{/* multiple meta elements */}
<meta name="description" content="Helmet application" />
<meta property="og:type" content="article" />
{/* multiple link elements */}
<link rel="canonical" href="http://mysite.com/example" />
<link rel="apple-touch-icon" href="http://mysite.com/img/apple-touch-icon-57x57.png" />
<link rel="apple-touch-icon" sizes="72x72" href="http://mysite.com/img/apple-touch-icon-72x72.png" />
{locales.map((locale) => {
<link rel="alternate" href="http://example.com/{locale}" hrefLang={locale} key={locale}/>
})}
{/* multiple script elements */}
<script src="http://include.com/pathtojs.js" type="text/javascript" />
{/* inline script elements */}
<script type="application/ld+json">{`
{
"@context": "http://schema.org"
}
`}</script>
{/* noscript elements */}
<noscript>{`
<link rel="stylesheet" type="text/css" href="foo.css" />
`}</noscript>
{/* inline style elements */}
<style type="text/css">{`
body {
background-color: blue;
}
p {
font-size: 12px;
}
`}</style>
</Helmet>
```
### HelmetDataContext
[HelmetDataContext]: #helmetdatacontext
The [HelmetDataContext] (it may be initialized as just an empty object) can be
provided to [HelmetProvider] for server-side rendering (SSR) purposes. After
the component tree has been rendered, the `helmet` field will be attached to
this context:
```tsx
type HelmetDataContext = {
helmet?: {
base: HelmetDatum;
bodyAttributes: HelmetDatum;
htmlAttributes: HelmetDatum;
link: HelmetDatum;
meta: HelmetDatum;
noscript: HelmetDatum;
script: HelmetDatum;
style: HelmetDatum;
title: HelmetDatum;
titleAttributes?: HelmetDatum;
priority: HelmetDatum;
};
};
// where each HelmetDatum has two methods allowing to get string and component
// representations of the corresponding elements or attrbiutes:
type HelmetDatum = {
toString(): string;
toComponent(): ReactNode;
};
```
### HelmetProvider
[HelmetProvider]: #helmetprovider
The [HelmetProvider] component provides [React Context] to [Helmet]
components, _i.e._ any [Helmet] components in the application tree must be
descendants of a single [HelmetProvider] instance.
**Props**
- `children` &mdash; **ReactNode** &mdash; The component tree to render in
the place of [HelmetProvider].
- `context` &mdash; [HelmetDataContext] | **undefined** &mdash; Optional.
A user-provided context object for server-side rendering (SSR) purposes.
[React Context]: https://react.dev/learn/passing-data-deeply-with-context
{
"exclude": [],
"include": ["**/*.ts", "**/*.tsx"],
"extends": "@tsconfig/recommended",
"compilerOptions": {
"lib": ["DOM", "DOM.Iterable", "ESNext"],
"types": ["vitest/globals"],
"isolatedModules": true,
"esModuleInterop": true,
"jsx": "react",
"module": "CommonJS",
"moduleResolution": "node",
"resolveJsonModule": true,
"target": "ESNext",
"strict": true,
"baseUrl": ".",
"skipLibCheck": true,
"jsx": "react-jsx",
"module": "Preserve",
"noEmit": true,
"forceConsistentCasingInFileNames": true,
"allowJs": true
"noUncheckedIndexedAccess": true,
"verbatimModuleSyntax": true
}
}
import { build } from 'esbuild';
import { dependencies, peerDependencies } from './package.json';
const shared = {
entryPoints: ['src/index.tsx'],
bundle: true,
external: Object.keys(dependencies).concat(Object.keys(peerDependencies)),
};
build({
...shared,
format: 'cjs',
outfile: 'lib/index.js',
});
build({
...shared,
outfile: 'lib/index.esm.js',
format: 'esm',
});
import type { StateUpdate } from './types';
declare const handleStateChangeOnClient: (newState: StateUpdate) => void;
export default handleStateChangeOnClient;
export declare enum 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"
}
export declare enum ATTRIBUTE_NAMES {
BODY = "bodyAttributes",
HTML = "htmlAttributes",
TITLE = "titleAttributes"
}
export declare enum TAG_NAMES {
BASE = "base",
BODY = "body",
HEAD = "head",
HTML = "html",
LINK = "link",
META = "meta",
NOSCRIPT = "noscript",
SCRIPT = "script",
STYLE = "style",
TITLE = "title",
FRAGMENT = "Symbol(react.fragment)"
}
export declare const SEO_PRIORITY_TAGS: {
link: {
rel: string[];
};
script: {
type: string[];
};
meta: {
charset: string;
name: string[];
property: string[];
};
};
export declare const VALID_TAG_NAMES: TAG_NAMES[];
export declare const REACT_TAG_MAP: {
accesskey: string;
charset: string;
class: string;
contenteditable: string;
contextmenu: string;
'http-equiv': string;
itemprop: string;
tabindex: string;
};
export declare const HTML_TAG_MAP: {
[key: string]: string;
};
export declare const HELMET_ATTRIBUTE = "data-rh";
import { Component } from 'react';
import type { HelmetServerState } from './types';
export interface DispatcherContextProp {
setHelmet: (newState: HelmetServerState) => void;
helmetInstances: {
get: () => HelmetDispatcher[];
add: (helmet: HelmetDispatcher) => void;
remove: (helmet: HelmetDispatcher) => void;
};
}
interface DispatcherProps {
context: DispatcherContextProp;
}
export default class HelmetDispatcher extends Component<DispatcherProps> {
rendered: boolean;
shouldComponentUpdate(nextProps: DispatcherProps): boolean;
componentDidUpdate(): void;
componentWillUnmount(): void;
emitChange(): void;
init(): void;
render(): any;
}
export {};
import type HelmetDispatcher from './Dispatcher';
import type { HelmetServerState } from './types';
export declare function clearInstances(): void;
export interface HelmetDataType {
instances: HelmetDispatcher[];
context: HelmetDataContext;
}
interface HelmetDataContext {
helmet: HelmetServerState;
}
export declare const isDocument: boolean;
export default class HelmetData implements HelmetDataType {
instances: any[];
canUseDOM: boolean;
context: HelmetDataContext;
value: {
setHelmet: (serverState: HelmetServerState) => void;
helmetInstances: {
get: () => any[];
add: (instance: HelmetDispatcher) => void;
remove: (instance: HelmetDispatcher) => void;
};
};
constructor(context: any, canUseDOM?: boolean);
}
export {};
import type { PropsWithChildren, ReactElement, ReactNode } from 'react';
import React, { Component } from 'react';
import type { HelmetProps } from './types';
export * from './types';
export { default as HelmetData } from './HelmetData';
export { default as HelmetProvider } from './Provider';
type Props = {
[key: string]: any;
};
export declare class Helmet extends Component<PropsWithChildren<HelmetProps>> {
static defaultProps: {
defer: boolean;
encodeSpecialCharacters: boolean;
prioritizeSeoTags: boolean;
};
shouldComponentUpdate(nextProps: HelmetProps): boolean;
mapNestedChildrenToProps(child: ReactElement, nestedChildren: ReactNode): {
innerHTML: string | number | true | ReactElement<any, string | React.JSXElementConstructor<any>> | Iterable<ReactNode> | React.ReactPortal;
cssText?: undefined;
} | {
cssText: string | number | true | ReactElement<any, string | React.JSXElementConstructor<any>> | Iterable<ReactNode> | React.ReactPortal;
innerHTML?: undefined;
};
flattenArrayTypeChildren(child: JSX.Element, arrayTypeChildren: {
[key: string]: JSX.Element[];
}, newChildProps: Props, nestedChildren: ReactNode): {};
mapObjectTypeChildren(child: JSX.Element, newProps: Props, newChildProps: Props, nestedChildren: ReactNode): {};
mapArrayTypeChildrenToProps(arrayTypeChildren: {
[key: string]: JSX.Element;
}, newProps: Props): {
[x: string]: any;
};
warnOnInvalidChildren(child: JSX.Element, nestedChildren: ReactNode): boolean;
mapChildrenToProps(children: ReactNode, newProps: Props): {
[x: string]: any;
};
render(): React.JSX.Element;
}
// src/index.tsx
import React3, { Component as Component3 } from "react";
import fastCompare from "react-fast-compare";
import invariant from "invariant";
// src/Provider.tsx
import React2, { Component } from "react";
// src/server.ts
import React from "react";
// src/constants.ts
var TAG_NAMES = /* @__PURE__ */ ((TAG_NAMES2) => {
TAG_NAMES2["BASE"] = "base";
TAG_NAMES2["BODY"] = "body";
TAG_NAMES2["HEAD"] = "head";
TAG_NAMES2["HTML"] = "html";
TAG_NAMES2["LINK"] = "link";
TAG_NAMES2["META"] = "meta";
TAG_NAMES2["NOSCRIPT"] = "noscript";
TAG_NAMES2["SCRIPT"] = "script";
TAG_NAMES2["STYLE"] = "style";
TAG_NAMES2["TITLE"] = "title";
TAG_NAMES2["FRAGMENT"] = "Symbol(react.fragment)";
return TAG_NAMES2;
})(TAG_NAMES || {});
var SEO_PRIORITY_TAGS = {
link: { rel: ["amphtml", "canonical", "alternate"] },
script: { type: ["application/ld+json"] },
meta: {
charset: "",
name: ["generator", "robots", "description"],
property: [
"og:type",
"og:title",
"og:url",
"og:image",
"og:image:alt",
"og:description",
"twitter:url",
"twitter:title",
"twitter:description",
"twitter:image",
"twitter:image:alt",
"twitter:card",
"twitter:site"
]
}
};
var VALID_TAG_NAMES = Object.values(TAG_NAMES);
var REACT_TAG_MAP = {
accesskey: "accessKey",
charset: "charSet",
class: "className",
contenteditable: "contentEditable",
contextmenu: "contextMenu",
"http-equiv": "httpEquiv",
itemprop: "itemProp",
tabindex: "tabIndex"
};
var HTML_TAG_MAP = Object.entries(REACT_TAG_MAP).reduce(
(carry, [key, value]) => {
carry[value] = key;
return carry;
},
{}
);
var HELMET_ATTRIBUTE = "data-rh";
// src/utils.ts
var HELMET_PROPS = {
DEFAULT_TITLE: "defaultTitle",
DEFER: "defer",
ENCODE_SPECIAL_CHARACTERS: "encodeSpecialCharacters",
ON_CHANGE_CLIENT_STATE: "onChangeClientState",
TITLE_TEMPLATE: "titleTemplate",
PRIORITIZE_SEO_TAGS: "prioritizeSeoTags"
};
var getInnermostProperty = (propsList, property) => {
for (let i = propsList.length - 1; i >= 0; i -= 1) {
const props = propsList[i];
if (Object.prototype.hasOwnProperty.call(props, property)) {
return props[property];
}
}
return null;
};
var getTitleFromPropsList = (propsList) => {
let innermostTitle = getInnermostProperty(propsList, "title" /* TITLE */);
const innermostTemplate = getInnermostProperty(propsList, HELMET_PROPS.TITLE_TEMPLATE);
if (Array.isArray(innermostTitle)) {
innermostTitle = innermostTitle.join("");
}
if (innermostTemplate && innermostTitle) {
return innermostTemplate.replace(/%s/g, () => innermostTitle);
}
const innermostDefaultTitle = getInnermostProperty(propsList, HELMET_PROPS.DEFAULT_TITLE);
return innermostTitle || innermostDefaultTitle || void 0;
};
var getOnChangeClientState = (propsList) => getInnermostProperty(propsList, HELMET_PROPS.ON_CHANGE_CLIENT_STATE) || (() => {
});
var getAttributesFromPropsList = (tagType, propsList) => propsList.filter((props) => typeof props[tagType] !== "undefined").map((props) => props[tagType]).reduce((tagAttrs, current) => ({ ...tagAttrs, ...current }), {});
var getBaseTagFromPropsList = (primaryAttributes, propsList) => propsList.filter((props) => typeof props["base" /* BASE */] !== "undefined").map((props) => props["base" /* BASE */]).reverse().reduce((innermostBaseTag, tag) => {
if (!innermostBaseTag.length) {
const keys = Object.keys(tag);
for (let i = 0; i < keys.length; i += 1) {
const attributeKey = keys[i];
const lowerCaseAttributeKey = attributeKey.toLowerCase();
if (primaryAttributes.indexOf(lowerCaseAttributeKey) !== -1 && tag[lowerCaseAttributeKey]) {
return innermostBaseTag.concat(tag);
}
}
}
return innermostBaseTag;
}, []);
var warn = (msg) => console && typeof console.warn === "function" && console.warn(msg);
var getTagsFromPropsList = (tagName, primaryAttributes, propsList) => {
const approvedSeenTags = {};
return propsList.filter((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((props) => props[tagName]).reverse().reduce((approvedTags, instanceTags) => {
const instanceSeenTags = {};
instanceTags.filter((tag) => {
let primaryAttributeKey;
const keys2 = Object.keys(tag);
for (let i = 0; i < keys2.length; i += 1) {
const attributeKey = keys2[i];
const lowerCaseAttributeKey = attributeKey.toLowerCase();
if (primaryAttributes.indexOf(lowerCaseAttributeKey) !== -1 && !(primaryAttributeKey === "rel" /* REL */ && tag[primaryAttributeKey].toLowerCase() === "canonical") && !(lowerCaseAttributeKey === "rel" /* REL */ && tag[lowerCaseAttributeKey].toLowerCase() === "stylesheet")) {
primaryAttributeKey = lowerCaseAttributeKey;
}
if (primaryAttributes.indexOf(attributeKey) !== -1 && (attributeKey === "innerHTML" /* INNER_HTML */ || attributeKey === "cssText" /* CSS_TEXT */ || attributeKey === "itemprop" /* ITEM_PROP */)) {
primaryAttributeKey = attributeKey;
}
}
if (!primaryAttributeKey || !tag[primaryAttributeKey]) {
return false;
}
const 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((tag) => approvedTags.push(tag));
const keys = Object.keys(instanceSeenTags);
for (let i = 0; i < keys.length; i += 1) {
const attributeKey = keys[i];
const tagUnion = {
...approvedSeenTags[attributeKey],
...instanceSeenTags[attributeKey]
};
approvedSeenTags[attributeKey] = tagUnion;
}
return approvedTags;
}, []).reverse();
};
var getAnyTrueFromPropsList = (propsList, checkedTag) => {
if (Array.isArray(propsList) && propsList.length) {
for (let index = 0; index < propsList.length; index += 1) {
const prop = propsList[index];
if (prop[checkedTag]) {
return true;
}
}
}
return false;
};
var reducePropsToState = (propsList) => ({
baseTag: getBaseTagFromPropsList(["href" /* HREF */], propsList),
bodyAttributes: getAttributesFromPropsList("bodyAttributes" /* BODY */, propsList),
defer: getInnermostProperty(propsList, HELMET_PROPS.DEFER),
encode: getInnermostProperty(propsList, HELMET_PROPS.ENCODE_SPECIAL_CHARACTERS),
htmlAttributes: getAttributesFromPropsList("htmlAttributes" /* HTML */, propsList),
linkTags: getTagsFromPropsList(
"link" /* LINK */,
["rel" /* REL */, "href" /* HREF */],
propsList
),
metaTags: getTagsFromPropsList(
"meta" /* META */,
[
"name" /* NAME */,
"charset" /* CHARSET */,
"http-equiv" /* HTTPEQUIV */,
"property" /* PROPERTY */,
"itemprop" /* ITEM_PROP */
],
propsList
),
noscriptTags: getTagsFromPropsList("noscript" /* NOSCRIPT */, ["innerHTML" /* INNER_HTML */], propsList),
onChangeClientState: getOnChangeClientState(propsList),
scriptTags: getTagsFromPropsList(
"script" /* SCRIPT */,
["src" /* SRC */, "innerHTML" /* INNER_HTML */],
propsList
),
styleTags: getTagsFromPropsList("style" /* STYLE */, ["cssText" /* CSS_TEXT */], propsList),
title: getTitleFromPropsList(propsList),
titleAttributes: getAttributesFromPropsList("titleAttributes" /* TITLE */, propsList),
prioritizeSeoTags: getAnyTrueFromPropsList(propsList, HELMET_PROPS.PRIORITIZE_SEO_TAGS)
});
var flattenArray = (possibleArray) => Array.isArray(possibleArray) ? possibleArray.join("") : possibleArray;
var checkIfPropsMatch = (props, toMatch) => {
const keys = Object.keys(props);
for (let i = 0; i < keys.length; i += 1) {
if (toMatch[keys[i]] && toMatch[keys[i]].includes(props[keys[i]])) {
return true;
}
}
return false;
};
var prioritizer = (elementsList, propsToMatch) => {
if (Array.isArray(elementsList)) {
return elementsList.reduce(
(acc, elementAttrs) => {
if (checkIfPropsMatch(elementAttrs, propsToMatch)) {
acc.priority.push(elementAttrs);
} else {
acc.default.push(elementAttrs);
}
return acc;
},
{ priority: [], default: [] }
);
}
return { default: elementsList, priority: [] };
};
var without = (obj, key) => {
return {
...obj,
[key]: void 0
};
};
// src/server.ts
var SELF_CLOSING_TAGS = ["noscript" /* NOSCRIPT */, "script" /* SCRIPT */, "style" /* STYLE */];
var encodeSpecialCharacters = (str, encode = true) => {
if (encode === false) {
return String(str);
}
return String(str).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#x27;");
};
var generateElementAttributesAsString = (attributes) => Object.keys(attributes).reduce((str, key) => {
const attr = typeof attributes[key] !== "undefined" ? `${key}="${attributes[key]}"` : `${key}`;
return str ? `${str} ${attr}` : attr;
}, "");
var generateTitleAsString = (type, title, attributes, encode) => {
const attributeString = generateElementAttributesAsString(attributes);
const flattenedTitle = flattenArray(title);
return attributeString ? `<${type} ${HELMET_ATTRIBUTE}="true" ${attributeString}>${encodeSpecialCharacters(
flattenedTitle,
encode
)}</${type}>` : `<${type} ${HELMET_ATTRIBUTE}="true">${encodeSpecialCharacters(
flattenedTitle,
encode
)}</${type}>`;
};
var generateTagsAsString = (type, tags, encode = true) => tags.reduce((str, t) => {
const tag = t;
const attributeHtml = Object.keys(tag).filter(
(attribute) => !(attribute === "innerHTML" /* INNER_HTML */ || attribute === "cssText" /* CSS_TEXT */)
).reduce((string, attribute) => {
const attr = typeof tag[attribute] === "undefined" ? attribute : `${attribute}="${encodeSpecialCharacters(tag[attribute], encode)}"`;
return string ? `${string} ${attr}` : attr;
}, "");
const tagContent = tag.innerHTML || tag.cssText || "";
const isSelfClosing = SELF_CLOSING_TAGS.indexOf(type) === -1;
return `${str}<${type} ${HELMET_ATTRIBUTE}="true" ${attributeHtml}${isSelfClosing ? `/>` : `>${tagContent}</${type}>`}`;
}, "");
var convertElementAttributesToReactProps = (attributes, initProps = {}) => Object.keys(attributes).reduce((obj, key) => {
const mapped = REACT_TAG_MAP[key];
obj[mapped || key] = attributes[key];
return obj;
}, initProps);
var generateTitleAsReactComponent = (_type, title, attributes) => {
const initProps = {
key: title,
[HELMET_ATTRIBUTE]: true
};
const props = convertElementAttributesToReactProps(attributes, initProps);
return [React.createElement("title" /* TITLE */, props, title)];
};
var generateTagsAsReactComponent = (type, tags) => tags.map((tag, i) => {
const mappedTag = {
key: i,
[HELMET_ATTRIBUTE]: true
};
Object.keys(tag).forEach((attribute) => {
const mapped = REACT_TAG_MAP[attribute];
const mappedAttribute = mapped || attribute;
if (mappedAttribute === "innerHTML" /* INNER_HTML */ || mappedAttribute === "cssText" /* CSS_TEXT */) {
const content = tag.innerHTML || tag.cssText;
mappedTag.dangerouslySetInnerHTML = { __html: content };
} else {
mappedTag[mappedAttribute] = tag[attribute];
}
});
return React.createElement(type, mappedTag);
});
var getMethodsForTag = (type, tags, encode = true) => {
switch (type) {
case "title" /* TITLE */:
return {
toComponent: () => generateTitleAsReactComponent(type, tags.title, tags.titleAttributes),
toString: () => generateTitleAsString(type, tags.title, tags.titleAttributes, encode)
};
case "bodyAttributes" /* BODY */:
case "htmlAttributes" /* HTML */:
return {
toComponent: () => convertElementAttributesToReactProps(tags),
toString: () => generateElementAttributesAsString(tags)
};
default:
return {
toComponent: () => generateTagsAsReactComponent(type, tags),
toString: () => generateTagsAsString(type, tags, encode)
};
}
};
var getPriorityMethods = ({ metaTags, linkTags, scriptTags, encode }) => {
const meta = prioritizer(metaTags, SEO_PRIORITY_TAGS.meta);
const link = prioritizer(linkTags, SEO_PRIORITY_TAGS.link);
const script = prioritizer(scriptTags, SEO_PRIORITY_TAGS.script);
const priorityMethods = {
toComponent: () => [
...generateTagsAsReactComponent("meta" /* META */, meta.priority),
...generateTagsAsReactComponent("link" /* LINK */, link.priority),
...generateTagsAsReactComponent("script" /* SCRIPT */, script.priority)
],
toString: () => (
// generate all the tags as strings and concatenate them
`${getMethodsForTag("meta" /* META */, meta.priority, encode)} ${getMethodsForTag(
"link" /* LINK */,
link.priority,
encode
)} ${getMethodsForTag("script" /* SCRIPT */, script.priority, encode)}`
)
};
return {
priorityMethods,
metaTags: meta.default,
linkTags: link.default,
scriptTags: script.default
};
};
var mapStateOnServer = (props) => {
const {
baseTag,
bodyAttributes,
encode = true,
htmlAttributes,
noscriptTags,
styleTags,
title = "",
titleAttributes,
prioritizeSeoTags
} = props;
let { linkTags, metaTags, scriptTags } = props;
let priorityMethods = {
toComponent: () => {
},
toString: () => ""
};
if (prioritizeSeoTags) {
({ priorityMethods, linkTags, metaTags, scriptTags } = getPriorityMethods(props));
}
return {
priority: priorityMethods,
base: getMethodsForTag("base" /* BASE */, baseTag, encode),
bodyAttributes: getMethodsForTag("bodyAttributes" /* BODY */, bodyAttributes, encode),
htmlAttributes: getMethodsForTag("htmlAttributes" /* HTML */, htmlAttributes, encode),
link: getMethodsForTag("link" /* LINK */, linkTags, encode),
meta: getMethodsForTag("meta" /* META */, metaTags, encode),
noscript: getMethodsForTag("noscript" /* NOSCRIPT */, noscriptTags, encode),
script: getMethodsForTag("script" /* SCRIPT */, scriptTags, encode),
style: getMethodsForTag("style" /* STYLE */, styleTags, encode),
title: getMethodsForTag("title" /* TITLE */, { title, titleAttributes }, encode)
};
};
var server_default = mapStateOnServer;
// src/HelmetData.ts
var instances = [];
var isDocument = !!(typeof window !== "undefined" && window.document && window.document.createElement);
var HelmetData = class {
instances = [];
canUseDOM = isDocument;
context;
value = {
setHelmet: (serverState) => {
this.context.helmet = serverState;
},
helmetInstances: {
get: () => this.canUseDOM ? instances : this.instances,
add: (instance) => {
(this.canUseDOM ? instances : this.instances).push(instance);
},
remove: (instance) => {
const index = (this.canUseDOM ? instances : this.instances).indexOf(instance);
(this.canUseDOM ? instances : this.instances).splice(index, 1);
}
}
};
constructor(context, canUseDOM) {
this.context = context;
this.canUseDOM = canUseDOM || false;
if (!canUseDOM) {
context.helmet = server_default({
baseTag: [],
bodyAttributes: {},
encodeSpecialCharacters: true,
htmlAttributes: {},
linkTags: [],
metaTags: [],
noscriptTags: [],
scriptTags: [],
styleTags: [],
title: "",
titleAttributes: {}
});
}
}
};
// src/Provider.tsx
var defaultValue = {};
var Context = React2.createContext(defaultValue);
var HelmetProvider = class _HelmetProvider extends Component {
static canUseDOM = isDocument;
helmetData;
constructor(props) {
super(props);
this.helmetData = new HelmetData(this.props.context || {}, _HelmetProvider.canUseDOM);
}
render() {
return /* @__PURE__ */ React2.createElement(Context.Provider, { value: this.helmetData.value }, this.props.children);
}
};
// src/Dispatcher.tsx
import { Component as Component2 } from "react";
import shallowEqual from "shallowequal";
// src/client.ts
var updateTags = (type, tags) => {
const headElement = document.head || document.querySelector("head" /* HEAD */);
const tagNodes = headElement.querySelectorAll(`${type}[${HELMET_ATTRIBUTE}]`);
const oldTags = [].slice.call(tagNodes);
const newTags = [];
let indexToDelete;
if (tags && tags.length) {
tags.forEach((tag) => {
const newElement = document.createElement(type);
for (const attribute in tag) {
if (Object.prototype.hasOwnProperty.call(tag, attribute)) {
if (attribute === "innerHTML" /* INNER_HTML */) {
newElement.innerHTML = tag.innerHTML;
} else if (attribute === "cssText" /* CSS_TEXT */) {
if (newElement.styleSheet) {
newElement.styleSheet.cssText = tag.cssText;
} else {
newElement.appendChild(document.createTextNode(tag.cssText));
}
} else {
const attr = attribute;
const value = typeof tag[attr] === "undefined" ? "" : tag[attr];
newElement.setAttribute(attribute, value);
}
}
}
newElement.setAttribute(HELMET_ATTRIBUTE, "true");
if (oldTags.some((existingTag, index) => {
indexToDelete = index;
return newElement.isEqualNode(existingTag);
})) {
oldTags.splice(indexToDelete, 1);
} else {
newTags.push(newElement);
}
});
}
oldTags.forEach((tag) => tag.parentNode?.removeChild(tag));
newTags.forEach((tag) => headElement.appendChild(tag));
return {
oldTags,
newTags
};
};
var updateAttributes = (tagName, attributes) => {
const elementTag = document.getElementsByTagName(tagName)[0];
if (!elementTag) {
return;
}
const helmetAttributeString = elementTag.getAttribute(HELMET_ATTRIBUTE);
const helmetAttributes = helmetAttributeString ? helmetAttributeString.split(",") : [];
const attributesToRemove = [...helmetAttributes];
const attributeKeys = Object.keys(attributes);
for (const attribute of attributeKeys) {
const value = attributes[attribute] || "";
if (elementTag.getAttribute(attribute) !== value) {
elementTag.setAttribute(attribute, value);
}
if (helmetAttributes.indexOf(attribute) === -1) {
helmetAttributes.push(attribute);
}
const indexToSave = attributesToRemove.indexOf(attribute);
if (indexToSave !== -1) {
attributesToRemove.splice(indexToSave, 1);
}
}
for (let i = attributesToRemove.length - 1; i >= 0; i -= 1) {
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 updateTitle = (title, attributes) => {
if (typeof title !== "undefined" && document.title !== title) {
document.title = flattenArray(title);
}
updateAttributes("title" /* TITLE */, attributes);
};
var commitTagChanges = (newState, cb) => {
const {
baseTag,
bodyAttributes,
htmlAttributes,
linkTags,
metaTags,
noscriptTags,
onChangeClientState,
scriptTags,
styleTags,
title,
titleAttributes
} = newState;
updateAttributes("body" /* BODY */, bodyAttributes);
updateAttributes("html" /* HTML */, htmlAttributes);
updateTitle(title, titleAttributes);
const tagUpdates = {
baseTag: updateTags("base" /* BASE */, baseTag),
linkTags: updateTags("link" /* LINK */, linkTags),
metaTags: updateTags("meta" /* META */, metaTags),
noscriptTags: updateTags("noscript" /* NOSCRIPT */, noscriptTags),
scriptTags: updateTags("script" /* SCRIPT */, scriptTags),
styleTags: updateTags("style" /* STYLE */, styleTags)
};
const addedTags = {};
const removedTags = {};
Object.keys(tagUpdates).forEach((tagType) => {
const { newTags, oldTags } = tagUpdates[tagType];
if (newTags.length) {
addedTags[tagType] = newTags;
}
if (oldTags.length) {
removedTags[tagType] = tagUpdates[tagType].oldTags;
}
});
if (cb) {
cb();
}
onChangeClientState(newState, addedTags, removedTags);
};
var _helmetCallback = null;
var handleStateChangeOnClient = (newState) => {
if (_helmetCallback) {
cancelAnimationFrame(_helmetCallback);
}
if (newState.defer) {
_helmetCallback = requestAnimationFrame(() => {
commitTagChanges(newState, () => {
_helmetCallback = null;
});
});
} else {
commitTagChanges(newState);
_helmetCallback = null;
}
};
var client_default = handleStateChangeOnClient;
// src/Dispatcher.tsx
var HelmetDispatcher = class extends Component2 {
rendered = false;
shouldComponentUpdate(nextProps) {
return !shallowEqual(nextProps, this.props);
}
componentDidUpdate() {
this.emitChange();
}
componentWillUnmount() {
const { helmetInstances } = this.props.context;
helmetInstances.remove(this);
this.emitChange();
}
emitChange() {
const { helmetInstances, setHelmet } = this.props.context;
let serverState = null;
const state = reducePropsToState(
helmetInstances.get().map((instance) => {
const props = { ...instance.props };
delete props.context;
return props;
})
);
if (HelmetProvider.canUseDOM) {
client_default(state);
} else if (server_default) {
serverState = server_default(state);
}
setHelmet(serverState);
}
// componentWillMount will be deprecated
// for SSR, initialize on first render
// constructor is also unsafe in StrictMode
init() {
if (this.rendered) {
return;
}
this.rendered = true;
const { helmetInstances } = this.props.context;
helmetInstances.add(this);
this.emitChange();
}
render() {
this.init();
return null;
}
};
// src/index.tsx
var Helmet = class extends Component3 {
static defaultProps = {
defer: true,
encodeSpecialCharacters: true,
prioritizeSeoTags: false
};
shouldComponentUpdate(nextProps) {
return !fastCompare(without(this.props, "helmetData"), without(nextProps, "helmetData"));
}
mapNestedChildrenToProps(child, nestedChildren) {
if (!nestedChildren) {
return null;
}
switch (child.type) {
case "script" /* SCRIPT */:
case "noscript" /* NOSCRIPT */:
return {
innerHTML: nestedChildren
};
case "style" /* STYLE */:
return {
cssText: nestedChildren
};
default:
throw new Error(
`<${child.type} /> elements are self-closing and can not contain children. Refer to our API for more information.`
);
}
}
flattenArrayTypeChildren(child, arrayTypeChildren, newChildProps, nestedChildren) {
return {
...arrayTypeChildren,
[child.type]: [
...arrayTypeChildren[child.type] || [],
{
...newChildProps,
...this.mapNestedChildrenToProps(child, nestedChildren)
}
]
};
}
mapObjectTypeChildren(child, newProps, newChildProps, nestedChildren) {
switch (child.type) {
case "title" /* TITLE */:
return {
...newProps,
[child.type]: nestedChildren,
titleAttributes: { ...newChildProps }
};
case "body" /* BODY */:
return {
...newProps,
bodyAttributes: { ...newChildProps }
};
case "html" /* HTML */:
return {
...newProps,
htmlAttributes: { ...newChildProps }
};
default:
return {
...newProps,
[child.type]: { ...newChildProps }
};
}
}
mapArrayTypeChildrenToProps(arrayTypeChildren, newProps) {
let newFlattenedProps = { ...newProps };
Object.keys(arrayTypeChildren).forEach((arrayChildName) => {
newFlattenedProps = {
...newFlattenedProps,
[arrayChildName]: arrayTypeChildren[arrayChildName]
};
});
return newFlattenedProps;
}
warnOnInvalidChildren(child, nestedChildren) {
invariant(
VALID_TAG_NAMES.some((name) => child.type === name),
typeof child.type === "function" ? `You may be attempting to nest <Helmet> components within each other, which is not allowed. Refer to our API for more information.` : `Only elements types ${VALID_TAG_NAMES.join(
", "
)} are allowed. Helmet does not support rendering <${child.type}> elements. Refer to our API for more information.`
);
invariant(
!nestedChildren || typeof nestedChildren === "string" || Array.isArray(nestedChildren) && !nestedChildren.some((nestedChild) => typeof nestedChild !== "string"),
`Helmet expects a string as a child of <${child.type}>. Did you forget to wrap your children in braces? ( <${child.type}>{\`\`}</${child.type}> ) Refer to our API for more information.`
);
return true;
}
mapChildrenToProps(children, newProps) {
let arrayTypeChildren = {};
React3.Children.forEach(children, (child) => {
if (!child || !child.props) {
return;
}
const { children: nestedChildren, ...childProps } = child.props;
const newChildProps = Object.keys(childProps).reduce((obj, key) => {
obj[HTML_TAG_MAP[key] || key] = childProps[key];
return obj;
}, {});
let { type } = child;
if (typeof type === "symbol") {
type = type.toString();
} else {
this.warnOnInvalidChildren(child, nestedChildren);
}
switch (type) {
case "Symbol(react.fragment)" /* FRAGMENT */:
newProps = this.mapChildrenToProps(nestedChildren, newProps);
break;
case "link" /* LINK */:
case "meta" /* META */:
case "noscript" /* NOSCRIPT */:
case "script" /* SCRIPT */:
case "style" /* STYLE */:
arrayTypeChildren = this.flattenArrayTypeChildren(
child,
arrayTypeChildren,
newChildProps,
nestedChildren
);
break;
default:
newProps = this.mapObjectTypeChildren(child, newProps, newChildProps, nestedChildren);
break;
}
});
return this.mapArrayTypeChildrenToProps(arrayTypeChildren, newProps);
}
render() {
const { children, ...props } = this.props;
let newProps = { ...props };
let { helmetData } = props;
if (children) {
newProps = this.mapChildrenToProps(children, newProps);
}
if (helmetData && !(helmetData instanceof HelmetData)) {
const data = helmetData;
helmetData = new HelmetData(data.context, true);
delete newProps.helmetData;
}
return helmetData ? /* @__PURE__ */ React3.createElement(HelmetDispatcher, { ...newProps, context: helmetData.value }) : /* @__PURE__ */ React3.createElement(Context.Consumer, null, (context) => /* @__PURE__ */ React3.createElement(HelmetDispatcher, { ...newProps, context }));
}
};
export {
Helmet,
HelmetData,
HelmetProvider
};
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.tsx
var src_exports = {};
__export(src_exports, {
Helmet: () => Helmet,
HelmetData: () => HelmetData,
HelmetProvider: () => HelmetProvider
});
module.exports = __toCommonJS(src_exports);
var import_react4 = __toESM(require("react"));
var import_react_fast_compare = __toESM(require("react-fast-compare"));
var import_invariant = __toESM(require("invariant"));
// src/Provider.tsx
var import_react2 = __toESM(require("react"));
// src/server.ts
var import_react = __toESM(require("react"));
// src/constants.ts
var TAG_NAMES = /* @__PURE__ */ ((TAG_NAMES2) => {
TAG_NAMES2["BASE"] = "base";
TAG_NAMES2["BODY"] = "body";
TAG_NAMES2["HEAD"] = "head";
TAG_NAMES2["HTML"] = "html";
TAG_NAMES2["LINK"] = "link";
TAG_NAMES2["META"] = "meta";
TAG_NAMES2["NOSCRIPT"] = "noscript";
TAG_NAMES2["SCRIPT"] = "script";
TAG_NAMES2["STYLE"] = "style";
TAG_NAMES2["TITLE"] = "title";
TAG_NAMES2["FRAGMENT"] = "Symbol(react.fragment)";
return TAG_NAMES2;
})(TAG_NAMES || {});
var SEO_PRIORITY_TAGS = {
link: { rel: ["amphtml", "canonical", "alternate"] },
script: { type: ["application/ld+json"] },
meta: {
charset: "",
name: ["generator", "robots", "description"],
property: [
"og:type",
"og:title",
"og:url",
"og:image",
"og:image:alt",
"og:description",
"twitter:url",
"twitter:title",
"twitter:description",
"twitter:image",
"twitter:image:alt",
"twitter:card",
"twitter:site"
]
}
};
var VALID_TAG_NAMES = Object.values(TAG_NAMES);
var REACT_TAG_MAP = {
accesskey: "accessKey",
charset: "charSet",
class: "className",
contenteditable: "contentEditable",
contextmenu: "contextMenu",
"http-equiv": "httpEquiv",
itemprop: "itemProp",
tabindex: "tabIndex"
};
var HTML_TAG_MAP = Object.entries(REACT_TAG_MAP).reduce(
(carry, [key, value]) => {
carry[value] = key;
return carry;
},
{}
);
var HELMET_ATTRIBUTE = "data-rh";
// src/utils.ts
var HELMET_PROPS = {
DEFAULT_TITLE: "defaultTitle",
DEFER: "defer",
ENCODE_SPECIAL_CHARACTERS: "encodeSpecialCharacters",
ON_CHANGE_CLIENT_STATE: "onChangeClientState",
TITLE_TEMPLATE: "titleTemplate",
PRIORITIZE_SEO_TAGS: "prioritizeSeoTags"
};
var getInnermostProperty = (propsList, property) => {
for (let i = propsList.length - 1; i >= 0; i -= 1) {
const props = propsList[i];
if (Object.prototype.hasOwnProperty.call(props, property)) {
return props[property];
}
}
return null;
};
var getTitleFromPropsList = (propsList) => {
let innermostTitle = getInnermostProperty(propsList, "title" /* TITLE */);
const innermostTemplate = getInnermostProperty(propsList, HELMET_PROPS.TITLE_TEMPLATE);
if (Array.isArray(innermostTitle)) {
innermostTitle = innermostTitle.join("");
}
if (innermostTemplate && innermostTitle) {
return innermostTemplate.replace(/%s/g, () => innermostTitle);
}
const innermostDefaultTitle = getInnermostProperty(propsList, HELMET_PROPS.DEFAULT_TITLE);
return innermostTitle || innermostDefaultTitle || void 0;
};
var getOnChangeClientState = (propsList) => getInnermostProperty(propsList, HELMET_PROPS.ON_CHANGE_CLIENT_STATE) || (() => {
});
var getAttributesFromPropsList = (tagType, propsList) => propsList.filter((props) => typeof props[tagType] !== "undefined").map((props) => props[tagType]).reduce((tagAttrs, current) => ({ ...tagAttrs, ...current }), {});
var getBaseTagFromPropsList = (primaryAttributes, propsList) => propsList.filter((props) => typeof props["base" /* BASE */] !== "undefined").map((props) => props["base" /* BASE */]).reverse().reduce((innermostBaseTag, tag) => {
if (!innermostBaseTag.length) {
const keys = Object.keys(tag);
for (let i = 0; i < keys.length; i += 1) {
const attributeKey = keys[i];
const lowerCaseAttributeKey = attributeKey.toLowerCase();
if (primaryAttributes.indexOf(lowerCaseAttributeKey) !== -1 && tag[lowerCaseAttributeKey]) {
return innermostBaseTag.concat(tag);
}
}
}
return innermostBaseTag;
}, []);
var warn = (msg) => console && typeof console.warn === "function" && console.warn(msg);
var getTagsFromPropsList = (tagName, primaryAttributes, propsList) => {
const approvedSeenTags = {};
return propsList.filter((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((props) => props[tagName]).reverse().reduce((approvedTags, instanceTags) => {
const instanceSeenTags = {};
instanceTags.filter((tag) => {
let primaryAttributeKey;
const keys2 = Object.keys(tag);
for (let i = 0; i < keys2.length; i += 1) {
const attributeKey = keys2[i];
const lowerCaseAttributeKey = attributeKey.toLowerCase();
if (primaryAttributes.indexOf(lowerCaseAttributeKey) !== -1 && !(primaryAttributeKey === "rel" /* REL */ && tag[primaryAttributeKey].toLowerCase() === "canonical") && !(lowerCaseAttributeKey === "rel" /* REL */ && tag[lowerCaseAttributeKey].toLowerCase() === "stylesheet")) {
primaryAttributeKey = lowerCaseAttributeKey;
}
if (primaryAttributes.indexOf(attributeKey) !== -1 && (attributeKey === "innerHTML" /* INNER_HTML */ || attributeKey === "cssText" /* CSS_TEXT */ || attributeKey === "itemprop" /* ITEM_PROP */)) {
primaryAttributeKey = attributeKey;
}
}
if (!primaryAttributeKey || !tag[primaryAttributeKey]) {
return false;
}
const 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((tag) => approvedTags.push(tag));
const keys = Object.keys(instanceSeenTags);
for (let i = 0; i < keys.length; i += 1) {
const attributeKey = keys[i];
const tagUnion = {
...approvedSeenTags[attributeKey],
...instanceSeenTags[attributeKey]
};
approvedSeenTags[attributeKey] = tagUnion;
}
return approvedTags;
}, []).reverse();
};
var getAnyTrueFromPropsList = (propsList, checkedTag) => {
if (Array.isArray(propsList) && propsList.length) {
for (let index = 0; index < propsList.length; index += 1) {
const prop = propsList[index];
if (prop[checkedTag]) {
return true;
}
}
}
return false;
};
var reducePropsToState = (propsList) => ({
baseTag: getBaseTagFromPropsList(["href" /* HREF */], propsList),
bodyAttributes: getAttributesFromPropsList("bodyAttributes" /* BODY */, propsList),
defer: getInnermostProperty(propsList, HELMET_PROPS.DEFER),
encode: getInnermostProperty(propsList, HELMET_PROPS.ENCODE_SPECIAL_CHARACTERS),
htmlAttributes: getAttributesFromPropsList("htmlAttributes" /* HTML */, propsList),
linkTags: getTagsFromPropsList(
"link" /* LINK */,
["rel" /* REL */, "href" /* HREF */],
propsList
),
metaTags: getTagsFromPropsList(
"meta" /* META */,
[
"name" /* NAME */,
"charset" /* CHARSET */,
"http-equiv" /* HTTPEQUIV */,
"property" /* PROPERTY */,
"itemprop" /* ITEM_PROP */
],
propsList
),
noscriptTags: getTagsFromPropsList("noscript" /* NOSCRIPT */, ["innerHTML" /* INNER_HTML */], propsList),
onChangeClientState: getOnChangeClientState(propsList),
scriptTags: getTagsFromPropsList(
"script" /* SCRIPT */,
["src" /* SRC */, "innerHTML" /* INNER_HTML */],
propsList
),
styleTags: getTagsFromPropsList("style" /* STYLE */, ["cssText" /* CSS_TEXT */], propsList),
title: getTitleFromPropsList(propsList),
titleAttributes: getAttributesFromPropsList("titleAttributes" /* TITLE */, propsList),
prioritizeSeoTags: getAnyTrueFromPropsList(propsList, HELMET_PROPS.PRIORITIZE_SEO_TAGS)
});
var flattenArray = (possibleArray) => Array.isArray(possibleArray) ? possibleArray.join("") : possibleArray;
var checkIfPropsMatch = (props, toMatch) => {
const keys = Object.keys(props);
for (let i = 0; i < keys.length; i += 1) {
if (toMatch[keys[i]] && toMatch[keys[i]].includes(props[keys[i]])) {
return true;
}
}
return false;
};
var prioritizer = (elementsList, propsToMatch) => {
if (Array.isArray(elementsList)) {
return elementsList.reduce(
(acc, elementAttrs) => {
if (checkIfPropsMatch(elementAttrs, propsToMatch)) {
acc.priority.push(elementAttrs);
} else {
acc.default.push(elementAttrs);
}
return acc;
},
{ priority: [], default: [] }
);
}
return { default: elementsList, priority: [] };
};
var without = (obj, key) => {
return {
...obj,
[key]: void 0
};
};
// src/server.ts
var SELF_CLOSING_TAGS = ["noscript" /* NOSCRIPT */, "script" /* SCRIPT */, "style" /* STYLE */];
var encodeSpecialCharacters = (str, encode = true) => {
if (encode === false) {
return String(str);
}
return String(str).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#x27;");
};
var generateElementAttributesAsString = (attributes) => Object.keys(attributes).reduce((str, key) => {
const attr = typeof attributes[key] !== "undefined" ? `${key}="${attributes[key]}"` : `${key}`;
return str ? `${str} ${attr}` : attr;
}, "");
var generateTitleAsString = (type, title, attributes, encode) => {
const attributeString = generateElementAttributesAsString(attributes);
const flattenedTitle = flattenArray(title);
return attributeString ? `<${type} ${HELMET_ATTRIBUTE}="true" ${attributeString}>${encodeSpecialCharacters(
flattenedTitle,
encode
)}</${type}>` : `<${type} ${HELMET_ATTRIBUTE}="true">${encodeSpecialCharacters(
flattenedTitle,
encode
)}</${type}>`;
};
var generateTagsAsString = (type, tags, encode = true) => tags.reduce((str, t) => {
const tag = t;
const attributeHtml = Object.keys(tag).filter(
(attribute) => !(attribute === "innerHTML" /* INNER_HTML */ || attribute === "cssText" /* CSS_TEXT */)
).reduce((string, attribute) => {
const attr = typeof tag[attribute] === "undefined" ? attribute : `${attribute}="${encodeSpecialCharacters(tag[attribute], encode)}"`;
return string ? `${string} ${attr}` : attr;
}, "");
const tagContent = tag.innerHTML || tag.cssText || "";
const isSelfClosing = SELF_CLOSING_TAGS.indexOf(type) === -1;
return `${str}<${type} ${HELMET_ATTRIBUTE}="true" ${attributeHtml}${isSelfClosing ? `/>` : `>${tagContent}</${type}>`}`;
}, "");
var convertElementAttributesToReactProps = (attributes, initProps = {}) => Object.keys(attributes).reduce((obj, key) => {
const mapped = REACT_TAG_MAP[key];
obj[mapped || key] = attributes[key];
return obj;
}, initProps);
var generateTitleAsReactComponent = (_type, title, attributes) => {
const initProps = {
key: title,
[HELMET_ATTRIBUTE]: true
};
const props = convertElementAttributesToReactProps(attributes, initProps);
return [import_react.default.createElement("title" /* TITLE */, props, title)];
};
var generateTagsAsReactComponent = (type, tags) => tags.map((tag, i) => {
const mappedTag = {
key: i,
[HELMET_ATTRIBUTE]: true
};
Object.keys(tag).forEach((attribute) => {
const mapped = REACT_TAG_MAP[attribute];
const mappedAttribute = mapped || attribute;
if (mappedAttribute === "innerHTML" /* INNER_HTML */ || mappedAttribute === "cssText" /* CSS_TEXT */) {
const content = tag.innerHTML || tag.cssText;
mappedTag.dangerouslySetInnerHTML = { __html: content };
} else {
mappedTag[mappedAttribute] = tag[attribute];
}
});
return import_react.default.createElement(type, mappedTag);
});
var getMethodsForTag = (type, tags, encode = true) => {
switch (type) {
case "title" /* TITLE */:
return {
toComponent: () => generateTitleAsReactComponent(type, tags.title, tags.titleAttributes),
toString: () => generateTitleAsString(type, tags.title, tags.titleAttributes, encode)
};
case "bodyAttributes" /* BODY */:
case "htmlAttributes" /* HTML */:
return {
toComponent: () => convertElementAttributesToReactProps(tags),
toString: () => generateElementAttributesAsString(tags)
};
default:
return {
toComponent: () => generateTagsAsReactComponent(type, tags),
toString: () => generateTagsAsString(type, tags, encode)
};
}
};
var getPriorityMethods = ({ metaTags, linkTags, scriptTags, encode }) => {
const meta = prioritizer(metaTags, SEO_PRIORITY_TAGS.meta);
const link = prioritizer(linkTags, SEO_PRIORITY_TAGS.link);
const script = prioritizer(scriptTags, SEO_PRIORITY_TAGS.script);
const priorityMethods = {
toComponent: () => [
...generateTagsAsReactComponent("meta" /* META */, meta.priority),
...generateTagsAsReactComponent("link" /* LINK */, link.priority),
...generateTagsAsReactComponent("script" /* SCRIPT */, script.priority)
],
toString: () => (
// generate all the tags as strings and concatenate them
`${getMethodsForTag("meta" /* META */, meta.priority, encode)} ${getMethodsForTag(
"link" /* LINK */,
link.priority,
encode
)} ${getMethodsForTag("script" /* SCRIPT */, script.priority, encode)}`
)
};
return {
priorityMethods,
metaTags: meta.default,
linkTags: link.default,
scriptTags: script.default
};
};
var mapStateOnServer = (props) => {
const {
baseTag,
bodyAttributes,
encode = true,
htmlAttributes,
noscriptTags,
styleTags,
title = "",
titleAttributes,
prioritizeSeoTags
} = props;
let { linkTags, metaTags, scriptTags } = props;
let priorityMethods = {
toComponent: () => {
},
toString: () => ""
};
if (prioritizeSeoTags) {
({ priorityMethods, linkTags, metaTags, scriptTags } = getPriorityMethods(props));
}
return {
priority: priorityMethods,
base: getMethodsForTag("base" /* BASE */, baseTag, encode),
bodyAttributes: getMethodsForTag("bodyAttributes" /* BODY */, bodyAttributes, encode),
htmlAttributes: getMethodsForTag("htmlAttributes" /* HTML */, htmlAttributes, encode),
link: getMethodsForTag("link" /* LINK */, linkTags, encode),
meta: getMethodsForTag("meta" /* META */, metaTags, encode),
noscript: getMethodsForTag("noscript" /* NOSCRIPT */, noscriptTags, encode),
script: getMethodsForTag("script" /* SCRIPT */, scriptTags, encode),
style: getMethodsForTag("style" /* STYLE */, styleTags, encode),
title: getMethodsForTag("title" /* TITLE */, { title, titleAttributes }, encode)
};
};
var server_default = mapStateOnServer;
// src/HelmetData.ts
var instances = [];
var isDocument = !!(typeof window !== "undefined" && window.document && window.document.createElement);
var HelmetData = class {
instances = [];
canUseDOM = isDocument;
context;
value = {
setHelmet: (serverState) => {
this.context.helmet = serverState;
},
helmetInstances: {
get: () => this.canUseDOM ? instances : this.instances,
add: (instance) => {
(this.canUseDOM ? instances : this.instances).push(instance);
},
remove: (instance) => {
const index = (this.canUseDOM ? instances : this.instances).indexOf(instance);
(this.canUseDOM ? instances : this.instances).splice(index, 1);
}
}
};
constructor(context, canUseDOM) {
this.context = context;
this.canUseDOM = canUseDOM || false;
if (!canUseDOM) {
context.helmet = server_default({
baseTag: [],
bodyAttributes: {},
encodeSpecialCharacters: true,
htmlAttributes: {},
linkTags: [],
metaTags: [],
noscriptTags: [],
scriptTags: [],
styleTags: [],
title: "",
titleAttributes: {}
});
}
}
};
// src/Provider.tsx
var defaultValue = {};
var Context = import_react2.default.createContext(defaultValue);
var HelmetProvider = class _HelmetProvider extends import_react2.Component {
static canUseDOM = isDocument;
helmetData;
constructor(props) {
super(props);
this.helmetData = new HelmetData(this.props.context || {}, _HelmetProvider.canUseDOM);
}
render() {
return /* @__PURE__ */ import_react2.default.createElement(Context.Provider, { value: this.helmetData.value }, this.props.children);
}
};
// src/Dispatcher.tsx
var import_react3 = require("react");
var import_shallowequal = __toESM(require("shallowequal"));
// src/client.ts
var updateTags = (type, tags) => {
const headElement = document.head || document.querySelector("head" /* HEAD */);
const tagNodes = headElement.querySelectorAll(`${type}[${HELMET_ATTRIBUTE}]`);
const oldTags = [].slice.call(tagNodes);
const newTags = [];
let indexToDelete;
if (tags && tags.length) {
tags.forEach((tag) => {
const newElement = document.createElement(type);
for (const attribute in tag) {
if (Object.prototype.hasOwnProperty.call(tag, attribute)) {
if (attribute === "innerHTML" /* INNER_HTML */) {
newElement.innerHTML = tag.innerHTML;
} else if (attribute === "cssText" /* CSS_TEXT */) {
if (newElement.styleSheet) {
newElement.styleSheet.cssText = tag.cssText;
} else {
newElement.appendChild(document.createTextNode(tag.cssText));
}
} else {
const attr = attribute;
const value = typeof tag[attr] === "undefined" ? "" : tag[attr];
newElement.setAttribute(attribute, value);
}
}
}
newElement.setAttribute(HELMET_ATTRIBUTE, "true");
if (oldTags.some((existingTag, index) => {
indexToDelete = index;
return newElement.isEqualNode(existingTag);
})) {
oldTags.splice(indexToDelete, 1);
} else {
newTags.push(newElement);
}
});
}
oldTags.forEach((tag) => tag.parentNode?.removeChild(tag));
newTags.forEach((tag) => headElement.appendChild(tag));
return {
oldTags,
newTags
};
};
var updateAttributes = (tagName, attributes) => {
const elementTag = document.getElementsByTagName(tagName)[0];
if (!elementTag) {
return;
}
const helmetAttributeString = elementTag.getAttribute(HELMET_ATTRIBUTE);
const helmetAttributes = helmetAttributeString ? helmetAttributeString.split(",") : [];
const attributesToRemove = [...helmetAttributes];
const attributeKeys = Object.keys(attributes);
for (const attribute of attributeKeys) {
const value = attributes[attribute] || "";
if (elementTag.getAttribute(attribute) !== value) {
elementTag.setAttribute(attribute, value);
}
if (helmetAttributes.indexOf(attribute) === -1) {
helmetAttributes.push(attribute);
}
const indexToSave = attributesToRemove.indexOf(attribute);
if (indexToSave !== -1) {
attributesToRemove.splice(indexToSave, 1);
}
}
for (let i = attributesToRemove.length - 1; i >= 0; i -= 1) {
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 updateTitle = (title, attributes) => {
if (typeof title !== "undefined" && document.title !== title) {
document.title = flattenArray(title);
}
updateAttributes("title" /* TITLE */, attributes);
};
var commitTagChanges = (newState, cb) => {
const {
baseTag,
bodyAttributes,
htmlAttributes,
linkTags,
metaTags,
noscriptTags,
onChangeClientState,
scriptTags,
styleTags,
title,
titleAttributes
} = newState;
updateAttributes("body" /* BODY */, bodyAttributes);
updateAttributes("html" /* HTML */, htmlAttributes);
updateTitle(title, titleAttributes);
const tagUpdates = {
baseTag: updateTags("base" /* BASE */, baseTag),
linkTags: updateTags("link" /* LINK */, linkTags),
metaTags: updateTags("meta" /* META */, metaTags),
noscriptTags: updateTags("noscript" /* NOSCRIPT */, noscriptTags),
scriptTags: updateTags("script" /* SCRIPT */, scriptTags),
styleTags: updateTags("style" /* STYLE */, styleTags)
};
const addedTags = {};
const removedTags = {};
Object.keys(tagUpdates).forEach((tagType) => {
const { newTags, oldTags } = tagUpdates[tagType];
if (newTags.length) {
addedTags[tagType] = newTags;
}
if (oldTags.length) {
removedTags[tagType] = tagUpdates[tagType].oldTags;
}
});
if (cb) {
cb();
}
onChangeClientState(newState, addedTags, removedTags);
};
var _helmetCallback = null;
var handleStateChangeOnClient = (newState) => {
if (_helmetCallback) {
cancelAnimationFrame(_helmetCallback);
}
if (newState.defer) {
_helmetCallback = requestAnimationFrame(() => {
commitTagChanges(newState, () => {
_helmetCallback = null;
});
});
} else {
commitTagChanges(newState);
_helmetCallback = null;
}
};
var client_default = handleStateChangeOnClient;
// src/Dispatcher.tsx
var HelmetDispatcher = class extends import_react3.Component {
rendered = false;
shouldComponentUpdate(nextProps) {
return !(0, import_shallowequal.default)(nextProps, this.props);
}
componentDidUpdate() {
this.emitChange();
}
componentWillUnmount() {
const { helmetInstances } = this.props.context;
helmetInstances.remove(this);
this.emitChange();
}
emitChange() {
const { helmetInstances, setHelmet } = this.props.context;
let serverState = null;
const state = reducePropsToState(
helmetInstances.get().map((instance) => {
const props = { ...instance.props };
delete props.context;
return props;
})
);
if (HelmetProvider.canUseDOM) {
client_default(state);
} else if (server_default) {
serverState = server_default(state);
}
setHelmet(serverState);
}
// componentWillMount will be deprecated
// for SSR, initialize on first render
// constructor is also unsafe in StrictMode
init() {
if (this.rendered) {
return;
}
this.rendered = true;
const { helmetInstances } = this.props.context;
helmetInstances.add(this);
this.emitChange();
}
render() {
this.init();
return null;
}
};
// src/index.tsx
var Helmet = class extends import_react4.Component {
static defaultProps = {
defer: true,
encodeSpecialCharacters: true,
prioritizeSeoTags: false
};
shouldComponentUpdate(nextProps) {
return !(0, import_react_fast_compare.default)(without(this.props, "helmetData"), without(nextProps, "helmetData"));
}
mapNestedChildrenToProps(child, nestedChildren) {
if (!nestedChildren) {
return null;
}
switch (child.type) {
case "script" /* SCRIPT */:
case "noscript" /* NOSCRIPT */:
return {
innerHTML: nestedChildren
};
case "style" /* STYLE */:
return {
cssText: nestedChildren
};
default:
throw new Error(
`<${child.type} /> elements are self-closing and can not contain children. Refer to our API for more information.`
);
}
}
flattenArrayTypeChildren(child, arrayTypeChildren, newChildProps, nestedChildren) {
return {
...arrayTypeChildren,
[child.type]: [
...arrayTypeChildren[child.type] || [],
{
...newChildProps,
...this.mapNestedChildrenToProps(child, nestedChildren)
}
]
};
}
mapObjectTypeChildren(child, newProps, newChildProps, nestedChildren) {
switch (child.type) {
case "title" /* TITLE */:
return {
...newProps,
[child.type]: nestedChildren,
titleAttributes: { ...newChildProps }
};
case "body" /* BODY */:
return {
...newProps,
bodyAttributes: { ...newChildProps }
};
case "html" /* HTML */:
return {
...newProps,
htmlAttributes: { ...newChildProps }
};
default:
return {
...newProps,
[child.type]: { ...newChildProps }
};
}
}
mapArrayTypeChildrenToProps(arrayTypeChildren, newProps) {
let newFlattenedProps = { ...newProps };
Object.keys(arrayTypeChildren).forEach((arrayChildName) => {
newFlattenedProps = {
...newFlattenedProps,
[arrayChildName]: arrayTypeChildren[arrayChildName]
};
});
return newFlattenedProps;
}
warnOnInvalidChildren(child, nestedChildren) {
(0, import_invariant.default)(
VALID_TAG_NAMES.some((name) => child.type === name),
typeof child.type === "function" ? `You may be attempting to nest <Helmet> components within each other, which is not allowed. Refer to our API for more information.` : `Only elements types ${VALID_TAG_NAMES.join(
", "
)} are allowed. Helmet does not support rendering <${child.type}> elements. Refer to our API for more information.`
);
(0, import_invariant.default)(
!nestedChildren || typeof nestedChildren === "string" || Array.isArray(nestedChildren) && !nestedChildren.some((nestedChild) => typeof nestedChild !== "string"),
`Helmet expects a string as a child of <${child.type}>. Did you forget to wrap your children in braces? ( <${child.type}>{\`\`}</${child.type}> ) Refer to our API for more information.`
);
return true;
}
mapChildrenToProps(children, newProps) {
let arrayTypeChildren = {};
import_react4.default.Children.forEach(children, (child) => {
if (!child || !child.props) {
return;
}
const { children: nestedChildren, ...childProps } = child.props;
const newChildProps = Object.keys(childProps).reduce((obj, key) => {
obj[HTML_TAG_MAP[key] || key] = childProps[key];
return obj;
}, {});
let { type } = child;
if (typeof type === "symbol") {
type = type.toString();
} else {
this.warnOnInvalidChildren(child, nestedChildren);
}
switch (type) {
case "Symbol(react.fragment)" /* FRAGMENT */:
newProps = this.mapChildrenToProps(nestedChildren, newProps);
break;
case "link" /* LINK */:
case "meta" /* META */:
case "noscript" /* NOSCRIPT */:
case "script" /* SCRIPT */:
case "style" /* STYLE */:
arrayTypeChildren = this.flattenArrayTypeChildren(
child,
arrayTypeChildren,
newChildProps,
nestedChildren
);
break;
default:
newProps = this.mapObjectTypeChildren(child, newProps, newChildProps, nestedChildren);
break;
}
});
return this.mapArrayTypeChildrenToProps(arrayTypeChildren, newProps);
}
render() {
const { children, ...props } = this.props;
let newProps = { ...props };
let { helmetData } = props;
if (children) {
newProps = this.mapChildrenToProps(children, newProps);
}
if (helmetData && !(helmetData instanceof HelmetData)) {
const data = helmetData;
helmetData = new HelmetData(data.context, true);
delete newProps.helmetData;
}
return helmetData ? /* @__PURE__ */ import_react4.default.createElement(HelmetDispatcher, { ...newProps, context: helmetData.value }) : /* @__PURE__ */ import_react4.default.createElement(Context.Consumer, null, (context) => /* @__PURE__ */ import_react4.default.createElement(HelmetDispatcher, { ...newProps, context }));
}
};
import type { PropsWithChildren } from 'react';
import React, { Component } from 'react';
import HelmetData from './HelmetData';
import type { HelmetServerState } from './types';
export declare const Context: React.Context<{}>;
interface ProviderProps {
context?: {
helmet?: HelmetServerState;
};
}
export default class HelmetProvider extends Component<PropsWithChildren<ProviderProps>> {
static canUseDOM: boolean;
helmetData: HelmetData;
constructor(props: PropsWithChildren<ProviderProps>);
render(): React.JSX.Element;
}
export {};
import type { MappedServerState } from './types';
declare const mapStateOnServer: (props: MappedServerState) => {
priority: {
toComponent: () => void;
toString: () => string;
};
base: {
toComponent: () => {};
toString: () => string;
};
bodyAttributes: {
toComponent: () => {};
toString: () => string;
};
htmlAttributes: {
toComponent: () => {};
toString: () => string;
};
link: {
toComponent: () => {};
toString: () => string;
};
meta: {
toComponent: () => {};
toString: () => string;
};
noscript: {
toComponent: () => {};
toString: () => string;
};
script: {
toComponent: () => {};
toString: () => string;
};
style: {
toComponent: () => {};
toString: () => string;
};
title: {
toComponent: () => {};
toString: () => string;
};
};
export default mapStateOnServer;
import type { HTMLAttributes, JSX } from 'react';
import type HelmetData from './HelmetData';
export type Attributes = {
[key: string]: string;
};
interface OtherElementAttributes {
[key: string]: string | number | boolean | null | undefined;
}
export type HtmlProps = JSX.IntrinsicElements['html'] & OtherElementAttributes;
export type BodyProps = JSX.IntrinsicElements['body'] & OtherElementAttributes;
export type LinkProps = JSX.IntrinsicElements['link'];
export type MetaProps = JSX.IntrinsicElements['meta'] & {
charset?: string | undefined;
'http-equiv'?: string | undefined;
itemprop?: string | undefined;
};
export type TitleProps = HTMLAttributes<HTMLTitleElement>;
export interface HelmetTags {
baseTag: HTMLBaseElement[];
linkTags: HTMLLinkElement[];
metaTags: HTMLMetaElement[];
noscriptTags: HTMLElement[];
scriptTags: HTMLScriptElement[];
styleTags: HTMLStyleElement[];
}
export interface HelmetDatum {
toString(): string;
toComponent(): React.Component<any>;
}
export interface HelmetHTMLBodyDatum {
toString(): string;
toComponent(): React.HTMLAttributes<HTMLBodyElement>;
}
export interface HelmetHTMLElementDatum {
toString(): string;
toComponent(): React.HTMLAttributes<HTMLHtmlElement>;
}
export interface HelmetServerState {
base: HelmetDatum;
bodyAttributes: HelmetHTMLBodyDatum;
htmlAttributes: HelmetHTMLElementDatum;
link: HelmetDatum;
meta: HelmetDatum;
noscript: HelmetDatum;
script: HelmetDatum;
style: HelmetDatum;
title: HelmetDatum;
titleAttributes: HelmetDatum;
priority: HelmetDatum;
}
export type MappedServerState = HelmetProps & HelmetTags & {
encode?: boolean;
};
export interface TagList {
[key: string]: HTMLElement[];
}
export interface StateUpdate extends HelmetTags {
bodyAttributes: BodyProps;
defer: boolean;
htmlAttributes: HtmlProps;
onChangeClientState: (newState: StateUpdate, addedTags: TagList, removedTags: TagList) => void;
title: string;
titleAttributes: TitleProps;
}
export interface HelmetProps {
async?: boolean;
base?: Attributes;
bodyAttributes?: BodyProps;
defaultTitle?: string;
defer?: boolean;
encodeSpecialCharacters?: boolean;
helmetData?: HelmetData;
htmlAttributes?: HtmlProps;
onChangeClientState?: (newState: StateUpdate, addedTags: HelmetTags, removedTags: HelmetTags) => void;
link?: LinkProps[];
meta?: MetaProps[];
noscript?: Attributes[];
script?: Attributes[];
style?: Attributes[];
title?: string;
titleAttributes?: Attributes;
titleTemplate?: string;
prioritizeSeoTags?: boolean;
}
export {};
interface PropList {
[key: string]: any;
}
type PropsList = PropList[];
type AttributeList = string[];
interface MatchProps {
[key: string]: string | AttributeList;
}
declare const reducePropsToState: (propsList: PropsList) => {
baseTag: any;
bodyAttributes: any;
defer: any;
encode: any;
htmlAttributes: any;
linkTags: any;
metaTags: any;
noscriptTags: any;
onChangeClientState: any;
scriptTags: any;
styleTags: any;
title: any;
titleAttributes: any;
prioritizeSeoTags: boolean;
};
export declare const flattenArray: (possibleArray: string[] | string) => string;
export { reducePropsToState };
export declare const prioritizer: (elementsList: HTMLElement[], propsToMatch: MatchProps) => {
priority: HTMLElement[];
default: HTMLElement[];
};
export declare const without: (obj: PropList, key: string) => {
[x: string]: any;
};
/// <reference types="vite/client" />
import { defineConfig } from 'vite';
import type { UserConfig } from 'vite';
import type { InlineConfig } from 'vitest';
import react from '@vitejs/plugin-react';
interface VitestConfigExport extends UserConfig {
test: InlineConfig;
}
export default defineConfig({
plugins: [react()],
test: {
globals: true,
environment: 'jsdom',
setupFiles: ['./__tests__/setup-test-env.ts'],
},
} as VitestConfigExport);