The Analysis Phase is Phase 2 of the Svelte compiler pipeline. It performs semantic analysis on the parsed AST from Phase 1, producing a ComponentAnalysis object that contains all metadata needed for Phase 3 (Transformation).
For information about how source code is parsed into an AST, see Parsing Phase. For information about how the analysis output is transformed into executable code, see Transformation Phase.
The Analysis Phase processes the output of parse() and produces structured metadata about the component:
Scope Analysis:
Binding Classification:
'state', 'derived', 'prop', 'normal', etc.)Mode Detection:
$state(), $derived(), $effect(), $props())Dependency Tracking:
Validation:
The analysis phase has two entry points:
analyze_component(root, source, options) - Analyzes .svelte component files packages/svelte/src/compiler/phases/2-analyze/index.js330-935analyze_module(source, options) - Analyzes .js/.ts module files with runes packages/svelte/src/compiler/phases/2-analyze/index.js256-322Sources: packages/svelte/src/compiler/phases/2-analyze/index.js1-1076 packages/svelte/src/compiler/types/index.d.ts1-328
Diagram: Analysis Phase Entry Points and Data Flow
Core Analysis Functions:
| Function | Location | Purpose |
|---|---|---|
analyze_component() | 2-analyze/index.js330-935 | Main entry for .svelte files; analyzes module, instance, and template |
analyze_module() | 2-analyze/index.js256-322 | Entry for .js/.ts files with runes |
create_scopes() | scope.js865-1390 | Builds scope hierarchy by walking AST |
js() | 2-analyze/index.js215-233 | Helper creating Js object with scope metadata |
get_rune() | scope.js1392-1495 | Identifies rune calls in expressions |
calculate_blockers() | 2-analyze/index.js946-1076 | Computes async statement dependencies |
Key Classes:
Binding scope.js88-196 - Represents a variable declaration with usage trackingScope scope.js605-798 - Manages declarations and references in a lexical scopeScopeRoot scope.js800-863 - Root scope manager with conflict trackingEvaluation scope.js198-603 - Partial evaluation of expressions for optimizationSources: packages/svelte/src/compiler/phases/2-analyze/index.js215-1076 packages/svelte/src/compiler/phases/scope.js88-1495
The Binding class scope.js88-196 represents a variable declaration with usage tracking. Each Binding is created when a variable is declared and accumulates information as the analyzer encounters references.
Diagram: Binding Class Structure
Binding Kinds (defined in scope.js88-196):
| Kind | Created By | Usage in Transform |
|---|---|---|
'normal' | Regular let/const/var | No reactive wrapper |
'state' | $state() call | Wrapped in $.mutable_source() |
'raw_state' | $state.raw() call | Wrapped in $.mutable_source() (no proxy) |
'derived' | $derived() or $derived.by() | Wrapped in $.derived() |
'prop' | $props() destructuring | Wrapped in $.prop() |
'bindable_prop' | $props() with $bindable() | Wrapped in $.prop() with PROPS_IS_BINDABLE |
'rest_prop' | $$props, $$restProps | Special handling for legacy compat |
'store_sub' | $storeName reference | Synthetic binding for store subscription |
'each' | Each block variables | Block-scoped iteration binding |
'snippet' | {#snippet name(...)} | Function declaration |
'legacy_reactive' | $: x = ... (legacy) | Wrapped in $.mutable_source() |
Key Properties:
mutated - Set to true if binding is mutated (e.g., obj.prop = x)reassigned - Set to true if binding is reassigned (e.g., x = 5)updated - Getter returning mutated || reassignedblocker - For async dependencies, points to $$blockers[n] expressionSources: packages/svelte/src/compiler/phases/scope.js88-196
The Scope class represents a lexical scope and manages declarations and references:
Key Methods:
declare() - Creates a new binding in this scope packages/svelte/src/compiler/phases/scope.js659-686get() - Looks up a binding by name (searches parent scopes) packages/svelte/src/compiler/phases/scope.js723-725owner() - Finds which scope owns a declaration packages/svelte/src/compiler/phases/scope.js743-745reference() - Records a reference to an identifier packages/svelte/src/compiler/phases/scope.js751-768evaluate() - Performs partial evaluation of expressions packages/svelte/src/compiler/phases/scope.js778-783Sources: packages/svelte/src/compiler/phases/scope.js591-784
Process Steps in analyze_component():
Initialize Scope Hierarchy 2-analyze/index.js331-344
ScopeRoot instancejs() helper for module script → module.scopejs() helper for instance script (parent: module.scope) → instance.scopecreate_scopes() for template (parent: instance.scope) → template.scopeHandle Store Subscriptions 2-analyze/index.js348-445
$storeName references'store_sub' bindings in instance scopeDetect Runes Mode 2-analyze/index.js449-457
Walk Module/Instance/Template ASTs 2-analyze/index.js705-793
walk() from zimmerframe with visitor objectAnalysisState and ComponentAnalysisProcess Exports and Props 2-analyze/index.js563-616
export let to bindable_propOrder Reactive Statements (legacy only) 2-analyze/index.js810
$: statementsCalculate Async Blockers 2-analyze/index.js693
await, compute which bindings depend on whichValidate and Report Errors 2-analyze/index.js813-852
Analyze CSS 2-analyze/index.js855-872
analyze_css() for selector analysisprune() to mark scoped elementswarn_unused() for unused selector warningsSources: packages/svelte/src/compiler/phases/2-analyze/index.js330-935
The analysis phase uses the visitor pattern via zimmerframe.walk() to traverse the AST. Each node type has a corresponding visitor function.
Key Visitors:
| Visitor | Purpose | File |
|---|---|---|
_ | Base visitor, handles scope switching | 2-analyze/index.js93-145 |
Identifier | Tracks references, validates usage | visitors/Identifier.js |
CallExpression | Detects rune calls, validates arguments | visitors/CallExpression.js1-263 |
VariableDeclarator | Detects state declarations, classifies bindings | visitors/VariableDeclarator.js |
ExportNamedDeclaration | Handles prop exports in legacy mode | visitors/ExportNamedDeclaration.js |
Component | Analyzes component usage, props, bindings | visitors/Component.js |
EachBlock | Creates each block scopes, validates | visitors/EachBlock.js |
BindDirective | Validates bindings, marks mutated bindings | visitors/BindDirective.js |
AnalysisState Structure:
Each visitor receives an AnalysisState object 2-analyze/types.d.ts1-45 containing:
This state is threaded through all visitors and updated as the traversal progresses. The scope field changes as the traversal enters/exits lexical scopes, while analysis accumulates metadata throughout.
Sources: packages/svelte/src/compiler/phases/2-analyze/types.d.ts1-45 packages/svelte/src/compiler/phases/2-analyze/index.js92-206
Runes are special function calls that declare reactive state. The analysis phase detects these and classifies their bindings accordingly.
Rune Detection Process:
The get_rune() function scope.js1392-1495 identifies rune calls by analyzing CallExpression nodes:
Classification by Rune:
| Rune Call | Binding Kind | Created In Visitor |
|---|---|---|
$state(value) | 'state' | VariableDeclarator |
$state.raw(value) | 'raw_state' | VariableDeclarator |
$derived(expr) | 'derived' | VariableDeclarator |
$derived.by(fn) | 'derived' | VariableDeclarator |
$props() | Multiple 'prop' | VariableDeclarator |
$bindable(fallback) | Marks as 'bindable_prop' | Inside $props() |
$effect(fn) | No binding | ExpressionStatement |
$effect.pre(fn) | No binding | ExpressionStatement |
$effect.tracking() | No binding | Returns boolean |
$effect.root(fn) | No binding | Returns cleanup function |
$inspect(...) | No binding | Dev-only inspection |
Validation Rules:
The CallExpression visitor 2-analyze/visitors/CallExpression.js1-263 enforces:
$effect() must be used as expression statement only (not assigned to variable)$bindable() can only appear inside $props() destructuringoptions.runes or analysis.runes is trueSources: packages/svelte/src/compiler/phases/scope.js1392-1495 packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js1-263 packages/svelte/src/compiler/phases/2-analyze/visitors/VariableDeclarator.js1-350
The analyzer behaves differently based on whether the component uses runes or legacy reactive syntax.
Detection Logic: packages/svelte/src/compiler/phases/2-analyze/index.js449-457
Legacy Mode Specific Analysis:
export let to bindable_prop bindings packages/svelte/src/compiler/phases/2-analyze/index.js563-616$: labeled statements packages/svelte/src/compiler/phases/2-analyze/index.js618-673$$props and $$restProps bindings packages/svelte/src/compiler/phases/2-analyze/index.js770-771Runes Mode Specific Analysis:
$$props is not used packages/svelte/src/compiler/phases/2-analyze/index.js695-703Sources: packages/svelte/src/compiler/phases/2-analyze/index.js449-810
In legacy mode, references to $storeName create synthetic bindings for automatic store subscriptions.
Process:
Detect $storeName references in module scope packages/svelte/src/compiler/phases/2-analyze/index.js349-444
Validate:
Create Synthetic Binding:
kind = 'store_sub'declaration_kind = 'synthetic'Special Cases:
$derived imported from 'svelte/store', allow both usagesSources: packages/svelte/src/compiler/phases/2-analyze/index.js348-445
For async top-level statements, the analyzer calculates which bindings need to wait for which statements to complete.
Purpose: When a component has await at the instance top level, later bindings may depend on async results. The analyzer builds a dependency chain so the transform phase can inject proper awaits.
Algorithm: packages/svelte/src/compiler/phases/2-analyze/index.js946-1076
await, mark as a blockerResult: Each binding gets a blocker property (a MemberExpression like $$blockers[0]) that the transform phase will use to generate await expressions.
Sources: packages/svelte/src/compiler/phases/2-analyze/index.js937-1076
The analyze_component() function returns a ComponentAnalysis object types/index.d.ts60-127 that contains all metadata needed for the Transformation Phase.
Diagram: ComponentAnalysis Structure
Key ComponentAnalysis Properties:
| Property | Type | Purpose |
|---|---|---|
name | string | Component name from get_component_name(filename) |
root | ScopeRoot | Root scope with conflict tracking |
module | Js | Module script scope/AST/metadata |
instance | Js | Instance script scope/AST/metadata |
template | Template | Template scope/AST/metadata |
runes | boolean | Component uses runes mode |
maybe_runes | boolean | Heuristic: might be accidental runes usage |
immutable | boolean | Component declared immutable |
exports | Export[] | { name, alias } for exported identifiers |
accessors | boolean | Should generate accessors |
custom_element | boolean | Is custom element |
inject_styles | boolean | Inject styles at runtime |
elements | RegularElement[] | All HTML elements (for CSS) |
css | CssAnalysis | CSS hash, keyframes, scoping info |
reactive_statements | Map | Legacy $: statements (ordered) |
binding_groups | Map | Groups for bind:group |
slot_names | Map<string, number> | Slot name → source position |
snippet_renderers | Map | Snippet render tags info |
uses_props | boolean | References $$props |
uses_rest_props | boolean | References $$restProps |
uses_slots | boolean | References $$slots |
uses_component_bindings | boolean | Has component bindings |
uses_render_tags | boolean | Uses {@render} |
needs_context | boolean | Needs context stack |
async_deriveds | Set<Binding> | Async derived states |
Js Object Structure 2-analyze/index.js215-233:
Template Object Structure:
This structure is used by:
client_component() 3-transform/client/transform-client.js146-545 - Client-side code generationserver_component() 3-transform/server/transform-server.js92-372 - Server-side code generationSources: packages/svelte/src/compiler/phases/2-analyze/index.js215-560 packages/svelte/src/compiler/types/index.d.ts60-127 packages/svelte/src/compiler/phases/3-transform/client/transform-client.js146-210
If the component has a <style> block, the analysis phase performs CSS analysis to determine selector usage and scoping.
Process:
metadata.scoped = trueCSS Hash Calculation: packages/svelte/src/compiler/phases/2-analyze/index.js538-544
Sources: packages/svelte/src/compiler/phases/2-analyze/index.js854-929 packages/svelte/src/compiler/phases/2-analyze/css/css-analyze.js packages/svelte/src/compiler/phases/2-analyze/css/css-prune.js
Throughout analysis, the phase validates component structure and reports errors:
Key Validations:
Rune Placement packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js1-263
$effect() must be expression statement$bindable() only in $props()Binding Validation packages/svelte/src/compiler/phases/2-analyze/visitors/BindDirective.js
Conflict Detection packages/svelte/src/compiler/phases/2-analyze/index.js829-852
on:event and onevent attribute usageScope Validation packages/svelte/src/compiler/phases/2-analyze/index.js377-396
Non-Reactive Warnings packages/svelte/src/compiler/phases/2-analyze/index.js728-768
Sources: packages/svelte/src/compiler/phases/2-analyze/index.js695-852 packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js1-263 packages/svelte/src/compiler/phases/2-analyze/visitors/BindDirective.js
The Analysis Phase bridges parsing and code generation by:
ComponentAnalysis object with all information needed for code generationThe output ComponentAnalysis is consumed by the Transformation Phase to generate optimized JavaScript that implements the component's reactive behavior.
Sources: packages/svelte/src/compiler/phases/2-analyze/index.js1-935 packages/svelte/src/compiler/phases/scope.js1-1495
Refresh this wiki