Configurazione

Metti le opzioni del plugin e le eventuali impostazioni di Vite in sitelo.config.js. Nessun vite.config.js è necessario.

export default {
  site: 'https://example.com',
  rss: {
    site: 'https://example.com',
    title: 'Il mio blog',
    description: 'Ultimi articoli',
    routePrefix: '/blog',
  },
  vite: {
    publicDir: 'static',
    build: {
      emptyOutDir: true,
      outDir: 'public',
    },
    server: {
      port: 8888,
    },
  },
}

Opzioni del plugin

Toolbar di sviluppo

Mentre sitelo (sviluppo) è in esecuzione, una piccola barra in fondo a ogni pagina mostra il file della pagina, i parametri e quante island server ci sono nella pagina. Usa il pulsante del viewport per ciclare fra le anteprime Desktop / Tablet / Mobile (in un iframe, così le media query combaciano), e Copy per un blocco di informazioni di debug quando apri una segnalazione. Non compare mai nell’output di sitelo build.

// sitelo.config.js
export default {
  devToolbar: false, // nascondi per tutti su questo progetto
}

Opzioni di Vite

Tutto ciò che sta sotto vite viene fuso nella configurazione di Vite. I flag della CLI (per esempio --port) hanno la precedenza su entrambi.

Le build di sitelo impostano build.rollupOptions.checks.pluginTimings: false. Altrimenti Rolldown segnala che gli hook dei plugin dominano la build, cosa che su un sito sitelo è sempre vera — generare le pagine è la build — quindi nomina lo stesso plugin a ogni esecuzione. Mettilo a true sotto vite quando profili i tuoi plugin.

Un vite.config.js esistente

È ancora supportato — o solo opzioni di Vite, oppure controllo completo del plugin:

// Solo opzioni Vite; sitelo inietta comunque il plugin
export default {
  publicDir: 'static',
  server: { port: 8888 },
}
// Registra il plugin da solo
import htmlPages from 'sitelo'

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

Se il plugin è già nella tua configurazione di Vite, metti lì le opzioni del plugin — e non anche come opzioni del plugin in sitelo.config.js (sitelo darà errore).

Sitemap e RSS

Imposta site per produrre dist/sitemap.xml.

RSS:

export default {
  rss: {
    site: 'https://example.com',
    title: 'Il mio blog',
    description: 'Ultimi articoli',
    routePrefix: '/blog',
  },
}

Produce dist/rss.xml con un elemento per ogni pagina sotto routePrefix.

Uno <script src> rotto e gli href dei fogli di stile fanno già fallire la build (vedi missingAssets). linkCheck copre l’altra metà: i link interni <a href> che puntano a una pagina che non esiste.

export default {
  linkCheck: true,   // 'warn' (predefinito), 'error', oppure un oggetto di opzioni
}

Dopo sitelo build, ogni link interno nell’output viene risolto e tutto ciò che non ha una pagina dietro viene segnalato, raggruppato per la pagina in cui compare:

[sitelo] 3 link interni rotti

  index.html
    ../escape           -> esce dalla directory di output
    /abuot              -> pagina inesistente
    /blog/missing-post  -> pagina inesistente

Come si risolvono i link

Il controllo gira sul sito prodotto, non sulla tabella delle rotte — quindi tiene conto di cleanUrls, dei gruppi di rotte, di mapOutputPath, dei file copiati da public/ e delle pagine prodotte da rotte dinamiche. Gira anche dopo l’ottimizzazione delle immagini e Pagefind, quindi vede esattamente ciò che viene pubblicato. Un link è valido quando gli risponde un file vero, provato nell’ordine in cui lo farebbe un host statico:

I link relativi (../about) si risolvono rispetto alla pagina che li contiene, e uno che esce dalla cartella di output viene segnalato. Le query string vengono ignorate nella risoluzione — /about?utm=x controlla /about.

I link esterni non vengono mai scaricati. https://, i relativi al protocollo //cdn.example.com, mailto:, tel: e gli altri schemi vengono saltati del tutto.

Opzioni

