This commit is contained in:
2024-03-22 03:47:51 +05:30
parent 8bcf3d211e
commit 89819f6fe2
28440 changed files with 3211033 additions and 2 deletions

69
node_modules/react-helmet-async/src/Dispatcher.js generated vendored Normal file
View File

@@ -0,0 +1,69 @@
import { Component } from 'react';
import shallowEqual from 'shallowequal';
import handleStateChangeOnClient from './client';
import mapStateOnServer from './server';
import { reducePropsToState } from './utils';
import Provider, { providerShape } from './Provider';
export default class Dispatcher extends Component {
static propTypes = {
context: providerShape.isRequired,
};
static displayName = 'HelmetDispatcher';
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 (Provider.canUseDOM) {
handleStateChangeOnClient(state);
} else if (mapStateOnServer) {
serverState = mapStateOnServer(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;
}
}

48
node_modules/react-helmet-async/src/HelmetData.js generated vendored Normal file
View File

@@ -0,0 +1,48 @@
import mapStateOnServer from './server';
const instances = [];
export function clearInstances() {
instances.length = 0;
}
export default class HelmetData {
instances = [];
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 = typeof document !== 'undefined') {
this.context = context;
this.canUseDOM = canUseDOM;
if (!canUseDOM) {
context.helmet = mapStateOnServer({
baseTag: [],
bodyAttributes: {},
encodeSpecialCharacters: true,
htmlAttributes: {},
linkTags: [],
metaTags: [],
noscriptTags: [],
scriptTags: [],
styleTags: [],
title: '',
titleAttributes: {},
});
}
}
}

45
node_modules/react-helmet-async/src/Provider.js generated vendored Normal file
View File

@@ -0,0 +1,45 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import HelmetData from './HelmetData';
const defaultValue = {};
export const Context = React.createContext(defaultValue);
export const providerShape = PropTypes.shape({
setHelmet: PropTypes.func,
helmetInstances: PropTypes.shape({
get: PropTypes.func,
add: PropTypes.func,
remove: PropTypes.func,
}),
});
const canUseDOM = typeof document !== 'undefined';
export default class Provider extends Component {
static canUseDOM = canUseDOM;
static propTypes = {
context: PropTypes.shape({
helmet: PropTypes.shape(),
}),
children: PropTypes.node.isRequired,
};
static defaultProps = {
context: {},
};
static displayName = 'HelmetProvider';
constructor(props) {
super(props);
this.helmetData = new HelmetData(this.props.context, Provider.canUseDOM);
}
render() {
return <Context.Provider value={this.helmetData.value}>{this.props.children}</Context.Provider>;
}
}

176
node_modules/react-helmet-async/src/client.js generated vendored Normal file
View File

@@ -0,0 +1,176 @@
import { HELMET_ATTRIBUTE, TAG_NAMES, TAG_PROPERTIES } from './constants';
import { flattenArray } from './utils';
const updateTags = (type, tags) => {
const headElement = document.head || document.querySelector(TAG_NAMES.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);
// eslint-disable-next-line
for (const attribute in tag) {
if (Object.prototype.hasOwnProperty.call(tag, attribute)) {
if (attribute === TAG_PROPERTIES.INNER_HTML) {
newElement.innerHTML = tag.innerHTML;
} else if (attribute === TAG_PROPERTIES.CSS_TEXT) {
if (newElement.styleSheet) {
newElement.styleSheet.cssText = tag.cssText;
} else {
newElement.appendChild(document.createTextNode(tag.cssText));
}
} else {
const value = typeof tag[attribute] === 'undefined' ? '' : tag[attribute];
newElement.setAttribute(attribute, value);
}
}
}
newElement.setAttribute(HELMET_ATTRIBUTE, 'true');
// Remove a duplicate tag from domTagstoRemove, so it isn't cleared.
if (
oldTags.some((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,
};
};
const 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 = [].concat(helmetAttributes);
const attributeKeys = Object.keys(attributes);
for (let i = 0; i < attributeKeys.length; i += 1) {
const attribute = attributeKeys[i];
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(','));
}
};
const updateTitle = (title, attributes) => {
if (typeof title !== 'undefined' && document.title !== title) {
document.title = flattenArray(title);
}
updateAttributes(TAG_NAMES.TITLE, attributes);
};
const commitTagChanges = (newState, cb) => {
const {
baseTag,
bodyAttributes,
htmlAttributes,
linkTags,
metaTags,
noscriptTags,
onChangeClientState,
scriptTags,
styleTags,
title,
titleAttributes,
} = newState;
updateAttributes(TAG_NAMES.BODY, bodyAttributes);
updateAttributes(TAG_NAMES.HTML, htmlAttributes);
updateTitle(title, titleAttributes);
const tagUpdates = {
baseTag: updateTags(TAG_NAMES.BASE, baseTag),
linkTags: updateTags(TAG_NAMES.LINK, linkTags),
metaTags: updateTags(TAG_NAMES.META, metaTags),
noscriptTags: updateTags(TAG_NAMES.NOSCRIPT, noscriptTags),
scriptTags: updateTags(TAG_NAMES.SCRIPT, scriptTags),
styleTags: updateTags(TAG_NAMES.STYLE, styleTags),
};
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);
};
// eslint-disable-next-line
let _helmetCallback = null;
const handleStateChangeOnClient = newState => {
if (_helmetCallback) {
cancelAnimationFrame(_helmetCallback);
}
if (newState.defer) {
_helmetCallback = requestAnimationFrame(() => {
commitTagChanges(newState, () => {
_helmetCallback = null;
});
});
} else {
commitTagChanges(newState);
_helmetCallback = null;
}
};
export default handleStateChangeOnClient;

