Saltar para o conteúdo
Blume
Português
Esc
navegarabrir⌘Jpré-visualizar
Nesta página

Fontes de conteúdo

Traga documentação de arquivos locais, de um repositório remoto ou de qualquer backend personalizado — e combine várias fontes em um único site static-first lido em tempo de build.

Por padrão, o Blume lê uma pasta de arquivos .md/.mdx. As fontes de conteúdo permitem trazer páginas de outro lugar — um repositório remoto, um CMS ou qualquer backend personalizado — e combinar várias fontes em um único site. As fontes são lidas em tempo de build; o Blume continua static-first.

O padrão

Sem nenhuma configuração, o Blume varre a raiz do seu conteúdo (docs, por padrão) como uma única fonte de sistema de arquivos implícita. As opções de nível superior content.root/include/exclude continuam funcionando exatamente como antes — nada a mudar.

import { defineConfig } from "blume";

export default defineConfig({
  content: { root: "docs" },
});

Múltiplas fontes

Adicione um array content.sources para compor fontes. Cada entrada recebe um namespace por meio de um prefix opcional, de modo que suas rotas fiquem aninhadas sob /<prefix>/…. Quando sources está presente, ele substitui o padrão implícito, portanto inclua uma entrada filesystem para a sua documentação local.

import { defineConfig } from "blume";

export default defineConfig({
  content: {
    sources: [
      // Local docs at the site root
      { type: "filesystem", root: "docs" },

      // Remote MDX from a GitHub repo, mounted under /sdk
      {
        type: "mdx-remote",
        prefix: "sdk",
        github: { owner: "acme", repo: "sdk", ref: "main", path: "docs" },
      },
    ],
  },
});

Se duas fontes resolverem para a mesma rota, o Blume reporta um erro de build BLUME_DUPLICATE_ROUTE — dê a cada fonte um prefix distinto.

Obsidian

A fonte integrada obsidian lê um vault do Obsidian no lugar onde ele está. Não há etapa de exportação nem nada gerado dentro do seu repositório: o vault continua sendo a fonte da verdade, e o Blume reduz o dialeto do Obsidian a Markdown conforme carrega.

import { defineConfig } from "blume";

export default defineConfig({
  content: {
    sources: [
      { type: "filesystem", root: "docs" },
      {
        type: "obsidian",
        prefix: "notes",
        vault: "vault",
        // Vault folder names to skip at any depth, on top of dot-folders
        exclude: ["Templates", "Daily"],
      },
    ],
  },
});

