sveltejs/svelte
Compiler
Active contributors: Rich Harris, Simon H, Dominic Gannaway, Paolo Ricciuti
Purpose
The compiler turns a .svelte source file into a JavaScript module that imports a small set of runtime primitives. It also runs a separate path for .svelte.js / .svelte.ts modules that contain runes outside a component (compileModule). Both share the same parser and analyzer; they diverge in the transform phase.
The public entry is packages/svelte/src/compiler/index.js, exporting compile, compileModule, parse, parseCss, preprocess, print, migrate, and VERSION. These are re-exported as the svelte/compiler subpath in packages/svelte/package.json.
Pipeline at a glance
graph TD
SRC[".svelte source"] --> RBOM["remove_bom()"]
RBOM --> P["_parse()<br/><code>phases/1-parse/index.js</code>"]
P --> PARSED["AST.Root<br/>{fragment, instance, module, options, css}"]
PARSED --> RTS{"parsed.metadata.ts?"}
RTS -->|yes| RTNS["remove_typescript_nodes()"]
RTS -->|no| MERGE
RTNS --> MERGE["combine validate_component_options + parsed.options"]
MERGE --> AC["analyze_component()<br/><code>phases/2-analyze/index.js</code>"]
AC --> ANALYSIS["ComponentAnalysis"]
ANALYSIS --> TC["transform_component()<br/><code>phases/3-transform/index.js</code>"]
TC --> RESULT["{js, css, ast, warnings, ...}"]Phases
1 — parse
packages/svelte/src/compiler/phases/1-parse/
index.jsexposes aParserclass and aparsehelper. The state machine is split intostate/fragment.js,state/element.js(~26 KB, the largest single state file),state/tag.js, andstate/text.js.read/options.jsparses<svelte:options>.read/script.jsandread/style.jsextract<script>and<style>blocks; the latter is reused byparseCss(packages/svelte/src/compiler/index.js).read/expression.jsandread/context.jsdefer to a forkedacorn(@sveltejs/acorn-typescript) for JS/TS parsing.remove_typescript_nodes.jsstripsas, type annotations, etc. once we know it's TS.utils/entities.jsis a 2.2k-line generated table of HTML entity decodings — the largest single source file in the compiler.
The output is the Svelte AST (AST.Root) plus an options object derived from <svelte:options> and from per-attribute parsing.
2 — analyze
packages/svelte/src/compiler/phases/2-analyze/
index.js(~1.3k lines) is the orchestrator. It builds theComponentAnalysisshape: a graph of scopes, declared variables,$state/$derived/$effectreferences, slot usage, dynamic CSS bindings, etc.visitors/contains a per-AST-node visitor (~70 files). Each visitor decorates the AST with metadata or pushes warnings/errors.css/runs CSS-specific passes:css-prune.jsremoves unused selectors based on the markup.css-analyze.js(alongsidecss-prune.js) discovers what selectors actually match.keyframes.js,css-warnings.js, etc.
utils/andtypes.d.tsdefine the analysis model.
The component scope graph is built by packages/svelte/src/compiler/phases/scope.js (~1.5k lines) — the second-largest single file in the compiler, after the entities table. It tracks bindings, references, mutation kinds ('state', 'derived', 'prop', 'normal', ...), and is queried throughout phase 3.
3 — transform
packages/svelte/src/compiler/phases/3-transform/
The transform splits into three sub-targets:
| Sub-target | File / dir | Purpose |
|---|---|---|
| Client | packages/svelte/src/compiler/phases/3-transform/client/transform-client.js (~720 LOC) + visitors/ |
Generates DOM-targeting JS that imports svelte/internal/client. |
| Server | packages/svelte/src/compiler/phases/3-transform/server/transform-server.js (~430 LOC) + visitors/ |
Generates HTML-emitting JS that imports svelte/internal/server. |
| CSS | packages/svelte/src/compiler/phases/3-transform/css/ |
Emits scoped/encapsulated CSS using hashes from analysis. |
A shared phases/3-transform/index.js dispatches to the right target based on compile_options.generate ('client' or 'server'), and a shared/ folder under each target holds helpers used by multiple visitors.
The visitors are per AST node type: EachBlock.js, IfBlock.js, BindDirective.js, SvelteElement.js, etc. There are over 70 client-side visitors and a similar number of server visitors. They use the b (statement) and x (expression) builders from packages/svelte/src/compiler/utils/builders.js to produce estree nodes, which esrap then prints to source.
Errors and warnings
packages/svelte/src/compiler/errors.js (1.7k lines, generated) and 840 lines) are produced by warnings.js (packages/svelte/scripts/process-messages from the markdown sources under packages/svelte/messages/compile-errors/ and messages/compile-warnings/. To add a new compile error, see Patterns and conventions.
state.js (packages/svelte/src/compiler/state.js) is a tiny module-level state holder used to carry the current source, filename, and warning filter through the pipeline without threading them through every function.
Validation of options
validate-options.js (packages/svelte/src/compiler/validate-options.js, ~10 KB) defines schemas for CompileOptions and ModuleCompileOptions, including default values and friendly errors. validate_component_options is called at the top of compile().
Migrate codemod
packages/svelte/src/compiler/migrate/index.js (~2k lines) implements the Svelte 4 → 5 migration. It reuses the parser and analyzer to build a ComponentAnalysis, then rewrites the source with magic-string. Exposed as svelte/compiler#migrate.
Print round-trip
packages/svelte/src/compiler/print/ re-emits a Svelte AST to source, used by tests under tests/print/ and by the migration tool when only specific nodes need to be re-emitted.
Preprocess
packages/svelte/src/compiler/preprocess/index.js exposes preprocess(source, transformers, options), which runs user-supplied script/style/markup transforms before the file reaches compile. Source-map handling lives in decode_sourcemap.js and replace_in_code.js. Public types are in preprocess/public.d.ts.
Key source files
| File | Purpose |
|---|---|
packages/svelte/src/compiler/index.js |
Public compile, compileModule, parse, etc. |
packages/svelte/src/compiler/phases/1-parse/index.js |
Parser entry + Parser class. |
packages/svelte/src/compiler/phases/1-parse/state/element.js |
Tag and attribute parser state machine (~26 KB). |
packages/svelte/src/compiler/phases/2-analyze/index.js |
Component/module analyzer. |
packages/svelte/src/compiler/phases/scope.js |
Scope graph. |
packages/svelte/src/compiler/phases/3-transform/client/transform-client.js |
Client-target transformer. |
packages/svelte/src/compiler/phases/3-transform/server/transform-server.js |
Server-target transformer. |
packages/svelte/src/compiler/utils/builders.js |
b.statement(...) / x.expression(...) builders. |
packages/svelte/src/compiler/migrate/index.js |
Svelte 4 → 5 codemod. |
packages/svelte/src/compiler/preprocess/index.js |
Pluggable preprocessor. |
Entry points for modification
If you need to change parsing of a new syntax, start in phases/1-parse/state/element.js or state/tag.js and add an AST node type to phases/1-parse/types.d.ts. For semantic checks, add or extend a visitor under phases/2-analyze/visitors/. For code generation, add the corresponding visitor under phases/3-transform/client/visitors/ and phases/3-transform/server/visitors/. Almost all changes touch all three phases.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.