@wordpress/element
Version:
Element React module for WordPress.
173 lines (172 loc) • 4.96 kB
JavaScript
// packages/element/src/create-interpolate-element.ts
import {
createElement,
cloneElement,
Fragment,
isValidElement
} from "./react.mjs";
var indoc;
var offset;
var output;
var stack;
var tokenizer = /<(\/)?(\w+)\s*(\/)/g;
function createFrame(element, tokenStart, tokenLength, prevOffset, leadingTextStart) {
return {
element,
tokenStart,
tokenLength,
prevOffset,
leadingTextStart,
children: []
};
}
function createInterpolateElement(interpolatedString, conversionMap) {
indoc = interpolatedString;
offset = 0;
output = [];
stack = [];
tokenizer.lastIndex = 0;
if (!isValidConversionMap(conversionMap)) {
throw new TypeError(
"The conversionMap provided is not valid. It must be an object with values that are React Elements"
);
}
do {
} while (proceed(conversionMap));
return createElement(Fragment, null, ...output);
}
var isValidConversionMap = (conversionMap) => {
const isObject = typeof conversionMap === "object" && conversionMap !== null;
const values = isObject && Object.values(conversionMap);
return isObject && values.length > 0 && values.every((element) => isValidElement(element));
};
function proceed(conversionMap) {
const next = nextToken();
const [tokenType, name, startOffset, tokenLength] = next;
const stackDepth = stack.length;
const leadingTextStart = startOffset > offset ? offset : null;
if (name && !conversionMap[name]) {
addText();
return false;
}
switch (tokenType) {
case "no-more-tokens":
if (stackDepth !== 0) {
const { leadingTextStart: stackLeadingText, tokenStart } = stack.pop();
output.push(indoc.substr(stackLeadingText, tokenStart));
}
addText();
return false;
case "self-closed":
if (0 === stackDepth) {
if (null !== leadingTextStart) {
output.push(
indoc.substr(
leadingTextStart,
startOffset - leadingTextStart
)
);
}
output.push(conversionMap[name]);
offset = startOffset + tokenLength;
return true;
}
addChild(
createFrame(conversionMap[name], startOffset, tokenLength)
);
offset = startOffset + tokenLength;
return true;
case "opener":
stack.push(
createFrame(
conversionMap[name],
startOffset,
tokenLength,
startOffset + tokenLength,
leadingTextStart
)
);
offset = startOffset + tokenLength;
return true;
case "closer":
if (1 === stackDepth) {
closeOuterElement(startOffset);
offset = startOffset + tokenLength;
return true;
}
const stackTop = stack.pop();
const text = indoc.substr(
stackTop.prevOffset,
startOffset - stackTop.prevOffset
);
stackTop.children.push(text);
stackTop.prevOffset = startOffset + tokenLength;
const frame = createFrame(
stackTop.element,
stackTop.tokenStart,
stackTop.tokenLength,
startOffset + tokenLength
);
frame.children = stackTop.children;
addChild(frame);
offset = startOffset + tokenLength;
return true;
default:
addText();
return false;
}
}
function nextToken() {
const matches = tokenizer.exec(indoc);
if (null === matches) {
return ["no-more-tokens"];
}
const startedAt = matches.index;
const [match, isClosing, name, isSelfClosed] = matches;
const length = match.length;
if (isSelfClosed) {
return ["self-closed", name, startedAt, length];
}
if (isClosing) {
return ["closer", name, startedAt, length];
}
return ["opener", name, startedAt, length];
}
function addText() {
const length = indoc.length - offset;
if (0 === length) {
return;
}
output.push(indoc.substr(offset, length));
}
function addChild(frame) {
const { element, tokenStart, tokenLength, prevOffset, children } = frame;
const parent = stack[stack.length - 1];
const text = indoc.substr(
parent.prevOffset,
tokenStart - parent.prevOffset
);
if (text) {
parent.children.push(text);
}
parent.children.push(cloneElement(element, null, ...children));
parent.prevOffset = prevOffset ? prevOffset : tokenStart + tokenLength;
}
function closeOuterElement(endOffset) {
const { element, leadingTextStart, prevOffset, tokenStart, children } = stack.pop();
const text = endOffset ? indoc.substr(prevOffset, endOffset - prevOffset) : indoc.substr(prevOffset);
if (text) {
children.push(text);
}
if (null !== leadingTextStart) {
output.push(
indoc.substr(leadingTextStart, tokenStart - leadingTextStart)
);
}
output.push(cloneElement(element, null, ...children));
}
var create_interpolate_element_default = createInterpolateElement;
export {
create_interpolate_element_default as default
};
//# sourceMappingURL=create-interpolate-element.mjs.map