🍡 mochi

SSR framework for Svelte 5 + Bun with islands-based selective hydration

Svelte config

Drop a svelte.config.js at the root of your app to customize the Svelte compiler. Mochi reads it on startup (and during mochi build) and merges its compilerOptions into the framework defaults.

// svelte.config.js
export default {
  compilerOptions: {
    experimental: {
      async: true,
    },
  },
};

The file is optional. If it is missing, Mochi logs a warning and uses its defaults — the most important one being experimental.async: true, which is what allows await at the top level of .svelte components and is currently a Svelte 5 opt-in.

Custom config path

By default Mochi looks for ./svelte.config.js at the current working directory. Point it elsewhere with svelteConfigPath on Mochi.serve() or build() — relative paths resolve against process.cwd(), absolute paths are used as-is:

await Mochi.serve({
  svelteConfigPath: './configs/svelte.staging.config.js',
  routes,
});

Merge order

  1. Framework defaults
  2. Your compilerOptions — overlaid on top of the defaults. You can override most fields.

Framework-owned fields

A small set of compilerOptions are managed by Mochi and cannot be overridden, because they are part of the framework’s contract with the compiler:

FieldWhy
generateSet to 'server' for SSR builds, 'client' for hydration
filenameAlways the actual file path
devTied to the development flag passed to Mochi.serve()

Everything else is yours to set.

Where it applies

The merged options are used everywhere Mochi invokes the Svelte compiler:

  • SSR compilation of .svelte files
  • SSR compilation of .svelte.js / .svelte.ts rune modules
  • Client-side island bundles (both .svelte and .svelte.[jt]s)
  • mdsvex .md / .svx files (server target)

Limitations

Only compilerOptions is read today. SvelteKit-style keys like preprocess, extensions, and kit are not supported.