Configuración

Pon las opciones del plugin y los ajustes opcionales de Vite en sitelo.config.js. No hace falta ningún vite.config.js.

export default {
  site: 'https://example.com',
  rss: {
    site: 'https://example.com',
    title: 'Mi blog',
    description: 'Últimas entradas',
    routePrefix: '/blog',
  },
  vite: {
    publicDir: 'static',
    build: {
      emptyOutDir: true,
      outDir: 'public',
    },
    server: {
      port: 8888,
    },
  },
}

Opciones del plugin

Barra de herramientas de desarrollo

Mientras sitelo (dev) está en marcha, una pequeña barra al pie de cada página muestra el archivo de la página, sus parámetros y cuántas islas de servidor hay en ella. Usa el botón de viewport para alternar entre Desktop / Tablet / Mobile (en un iframe, para que las media queries coincidan), y Copy para obtener un volcado de depuración al abrir incidencias. Nunca aparece en la salida de sitelo build.

// sitelo.config.js
export default {
  devToolbar: false, // ocúltala para todo el mundo en este proyecto
}

Opciones de Vite

Todo lo que haya bajo vite se fusiona con la configuración de Vite. Las opciones de la CLI (por ejemplo --port) tienen prioridad sobre ambas.

Si ya tienes un vite.config.js

Sigue siendo compatible — o solo opciones de Vite, o control total del plugin:

// Solo opciones de Vite; sitelo sigue inyectando el plugin
export default {
  publicDir: 'static',
  server: { port: 8888 },
}
// Registra el plugin tú mismo
import htmlPages from 'sitelo'

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

Si el plugin ya está en tu configuración de Vite, pon ahí las opciones del plugin — y no también como opciones de plugin en sitelo.config.js (sitelo dará error).

Sitemap y RSS

Define site para emitir dist/sitemap.xml.

RSS:

export default {
  rss: {
    site: 'https://example.com',
    title: 'Mi blog',
    description: 'Últimas entradas',
    routePrefix: '/blog',
  },
}

Produce dist/rss.xml con un elemento por cada página bajo routePrefix.

Comprobación de enlaces

Los <script src> y los href de hojas de estilo rotos ya hacen fallar la compilación (ver missingAssets). linkCheck cubre la otra mitad: los enlaces internos <a href> que apuntan a una página que no existe.

export default {
  linkCheck: true,   // 'warn' (por defecto), 'error', o un objeto de opciones
}

Después de sitelo build, se resuelve cada enlace interno de la salida y se informa de todo lo que no tenga una página detrás, agrupado por la página en la que aparece:

[sitelo] 3 enlaces internos rotos

  index.html
    ../escape           -> se sale del directorio de salida
    /abuot              -> no existe esa página
    /blog/missing-post  -> no existe esa página

Cómo se resuelven los enlaces

La comprobación se ejecuta sobre el sitio emitido, no sobre la tabla de rutas, así que tiene en cuenta cleanUrls, los grupos de rutas, mapOutputPath, los archivos copiados desde public/ y las páginas producidas por rutas dinámicas. También se ejecuta después de la optimización de imágenes y de Pagefind, así que ve exactamente lo que se publica. Un enlace es válido cuando un archivo real responde a él, probando en el mismo orden que un hosting estático:

Los enlaces relativos (../about) se resuelven respecto a la página que los contiene, y se informa de los que se salen del directorio de salida. Las cadenas de consulta se ignoran al resolver: /about?utm=x comprueba /about.

Los enlaces externos nunca se descargan. https://, los relativos al protocolo //cdn.example.com, mailto:, tel: y otros esquemas se omiten por completo.

Opciones

