@intlayer/core
Version:
Includes core Intlayer functions like translation, dictionary, and utility functions shared across multiple packages.
123 lines (122 loc) • 3.77 kB
JavaScript
//#region src/interpreter/getHTML.ts
/**
* Helper to parse attributes from a tag string.
*/
const parseAttributes = (attributes) => {
const props = {};
const attrRegex = /([a-zA-Z0-9-]+)="([^"]*)"/g;
let match = attrRegex.exec(attributes);
while (match !== null) {
props[match[1]] = match[2];
match = attrRegex.exec(attributes);
}
return props;
};
const astCache = /* @__PURE__ */ new Map();
const parseHTML = (content) => {
if (astCache.has(content)) return astCache.get(content);
if (typeof content !== "string") return [];
const tagRegex = /<(\/)?([a-zA-Z0-9.-]+)([\s\S]*?)(\/?)>/g;
const elements = [];
const stack = [];
let lastIndex = 0;
let match = tagRegex.exec(content);
const appendChild = (child) => {
(stack.length > 0 ? stack[stack.length - 1].children : elements).push(child);
};
while (match !== null) {
const [fullMatch, isClosingRaw, tagName, attributesRaw, isSelfClosingRaw] = match;
const matchIndex = match.index;
if (matchIndex > lastIndex) appendChild(content.slice(lastIndex, matchIndex));
const isClosing = isClosingRaw === "/";
const isSelfClosing = isSelfClosingRaw === "/" || attributesRaw.trim().endsWith("/") || fullMatch.endsWith("/>");
const cleanedAttributes = attributesRaw.trim().replace(/\/$/, "").trim();
if (isClosing) {
const last = stack[stack.length - 1];
if (last && last.tagName === tagName) {
const popped = stack.pop();
if (popped) appendChild({
tagName: popped.tagName,
props: popped.props,
children: popped.children
});
}
} else if (isSelfClosing) appendChild({
tagName,
props: parseAttributes(cleanedAttributes),
children: []
});
else {
const tagProps = parseAttributes(cleanedAttributes);
stack.push({
tagName,
children: [],
props: tagProps
});
}
lastIndex = matchIndex + fullMatch.length;
match = tagRegex.exec(content);
}
if (lastIndex < content.length) appendChild(content.slice(lastIndex));
while (stack.length > 0) {
const last = stack.pop();
if (last) appendChild({
tagName: last.tagName,
props: last.props,
children: last.children
});
}
astCache.set(content, elements);
return elements;
};
/**
* Interprets a string containing HTML-like tags and replaces them with provided components or strings.
*/
const getHTML = (content, values) => {
const ast = parseHTML(content);
let keyCounter = 0;
const renderASTNode = (node) => {
if (typeof node === "string") return node;
const { tagName, props, children } = node;
const renderedChildren = children.flatMap(renderASTNode);
const index = keyCounter++;
let override = values[tagName];
if (!override) {
const lowerTagName = tagName.toLowerCase();
const foundKey = Object.keys(values).find((key) => key.toLowerCase() === lowerTagName);
if (foundKey) override = values[foundKey];
}
const key = `html-tag-${tagName}-${index}`;
if (typeof override === "function") return override({
...props,
children: renderedChildren,
key
});
if (typeof override === "string") {
const component = values[override];
if (typeof component === "function") return component({
...props,
children: renderedChildren,
key
});
return renderedChildren;
}
if (typeof override === "object" && override !== null && "tag" in override) {
const { tag: targetTag, props: extraProps } = override;
const component = values[targetTag];
if (typeof component === "function") return component({
...props,
...extraProps,
children: renderedChildren,
key
});
return renderedChildren;
}
return renderedChildren;
};
const result = ast.flatMap(renderASTNode);
return result.length === 1 ? result[0] : result;
};
//#endregion
export { getHTML };
//# sourceMappingURL=getHTML.mjs.map