Skip to content

API

ogygia/vite

The Vite plugin and its config surface.

On this page

Auto-generated from the package’s own .d.ts on every build — signatures, JSDoc, and examples straight from the source.

import { … } from 'ogygia/vite';

Exports collectIslandDepModulepreloads · derive_css_scope_owners · dev_hmr_client_source · import_keys_hint · island_vpaths_affected_by_file · islandChunkFileName · islandDepsHandoffPath · islandId · islandPublicUrl · islandVirtualId · mpaSpeculationRules · needs_csr_false_full_reload · needs_island_entry_full_reload · normalize_import_keys · ogygia · regionId · regionIdentity · rewrite_island_sourcemap_sources · rewrite_lake_import_to_placeholder · same_module_path · strategyKey · wrapperVirtualId · CLIENT_BINDING_STUB · DEFAULT_IMPORT_KEYS · ContentPreset · OgygiaOptions · OgygiaPreset · OgygiaRateLimit · RegionsOptions · ImportKeys

collectIslandDepModulepreloads

function

From a client generateBundle output, collect transitive static imports for each og-region.<id>.js facade. Keys/values are public URLs (/_app/immutable/…). Used so SSR can modulepreload hashed dependency chunks for hydrate: 'load' islands (Vite’s auto graph does not apply to @vite-ignore import(entry)).

function collectIslandDepModulepreloads(bundle: Record<string, {
  type: string;
  fileName?: string;
  imports?: string[];
  dynamicImports?: string[];
  viteMetadata?: {
    importedCss?: Set<string> | string[];
  };
}>): {
  js: Record<string, string[]>;
  css: Record<string, string[]>;
};

derive_css_scope_owners

function

Which top-level route scopes can reach abs_file — walked UP the dev module graph until route files are hit. A scope is a route file's first path segment under src/routes ('(docs)', 'playground', '' for a root-level route file). [] = no route owner found; the client treats that as shared and joins anywhere (the safe default for exotic graphs).

function derive_css_scope_owners(abs_file: string, root: string, graphs: Array<{
  getModulesByFile?: (f: string) => Set<DevGraphModule> | undefined;
} | undefined | null>): string[];

dev_hmr_client_source

function

Client bridge source for virtual:ogygia/dev-hmr (vite serve only): a LAZY map of app CSS under /src, joined per-file when the plugin broadcasts a scoped ogygia:css event — plus full-reload on vite:error.

