Communitygithub.com

timerise-ai/blog-markdown

Skill para agente: construir um blog multilíngue baseado em arquivos Markdown — posts particionados por localidade, slugs por localidade unidos por uma chave de tradução compartilhada, páginas de tag com identidade de slug, posts relacionados, arte de capa, RSS, sitemap, hreflang, validador de conteúdo em CI — no Next.js App Router, sem CMS

O que é blog-markdown?

Este skill capacita um agente de IA a construir um blog multilíngue completo utilizando arquivos Markdown em vez de um CMS, especificamente para o Next.js App Router. O conceito central é que a identidade de cada post é definida por um `translationKey`, e não pelo slug, permitindo que slugs sejam localizados para SEO enquanto o `translationKey` unifica as traduções. Isso alimenta funcionalidades como hreflang, alternates, troca de idioma, posts relacionados e redirecionamentos entre localidades. O skill também gerencia tags, onde a identidade é o slug (não o rótulo), evitando duplicatas; calcula tempo de leitura; e inclui um fallback parser para YAML malformado, evitando quebras na build. Além disso, fornece referências detalhadas para configuração de frontmatter, carregamento de conteúdo com memoização, páginas de tag, SEO, sitemap, RSS e validação em CI. Com base na experiência de um engenheiro que já implementou esse módulo, o skill estabelece regras rígidas para evitar armadilhas comuns, como depender de slugs para buscas entre localidades ou renderizar constantes de tempo de leitura não computadas.

Funciona com~Claude Code~Codex CLI~Cursor
npx skills add timerise-ai/blog-markdown

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

Blog Markdown (multilíngue, com tags)

Um blog onde o conteúdo são arquivos, não linhas em um banco de dados. Todo o design gira em torno de uma ideia: a identidade de um post é sua translationKey, não seu slug. Os slugs são localizados para SEO e diferem por idioma; a chave é o que faz três arquivos serem um único artigo, e é ela que alimenta hreflang, o seletor de idioma, posts relacionados e redirecionamentos entre localidades. Acertar esse campo resolve todo o resto.

Escrito pelo engenheiro que já enviou este módulo em produção. A implementação anterior contra a qual foi auditado era um blog de marketing multilíngue, gerado estaticamente, com seu sitemap e superfície de SEO. Os fatos e regras abaixo são o que o conjunto de camadas de conteúdo e a build verificam; references/provenance.md contém o registro.

Quando usar

  • Conteúdo escrito como markdown no repositório, implantado junto com o aplicativo
  • Mais de um idioma, com slugs diferentes por idioma
  • Páginas de tag, posts relacionados, feeds ou hreflang estão no escopo
  • Você deseja que tudo seja gerado estaticamente no momento da build

Quando NÃO usar

Em vez dissoUse
Um CMS hospedado é a fonte da verdadeo SDK desse CMS; mantenha apenas references/content-model.md
Um site de documentação / central de ajuda (categorias, barra lateral, busca)um skill de central de ajuda — modelo de navegação diferente
Um blog de único local com <10 postsfs puro + gray-matter inline; isso aqui é overhead
Renderizar markdown enviado por usuárioum renderizador com sanitização; veja a nota sobre XSS em references/rendering.md

Arquitetura

content/blog/<locale>/<localized-slug>.md   # frontmatter + corpo
        |
        v
 parseFrontmatter  --YAML, com fallback para um parser linha a linha em aspas malformadas
        |
        v
   getAllPosts(locale)  <-- memoizado por localidade em produção
        |
        +--> translationKey ----> alternates / hreflang / troca de idioma
        +--> tags -> SLUG da tag ---> páginas de tag (slug é a identidade, não o rótulo)
        +--> related (slugs EN) -> posts relacionados, com fallback para EN
        |
        v
  /blog   /blog/[slug]   /blog/tag/[slug]   sitemap.xml   feed.xml