OpzionePredefinitoDescrizione
mode'warn''warn' registra e prosegue; 'error' fa fallire la build — utile in CI
exclude[]Glob o espressioni regolari di href da saltare
checkFragmentsfalseVerifica anche che i target #frammento esistano nella pagina collegata
export default {
  linkCheck: {
    mode: 'error',                    // fai fallire la build su un link morto
    checkFragments: true,             // verifica anche i target #frammento
    exclude: ['/api/**', /^\/legacy\//],
  },
}

checkFragments è disattivato per impostazione predefinita perché gli id aggiunti da JavaScript lato client non sono nell’HTML costruito, e verrebbero segnalati come mancanti. Contano come target di frammento sia gli attributi id sia i vecchi name delle ancore.

Siti serviti da una base

Se il tuo sito è pubblicato sotto una base (per esempio un sito di progetto su GitHub Pages), un link relativo alla radice che non porta quella base viene segnalato. /about su un sito servito da /repo/ manda il browser alla radice dell’host, non dentro il tuo sito — risolverlo comunque rispetto all’output nasconderebbe esattamente l’errore che vale la pena cogliere. Usa exclude quando è voluto.

Audit Lighthouse

sitelo lighthouse analizza la build finita con Lighthouse. È una peer dependency opzionale:

npm install -D lighthouse

sitelo serve dist/ nello stesso modo di sitelo preview, punta un Chrome headless a ogni pagina e stampa i punteggi:

[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

Ogni pagina viene analizzata all’URL con cui il sito la collega — /docs, mai dist/docs.html — quindi cleanUrls, i gruppi di rotte e una base sono tutti tenuti in conto. Aggiungi delle soglie e il report diventa un controllo: qualunque punteggio sotto la sua soglia fa fallire il comando.

export default {
  lighthouse: {
    exclude: ['404.html'],   // la pagina 404 raramente vale un audit
    thresholds: {
      performance: 90,
      accessibility: 100,
      'best-practices': 95,
      seo: 100,
    },
  },
}

I punteggi si scrivono nel modo in cui li mostra Lighthouse (0–100); funzionano anche le sue frazioni 0–1. Usa mode: 'warn' per registrare invece di far fallire.

export default {
  lighthouse: {
    formFactor: 'desktop',   // il preset desktop di Lighthouse
    runs: 3,                 // tre esecuzioni per pagina, punteggio mediano
    output: true,            // report completi in .sitelo/lighthouse/
    onBuild: true,           // esegui l’audit anche alla fine di sitelo build
    flags: { throttlingMethod: 'provided' },
  },
}

Lighthouse guida un Chrome vero: installane uno dove gira l’audit, oppure punta CHROME_PATH a un eseguibile. I punteggi di prestazione si muovono fra un’esecuzione e l’altra, quindi usa runs: 3 prima di fissarci sopra una soglia.

Ricerca con Pagefind

Ricerca statica opzionale mossa da Pagefind, una peer dependency facoltativa. Installala quando vuoi la ricerca, poi abilita l’indicizzazione, marca i contenuti, monta l’interfaccia ed esegui sitelo build.

npm install -D pagefind

1. Abilita l’indicizzazione

Imposta pagefind: true. Dopo sitelo build ottieni dist/pagefind/, e per impostazione predefinita una copia in public/pagefind/ così il prossimo sitelo (sviluppo) o sitelo preview può servire /pagefind/ senza ricostruire.

export default {
  pagefind: true,
}

2. Marca i contenuti e aggiungi un punto d’innesto

Metti data-pagefind-body sul contenuto principale così navigazione e piè di pagina non vengono indicizzati. Lascia un elemento vuoto per l’interfaccia di ricerca:

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

export default () =>
  html({ lang: 'it' },
    head(
      title('Il mio sito'),
      link({ rel: 'stylesheet', href: '/styles.css' }),
      script({ type: 'module', src: '/main.js' }),
    ),
    body(
      header(
        a({ href: '/' }, 'Home'),
        div({ id: 'search' }),
      ),
      main({ 'data-pagefind-body': '' },
        h1('Ciao'),
        p('Viene indicizzata solo questa regione.'),
      ),
    ),
  )

3. Monta l’interfaccia di Pagefind

Carica /pagefind/pagefind-ui.js e pagefind-ui.css dal tuo script client (solo dopo che una build ha prodotto l’indice):

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

  // L’indice esiste solo dopo `sitelo build` (sincronizzato in public/pagefind per impostazione predefinita)
  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. Fai la build e ignora il bundle sincronizzato

sitelo build
# poi: sitelo preview — oppure sitelo (dev) usando public/pagefind
public/pagefind/

Le opzioni avanzate (syncPublic, glob, lingua, selettori, …) vanno su un oggetto: pagefind: { syncPublic: false, glob: '**/*.html' }. Opzioni complete dell’interfaccia: pagefind.app.

404

Crea src/404.ht.js per ottenere dist/404.html. Altrimenti ne viene generata una predefinita e pulita.