76
node_modules/react-helmet-async/src/constants.js generated vendored Normal file
View File

@@ -0,0 +1,76 @@
export const 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 const ATTRIBUTE_NAMES = {
BODY: 'bodyAttributes',
HTML: 'htmlAttributes',
TITLE: 'titleAttributes',
};
export const 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 const SEO_PRIORITY_TAGS = {
link: { rel: ['amphtml', 'canonical', 'alternate'] },
script: { type: ['application/ld+json'] },
meta: {
charset: '',
name: ['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.keys(TAG_NAMES).map(name => TAG_NAMES[name]);
export const REACT_TAG_MAP = {
accesskey: 'accessKey',
charset: 'charSet',
class: 'className',
contenteditable: 'contentEditable',
contextmenu: 'contextMenu',
'http-equiv': 'httpEquiv',
itemprop: 'itemProp',
tabindex: 'tabIndex',
};
export const HTML_TAG_MAP = Object.keys(REACT_TAG_MAP).reduce((obj, key) => {
obj[REACT_TAG_MAP[key]] = key;
return obj;
}, {});
export const HELMET_ATTRIBUTE = 'data-rh';

247
node_modules/react-helmet-async/src/index.js generated vendored Normal file
View File

@@ -0,0 +1,247 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import fastCompare from 'react-fast-compare';
import invariant from 'invariant';
import { Context } from './Provider';
import HelmetData from './HelmetData';
import Dispatcher from './Dispatcher';
import { without } from './utils';
import { TAG_NAMES, VALID_TAG_NAMES, HTML_TAG_MAP } from './constants';
export { default as HelmetData } from './HelmetData';
export { default as HelmetProvider } from './Provider';
/* eslint-disable class-methods-use-this */
export class Helmet extends Component {
/**
* @param {Object} base: {"target": "_blank", "href": "http://mysite.com/"}
* @param {Object} bodyAttributes: {"className": "root"}
* @param {String} defaultTitle: "Default Title"
* @param {Boolean} defer: true
* @param {Boolean} encodeSpecialCharacters: true
* @param {Object} htmlAttributes: {"lang": "en", "amp": undefined}
* @param {Array} link: [{"rel": "canonical", "href": "http://mysite.com/example"}]
* @param {Array} meta: [{"name": "description", "content": "Test description"}]
* @param {Array} noscript: [{"innerHTML": "<img src='http://mysite.com/js/test.js'"}]
* @param {Function} onChangeClientState: "(newState) => console.log(newState)"
* @param {Array} script: [{"type": "text/javascript", "src": "http://mysite.com/js/test.js"}]
* @param {Array} style: [{"type": "text/css", "cssText": "div { display: block; color: blue; }"}]
* @param {String} title: "Title"
* @param {Object} titleAttributes: {"itemprop": "name"}
* @param {String} titleTemplate: "MySite.com - %s"
* @param {Boolean} prioritizeSeoTags: false
*/
/* eslint-disable react/forbid-prop-types, react/require-default-props */
static propTypes = {
base: PropTypes.object,
bodyAttributes: PropTypes.object,
children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]),
defaultTitle: PropTypes.string,
defer: PropTypes.bool,
encodeSpecialCharacters: PropTypes.bool,
htmlAttributes: PropTypes.object,
link: PropTypes.arrayOf(PropTypes.object),
meta: PropTypes.arrayOf(PropTypes.object),
noscript: PropTypes.arrayOf(PropTypes.object),
onChangeClientState: PropTypes.func,
script: PropTypes.arrayOf(PropTypes.object),
style: PropTypes.arrayOf(PropTypes.object),
title: PropTypes.string,
titleAttributes: PropTypes.object,
titleTemplate: PropTypes.string,
prioritizeSeoTags: PropTypes.bool,
helmetData: PropTypes.object,
};
/* eslint-enable react/prop-types, react/forbid-prop-types, react/require-default-props */
static defaultProps = {
defer: true,
encodeSpecialCharacters: true,
prioritizeSeoTags: false,
};
static displayName = 'Helmet';
shouldComponentUpdate(nextProps) {
return !fastCompare(without(this.props, 'helmetData'), without(nextProps, 'helmetData'));
}
mapNestedChildrenToProps(child, nestedChildren) {
if (!nestedChildren) {
return null;
}
switch (child.type) {
case TAG_NAMES.SCRIPT:
case TAG_NAMES.NOSCRIPT:
return {
innerHTML: nestedChildren,
};
case TAG_NAMES.STYLE:
return {
cssText: nestedChildren,
};
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 TAG_NAMES.TITLE:
return {
...newProps,
[child.type]: nestedChildren,
titleAttributes: { ...newChildProps },
};
case TAG_NAMES.BODY:
return {
...newProps,
bodyAttributes: { ...newChildProps },
};
case TAG_NAMES.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 = {};
React.Children.forEach(children, child => {
if (!child || !child.props) {
return;
}
const { children: nestedChildren, ...childProps } = child.props;
// convert React props to HTML attributes
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 TAG_NAMES.FRAGMENT:
newProps = this.mapChildrenToProps(nestedChildren, newProps);
break;
case TAG_NAMES.LINK:
case TAG_NAMES.META:
case TAG_NAMES.NOSCRIPT:
case TAG_NAMES.SCRIPT:
case TAG_NAMES.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)) {
helmetData = new HelmetData(helmetData.context, helmetData.instances);
}
return helmetData ? (
// eslint-disable-next-line react/jsx-props-no-spreading
<Dispatcher {...newProps} context={helmetData.value} helmetData={undefined} />
) : (
<Context.Consumer>
{(
context // eslint-disable-next-line react/jsx-props-no-spreading
) => <Dispatcher {...newProps} context={context} />}
</Context.Consumer>
);
}
}

197
node_modules/react-helmet-async/src/server.js generated vendored Normal file
View File

@@ -0,0 +1,197 @@
import React from 'react';
import {
HELMET_ATTRIBUTE,
TAG_NAMES,
REACT_TAG_MAP,
TAG_PROPERTIES,
ATTRIBUTE_NAMES,
SEO_PRIORITY_TAGS,
} from './constants';
import { flattenArray, prioritizer } from './utils';
const SELF_CLOSING_TAGS = [TAG_NAMES.NOSCRIPT, TAG_NAMES.SCRIPT, TAG_NAMES.STYLE];
const 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;');
};
const generateElementAttributesAsString = attributes =>
Object.keys(attributes).reduce((str, key) => {
const attr = typeof attributes[key] !== 'undefined' ? `${key}="${attributes[key]}"` : `${key}`;
return str ? `${str} ${attr}` : attr;
}, '');
const 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}>`;
};
const generateTagsAsString = (type, tags, encode) =>
tags.reduce((str, tag) => {
const attributeHtml = Object.keys(tag)
.filter(
attribute =>
!(attribute === TAG_PROPERTIES.INNER_HTML || attribute === TAG_PROPERTIES.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}>`
}`;
}, '');
const convertElementAttributesToReactProps = (attributes, initProps = {}) =>
Object.keys(attributes).reduce((obj, key) => {
obj[REACT_TAG_MAP[key] || key] = attributes[key];
return obj;
}, initProps);
const generateTitleAsReactComponent = (type, title, attributes) => {
// assigning into an array to define toString function on it
const initProps = {
key: title,
[HELMET_ATTRIBUTE]: true,
};
const props = convertElementAttributesToReactProps(attributes, initProps);
return [React.createElement(TAG_NAMES.TITLE, props, title)];
};
const generateTagsAsReactComponent = (type, tags) =>
tags.map((tag, i) => {
const mappedTag = {
key: i,
[HELMET_ATTRIBUTE]: true,
};
Object.keys(tag).forEach(attribute => {
const mappedAttribute = REACT_TAG_MAP[attribute] || attribute;
if (
mappedAttribute === TAG_PROPERTIES.INNER_HTML ||
mappedAttribute === TAG_PROPERTIES.CSS_TEXT
) {
const content = tag.innerHTML || tag.cssText;
mappedTag.dangerouslySetInnerHTML = { __html: content };
} else {
mappedTag[mappedAttribute] = tag[attribute];
}
});
return React.createElement(type, mappedTag);
});
const getMethodsForTag = (type, tags, encode) => {
switch (type) {
case TAG_NAMES.TITLE:
return {
toComponent: () =>
generateTitleAsReactComponent(type, tags.title, tags.titleAttributes, encode),
toString: () => generateTitleAsString(type, tags.title, tags.titleAttributes, encode),
};
case ATTRIBUTE_NAMES.BODY:
case ATTRIBUTE_NAMES.HTML:
return {
toComponent: () => convertElementAttributesToReactProps(tags),
toString: () => generateElementAttributesAsString(tags),
};
default:
return {
toComponent: () => generateTagsAsReactComponent(type, tags),
toString: () => generateTagsAsString(type, tags, encode),
};
}
};
const 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);
// need to have toComponent() and toString()
const priorityMethods = {
toComponent: () => [
...generateTagsAsReactComponent(TAG_NAMES.META, meta.priority),
...generateTagsAsReactComponent(TAG_NAMES.LINK, link.priority),
...generateTagsAsReactComponent(TAG_NAMES.SCRIPT, script.priority),
],
toString: () =>
// generate all the tags as strings and concatenate them
`${getMethodsForTag(TAG_NAMES.META, meta.priority, encode)} ${getMethodsForTag(
TAG_NAMES.LINK,
link.priority,
encode
)} ${getMethodsForTag(TAG_NAMES.SCRIPT, script.priority, encode)}`,
};
return {
priorityMethods,
metaTags: meta.default,
linkTags: link.default,
scriptTags: script.default,
};
};
const mapStateOnServer = props => {
const {
baseTag,
bodyAttributes,
encode,
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(TAG_NAMES.BASE, baseTag, encode),
bodyAttributes: getMethodsForTag(ATTRIBUTE_NAMES.BODY, bodyAttributes, encode),
htmlAttributes: getMethodsForTag(ATTRIBUTE_NAMES.HTML, htmlAttributes, encode),
link: getMethodsForTag(TAG_NAMES.LINK, linkTags, encode),
meta: getMethodsForTag(TAG_NAMES.META, metaTags, encode),
noscript: getMethodsForTag(TAG_NAMES.NOSCRIPT, noscriptTags, encode),
script: getMethodsForTag(TAG_NAMES.SCRIPT, scriptTags, encode),
style: getMethodsForTag(TAG_NAMES.STYLE, styleTags, encode),
title: getMethodsForTag(TAG_NAMES.TITLE, { title, titleAttributes }, encode),
};
};
export default mapStateOnServer;

259
node_modules/react-helmet-async/src/utils.js generated vendored Normal file
View File

@@ -0,0 +1,259 @@
import { TAG_NAMES, TAG_PROPERTIES, ATTRIBUTE_NAMES } from './constants';
const HELMET_PROPS = {
DEFAULT_TITLE: 'defaultTitle',
DEFER: 'defer',
ENCODE_SPECIAL_CHARACTERS: 'encodeSpecialCharacters',
ON_CHANGE_CLIENT_STATE: 'onChangeClientState',
TITLE_TEMPLATE: 'titleTemplate',
PRIORITIZE_SEO_TAGS: 'prioritizeSeoTags',
};
const 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;
};
const getTitleFromPropsList = propsList => {
let innermostTitle = getInnermostProperty(propsList, TAG_NAMES.TITLE);
const innermostTemplate = getInnermostProperty(propsList, HELMET_PROPS.TITLE_TEMPLATE);
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(propsList, HELMET_PROPS.DEFAULT_TITLE);
return innermostTitle || innermostDefaultTitle || undefined;
};
const getOnChangeClientState = propsList =>
getInnermostProperty(propsList, HELMET_PROPS.ON_CHANGE_CLIENT_STATE) || (() => {});
const getAttributesFromPropsList = (tagType, propsList) =>
propsList
.filter(props => typeof props[tagType] !== 'undefined')
.map(props => props[tagType])
.reduce((tagAttrs, current) => ({ ...tagAttrs, ...current }), {});
const getBaseTagFromPropsList = (primaryAttributes, propsList) =>
propsList
.filter(props => typeof props[TAG_NAMES.BASE] !== 'undefined')
.map(props => props[TAG_NAMES.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;
}, []);
// eslint-disable-next-line no-console
const warn = msg => console && typeof console.warn === 'function' && console.warn(msg);
const getTagsFromPropsList = (tagName, primaryAttributes, propsList) => {
// Calculate list of tags, giving priority innermost component (end of the 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 keys = Object.keys(tag);
for (let i = 0; i < keys.length; i += 1) {
const attributeKey = keys[i];
const lowerCaseAttributeKey = attributeKey.toLowerCase();
// Special rule with link tags, since rel and href are both primary tags, rel takes priority
if (
primaryAttributes.indexOf(lowerCaseAttributeKey) !== -1 &&
!(
primaryAttributeKey === TAG_PROPERTIES.REL &&
tag[primaryAttributeKey].toLowerCase() === 'canonical'
) &&
!(
lowerCaseAttributeKey === TAG_PROPERTIES.REL &&
tag[lowerCaseAttributeKey].toLowerCase() === 'stylesheet'
)
) {
primaryAttributeKey = lowerCaseAttributeKey;
}
// Special case for innerHTML which doesn't work lowercased
if (
primaryAttributes.indexOf(attributeKey) !== -1 &&
(attributeKey === TAG_PROPERTIES.INNER_HTML ||
attributeKey === TAG_PROPERTIES.CSS_TEXT ||
attributeKey === TAG_PROPERTIES.ITEM_PROP)
) {
primaryAttributeKey = attributeKey;
}
}
if (!primaryAttributeKey || !tag[primaryAttributeKey]) {
return false;
}
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));
// Update seen tags with tags from this instance
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();
};
const 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;
};
const reducePropsToState = propsList => ({
baseTag: getBaseTagFromPropsList([TAG_PROPERTIES.HREF], propsList),
bodyAttributes: getAttributesFromPropsList(ATTRIBUTE_NAMES.BODY, propsList),
defer: getInnermostProperty(propsList, HELMET_PROPS.DEFER),
encode: getInnermostProperty(propsList, HELMET_PROPS.ENCODE_SPECIAL_CHARACTERS),
htmlAttributes: getAttributesFromPropsList(ATTRIBUTE_NAMES.HTML, propsList),
linkTags: getTagsFromPropsList(
TAG_NAMES.LINK,
[TAG_PROPERTIES.REL, TAG_PROPERTIES.HREF],
propsList
),
metaTags: getTagsFromPropsList(
TAG_NAMES.META,
[
TAG_PROPERTIES.NAME,
TAG_PROPERTIES.CHARSET,
TAG_PROPERTIES.HTTPEQUIV,
TAG_PROPERTIES.PROPERTY,
TAG_PROPERTIES.ITEM_PROP,
],
propsList
),
noscriptTags: getTagsFromPropsList(TAG_NAMES.NOSCRIPT, [TAG_PROPERTIES.INNER_HTML], propsList),
onChangeClientState: getOnChangeClientState(propsList),
scriptTags: getTagsFromPropsList(
TAG_NAMES.SCRIPT,
[TAG_PROPERTIES.SRC, TAG_PROPERTIES.INNER_HTML],
propsList
),
styleTags: getTagsFromPropsList(TAG_NAMES.STYLE, [TAG_PROPERTIES.CSS_TEXT], propsList),
title: getTitleFromPropsList(propsList),
titleAttributes: getAttributesFromPropsList(ATTRIBUTE_NAMES.TITLE, propsList),
prioritizeSeoTags: getAnyTrueFromPropsList(propsList, HELMET_PROPS.PRIORITIZE_SEO_TAGS),
});
export const flattenArray = possibleArray =>
Array.isArray(possibleArray) ? possibleArray.join('') : possibleArray;
export { reducePropsToState };
const checkIfPropsMatch = (props, toMatch) => {
const keys = Object.keys(props);
for (let i = 0; i < keys.length; i += 1) {
// e.g. if rel exists in the list of allowed props [amphtml, alternate, etc]
if (toMatch[keys[i]] && toMatch[keys[i]].includes(props[keys[i]])) {
return true;
}
}
return false;
};
export const 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 };
};
export const without = (obj, key) => {
return {
...obj,
[key]: undefined,
};
};