Open-Source Wikis

/

Svelte

/

Systems

/

Compile pipeline

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 (acorn Program).
  • module — the <script context="module"> AST.
  • css — the parsed stylesheet (from read/style.js).
  • options — parsed <svelte:options> attributes.
  • metadata.tstrue when the script declared lang="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 pruning2-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_style rather 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 to visitors/<NodeType>.js.
  • Each visitor uses b (statements) and x (expressions) builders from packages/svelte/src/compiler/utils/builders.js to construct estree nodes.
  • The output module imports from svelte/internal/clientmount is 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 than astring).

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 mostly renderer.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

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Compile pipeline – Svelte wiki | Factory