@dr.pogodin/react-helmet
Version:
Thread-safe Helmet for React 19+ and friends
224 lines (221 loc) • 7.66 kB
JavaScript
;
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 = (str, encode = true) => {
if (!encode) return str;
return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''');
};
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;
let attr;
if (value === undefined) attr = attrName;else {
// Perhaps, if `value` is not a string we should just do
// "attr = attrName", as in the case just above. Playing
// it safe now, to avoid changes from the original code.
const valStr = typeof value === 'string' ? value : String(value);
attr = `${attrName}="${encodeSpecialCharacters(valStr, 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, ops = {}) {
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 newServerState(heap) {
// TODO: Should this function to be attached to the heap itself?
const getState = () => {
// eslint-disable-next-line no-param-reassign
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