Processamento seguro de Markdown e MDX com Claude Code
Processe Markdown/MDX com Claude Code: AST, frontmatter, XSS, links e QA de locales.
Markdown não é apenas texto
Um artigo publicado em Markdown ou MDX não é só um bloco de parágrafos. Ele carrega frontmatter, description para SEO, hierarquia de títulos, âncoras geradas, blocos de código, links internos, fontes oficiais, rotas por idioma e às vezes HTML cru. Se você pedir ao Claude Code “melhore este artigo” sem um contrato técnico, o texto pode ficar melhor, mas o slug pode mudar, o CTA pode sumir, a imagem hero pode ser alterada ou uma locale pode virar um resumo fino.
A regra prática é separar escrita e verificação. Claude Code pode reescrever, localizar e expandir. A estrutura precisa ser checada por scripts. Markdown e MDX devem ser lidos com AST, ou árvore de sintaxe abstrata. Frontmatter deve ser validado como dado. HTML precisa de uma fronteira explícita de sanitização. E as dez locales precisam ser vistas como um conjunto.
As fontes principais foram verificadas em 2 de junho de 2026. O guia de unified explica o pipeline de parse, transform e stringify. A página de syntax trees mostra por que AST é mais seguro que regex de linha. Para Markdown, use remark e remark-parse. Para MDX, veja a documentação oficial do MDX. Para frontmatter, gray-matter é direto. Para HTML e XSS, compare rehype-sanitize com o OWASP XSS Prevention Cheat Sheet. Para limitar o agente, leia Claude Code overview e settings.
flowchart LR
A["Arquivo MDX"] --> B["frontmatter"]
B --> C["schema validation"]
A --> D["remark / MDX AST"]
D --> E["títulos, fences, links"]
D --> F["pipeline rehype"]
F --> G["sanitize"]
C --> H["locale e build checks"]
E --> H
G --> H
Escolha o parser antes de editar
A primeira instrução ao Claude Code deve nomear a toolchain. “Parse Markdown” é amplo demais. Para um arquivo pequeno, uma regex parece boa; para conteúdo publicado, ela falha em casos comuns.
| Necessidade | Melhor escolha | Atalho arriscado |
|---|---|---|
| Ler títulos, links e code fences | remark-parse com AST traversal | Regex ^## no texto |
Suportar JSX em .mdx | remark-mdx ou compiler MDX | Parser só de Markdown |
| Gerar HTML | remark-rehype para rehype | Concatenar strings HTML |
| Permitir raw HTML | rehype-raw e rehype-sanitize | Só allowDangerousHtml |
| Ler frontmatter | gray-matter com schema checks | Split manual de YAML |
AST separa significado. Um ## título falso dentro de bloco de código não pode entrar no índice. Uma URL dentro de props MDX pode não ser um link editorial. tags: Claude Code, Markdown é string, não array. Parser e schema pegam esse tipo de erro antes da revisão humana.
Quatro casos de uso práticos
O primeiro caso é atualizar um artigo já publicado. Title, description, updatedDate, links oficiais, links internos, exemplos de código e CTA precisam caminhar juntos. No ClaudeCodeLab, isso se conecta a boas práticas de CLAUDE.md e web scraping com Claude Code, sem tocar em outros slugs.
O segundo caso é documentação com componentes MDX. Callouts, abas, cards de preço, FAQ e exemplos vivos são úteis, mas misturam Markdown e JSX. Um checker que não entende MDX pode quebrar componentes ou ignorar links relevantes.
O terceiro caso é publicação multilíngue. Um canonical japonês forte não resolve se português, espanhol, francês ou indonésio viram resumos. Cada locale precisa ter exemplos, armadilhas, snippets executáveis, links oficiais, links internos, CTA e nota de verificação.
O quarto caso é operação comercial de conteúdo. Páginas de Gumroad, training, recursos gratuitos e emails frequentemente reaproveitam Markdown. Quanto mais perto de compra ou consultoria, mais importante é verificar code fences, links e HTML.
Setup mínimo para copiar
Os exemplos usam Node.js 18 ou superior e ESM. Comece em uma pasta de teste antes de levar para o repositório.
mkdir mdx-audit-demo
cd mdx-audit-demo
npm init -y
npm pkg set type=module
npm install unified remark-parse remark-mdx remark-gfm gray-matter
npm install unist-util-visit github-slugger
npm install remark-rehype rehype-raw rehype-sanitize rehype-stringify
mkdir tools
Exemplo 1: auditar frontmatter, títulos, fences e links
Este script lê frontmatter com gray-matter, faz parse do body com remark e suporte MDX, e falha se campos obrigatórios faltarem, se description passar de 120 caracteres, se um code fence não tiver linguagem ou se faltarem links internos e externos.
// tools/audit-mdx.mjs
import fs from "node:fs/promises";
import matter from "gray-matter";
import GithubSlugger from "github-slugger";
import { unified } from "unified";
import remarkParse from "remark-parse";
import remarkMdx from "remark-mdx";
import remarkGfm from "remark-gfm";
import { visit } from "unist-util-visit";
const file = process.argv[2];
if (!file) {
throw new Error("Usage: node tools/audit-mdx.mjs article.mdx");
}
const source = await fs.readFile(file, "utf8");
const { data, content } = matter(source);
const errors = [];
const links = { internal: [], external: [] };
const headings = [];
const codeBlocks = [];
for (const key of ["title", "description", "pubDate", "heroImage", "lang"]) {
if (typeof data[key] !== "string" || data[key].trim() === "") {
errors.push(`frontmatter.${key} is required`);
}
}
if ([...String(data.description ?? "")].length > 120) {
errors.push("description must be 120 characters or fewer");
}
if (!Array.isArray(data.tags) || data.tags.length === 0) {
errors.push("frontmatter.tags must be a non-empty array");
}
const tree = unified()
.use(remarkParse)
.use(remarkMdx)
.use(remarkGfm)
.parse(content);
const slugger = new GithubSlugger();
visit(tree, (node) => {
if (node.type === "heading") {
const text = plainText(node);
headings.push({ depth: node.depth, text, slug: slugger.slug(text) });
}
if (node.type === "code") {
codeBlocks.push({ lang: node.lang || "", meta: node.meta || "" });
if (!node.lang) errors.push("code fence is missing a language");
}
if (node.type === "link") {
const url = String(node.url || "");
if (url.startsWith("http")) links.external.push(url);
if (url.startsWith("/")) links.internal.push(url);
}
});
if (links.internal.length === 0) errors.push("missing internal link");
if (links.external.length === 0) errors.push("missing external link");
if (errors.length > 0) {
console.error(errors.map((error) => `- ${error}`).join("\n"));
process.exit(1);
}
console.log(JSON.stringify({ headings, codeBlocks, links }, null, 2));
function plainText(node) {
if (typeof node.value === "string") return node.value;
if (!Array.isArray(node.children)) return "";
return node.children.map(plainText).join("");
}
node tools/audit-mdx.mjs site/src/content/blog-pt/example.mdx
Exemplo 2: converter Markdown para HTML seguro
Se você não precisa de raw HTML, não habilite. Se precisa, faça parse e sanitize logo depois. allowDangerousHtml sozinho não é defesa.
// tools/markdown-to-safe-html.mjs
import fs from "node:fs/promises";
import { unified } from "unified";
import remarkParse from "remark-parse";
import remarkGfm from "remark-gfm";
import remarkRehype from "remark-rehype";
import rehypeRaw from "rehype-raw";
import rehypeSanitize, { defaultSchema } from "rehype-sanitize";
import rehypeStringify from "rehype-stringify";
const file = process.argv[2];
const markdown = await fs.readFile(file, "utf8");
const schema = {
...defaultSchema,
attributes: {
...defaultSchema.attributes,
code: [["className", /^language-/]],
},
};
const html = await unified()
.use(remarkParse)
.use(remarkGfm)
.use(remarkRehype, { allowDangerousHtml: true })
.use(rehypeRaw)
.use(rehypeSanitize, schema)
.use(rehypeStringify)
.process(markdown);
console.log(String(html));
A ordem importa. rehype-raw devolve HTML cru para a árvore HTML; rehype-sanitize remove tags e atributos não permitidos. Sem a segunda etapa, conteúdo perigoso pode chegar ao DOM renderizado.
Exemplo 3: verificar as dez locales
Este script confirma que o mesmo slug existe em todos os idiomas, que heroImage foi preservado, que updatedDate está correto e que cada body contém link interno e externo.
// tools/check-locales.mjs
import fs from "node:fs";
import path from "node:path";
import matter from "gray-matter";
const slug = "claude-code-markdown-processing.mdx";
const expectedHero = "/images/hero/hero-077.png";
const locales = [
["ja", "site/src/content/blog"],
["en", "site/src/content/blog-en"],
["zh", "site/src/content/blog-zh"],
["ko", "site/src/content/blog-ko"],
["es", "site/src/content/blog-es"],
["fr", "site/src/content/blog-fr"],
["de", "site/src/content/blog-de"],
["pt", "site/src/content/blog-pt"],
["hi", "site/src/content/blog-hi"],
["id", "site/src/content/blog-id"],
];
const errors = [];
for (const [lang, dir] of locales) {
const file = path.join(dir, slug);
const source = fs.readFileSync(file, "utf8");
const { data, content } = matter(source);
if (data.lang !== lang) errors.push(`${lang}: lang mismatch`);
if (data.heroImage !== expectedHero) errors.push(`${lang}: hero changed`);
if (data.updatedDate !== "2026-06-02") {
errors.push(`${lang}: updatedDate mismatch`);
}
if ([...String(data.description ?? "")].length > 120) {
errors.push(`${lang}: description too long`);
}
if (!content.includes("https://")) errors.push(`${lang}: no external link`);
if (!content.includes("](/")) errors.push(`${lang}: no internal link`);
}
if (errors.length > 0) {
console.error(errors.map((error) => `- ${error}`).join("\n"));
process.exit(1);
}
console.log("locale set is consistent");
Falhas concretas
| Falha | Resultado | Proteção |
|---|---|---|
| Ler títulos com regex | Falso título dentro de código entra no índice | Percorrer nós heading |
tags vira string | Filtros e posts relacionados quebram | Validar tipos do frontmatter |
| Slug inconsistente | Âncoras quebram por idioma | Usar o mesmo slugger |
| Confiar em raw HTML | Risco XSS por tags ou atributos | Sanitizar com schema |
| Não checar links externos | Docs oficiais mudam sem alerta | Testar antes de publicar |
| Prompt amplo demais | Arquivos de outros workers são alterados | Fixar owned_files |
Essas falhas devem estar no prompt. Claude Code responde melhor a restrições verificáveis do que a uma frase vaga como “deixe mais profissional”.
Prompt seguro para Claude Code
task: "Refresh one published MDX article"
owned_files:
- "site/src/content/blog-pt/claude-code-markdown-processing.mdx"
preserve:
- "slug path"
- "heroImage"
- "unrelated dirty files"
required:
- "updatedDate: 2026-06-02"
- "description <= 120 characters"
- "AST-based Markdown checks"
- "official external links"
- "internal links and monetization CTA"
forbidden:
- "regex-only heading parsing"
- "raw HTML without sanitization"
- "thin locale summaries"
verification:
- "node scripts/check-code-fences.mjs"
- "node scripts/check-updated-article-quality.mjs"
Checagem antes de publicar e CTA
Antes de publicar, combine scripts e revisão humana. Scripts checam estrutura, metadata, fences, links e profundidade. A pessoa revisa naturalidade, intenção de busca, leitura mobile e CTA.
node tools/audit-mdx.mjs site/src/content/blog-pt/claude-code-markdown-processing.mdx
node tools/check-locales.mjs
node scripts/check-code-fences.mjs
node scripts/check-updated-article-quality.mjs
Para uso individual, comece pela cheatsheet gratuita de Claude Code. Para prompts reutilizáveis de review e escrita, use os Claude Code prompt templates. Para equipes que precisam de permissões, CI, fluxo multilíngue e revisão editorial, veja treinamento e consultoria Claude Code.
Resultado do teste prático
Nesta atualização, Masa tratou o artigo como pipeline real de conteúdo: description curta, updatedDate, heroImage preservado, linguagens em code fences, links oficiais, locales completos e CTA. A auditoria baseada em AST cobre erros que regex não vê, principalmente títulos dentro de blocos de código e sintaxe MDX perto de componentes. No final, os comandos usados foram node scripts/check-code-fences.mjs e node scripts/check-updated-article-quality.mjs. A lição é que Claude Code fica confiável quando o contrato do artigo é executável, não apenas quando o prompt pede “mais qualidade”.
PDF grátis: cheatsheet do Claude Code
Informe seu e-mail e baixe uma página com comandos, hábitos de revisão e workflows seguros.
Cuidamos dos seus dados e não enviamos spam.
Sobre o autor
Masa
Engenheiro focado em workflows práticos com Claude Code.
Artigos relacionados
Escada de segurança de permissões no Claude Code
Amplie de read-only para edições limitadas, comandos de prova e deploy checks sem perder controle.
Claude Code Small PR Proof Pack: pequenas mudanças fáceis de revisar
Um pacote de prova para PRs do Claude Code: diff, checks, URL pública, CTA e rollback.
Gate de revisão antes do commit com Claude Code
Revisão antes do commit com Claude Code: diff, build, URL pública, Gumroad, consultoria, testes e arquivos fora do escopo.