Fatos críticos

  1. As relações são escritas uma vez, no locale padrão, como slugs do locale padrão. Um arquivo traduzido pode omitir related completamente e ainda obter os posts relacionados corretos, resolvidos via translationKey, de modo que cada tradução renderize a mesma seção de relacionados. O teste de fallback garante isso.
  2. A identidade de uma tag é seu slug, não seu rótulo. "AI Agents" e "AI agents" resultam no mesmo slug e na mesma URL. Agrupe por slug e combine posts por slug, para que todas as variantes de grafia de um post apareçam na mesma página de tag. Dois testes garantem isso.
  3. gray-matter lança exceção em um escalar YAML entre aspas duplas contendo caracteres de aspas literais — aspas tipográficas em um excerto traduzido fazem isso. Sem um parser de fallback a build para naquele único arquivo; o fallback linha a linha mantém o carregamento. Verificado contra a biblioteca e coberto por um teste.
  4. gray-matter armazena em cache uma análise com falha como resultado vazio. Ele escreve sua entrada de cache antes de analisar, então a segunda e todas as análises posteriores de um arquivo que lançou exceção retornam {} — sem erro, sem frontmatter. Sempre chame matter(contents, {}); qualquer objeto de opções desativa o cache.
  5. O carregamento é O(posts x localidades) por página, a menos que você memorize. Memoize por localidade para que cada arquivo de conteúdo seja lido uma vez por build; resolver alternates em generateMetadata resulta em uma amplificação de 71x de leitura de arquivo, medida na implementação anterior.
  6. getPostSlugs deve filtrar .md. O filtro evita que um .DS_Store perdido se torne uma leitura de .DS_Store.md e um ENOENT. Um teste garante isso.
  7. Páginas de tag não têm identidade entre localidades. Tags são texto livre por localidade, então o seletor de idioma deve cair no índice do blog em uma URL de tag, em vez de construir uma URL que retorne 404.

Regras rígidas

Nunca use o slug como chave para buscas entre localidades. Os slugs são localizados propositalmente. translationKey é a coluna de junção; uma correspondência de slug entre localidades é coincidência, não relação.

Nunca deixe getPostBySlug decidir a visibilidade de rascunhos implicitamente. Filtre rascunhos no carregador; dynamicParams = false os esconde apenas como efeito colateral e para de fazê-lo assim que a flag mudar. O teste de rascunho garante o filtro.

Nunca renderize uma constante "5 min de leitura". Calcule a partir do corpo; custa um split, e o teste de tempo de leitura garante isso.

Nunca derive a URL de uma página de tag do rótulo no momento da leitura e o rótulo da URL no momento da renderização sem uma verificação de colisão. Esse caminho de ida e volta só é sem perdas enquanto cada rótulo resultar em um slug único.

Início rápido

  1. Defina o contrato de localidade e o esquema de frontmatter — content-model.md
  2. Construa o carregador (memoizado, filtrado por .md, parser de fallback) — content-loader.md
  3. Conecte localidades, alternates e redirecionamentos entre localidades — i18n-and-routing.md
  4. Adicione o agrupamento de tags e páginas de tag — tags.md
  5. Construa as três rotas, metadados e JSON-LD — pages-and-seo.md
  6. Renderize corpos e capas — rendering.md
  7. Adicione o script de validação e leia as lacunas operacionais — operations.md
  8. Execute os fixtures e testes — testing.md

Portando isso para um aplicativo existente? Preencha primeiro a tabela de junção em adaptation.md — leva dez minutos e economiza uma renomeação.

Diretório de referência

CenárioPalavras-chave de gatilhoReferência
Campos de frontmatter, rascunhos, chaves de tradução, relaçõesfrontmatter, schema, translationKey, draft, related, excerptcontent-model.md
Leitura de arquivos, cache, falhas de YAML, tempo de leituragray-matter, fs, cache, memoize, ENOENT, YAMLException, parsecontent-loader.md
Localidades, hreflang, alternates, links de localidade erradai18n, locale, hreflang, alternates, canonical, language switcher, redirecti18n-and-routing.md
URLs de tag, colisões, páginas de tag enxutastag, slug, diacritics, collision, tag page, taxonomytags.md
As rotas, metadados, JSON-LD, sitemap, RSSpage.tsx, generateStaticParams, generateMetadata, dynamicParams, sitemap, RSS, feed, BlogPostingpages-and-seo.md
Markdown para React, blocos de código, imagens de capareact-markdown, remark-gfm, syntax highlighting, cover, motif, accent, XSSrendering.md
Validando conteúdo, custo de build, fluxo do editorvalidation, CI check, build time, drafts, authoring, operatoroperations.md
Fixtures e o conjunto de testes aprovadotest, fixture, vitest, node:test, regression, asserttesting.md
Adaptando a um aplicativo host: junções, renomeações, sondaadapt, port, host, seam, rename, CMS, integrateadaptation.md
O registro de auditoria: o que mudou, foi mantido e adicionado vs. a implementação anteriorprovenance, deviations, fidelity, audit, ledgerprovenance.md

Habilidades Relacionadas