Configuração

Põe as opções do plugin e as definições opcionais do Vite em sitelo.config.js. Não é preciso nenhum vite.config.js.

export default {
  site: 'https://example.com',
  rss: {
    site: 'https://example.com',
    title: 'O meu blogue',
    description: 'Últimos artigos',
    routePrefix: '/blog',
  },
  vite: {
    publicDir: 'static',
    build: {
      emptyOutDir: true,
      outDir: 'public',
    },
    server: {
      port: 8888,
    },
  },
}

Opções do plugin

Barra de ferramentas de desenvolvimento

Enquanto o sitelo (dev) corre, uma pequena barra no fundo de cada página mostra o ficheiro da página, os parâmetros e quantas ilhas de servidor existem nela. Usa o botão de viewport para alternar entre Desktop / Tablet / Mobile (num iframe, para que as media queries batam certo), e Copy para obteres um bloco de depuração ao abrir issues. Nunca aparece na saída de sitelo build.

// sitelo.config.js
export default {
  devToolbar: false, // ocultar para toda a gente neste projeto
}

Opções do Vite

Tudo o que estiver sob vite é fundido na configuração do Vite. As opções da CLI (por exemplo --port) têm precedência sobre ambas.

Se já tens um vite.config.js

Continua a ser suportado — ou só opções do Vite, ou controlo total do plugin:

// Apenas opções do Vite; o sitelo continua a injetar o plugin
export default {
  publicDir: 'static',
  server: { port: 8888 },
}
// Registar o plugin manualmente
import htmlPages from 'sitelo'

export default {
  plugins: [htmlPages({
    site: 'https://example.com',
  })],
}

Se o plugin já estiver na tua configuração do Vite, põe aí as opções do plugin — e não também como opções de plugin em sitelo.config.js (o sitelo dará erro).

Sitemap e RSS

Define site para emitir dist/sitemap.xml.

RSS:

export default {
  rss: {
    site: 'https://example.com',
    title: 'O meu blogue',
    description: 'Últimos artigos',
    routePrefix: '/blog',
  },
}

Produz dist/rss.xml com um item por cada página sob routePrefix.

Verificação de ligações

Os <script src> e hrefs de folhas de estilo partidos já fazem falhar a compilação (vê missingAssets). O linkCheck cobre a outra metade: as ligações internas <a href> que apontam para uma página que não existe.

export default {
  linkCheck: true,   // 'warn' (por omissão), 'error', ou um objeto de opções
}

Depois de sitelo build, cada ligação interna da saída é resolvida e tudo o que não tenha uma página por trás é reportado, agrupado pela página onde aparece:

[sitelo] 3 ligações internas partidas

  index.html
    ../escape           -> sai do diretório de saída
    /abuot              -> não existe essa página
    /blog/missing-post  -> não existe essa página

Como as ligações são resolvidas

A verificação corre sobre o site emitido, não sobre a tabela de rotas — por isso tem em conta cleanUrls, os grupos de rotas, mapOutputPath, os ficheiros copiados de public/ e as páginas produzidas por rotas dinâmicas. Corre também depois da otimização de imagens e do Pagefind, por isso vê exatamente o que é publicado. Uma ligação é válida quando um ficheiro real lhe responde, tentado pela ordem que um alojamento estático usaria:

As ligações relativas (../about) são resolvidas relativamente à página que as contém, e as que saem do diretório de saída são reportadas. As query strings são ignoradas na resolução — /about?utm=x verifica /about.

As ligações externas nunca são descarregadas. https://, as relativas ao protocolo //cdn.example.com, mailto:, tel: e outros esquemas são saltados por completo.

Opções

