Konfiguration

Plugin-Optionen und optionale Vite-Einstellungen gehören in sitelo.config.js. Eine vite.config.js ist nicht erforderlich.

export default {
  site: 'https://example.com',
  rss: {
    site: 'https://example.com',
    title: 'Mein Blog',
    description: 'Neueste Beiträge',
    routePrefix: '/blog',
  },
  vite: {
    publicDir: 'static',
    build: {
      emptyOutDir: true,
      outDir: 'public',
    },
    server: {
      port: 8888,
    },
  },
}

Plugin-Optionen

Entwickler-Toolbar

Während sitelo (dev) läuft, zeigt eine schmale Leiste am unteren Rand jeder Seite die Seitendatei, die Parameter und die Anzahl der Server-Islands auf der Seite. Mit der Viewport-Schaltfläche wechselst du zwischen den Vorschauen Desktop / Tablet / Mobile (in einem iframe, damit Media Queries passen), und Copy liefert einen Debug-Block für Fehlermeldungen. In der Ausgabe von sitelo build taucht sie nie auf.

// sitelo.config.js
export default {
  devToolbar: false, // für alle in diesem Projekt ausblenden
}

Vite-Optionen

Alles unter vite wird in Vites Konfiguration eingemischt. CLI-Optionen (etwa --port) haben Vorrang vor beidem.

Vorhandene vite.config.js

Wird weiterhin unterstützt — entweder nur Vite-Optionen oder volle Kontrolle über das Plugin:

// Nur Vite-Optionen; sitelo bindet das Plugin trotzdem ein
export default {
  publicDir: 'static',
  server: { port: 8888 },
}
// Das Plugin selbst registrieren
import htmlPages from 'sitelo'

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

Steckt das Plugin bereits in deiner Vite-Konfiguration, gehören die Plugin-Optionen dorthin — und nicht zusätzlich als Plugin-Optionen in sitelo.config.js (sitelo meldet dann einen Fehler).

Sitemap und RSS

Setze site, um dist/sitemap.xml zu erzeugen.

RSS:

export default {
  rss: {
    site: 'https://example.com',
    title: 'Mein Blog',
    description: 'Neueste Beiträge',
    routePrefix: '/blog',
  },
}

Erzeugt dist/rss.xml mit einem Eintrag für jede Seite unter routePrefix.

Defekte <script src> und Stylesheet-hrefs lassen den Build bereits fehlschlagen (siehe missingAssets). linkCheck deckt die andere Hälfte ab: interne <a href>-Links, die auf eine nicht existierende Seite zeigen.

export default {
  linkCheck: true,   // 'warn' (Standard), 'error', oder ein Optionsobjekt
}

Nach sitelo build wird jeder interne Link in der Ausgabe aufgelöst, und alles ohne Seite dahinter wird gemeldet, gruppiert nach der Seite, auf der es vorkommt:

[sitelo] 3 defekte interne Links

  index.html
    ../escape           -> verlässt das Ausgabeverzeichnis
    /abuot              -> Seite existiert nicht
    /blog/missing-post  -> Seite existiert nicht

Wie Links aufgelöst werden

Die Prüfung läuft gegen die erzeugte Website, nicht gegen die Routentabelle — sie berücksichtigt also cleanUrls, Routengruppen, mapOutputPath, aus public/ kopierte Dateien und von dynamischen Routen erzeugte Seiten. Sie läuft außerdem nach der Bildoptimierung und nach Pagefind, sieht also genau das, was ausgeliefert wird. Ein Link ist gültig, wenn eine echte Datei ihn beantwortet, geprüft in der Reihenfolge, die ein statischer Hoster verwenden würde:

Relative Links (../about) werden gegen die Seite aufgelöst, die sie enthält, und solche, die aus dem Ausgabeverzeichnis herausführen, werden gemeldet. Query-Strings bleiben beim Auflösen unbeachtet — /about?utm=x prüft /about.

Externe Links werden nie abgerufen. https://, protokollrelative //cdn.example.com, mailto:, tel: und andere Schemata werden vollständig übersprungen.

Optionen

