blob: d4f6c26384cf3c44a61d731f3f533845eccf9fcc [file]
#!/usr/bin/env node
const fs = require('fs');
const path = require('path');
const docsRoot = path.resolve(__dirname, '../docs');
// Find all .mdx files
function findMdx(dir) {
const results = [];
for (const entry of fs.readdirSync(dir, {withFileTypes: true})) {
const full = path.join(dir, entry.name);
if (entry.isDirectory()) results.push(...findMdx(full));
else if (entry.name.endsWith('.mdx')) results.push(full);
}
return results;
}
// Valid JSX/HTML tags that should NOT be escaped
const validTags = new Set([
'Tabs', 'TabItem', 'Stable', 'Unstable', 'ConfigTable', 'Label', 'AllVersions',
'div', 'span', 'img', 'a', 'br', 'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
'table', 'thead', 'tbody', 'tr', 'td', 'th', 'ul', 'ol', 'li',
'strong', 'em', 'code', 'pre', 'blockquote', 'hr', 'sup', 'sub',
'details', 'summary', 'iframe',
]);
function isValidTag(tag) {
// Check opening or self-closing
const name = tag.replace(/^\//, '').split(/[\s/]/)[0];
return validTags.has(name);
}
function processFile(filePath) {
let content = fs.readFileSync(filePath, 'utf-8');
const lines = content.split('\n');
let inCodeFence = false;
let modified = false;
const processed = lines.map(line => {
// Track code fences
if (line.match(/^```/)) {
inCodeFence = !inCodeFence;
return line;
}
if (inCodeFence) return line;
// Skip import lines
if (line.startsWith('import ')) return line;
// Skip lines that are pure JSX components (start with < and a valid tag)
if (line.trim().startsWith('<') && line.trim().match(/^<\/?(Tabs|TabItem|Stable|Unstable|ConfigTable|Label|AllVersions|div|img|iframe)/)) {
return line;
}
// Process the line: escape < that don't start valid tags
let result = '';
let inInlineCode = false;
let i = 0;
while (i < line.length) {
if (line[i] === '`') {
inInlineCode = !inInlineCode;
result += line[i];
i++;
continue;
}
if (inInlineCode) {
result += line[i];
i++;
continue;
}
if (line[i] === '<') {
// Look ahead to determine if this is a valid tag
const rest = line.slice(i + 1);
const tagMatch = rest.match(/^(\/?\w[\w-]*)/);
if (tagMatch) {
const tagName = tagMatch[1];
if (isValidTag(tagName)) {
result += line[i];
i++;
continue;
}
}
// Check if it's an HTML comment
if (rest.startsWith('!--')) {
result += line[i];
i++;
continue;
}
// Check if it's inside a JSX expression {<...>}
// This is a non-valid tag, escape it
result += '\\<';
modified = true;
i++;
continue;
}
// Also handle bare { that could be parsed as JSX expressions in certain contexts
result += line[i];
i++;
}
return result;
});
if (modified) {
fs.writeFileSync(filePath, processed.join('\n'));
console.log(` Fixed: ${path.relative(docsRoot, filePath)}`);
}
}
const files = findMdx(docsRoot);
console.log(`Processing ${files.length} .mdx files...`);
for (const file of files) {
processFile(file);
}
console.log('Done!');