OpçãoPor omissãoDescrição
mode'warn''warn' regista e continua; 'error' faz falhar a compilação — útil em CI
exclude[]Globs ou expressões regulares de hrefs a saltar
checkFragmentsfalseVerifica também se os destinos #fragmento existem na página ligada
export default {
  linkCheck: {
    mode: 'error',                    // falha a compilação perante uma ligação morta
    checkFragments: true,             // verifica também os destinos #fragmento
    exclude: ['/api/**', /^\/legacy\//],
  },
}

checkFragments vem desligado porque os ids acrescentados por JavaScript de cliente não estão no HTML compilado, e seriam reportados como inexistentes. Contam como destinos de fragmento tanto id como o antigo atributo de âncora name.

Sites servidos a partir de uma base

Se o teu site for implementado sob uma base (um site de projeto do GitHub Pages, por exemplo), qualquer ligação relativa à raiz que não leve essa base é reportada. Um /about num site servido a partir de /repo/ manda o navegador para a raiz do host, não para dentro do teu site — resolvê-la à mesma contra a saída esconderia precisamente o erro que vale a pena apanhar. Usa exclude quando for de propósito.

Auditorias Lighthouse

sitelo lighthouse audita a compilação final com Lighthouse. É uma dependência par opcional:

npm install -D lighthouse

O sitelo serve dist/ tal como o sitelo preview, aponta um Chrome headless a cada página e imprime as pontuações:

[sitelo] lighthouse mobile - 3 pages

  page           perf  a11y  best   seo
  /                98   100   100   100
  /docs            95   100   100   100
  /docs/routing    97   100   100   100

[sitelo] lighthouse audited 3 pages in 31.4s

Cada página é auditada no URL que o site liga — /docs, nunca dist/docs.html —, por isso cleanUrls, os grupos de rotas e um base ficam cobertos. Com limiares, o relatório passa a ser uma verificação: qualquer pontuação abaixo faz o comando falhar.

export default {
  lighthouse: {
    exclude: ['404.html'],   // a página 404 raramente vale uma auditoria
    thresholds: {
      performance: 90,
      accessibility: 100,
      'best-practices': 95,
      seo: 100,
    },
  },
}

As pontuações escrevem-se como o Lighthouse as mostra (0100); as frações de 0 a 1 também funcionam. Usa mode: 'warn' para registar sem falhar.

export default {
  lighthouse: {
    formFactor: 'desktop',   // predefinição desktop do Lighthouse
    runs: 3,                 // três execuções por página, pontuação mediana
    output: true,            // relatórios completos em .sitelo/lighthouse/
    onBuild: true,           // audita também no fim de sitelo build
    flags: { throttlingMethod: 'provided' },
  },
}

O Lighthouse conduz um Chrome real: instala um onde a auditoria corre, ou aponta CHROME_PATH a um binário. As pontuações de desempenho variam entre execuções, por isso usa runs: 3 antes de fixares um limiar sobre elas.

Pesquisa Pagefind

Pesquisa estática opcional com o Pagefind, uma dependência de pares opcional. Instala-o quando quiseres pesquisa, depois ativa a indexação, marca o conteúdo, monta a interface e corre sitelo build.

npm install -D pagefind

1. Ativa a indexação

Põe pagefind: true. Depois de sitelo build obténs dist/pagefind/ e, por omissão, uma cópia em public/pagefind/ para que o próximo sitelo (dev) ou sitelo preview possa servir /pagefind/ sem recompilar.

export default {
  pagefind: true,
}

2. Marca o conteúdo e acrescenta um ponto de montagem

Põe data-pagefind-body no conteúdo principal para que a navegação e o rodapé não sejam indexados. Deixa um elemento vazio para a interface de pesquisa:

import {
  html, head, title, link, script, body, header, a, div, main, h1, p,
} from 'javascript-to-html'

export default () =>
  html({ lang: 'pt' },
    head(
      title('O meu site'),
      link({ rel: 'stylesheet', href: '/styles.css' }),
      script({ type: 'module', src: '/main.js' }),
    ),
    body(
      header(
        a({ href: '/' }, 'Início'),
        div({ id: 'search' }),
      ),
      main({ 'data-pagefind-body': '' },
        h1('Olá'),
        p('Só esta região é indexada.'),
      ),
    ),
  )

3. Monta a interface do Pagefind

Carrega /pagefind/pagefind-ui.js e pagefind-ui.css a partir do teu script de cliente (só depois de uma compilação ter produzido o índice):

async function initSearch() {
  const mount = document.querySelector('#search')
  if (!mount) return

  // O índice só existe depois de `sitelo build` (sincronizado para public/pagefind por omissão)
  try {
    const probe = await fetch('/pagefind/pagefind-ui.js', { method: 'HEAD' })
    if (!probe.ok) return
  } catch {
    return
  }

  const style = document.createElement('link')
  style.rel = 'stylesheet'
  style.href = '/pagefind/pagefind-ui.css'
  document.head.appendChild(style)

  await new Promise((resolve, reject) => {
    const script = document.createElement('script')
    script.src = '/pagefind/pagefind-ui.js'
    script.onload = resolve
    script.onerror = reject
    document.body.appendChild(script)
  })

  new window.PagefindUI({
    element: '#search',
    showImages: false,
  })
}

initSearch()

4. Compila e ignora o pacote sincronizado

sitelo build
# depois: sitelo preview — ou sitelo (dev) a usar public/pagefind
public/pagefind/

As opções avançadas (syncPublic, glob, idioma, seletores, …) vão num objeto: pagefind: { syncPublic: false, glob: '**/*.html' }. Todas as opções da interface: pagefind.app.

404

Cria src/404.ht.js para obteres dist/404.html. Caso contrário é gerada uma página por omissão.