OptionStandardBeschreibung
mode'warn''warn' protokolliert und macht weiter; 'error' lässt den Build fehlschlagen — nützlich in der CI
exclude[]Globs oder reguläre Ausdrücke für zu überspringende hrefs
checkFragmentsfalsePrüft zusätzlich, ob #fragment-Ziele in der verlinkten Seite existieren
export default {
  linkCheck: {
    mode: 'error',                    // lässt den Build bei einem toten Link fehlschlagen
    checkFragments: true,             // prüft auch #fragment-Ziele
    exclude: ['/api/**', /^\/legacy\//],
  },
}

checkFragments ist standardmäßig aus, weil ids, die per Client-JavaScript entstehen, nicht im gebauten HTML stehen und als fehlend gemeldet würden. Als Fragment-Ziele zählen sowohl id als auch das alte Anker-Attribut name.

Websites unter einer Base

Wird deine Website unter einer base ausgeliefert (etwa eine GitHub-Pages-Projektseite), dann wird jeder wurzelrelative Link gemeldet, der diese Base nicht trägt. Ein /about auf einer Website unter /repo/ schickt den Browser zur Wurzel des Hosts statt in deine Website — es trotzdem gegen die Ausgabe aufzulösen würde genau den Fehler verbergen, der es wert ist, gefunden zu werden. Nutze exclude, wenn es Absicht ist.

Lighthouse-Audits

sitelo lighthouse prüft den fertigen Build mit Lighthouse. Das Paket ist eine optionale Peer-Dependency:

npm install -D lighthouse

sitelo serviert dist/ genauso wie sitelo preview, schickt ein Headless-Chrome auf jede Seite und gibt die Scores aus:

[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

Jede Seite wird unter der URL geprüft, die die Website selbst verlinkt — /docs, nie dist/docs.html —, damit sind cleanUrls, Route-Gruppen und ein base berücksichtigt. Mit Schwellenwerten wird aus dem Bericht eine Prüfung: Was darunter liegt, lässt den Befehl fehlschlagen.

export default {
  lighthouse: {
    exclude: ['404.html'],   // die 404-Seite lohnt einen Audit selten
    thresholds: {
      performance: 90,
      accessibility: 100,
      'best-practices': 95,
      seo: 100,
    },
  },
}

Scores werden so geschrieben, wie Lighthouse sie anzeigt (0100); die eigenen Brüche von 0 bis 1 funktionieren ebenso. Mit mode: 'warn' wird nur geloggt.

export default {
  lighthouse: {
    formFactor: 'desktop',   // Desktop-Preset von Lighthouse
    runs: 3,                 // drei Durchläufe pro Seite, Median als Ergebnis
    output: true,            // vollständige Berichte in .sitelo/lighthouse/
    onBuild: true,           // auch am Ende von sitelo build prüfen
    flags: { throttlingMethod: 'provided' },
  },
}

Lighthouse steuert ein echtes Chrome: eines dort installieren, wo der Audit läuft, oder CHROME_PATH auf eine Binary zeigen lassen. Performance-Scores schwanken zwischen Durchläufen — vor einem Schwellenwert darauf lohnt runs: 3.

Pagefind-Suche

Optionale statische Suche mit Pagefind, einer optionalen Peer-Dependency. Installiere sie, wenn du Suche möchtest, aktiviere dann die Indexierung, markiere den Inhalt, hänge die Oberfläche ein und führe sitelo build aus.

npm install -D pagefind

1. Indexierung aktivieren

Setze pagefind: true. Nach sitelo build bekommst du dist/pagefind/ und standardmäßig eine Kopie in public/pagefind/, damit das nächste sitelo (dev) oder sitelo preview /pagefind/ ohne erneuten Build ausliefern kann.

export default {
  pagefind: true,
}

2. Inhalt markieren und einen Einhängepunkt ergänzen

Setze data-pagefind-body auf den Hauptinhalt, damit Navigation und Fußzeile nicht indexiert werden. Lass ein leeres Element für die Suchoberfläche stehen:

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

export default () =>
  html({ lang: 'de' },
    head(
      title('Meine Website'),
      link({ rel: 'stylesheet', href: '/styles.css' }),
      script({ type: 'module', src: '/main.js' }),
    ),
    body(
      header(
        a({ href: '/' }, 'Startseite'),
        div({ id: 'search' }),
      ),
      main({ 'data-pagefind-body': '' },
        h1('Hallo'),
        p('Nur dieser Bereich wird indexiert.'),
      ),
    ),
  )

3. Die Pagefind-Oberfläche einhängen

Lade /pagefind/pagefind-ui.js und pagefind-ui.css aus deinem Client-Skript (erst nachdem ein Build den Index erzeugt hat):

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

  // Der Index existiert erst nach `sitelo build` (wird standardmäßig nach public/pagefind synchronisiert)
  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. Bauen und das synchronisierte Bundle ignorieren

sitelo build
# danach: sitelo preview — oder sitelo (dev) mit public/pagefind
public/pagefind/

Fortgeschrittene Optionen (syncPublic, glob, Sprache, Selektoren, …) stehen in einem Objekt: pagefind: { syncPublic: false, glob: '**/*.html' }. Alle Optionen der Oberfläche: pagefind.app.

404

Lege src/404.ht.js an, um dist/404.html zu erhalten. Andernfalls wird eine schlichte Standardseite erzeugt.