sveltejs/svelte
Compile pipeline
Active contributors: Rich Harris, Simon H, Paolo Ricciuti
This page is the cross-cutting view of how a .svelte source file becomes JavaScript. The full file-by-file breakdown is in Compiler; this page focuses on the data shapes that flow between phases.
Three phases
graph LR
subgraph "Phase 1: parse"
P1["1-parse/index.js"]
end
subgraph "Phase 2: analyze"
P2["2-analyze/index.js"]
end
subgraph "Phase 3: transform (per target)"
P3C["3-transform/client/transform-client.js"]
P3S["3-transform/server/transform-server.js"]
P3CSS["3-transform/css/"]
end
P1 -->|AST.Root| P2
P2 -->|ComponentAnalysis| P3C
P2 -->|ComponentAnalysis| P3S
P2 -->|css analysis| P3CSS
P3C --> JSC["client JS"]
P3S --> JSS["server JS"]
P3CSS --> CSS["scoped CSS"]Phase 1 — parse
Entry: packages/svelte/src/compiler/phases/1-parse/index.js
Output: an AST.Root (defined in packages/svelte/src/compiler/types/template.d.ts) with these top-level fields:
fragment— the markup tree.instance— the<script>block AST (acornProgram).module— the<script context="module">AST.css— the parsed stylesheet (fromread/style.js).options— parsed<svelte:options>attributes.metadata.ts—truewhen the script declaredlang="ts".
The parser is a hand-rolled state machine. Top-level state functions live in phases/1-parse/state/:
| State function | Triggers |
|---|---|
fragment |
Markup default state; switches on < and {. |
element |
Inside <tag attr=...>; consumes attributes. |
tag |
Inside {...}, {#each ...}, {:else}, {/if}, etc. |
text |
Plain text + entity decoding (utils/entities.js). |
JavaScript and TypeScript expressions are delegated to acorn (@sveltejs/acorn-typescript) via acorn.js.
Phase 2 — analyze
Entry: packages/svelte/src/compiler/phases/2-analyze/index.js
Output: a ComponentAnalysis (defined in 2-analyze/types.d.ts) plus warnings/errors emitted into a global state via packages/svelte/src/compiler/state.js.
Key analyses:
- Scope graph — built by
packages/svelte/src/compiler/phases/scope.js. Walks the script, module, and template; tracks every binding's kind ('state','derived','prop','rest_prop','each','snippet_argument','normal', ...). - Runes detection — flags whether the component is in runes or legacy mode. Determined per-component, not per-repo.
- CSS pruning —
2-analyze/css/css-prune.js(~1.2k lines) walks selectors against the markup and marks unused ones, supporting:global(),:host, slotted descendants, and HMR boundaries. - Dynamic style/class binding analysis — needed because the transformer compiles them into
set_class/set_stylerather than literal output. - Validation — per-node visitors under
2-analyze/visitors/enforce framework invariants. The visitor file names match AST node types:EachBlock.js,BindDirective.js,Component.js, etc.
Phase 3 — transform
Entry: packages/svelte/src/compiler/phases/3-transform/index.js. This dispatches to one of three sub-pipelines based on compile_options.generate:
3a. Client transform
Entry: packages/svelte/src/compiler/phases/3-transform/client/transform-client.js (~720 lines).
- Walks the AST with
zimmerframe, dispatching tovisitors/<NodeType>.js. - Each visitor uses
b(statements) andx(expressions) builders frompackages/svelte/src/compiler/utils/builders.jsto construct estree nodes. - The output module imports from
svelte/internal/client—mountis implicit (the user calls it), but template helpers, blocks, bindings, and reactivity primitives are all imported explicitly. - Output emitted via
esrap(used because it preserves source-map fidelity better thanastring).
3b. Server transform
Entry: packages/svelte/src/compiler/phases/3-transform/server/transform-server.js (~430 lines).
- Same structure as the client, but with a separate
visitors/directory and a much smaller emit surface. - Output module imports from
svelte/internal/server. Calls are mostlyrenderer.push("...")-style.
3c. CSS transform
Entry: packages/svelte/src/compiler/phases/3-transform/css/. Produces the scoped stylesheet using:
- The selector hash from the analysis phase.
- The list of pruned-unused selectors.
<style global>opt-outs.
The result is plumbed into compile_result.css.code.
Cross-phase shared modules
| Module | Used by |
|---|---|
packages/svelte/src/compiler/state.js |
Module-scoped current source/filename/warning filter. |
packages/svelte/src/compiler/utils/builders.js |
Phase 3 visitors. |
packages/svelte/src/compiler/phases/patterns.js |
Identifier predicates (is_reserved). |
packages/svelte/src/compiler/phases/bindings.js |
Built-in binding metadata. |
packages/svelte/src/compiler/phases/nodes.js |
AST helpers shared by phases 2 and 3. |
packages/svelte/src/compiler/phases/css.js |
Tiny shared CSS helpers. |
Source maps
The compiler accepts a filename and produces result.js.map and result.css.map. Source-map handling sits inside esrap for JS and inside the CSS transformer for CSS. Tests under packages/svelte/tests/sourcemaps/ exercise this end-to-end.
Validation of compile options
packages/svelte/src/compiler/validate-options.js (~10 KB) validates CompileOptions and ModuleCompileOptions. The result is the ValidatedCompileOptions shape that the rest of the pipeline assumes.
See also
- Compiler — file-by-file walkthrough.
- Hydration — what the client and server transforms must agree on.
- Patterns and conventions — how to author a new error/warning, builder usage, etc.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.