Entregando um recurso de pacote compartilhado em dois repositórios para uma única rota
O pedido era pequeno no papel: adicionar um feed RSS. Um levantamento já tinha marcado isso como a lacuna de maior valor no site, e o ajuste parecia ser um arquivo de quinze linhas: filtrar a coleção de blog pela data de publicação, entregar para o @astrojs/rss, pronto.
Aí eu me fiz uma pergunta que o chamado não tinha feito: isso deveria morar no pacote de componentes compartilhado em vez de só neste site? Tanto o imperfectsystems.com quanto o vdaluz.com puxam do mesmo pacote @vdaluz/astro-blog, e o vdaluz.com também não tem feed RSS nenhum. Restringir o ajuste a um repositório significava que o segundo site enfrentaria exatamente as mesmas quinze linhas depois, provavelmente copiadas e coladas com uma pequena divergência.
O próprio README do pacote deixou o limite claro antes de eu ter que adivinhar: “isto é uma biblioteca de componentes, não um blog pronto para usar… rotas ficam em cada app.” Uma rota de RSS completa pertence a um site. Mas a forma de um item de RSS (título, link, descrição, categorias, construídos a partir de um post) não se importa em qual site está. Então eu dividi: um helper puro de mapeamento de dados, buildRssItems, vai no pacote. A rota em si, a chamada real para rss(), fica local em cada site. É o mesmo padrão que o pacote já usa para o helper de JSON-LD.
A parte que realmente me atrasou não foi código, foi consequência. Esse pacote é distribuído como um tarball do GitHub fixado por tag, não um pacote do registro npm. Assim que o lockfile de um consumidor registra o hash daquele tarball, a tag fica efetivamente congelada. Atualize sem cuidado e um bug não vira um conserto rápido, vira uma nova tag e uma segunda migração.
Então, antes de marcar qualquer coisa com tag, eu provei que a mudança funcionava. npm pack no repositório do pacote, instalar o tarball resultante localmente no imperfectsystems.com, buildar, e ler o XML gerado de verdade. Só depois de confirmar três posts, ordem correta, links funcionando, eu reverti aquela instalação de teste e cortei a tag de verdade.
Aí um detalhe que quase passou batido: eu reverti minhas edições de teste no package.json, o que está certo, mas isso significava que a próxima atualização de verdade precisava puxar do tarball real publicado no GitHub, não do arquivo local que eu tinha acabado de testar. Se eu tivesse commitado um lockfile apontando para um caminho local, o CI do site (que não tem acesso ao sistema de arquivos da minha máquina) falharia logo no próximo deploy. Eu confirmei que a tag já estava ativa no GitHub de verdade (seguindo o redirecionamento até um 200 real) antes de tocar na dependência do site.
Já implantado, o feed voltou com o content-type errado na minha primeira checagem ao vivo, text/html, servindo a página 404. Tentei de novo alguns segundos depois: application/xml, corpo correto. Um cache de borda desatualizado num POP da Cloudflare ainda não tinha se atualizado. É um bom lembrete de que “o deploy terminou” e “todo nó de borda concorda com o deploy” não são a mesma afirmação, especialmente logo no momento em que um rollout termina.
O feed valida limpo contra o validador W3C agora. Uma recomendação, não um erro: está faltando um atom link auto-referenciado, que é só o padrão do @astrojs/rss. Vou deixar isso quieto.
Leitura relacionada
Compartilhando um blog Astro entre dois sites
Você não consegue extrair um blog, mas consegue extrair seus componentes: um contrato de tokens para duas paletas, e as duas mordidas de CI que nunca acontecem num notebook.
Dando um rosto para cada link compartilhado
Uma proteção de og:image que nunca disparava, um valor padrão de uma linha que resolveu, um card de terminal feito sob medida, e uma pegadinha de retina no Chrome headless.
O favicon que ficou quebrado por meses e ninguém teria percebido
Um lote de otimização de performance com uma metade fácil (prerender, otimização de PNG) e uma falha silenciosa: um favicon SVG cuja referência de imagem externa os navegadores descartam sem avisar.
Você também pode achar útil
NordPass
Gerenciador de senhas da equipe por trás da NordVPN, com um plano gratuito.
Como afiliado da NordPass, ganho com compras qualificadas.
Saiba maisProton VPN
VPN comercial com filtragem NetShield e interruptor de desligamento automático.
Como parceiro da Proton, ganho com compras qualificadas dos serviços de privacidade e segurança da Proton (Pass, Mail, VPN, Drive).
Saiba maisProton Drive
Armazenamento em nuvem criptografado, da equipe por trás do Proton Mail.
Como parceiro da Proton, ganho com compras qualificadas dos serviços de privacidade e segurança da Proton (Pass, Mail, VPN, Drive).
Saiba mais