Complete website build including: - Build Your Kit store page with cart system, sectioned layout (Hardware, Software, Attachments, Spare Parts), inline quote request form, and sticky sidebar summary - 16+ pages: Education, Platform, Resources, News, About Us, Download, Contact, Rover, Code Editor, Robot Simulator, etc. - 89+ MDX resource articles and 18 news posts - Store product images scraped from micromelon.com.au - Quote request API route with Airtable integration - Dynamic back links and cover photos on resource pages - Redesigned downloads page - Fixed corrupted MDX code blocks
53 lines
1.8 KiB
JavaScript
53 lines
1.8 KiB
JavaScript
import fs from 'fs';
|
|
import path from 'path';
|
|
import { fileURLToPath } from 'url';
|
|
|
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
const dir = path.join(__dirname, '..', 'content', 'resources');
|
|
|
|
// Known safe HTML/JSX tags that MDX should handle
|
|
const safeTags = new Set([
|
|
'iframe', 'video', 'source', 'br', 'hr', 'img',
|
|
'div', 'span', 'p', 'a',
|
|
'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
|
|
'ul', 'ol', 'li',
|
|
'strong', 'em', 'b', 'i', 'u',
|
|
'code', 'pre',
|
|
'blockquote',
|
|
'table', 'tr', 'td', 'th', 'thead', 'tbody',
|
|
]);
|
|
|
|
let fixed = 0;
|
|
for (const f of fs.readdirSync(dir).filter(f => f.endsWith('.mdx'))) {
|
|
const filepath = path.join(dir, f);
|
|
const original = fs.readFileSync(filepath, 'utf8');
|
|
|
|
// Split into frontmatter and content
|
|
const fmEnd = original.indexOf('---', 4);
|
|
if (fmEnd === -1) continue;
|
|
const frontmatter = original.substring(0, fmEnd + 3);
|
|
let content = original.substring(fmEnd + 3);
|
|
|
|
// Escape angle brackets that look like HTML tags but aren't valid JSX
|
|
// Match < followed by something that's not a known safe tag or /
|
|
content = content.replace(/<(?!\/?(?:iframe|video|source|br|hr|img)\b)([^>]*?)>/g, (match, inner) => {
|
|
// Keep markdown image syntax  - these don't match since they start with !
|
|
// Keep valid looking JSX/HTML
|
|
const tagName = inner.split(/[\s/]/)[0].toLowerCase();
|
|
if (safeTags.has(tagName) || tagName.startsWith('!--')) return match;
|
|
// It's a stray angle bracket - escape it
|
|
return `\\<${inner}\\>`;
|
|
});
|
|
|
|
// Also escape bare < that aren't part of tags (like math: x < y)
|
|
// But be careful not to double-escape
|
|
content = content.replace(/([^\\])<([^/!a-zA-Z\\])/g, '$1\\<$2');
|
|
|
|
const result = frontmatter + content;
|
|
if (result !== original) {
|
|
fs.writeFileSync(filepath, result);
|
|
fixed++;
|
|
}
|
|
}
|
|
console.log(`Fixed JSX issues in ${fixed} files`);
|