Os [[Wikilinks]] viram links de rota, endereçados pelo nome da nota em todo o vault em vez de pelo caminho, do jeito que o Obsidian endereça as notas. Texto de link personalizado ([[Note|label]]), âncoras de cabeçalho ([[Note#Install]]), caminhos completos ([[folder/Note]] e [[folder/Note.md]]), os caminhos parciais que a configuração padrão “shortest path when possible” do Obsidian escreve ([[guides/Note]]) e a forma [[Note\|label]] que o Obsidian escreve dentro de uma célula de tabela — tudo funciona; e uma nota que define slug no frontmatter é linkada na rota que esse slug publica. Quando duas notas compartilham um nome, vence a nota cujo caminho completo no vault é exatamente esse nome — o Obsidian resolve um link como caminho antes de resolver como nome — e depois a primeira na ordem do vault (pastas antes de notas, sem diferenciar maiúsculas de minúsculas, como o explorador de arquivos do Obsidian). O Blume avisa apenas quando um wikilink de fato é resolvido por meio de uma colisão dessas; escreva um caminho mais longo para desambiguar. Uma referência de bloco ([[Note#^id]]) leva à nota dela sem âncora: os blocos são renderizados sem id em que aterrissar. Uma âncora de cabeçalho é resolvida contra os cabeçalhos reais da nota de destino, correspondidos do jeito que o autocomplete do Obsidian os escreve (com **bold**, `code` e sintaxe de link removidos) e convertidos em slug pela mesma passagem extractHeadings que preenche o manifesto da página — de modo que um link para #Install cai no cabeçalho, e não em um id que nenhuma página emite. [[#Install]] endereça um cabeçalho na própria nota que você está escrevendo. Um link para um cabeçalho que não existe mantém o link da página, descarta a âncora e emite um aviso.

O frontmatter mantém o que o esquema de página do Blume aceita, mais qualquer chave que você declare em frontmatter.extend (ou, para notas daquele type, o frontmatter de um tipo de conteúdo); toda outra propriedade do Obsidian — campos do Dataview, datas do Templater, publish e os próprios tags, aliases e cssclasses do Obsidian — é descartada quando uma nota é reduzida, então um vault escrito com a UI de Properties compila sem erros de frontmatter. aliases é descartado em vez de resolvido — alvos de link por alias ainda não são suportados. Uma imagem Markdown relativa ao lado de uma nota (![chart](./chart.png)) é servida a partir do vault e, quando o vault fica dentro do seu repositório git, as páginas do vault ganham datas de “Última atualização” derivadas do git, como qualquer outra página. Os links de “Editar esta página” são resolvidos por meio de github.dir, então um vault que fica ao lado do app de documentação em um monorepo ainda linka para o arquivo dele; um vault fora do repositório não recebe link.

Diretórios de idioma e snapshots de versão dentro do vault são lidos do mesmo jeito que a fonte de sistema de arquivos os lê: fr/Note.md publica sob /fr/ com i18n configurado, v1.0/Note.md sob /v1.0/ com versões, e wikilinks para essas notas apontam para a rota que cada uma publica.

Um link para uma nota index cai na rota da pasta dela, em vez de em um /index fantasma. Um wikilink não resolvido degrada para texto simples com um aviso de build, em vez de quebrar o build, então um vault no meio de uma refatoração ainda publica. %%comments%% de uma única linha são removidos, um wikilink dentro de um comentário HTML (<!-- [[Draft]] -->) é deixado em paz, já que o Obsidian também o esconde, e uma nota sem title no frontmatter é intitulada pelo nome do arquivo — a mesma regra que o próprio Obsidian aplica. Uma nota index é a única exceção: ela nomeia uma rota, e não uma nota, então o título dela recai na derivação usual do Blume (primeiro cabeçalho, depois o segmento humanizado). Código em blocos cercados, indentado e inline passa literalmente, então uma nota que documenta a sintaxe sobrevive.

Pastas que começam com ponto são ignoradas, incluindo o próprio diretório de configuração .obsidian do Obsidian e a .trash — que o watcher de desenvolvimento também ignora, então mover um painel no app ou mandar uma nota para a lixeira não refaz o build do seu site. Editar uma nota, sim. Os diretórios que nenhuma varredura de conteúdo lê (node_modules, dist, .git, …) também são ignorados, então um vault com raiz no próprio projeto não publica os READMEs das dependências. Symlinks dentro do vault são seguidos, do mesmo jeito que a fonte de sistema de arquivos os segue, então uma pasta compartilhada linkada no vault é publicada junto. Um vault que fica dentro de content.root precisa ser excluído da fonte de sistema de arquivos (exclude: ["vault/**"]); o blume version cut então o deixa de fora do snapshot, já que o vault continua publicando as próprias notas como atuais.

Ainda não reduzidos: callouts (> [!note]) são renderizados como blockquotes simples, embeds (![[image.png]]) passam intocados, %%comments%% de várias linhas são deixados no lugar e não há grafo de backlinks.

MDX remoto

A fonte integrada mdx-remote busca arquivos .md/.mdx brutos via HTTP. Enumere os arquivos a partir de uma subárvore de um repositório do GitHub (github) ou explicitamente contra uma URL base bruta (url + files):

{
  type: "mdx-remote",
  prefix: "sdk",
  url: "https://raw.githubusercontent.com/acme/sdk/main/docs",
  files: ["intro.mdx", "guide.mdx"],
}

O token de um repositório privado é lido da variável de ambiente GITHUB_TOKEN — ele nunca é embutido na sua configuração ou na saída gerada, e é enviado somente para os próprios hosts do GitHub (api.github.com, raw.githubusercontent.com), nunca para uma base url personalizada.

As páginas remotas são renderizadas com fidelidade total de MDX-mais-componentes: seus corpos são materializados em um diretório de staging oculto e renderizados pelo Astro junto com a sua documentação local, de modo que callouts, abas e todos os demais componentes do Blume continuam funcionando.

Cache e builds offline

Cada fonte remota mantém um snapshot em .blume/cache/<source>/. Se uma busca falhar — uma instabilidade de rede ou uma indisponibilidade do CMS — o Blume serve o último snapshot válido conhecido com um aviso, em vez de quebrar o build. O cache fica dentro de .blume/ e é regenerado, nunca versionado.

Em desenvolvimento, o conteúdo remoto é buscado uma vez e congelado durante a sessão; reinicie o servidor de desenvolvimento para atualizá-lo. As fontes locais do sistema de arquivos recarregam a quente normalmente. Para, em vez disso, consultar periodicamente uma fonte remota em busca de alterações, defina pollInterval (em segundos) nela — o servidor de desenvolvimento refaz a busca nesse intervalo e recarrega apenas quando o conteúdo realmente muda. Deixe-o sem definir para evitar chamadas à API enquanto você trabalha.

GitHub Releases

A fonte integrada github-releases transforma as releases de um repositório em um changelog: cada release vira uma entrada type: changelog, de modo que suas notas de versão são o seu changelog — nada a escrever duas vezes. Combinado com a linha do tempo de changelog gerada, publicar uma release no GitHub entrega uma entrada de changelog.

import { defineConfig } from "blume";

export default defineConfig({
  content: {
    sources: [
      { type: "filesystem", root: "content" },
      {
        type: "github-releases",
        prefix: "changelog",
        owner: "acme",
        repo: "sdk",
        // prereleases: false,  // include prereleases (default off)
        // drafts: false,       // include drafts (needs a write token)
        // limit: 100,          // cap releases, newest-first
      },
    ],
  },
});

Cada release é mapeada automaticamente para os campos do changelog: seu nome (ou tag) vira o título, sua data de publicação define a ordem da linha do tempo, a tag vira changelog.version, e as prereleases são marcadas como Prerelease (as demais como Release). As notas são renderizadas como o corpo da entrada. Dê à fonte um prefix para que suas páginas de release fiquem aninhadas sob uma rota como /changelog/v1-2-0.

Um repositório privado se autentica com a variável de ambiente GITHUB_TOKEN — o mesmo token que os outros recursos do GitHub usam, nunca embutido na sua configuração. Como toda fonte remota, ela é armazenada em cache em .blume/cache/<source>/ e servida offline se a API estiver inacessível. Como um changelog é complementar, uma falha de busca sem cache (digamos, um build de CI sem token) degrada para um changelog vazio com um aviso, em vez de quebrar o build — defina GITHUB_TOKEN nos seus ambientes de CI e de deploy para preenchê-lo.

Sanity

A fonte integrada sanity executa uma consulta GROQ e mapeia os campos de cada documento para o frontmatter e seu corpo em Portable Text para Markdown. O pacote @sanity/client é uma dependência peer opcional — instale-o apenas se você usar esta fonte.

import { defineConfig } from "blume";

export default defineConfig({
  content: {
    sources: [
      { type: "filesystem", root: "docs" },
      {
        type: "sanity",
        prefix: "guides",
        projectId: "abc123",
        dataset: "production",
        query: `*[_type == "guide"]`,
        // Field paths default to title / slug.current / body / _updatedAt
        fields: { slug: "slug.current", body: "content" },
      },
    ],
  },
});

Um token de leitura para um dataset privado vem da variável de ambiente SANITY_TOKEN. Tipos de bloco personalizados de Portable Text são mapeados para componentes do Blume por meio da opção serializers do adaptador, disponível quando você constrói sanitySource diretamente via uma fonte personalizada.

Notion

A fonte integrada notion transforma um banco de dados do Notion em uma coleção: cada linha vira uma página, suas propriedades viram frontmatter e sua árvore de blocos vira MDX. Callouts, toggles, colunas e blocos de código são mapeados para os componentes correspondentes do Blume. O @notionhq/client (v5 ou posterior) é uma dependência peer opcional; o Blume lê o banco de dados por meio da primeira fonte de dados dele.

import { defineConfig } from "blume";

export default defineConfig({
  content: {
    sources: [
      { type: "filesystem", root: "docs" },
      {
        type: "notion",
        prefix: "handbook",
        database: process.env.NOTION_DB_ID,
        // Property names default to the title-typed prop / Description / Slug / Order
        // Set publishedValue to treat Status as a publish gate (opt-in)
        publishedValue: "Published",
      },
    ],
  },
});

O token de integração vem da variável de ambiente NOTION_TOKEN (compartilhe o banco de dados com a sua integração). Por padrão, todas as páginas são importadas; defina publishedValue para fazer da propriedade Status um controle de publicação — qualquer outro valor passa então a ser mapeado para draft: true, que os builds de produção descartam. As URLs de imagem do Notion são assinadas e expiram, portanto o adaptador as baixa em tempo de build para os assets do site e reescreve as referências — uma imagem de CMS nunca apodrece um build estático. As chamadas à API são ritmadas por um pequeno pool de requisições (3 por vez, correspondendo ao limite de taxa por integração do Notion), de modo que bancos de dados com centenas de páginas são importados sem esbarrar em respostas 429; defina concurrency na fonte para ajustar isso.

Preview e sincronização

Duas flags controlam como o conteúdo remoto é buscado e o que é incluído:

  • --preview em blume dev ou blume build renderiza rascunhos e traz conteúdo não publicado do CMS — o Sanity muda para sua perspectiva previewDrafts, e o Notion para de filtrar por Status. Builds de produção sem a flag excluem rascunhos normalmente, então um build de preview é uma forma segura de revisar trabalho não publicado antes de ele ir ao ar.
  • blume sync refaz a busca de todas as fontes remotas e regenera o runtime. O ambiente de desenvolvimento é cache-first — uma fonte remota é buscada uma vez e servida a partir de .blume/cache ao reiniciar (rápido e tolerante a offline), portanto blume sync é como você traz o conteúdo mais recente do CMS sem reiniciar o servidor de desenvolvimento (um servidor em execução recarrega a quente). Adicione --force para descartar o cache antes, ou defina pollInterval em uma fonte para atualizar automaticamente.
blume dev --preview      # author workflow: see drafts live
blume build --preview    # render a full preview build
blume sync               # refresh remote content now
blume sync --force       # ...ignoring any cached snapshot

Fontes personalizadas

Qualquer objeto que implemente a interface ContentSource pode ser passado diretamente, e é assim que um adaptador com serializers personalizados — ou qualquer backend não integrado — se conecta sem que seu SDK toque a instalação principal:

import { defineConfig } from "blume";
import { sanitySource } from "blume/sources/sanity.ts";

export default defineConfig({
  content: {
    sources: [
      { type: "filesystem", root: "docs" },
      {
        type: "custom",
        source: sanitySource({
          name: "guides",
          prefix: "guides",
          projectId: "abc123",
          dataset: "production",
          query: `*[_type == "guide"]`,
          // Map custom Portable Text blocks to Blume components
          serializers: {
            callout: (block) => `<Callout>${block.text}</Callout>`,
          },
        }),
      },
    ],
  },
});

Uma fonte normaliza seu formato nativo (Portable Text, blocos do Notion, HTML remoto) para texto Markdown/MDX, de modo que os mesmos componentes e recursos de markdown se aplicam, não importa de onde uma página venha.

Uma fonte personalizada que lê arquivos locais deve definir sourcePath em cada entrada e contentRoot na própria fonte. O sourcePath nomeia o arquivo nos diagnósticos e resolve as imagens relativas ao lado dele; o contentRoot delimita o log do git que data as páginas, então sem ele as páginas da fonte não recebem data de “Última atualização” derivada do git.

Esta página foi útil?