LAZY is load-bearing. The bridge used to join every /src stylesheet EAGERLY, which painted the whole app's CSS onto every page — invisible while one app owned one look, but the moment two sub-apps share one project (the (docs) / playground split), each page wore the other's skin in dev while prod (Kit's per-route CSS) stayed clean. Now a page boots with exactly its SSR CSS, and joins a stylesheet only when it changes AND this page's route scope owns it (the ogygia-dev-scope meta the handle stamps vs. the owners the plugin derives from the module graph). Once joined, later edits soft-apply through Vite's normal CSS HMR.

Do not strip Kit’s <style data-sveltekit> FOUC bag. Under csr = false that bag is how page + component CSS is delivered (no client module graph for route shells). Removing it blanks the page; a MutationObserver would also delete FOUC styles the SPA router merges in on navigation.

function dev_hmr_client_source(): string;

import_keys_hint

function

function import_keys_hint(import_keys: ImportKeys): RegExp;

island_vpaths_affected_by_file

function

Virtual island ids whose generated source must be dropped when file changes or is deleted. Island ids are hash(componentPath\\0strategyKey) — renaming a host route keeps the same id, so Vite's moduleGraph must be invalidated or it keeps serving the old import.

function island_vpaths_affected_by_file(file: string, entries: Iterable<[string, {
  hostPath?: string | null;
  componentPath?: string | null;
}]>): string[];

islandChunkFileName

function

Deterministic client chunk path for a hydrate island (mirrors og-runtime.<hash>.js). SSR bakes this into <ogygia-region entry> so the sticky runtime can import(entry) with no app-wide regions map — Kit builds server before client, so content-hashed Vite names can't hand off.

function islandChunkFileName(iid: string): string;

islandDepsHandoffPath

function

Stable handoff path: client generateBundle writes; SSR reads at render (Kit is SSR-first).

function islandDepsHandoffPath(root: string): string;

islandId

function

Deterministic short id for a region (stable across dev + build). When salt is set (production OGYGIA_SECRET), ids are not offline-computable (P1-ID). Paths are always posix so SSR/client builds agree across OS path separators.

function islandId(relHostPath: string, index: string | number, salt?: string): string;

islandPublicUrl

function

Public URL for islandChunkFileName (leading slash, same shape as runtime-url).

function islandPublicUrl(iid: string): string;

islandVirtualId

function

Virtual island ENTRY module id — JS re-export of the real component (not a thin .svelte).

const islandVirtualId: (iid: string) => string

mpaSpeculationRules

function

Static Speculation Rules for MPA mode (router: false) — the server handle injects them into every page head. Document rules covering same-origin links, with a per-link/subtree opt-out (data-ogygia-speculate="off", re-enable with "on"); the region endpoint is never speculated. BOTH lists ship: a prerender-capable browser prerenders (prefetch is its first stage), a prefetch-only browser prefetches, an unsupporting one ignores the JSON entirely — graceful by construction, no JS fallback. In SPA mode no rules exist at all: speculation caches serve real navigations only, which a body-swap router can never read.

function mpaSpeculationRules(): string;

needs_csr_false_full_reload

function

Host route shells (+page / +layout / …) never join the browser module graph under csr=false, so Vite's fine-grained HMR for them has no client importer. Force a full reload. Standalone CSS is soft-updated via virtual:ogygia/dev-hmr (Vite inject after Kit FOUC). If that soft path fails, the client bridge listens for vite:error and reloads the document.

function needs_csr_false_full_reload(file: string): boolean;

needs_island_entry_full_reload

function

Island entry .svelte files (the import X from '…' with { hydrate }) sit behind a virtual wrapper; Svelte soft HMR through that edge is unreliable. Shared .ts deps still soft-update.

function needs_island_entry_full_reload(file: string, entries: Iterable<{
  componentPath?: string | null;
}>): boolean;

normalize_import_keys

function

function normalize_import_keys(overrides?: Partial<ImportKeys> | null): ImportKeys;

ogygia

function

Vite plugin: transforms with { hydrate | defer | preset } imports into islands, serves virtual island modules, and wires signed region endpoints for deferred HTML.

Place before sveltekit() in vite.config.

function ogygia(options?: OgygiaOptions): Plugin[];
  • options — Plugin configuration. See OgygiaOptions.

Returns Vite plugins (ogygia pre + island sourcemap fix post). Vite flattens the array.

regionId

function

Hash an identity string (optional production salt) → 12-char region id.

function regionId(identityKey: string, salt?: string): string;

regionIdentity

function

Cross-host stable identity: posix component path + strategyKey. Drives region ids so multiple hosts / import sites / <A /> usages share one module.

function regionIdentity(componentRelPath: string, mark: {
  strategy: string;
  options?: Record<string, unknown> | null;
}): string;

rewrite_island_sourcemap_sources

function

Rewrite vite-plugin-svelte island sourcemap sources so Vite treats them as virtual.

Svelte emits the basename of virtual:ogygia/island/<id>.svelte (just <id>.svelte). That string does not match Vite's virtualSourceRE, so injectSourcesContent tries a disk read and warns "points to missing source files". Pointing sources back at the full virtual module id silences the warning (and keeps maps coherent).

function rewrite_island_sourcemap_sources(moduleId: string, sources: (string | null)[] | undefined): string[];

rewrite_lake_import_to_placeholder

function

Rewrite a lake binding's import to the render-nothing placeholder (client island modules only). Default imports are repointed; named imports drop that specifier (and keep siblings) then add a default import of the placeholder under the same local name.

function rewrite_lake_import_to_placeholder(src: string, local: string, placeholder: string): string;

same_module_path

function

Absolute path equality with querystrings stripped (host vs component vs Vite watch paths).

function same_module_path(a: string | null | undefined, b: string | null | undefined): boolean;

strategyKey

function

Fingerprint of a region mark for dedupe. Same component path + same key → one wrapper/entry.

function strategyKey(mark: {
  strategy: string;
  options?: Record<string, unknown> | null;
}): string;

wrapperVirtualId

function

Portable wrapper module id — SSR / csr=true host binding (Island/ServerIsland/Lake shell).

function wrapperVirtualId(iid: string): string;

CLIENT_BINDING_STUB

const

csr=false CLIENT host binding target. Kit still emits those page nodes; pointing marked imports here (instead of wrappers) keeps N island wrappers/entries out of the page graph so emitFile owns hydrate modules and Rolldown does not thin-facade them. Hydration uses import(entry) only — this stub is never loaded for island JS.

Beside the stub the transform emits import 'virtual:ogygia/fouc-css/<entry>' so Kit still links stylesheets (FOUC) via CSS-only side effects. Importing the full .svelte JS module dual-owns it with the emitFile island entry and Rolldown thin-facades og-region.*. Omitting CSS entirely orphans rules (scoped class hashes, no stylesheet).

const CLIENT_BINDING_STUB = "virtual:ogygia/client-binding-stub"

DEFAULT_IMPORT_KEYS

const

Default import-attribute keys — the two-dial grammar. Internal role names stay hydrate/defer (the wire format + runtime keep those anchors); the user-facing ATTRIBUTE names are the values. Override via ogygia({ importKeys }).

  • render attribute → the MODE: static (inline HTML) | deferred (a hole, fetched) | live (a hole that revalidates). Defaults to static.
  • wake attribute → when the region comes alive: for static that is HYDRATION; for deferred/live that is the FETCH schedule.
const DEFAULT_IMPORT_KEYS: {
  readonly wake: "wake";
  readonly render: "render";
  readonly preset: "preset";
  readonly region: "region";
}

ContentPreset

interface

A named CONTENT preset — a partial content-config referenced by a literal preset: 'name' on a loader macro (import.meta.og.loader.folder('../docs', { preset: 'name' })). The referencing collection's files compile as their own module VARIANTS (?og_preset=name), so the same file used by another collection under another preset renders independently — never a conflict.

interface ContentPreset {/*…*/}

ContentPreset.markdown

Markdown options merged over content.markdown (per setting key — depth-2 replace).

markdown?: MarkdownOptions;

OgygiaOptions

interface

Options for the ogygia Vite plugin — one top-level key per subsystem, each subsystem defaults + its own presets.

interface OgygiaOptions {/*…*/}
import { ogygia } from 'ogygia/vite';
export default defineConfig({
  plugins: [
    ogygia({
      regions: {
        visible: { margin: '200px' },
        presets: { chart: { wake: 'visible', margin: '200px' } }
      },
      regionTtl: 3600
    }),
    sveltekit()
  ]
});

OgygiaOptions.regions

The regions subsystem: island defaults (visible.margin) + named island presets.

regions?: RegionsOptions;

OgygiaOptions.router

Client-side SPA router — app-wide, on by default. It intercepts same-origin links, swaps <body>, merges <head>, and keeps data-ogygia-keep chrome across navigations. No component to place: the server handle injects the runtime + the ogygia-router meta into every page.

  • true (default) — router on, View Transitions on, form continuity on.
  • false — router off (the whole feature is tree-shaken out; same-origin links do full MPA loads, and form continuity — which rides SPA navigation — goes with it).
  • { viewTransitions: false } — router on, but no View Transitions API on navigation.
  • { forms: false } — router on, but an island's half-filled form fields are NOT carried across SPA navigation (continuity off).

Per-page escape hatch (no second config): a page opts itself out of View Transitions by emitting <svelte:head><meta name="ogygia-router" content="plain" /></svelte:head> — the handle injects the app default but a page that sets its own meta wins.

router?: boolean | {
    viewTransitions?: boolean;
    forms?: boolean;
  };

OgygiaOptions.content

The content subsystem. markdown configures the mdsvex preprocessor (themes, remark plugins, heading ids…) — the defaults for every markdown file. presets are named variants a loader macro opts a whole collection into ({ preset: 'name' }); requires markdown to be set (the defaults are the base every preset merges over). The rest are dev HMR options.

content?: ContentPluginOptions & {
    markdown?: MarkdownOptions;
    presets?: Record<string, ContentPreset>;
  };

OgygiaOptions.importKeys

Rename the import-attribute keys claimed by the transform. Defaults stay hydrate / defer / preset. Escape hatch if another tool already uses those names on the same imports.

Preset definitions (RegionsOptions.presets) still use canonical hydrate / defer / margin / remount — only the with { … } spellings in source change.

importKeys?: Partial<ImportKeys>;

OgygiaOptions.rateLimit

Per-IP budget for the signed island endpoint served by ogygiaHandle(). Default { max: 60, windowMs: 60_000 }. Pass false to disable.

rateLimit?: false | OgygiaRateLimit;

OgygiaOptions.sessionCookie

Cookie name to seal into the region MAC (opt-in). Empty/prerender stays unbound. Harvested capability URLs then fail verification without that cookie. Default false (unbound).

sessionCookie?: false | string;

OgygiaOptions.regionTtl

Capability URL lifetime in seconds (default 3600). Clamped to [60, 86400]. Keep short for harvested-URL risk; raise only if long-lived tabs must keep deferred holes valid.

regionTtl?: number;

OgygiaOptions.standalone

standalone?: boolean;

OgygiaPreset

interface

Named strategy bundle referenced from source via with { preset: 'name' } (or the renamed importKeys.preset key).

A preset speaks the SAME two-dial grammar as an inline import: render (the delivery mode) + wake (the schedule), plus the tuning options that aren't allowed inline (margin, maxAge, …).

interface OgygiaPreset {/*…*/}

OgygiaPreset.render

Delivery mode (the render import attribute): 'static' (default — an island that hydrates) | 'deferred' (a hole whose HTML is fetched) | 'live' (a hole that revalidates).

render?: 'static' | 'deferred' | 'live';

OgygiaPreset.wake

Schedule (the wake import attribute): hydration when render is 'static', the FETCH schedule when 'deferred'/'live'. 'load' | 'idle' | 'visible' | 'interaction' | a CSS media query | 'none' (a frozen lake).

wake?: string;

OgygiaPreset.margin

IntersectionObserver rootMargin when the schedule is 'visible'.

margin?: string;

OgygiaPreset.maxAge

render: 'deferred' — response cache max-age for the hole's HTML: seconds (number) or a duration string ('30s' | '5m' | '1h'). Absent or 0no-store: the hole is dynamic, re-rendered on every request. A positive value opts into a private, max-age browser cache. Signed into the hole's endpoint so a harvested URL can't be re-pointed at a longer cache.

With render: 'live' this is instead the client revalidate staleness — use a duration string to stay unit-explicit across both.

maxAge?: number | string;

OgygiaPreset.onExpire

render: 'live' — past maxAge, whether to clear the hole ('empty') or refetch ('fetch').

onExpire?: 'empty' | 'fetch';

OgygiaPreset.revalidate

render: 'live' — the revalidate schedule (false disables). Defaults to wake.

revalidate?: false | string;

OgygiaPreset.keep

Continuity name (the keep import attribute): the live island relocates across SPA navigation instead of remounting when the next page carries the same name.

keep?: string;

OgygiaRateLimit

interface

Per-IP budget for the signed deferred-region / lake-remount endpoint.

interface OgygiaRateLimit {/*…*/}

OgygiaRateLimit.max

Max requests per window (default 60). 0 disables allowing any.

max?: number;

OgygiaRateLimit.windowMs

Sliding window length in milliseconds (default 60_000).

windowMs?: number;

RegionsOptions

interface

The regions subsystem — island defaults + the named preset dictionary. One grammar shared by every subsystem: defaults at the root, variance in presets, referenced from a use site by a LITERAL preset: 'name' (here: import X from '…' with { preset: 'name' }).

interface RegionsOptions {/*…*/}

RegionsOptions.visible

Global defaults for islands that use wake: 'visible' / render+wake: 'visible' without their own margin (via a preset).

visible?: {
    margin?: string;
  };

RegionsOptions.presets

Named strategy bundles. Reference one from an import: import Chart from '$lib/Chart.svelte' with { preset: 'chart' };

presets?: Record<string, OgygiaPreset>;

ImportKeys

type

Import-attribute key names claimed by the transform (with { wake | render | preset | region }). Override via ogygia({ importKeys }) when another tool already uses the default names.

type ImportKeys = {
  wake: string;
  render: string;
  preset: string;
  region: string;
};