Files
openclaw/scripts/lib/mintlify-accordion.mjs
2026-04-28 08:13:21 +01:00

94 lines
2.7 KiB
JavaScript

export const MINTLIFY_ACCORDION_INDENT_MESSAGE =
"Mintlify component closing tag is indented deeper than its opening tag; Mintlify can parse following markdown as nested content.";
const MINTLIFY_REPAIRED_COMPONENTS = new Set([
"Accordion",
"Warning",
"Note",
"Tip",
"ParamField",
"Steps",
"Step",
]);
function visitMintlifyComponentIndentation(raw, onMisindentedClose, onMisindentedOpen) {
const lines = raw.split(/\r?\n/u);
const componentStack = [];
let inCodeFence = false;
for (let index = 0; index < lines.length; index += 1) {
const line = lines[index];
if (/^\s*(```|~~~)/u.test(line)) {
inCodeFence = !inCodeFence;
continue;
}
if (inCodeFence) {
continue;
}
const openComponent = line.match(/^(\s*)<([A-Z][A-Za-z0-9]*)\b/u);
if (openComponent && MINTLIFY_REPAIRED_COMPONENTS.has(openComponent[2])) {
let indent = openComponent[1].length;
if (componentStack.length === 0 && openComponent[2] === "ParamField" && indent > 0) {
onMisindentedOpen?.({ openComponent, index, line, lines });
indent = 0;
}
componentStack.push({
indent,
name: openComponent[2],
});
continue;
}
const closeComponent = line.match(/^(\s*)<\/([A-Z][A-Za-z0-9]*)>/u);
if (!closeComponent || !MINTLIFY_REPAIRED_COMPONENTS.has(closeComponent[2])) {
continue;
}
const opening = componentStack.pop();
if (opening?.name === closeComponent[2] && closeComponent[1].length > opening.indent) {
onMisindentedClose({ closeComponent, index, line, lines, opening });
}
}
return lines;
}
export function checkMintlifyAccordionIndentation(raw) {
const errors = [];
visitMintlifyComponentIndentation(raw, ({ closeComponent, index }) => {
errors.push({
line: index + 1,
column: closeComponent[1].length + 1,
message: MINTLIFY_ACCORDION_INDENT_MESSAGE,
});
});
return errors;
}
export function repairMintlifyAccordionIndentation(raw) {
let changed = false;
const lines = visitMintlifyComponentIndentation(
raw,
({ closeComponent, index, line, lines, opening }) => {
lines[index] = `${" ".repeat(opening.indent)}${line.slice(closeComponent[1].length)}`;
changed = true;
},
({ openComponent, index, line, lines }) => {
lines[index] = line.slice(openComponent[1].length);
changed = true;
},
);
for (let index = lines.length - 1; index > 0; index--) {
if (!/^\s*<\/[A-Z][A-Za-z0-9]*>/u.test(lines[index])) {
continue;
}
if (!/^\s*[-*+]\s+/u.test(lines[index - 1])) {
continue;
}
lines.splice(index, 0, "");
changed = true;
}
return changed ? lines.join("\n") : raw;
}