Trabalhando com Markdown em TypeScript: do parsing aos plugins personalizados
\nO Markdown continua sendo uma das linguagens de marcação mais populares para documentação, blogs e plataformas educacionais. Ao desenvolver em TypeScript, surge a necessidade não apenas de exibir Markdown, mas também de gerenciar flexivelmente o processo de sua transformação. Neste artigo, abordaremos três bibliotecas principais que ajudarão você a integrar Markdown ao seu projeto TypeScript e mostraremos como criar suas próprias extensões.
\n\nPor que o TypeScript é importante para trabalhar com Markdown?
\nO TypeScript adiciona tipagem estrita, o que é crucial ao processar conteúdo do usuário. Você sabe exatamente quais dados vêm dos arquivos Markdown e pode evitar erros em tempo de execução. Além disso, o TypeScript simplifica a criação de plugins e middlewares para parsers.
\n\nVisão geral das bibliotecas populares
\n- \n
- marked — o parser mais rápido e simples. Configuração mínima, excelente desempenho. \n
- unified / remark — ecossistema para análise sintática. Permite criar cadeias complexas de transformações (AST -> HTML -> React). \n
- showdown — uma alternativa leve com suporte a extensões, bem documentada. \n
Exemplo 1: Parsing básico com marked
\nA biblioteca marked é ideal para um início rápido. Instale-a via npm:
npm install marked\nOs tipos para TypeScript são fornecidos juntos. Exemplo de uso:
\nimport { marked } from 'marked';\n\nconst markdownContent = `# Olá, TypeScript!\nIsso é **texto em negrito** e `código`.\n- Item de lista\n- Mais um`;\n\nconst html = marked.parse(markdownContent);\nconsole.log(html);\n// Saída: <h1>Olá, TypeScript!</h1>\n// <p>Isso é <strong>texto em negrito</strong> e <code>código</code>.</p>\n// <ul><li>Item de lista</li><li>Mais um</li></ul>\nPara trabalho assíncrono, use marked.parseAsync(). Isso é útil ao carregar arquivos da rede.
Exemplo 2: Renderizador personalizado com tipagem
\nÀs vezes, é necessário alterar o comportamento padrão. Por exemplo, adicionar classes a links ou processar imagens. Vamos criar um renderizador personalizado em TypeScript:
\nimport { marked, Renderer } from 'marked';\n\ninterface CustomRenderer extends Renderer {\n link(href: string, title: string | null, text: string): string;\n}\n\nconst renderer: Partial<CustomRenderer> = {\n link({ href, title, text }) {\n return `<a href="${href}" target="_blank" rel="noopener noreferrer" class="external-link">${text}</a>`;\n }\n};\n\nmarked.use({ renderer });\n\nconst markdown = 'Visite [PythonLib](https://pythonlib.ru)';\nconst result = marked.parse(markdown);\n// <a href="https://pythonlib.ru" target="_blank" class="external-link">PythonLib</a>\nO tipo Partial<CustomRenderer> garante que estamos sobrescrevendo apenas os métodos necessários.
Exemplo 3: Trabalhando com AST através de unified e remark
\nSe você precisa de controle total sobre a estrutura do documento, use unified e remark. Instalação:
npm install unified remark-parse remark-stringify remark-html\nExemplo de parsing em árvore sintática (AST) e vice-versa:
\nimport { unified } from 'unified';\nimport remarkParse from 'remark-parse';\nimport remarkStringify from 'remark-stringify';\nimport remarkHtml from 'remark-html';\n\nconst markdown = '# Título\\nIsso é **importante**.';\n\nconst file = await unified()\n .use(remarkParse) // Parsing para AST\n .use(remarkStringify) // De volta para Markdown (para verificação)\n .process(markdown);\n\nconsole.log(String(file)); // Markdown original\n\nconst htmlFile = await unified()\n .use(remarkParse)\n .use(remarkHtml) // Transformação