OpciónPor defectoDescripción
mode'warn''warn' registra y continúa; 'error' hace fallar la compilación — útil en CI
exclude[]Globs o expresiones regulares de hrefs que hay que omitir
checkFragmentsfalseVerifica además que los destinos #fragmento existan en la página enlazada
export default {
  linkCheck: {
    mode: 'error',                    // falla la compilación si hay un enlace roto
    checkFragments: true,             // verifica también los destinos #fragmento
    exclude: ['/api/**', /^\/legacy\//],
  },
}

checkFragments viene desactivado porque los ids que añade el JavaScript de cliente no están en el HTML compilado, y se reportarían como inexistentes. Cuentan como destinos de fragmento tanto id como el atributo heredado name de las anclas.

Sitios servidos desde una base

Si tu sitio se despliega bajo una base (un sitio de proyecto de GitHub Pages, por ejemplo), se informa de cualquier enlace relativo a la raíz que no lleve esa base. Un /about en un sitio servido desde /repo/ manda el navegador a la raíz del host, no a tu sitio; resolverlo igualmente contra la salida ocultaría justo el error que vale la pena detectar. Usa exclude cuando sea intencionado.

Auditorías Lighthouse

sitelo lighthouse audita la compilación final con Lighthouse. Es una dependencia par opcional:

npm install -D lighthouse

sitelo sirve dist/ igual que sitelo preview, apunta un Chrome headless a cada página e imprime las puntuaciones:

[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 se audita en la URL que el sitio enlaza — /docs, nunca dist/docs.html —, así que cleanUrls, los grupos de rutas y un base quedan cubiertos. Con umbrales, el informe pasa a ser una comprobación: cualquier puntuación por debajo hace fallar el comando.

export default {
  lighthouse: {
    exclude: ['404.html'],   // la página 404 rara vez merece una auditoría
    thresholds: {
      performance: 90,
      accessibility: 100,
      'best-practices': 95,
      seo: 100,
    },
  },
}

Las puntuaciones se escriben como las muestra Lighthouse (0100); sus fracciones de 0 a 1 también valen. Usa mode: 'warn' para registrar sin fallar.

export default {
  lighthouse: {
    formFactor: 'desktop',   // preajuste de escritorio de Lighthouse
    runs: 3,                 // tres ejecuciones por página, puntuación mediana
    output: true,            // informes completos en .sitelo/lighthouse/
    onBuild: true,           // audita también al final de sitelo build
    flags: { throttlingMethod: 'provided' },
  },
}

Lighthouse controla un Chrome real: instálalo donde se ejecute la auditoría o apunta CHROME_PATH a un binario. Las puntuaciones de rendimiento varían entre ejecuciones, así que usa runs: 3 antes de fijar un umbral sobre ellas.

Búsqueda con Pagefind

Búsqueda estática opcional con Pagefind, una dependencia peer opcional. Instálala cuando quieras búsqueda, luego activa la indexación, marca el contenido, monta la interfaz y ejecuta sitelo build.

npm install -D pagefind

1. Activa la indexación

Pon pagefind: true. Después de sitelo build obtienes dist/pagefind/ y, por defecto, una copia en public/pagefind/ para que el siguiente sitelo (dev) o sitelo preview pueda servir /pagefind/ sin recompilar.

export default {
  pagefind: true,
}

2. Marca el contenido y añade un punto de montaje

Pon data-pagefind-body en el contenido principal para que no se indexen la navegación ni el pie. Deja un elemento vacío para la interfaz de búsqueda:

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

export default () =>
  html({ lang: 'es' },
    head(
      title('Mi sitio'),
      link({ rel: 'stylesheet', href: '/styles.css' }),
      script({ type: 'module', src: '/main.js' }),
    ),
    body(
      header(
        a({ href: '/' }, 'Inicio'),
        div({ id: 'search' }),
      ),
      main({ 'data-pagefind-body': '' },
        h1('Hola'),
        p('Solo se indexa esta región.'),
      ),
    ),
  )

3. Monta la interfaz de Pagefind

Carga /pagefind/pagefind-ui.js y pagefind-ui.css desde tu script de cliente (solo después de que una compilación haya producido el índice):

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

  // El índice solo existe tras `sitelo build` (se sincroniza a public/pagefind por defecto)
  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 el paquete sincronizado

sitelo build
# luego: sitelo preview — o sitelo (dev) usando public/pagefind
public/pagefind/

Las opciones avanzadas (syncPublic, glob, idioma, selectores, …) van en un objeto: pagefind: { syncPublic: false, glob: '**/*.html' }. Todas las opciones de la interfaz: pagefind.app.

404

Crea src/404.ht.js para obtener dist/404.html. Si no, se genera uno por defecto.