| #!/usr/bin/env node |
| |
| const fs = require('fs'); |
| const path = require('path'); |
| |
| const docsRoot = path.resolve(__dirname, '../docs'); |
| |
| 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; |
| } |
| |
| 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 => { |
| if (line.match(/^```/)) { |
| inCodeFence = !inCodeFence; |
| return line; |
| } |
| if (inCodeFence) return line; |
| if (line.startsWith('import ')) return line; |
| |
| // Skip lines that are JSX component usage (contain JSX attributes with {}) |
| if (line.match(/^\s*<(Tabs|TabItem|Stable|Unstable|ConfigTable|Label|AllVersions|div|img)/)) return line; |
| if (line.match(/html=\{/)) return line; |
| if (line.match(/groupId="/)) return line; |
| if (line.match(/style=\{\{/)) return line; |
| if (line.match(/className="/)) return line; |
| |
| // Escape { and } that are NOT part of JSX expressions |
| // Only escape if the line contains { but is clearly not JSX |
| if (line.includes('{') || line.includes('}')) { |
| // Check if this is a JSX expression line (starts with or is part of a component) |
| if (line.match(/^\s*<\/?(Tabs|TabItem|Stable|Unstable|ConfigTable|Label|AllVersions)/)) return line; |
| |
| // Escape curlies in lines that have them in regular text/HTML content |
| let result = ''; |
| let inInlineCode = false; |
| for (let i = 0; i < line.length; i++) { |
| if (line[i] === '`') { |
| inInlineCode = !inInlineCode; |
| result += line[i]; |
| continue; |
| } |
| if (inInlineCode) { |
| result += line[i]; |
| continue; |
| } |
| if (line[i] === '{') { |
| // Check if this is a JSX expression we should keep |
| // Keep: {xxxHtml}, {children}, {'<'}, style={{...}} |
| const rest = line.slice(i); |
| if (rest.match(/^\{[a-zA-Z]+Html\}/)) { |
| result += line[i]; |
| continue; |
| } |
| if (rest.match(/^\{\{/)) { |
| // style={{ }}, keep double curlies |
| result += line[i]; |
| continue; |
| } |
| if (rest.match(/^\{'[<>{}]'\}/)) { |
| result += line[i]; |
| continue; |
| } |
| // This is a text curly brace, escape it |
| result += "{'\\{'}"; |
| modified = true; |
| continue; |
| } |
| if (line[i] === '}') { |
| // Check if this is closing a JSX expression we kept |
| const before = result; |
| if (before.match(/\{[a-zA-Z]+Html$/)) { |
| result += line[i]; |
| continue; |
| } |
| if (before.match(/\{\{[^}]*$/)) { |
| result += line[i]; |
| continue; |
| } |
| if (before.match(/\{'[<>{}]'$/)) { |
| result += line[i]; |
| continue; |
| } |
| if (before.match(/\{'\\{'\}?$/)) { |
| // Already escaped opening brace |
| result += line[i]; |
| continue; |
| } |
| // This is a text curly brace, escape it |
| result += "{'\\}'}"; |
| modified = true; |
| continue; |
| } |
| result += line[i]; |
| } |
| return result; |
| } |
| return line; |
| }); |
| |
| if (modified) { |
| fs.writeFileSync(filePath, processed.join('\n')); |
| console.log(` Fixed braces: ${path.relative(docsRoot, filePath)}`); |
| } |
| } |
| |
| const files = findMdx(docsRoot); |
| console.log(`Processing ${files.length} .mdx files for brace escaping...`); |
| for (const file of files) { |
| processFile(file); |
| } |
| console.log('Done!'); |