Skip to content

Feature: Allow swapping the Svelte compiler module (pluggable compiler) #1348

@baseballyama

Description

@baseballyama

Describe the problem

Summary

Allow @sveltejs/vite-plugin-svelte to load a compiler module from a user-provided source instead of always importing svelte/compiler. This would allow alternative compiler implementations (Rust ports, profiling wrappers, debug tools, test mocks) without requiring a fork of this plugin.

Motivation

Today, users of an alternative Svelte compiler must fork vite-plugin-svelte only to change the compiler import. The change is usually the same in every fork: replace from 'svelte/compiler' with another module specifier. This creates unnecessary maintenance work because every upstream release must be rebased for a very small change.

Use cases

  • Rust compiler ports — for example, rsvelte provides compile, compileModule, preprocess, and parse through an N-API module. Users currently need a forked plugin.
  • Profiling / instrumentation wrappers — wrap the official compiler to collect timing data or inspect the AST.
  • Test mocks — replace the compiler in unit tests without monkey-patching.

Describe the proposed solution

Add a compiler option to svelte():

import { svelte } from '@sveltejs/vite-plugin-svelte';
import * as rsvelte from '@rsvelte/compiler';
export default {
  plugins: [
    svelte({
      // Option 1: pre-imported module
      compiler: rsvelte,
      // Option 2: module specifier
      // compiler: '@rsvelte/compiler',
    })
  ]
};

The compiler module must export the same API as svelte/compiler:

  • compile(source, options): CompileResult
  • compileModule(source, options): CompileResult
  • parse(source, options): AST
  • preprocess(source, preprocessors, options): Promise
  • VERSION: string

Default behavior stays the same:

await import('svelte/compiler')

Implementation sketch

Resolve the user-provided compiler once during configResolved and store it on the existing PluginAPI object.

Replace static imports like:

import * as svelte from 'svelte/compiler'

with reads from api.compiler.

Resolution rules:

  • compiler is a module object → use directly
  • compiler is a string → await import(compiler)
  • compiler is missing → await import('svelte/compiler')

The change should be small.

Considerations

  • Type safety
    Add a Compiler type (or re-export typeof import('svelte/compiler')) in public.d.ts. This would make incompatible compiler implementations fail with a TypeScript error instead of a runtime error.
  • Version mismatch
    Generated output must match the installed svelte runtime version. If compiler.VERSION differs from the installed svelte version, runtime issues are possible. Recommendation: show a warning during configResolved when versions do not match.
  • Sourcemaps
    No sourcemap changes are needed because the compile output format stays the same.
  • Inspector / dep optimizer
    vite-plugin-svelte-inspector and the dep optimizer use compile output and AST data, but do not directly import svelte/compiler, so they should not require changes.

Prior art

  • postcss supports custom parsers and plugins
  • rollup supports acornInjectPlugins
  • vite supports customLogger
  • esbuild-loader allows a custom esbuild instance

Out of scope

  • Custom HMR or resolver hooks
    Alternative compilers may provide extra features, but those should be discussed separately. This proposal only covers replacing the compiler import.
  • Bundling the compiler
    The compiler should still be resolved from the user’s project dependencies.

Alternatives considered

Keep fork of vite-plugin-svelte.

Importance

would make my life easier

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requesttriageAwaiting triage by a project member

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions