Skip to content

Commit

Permalink
chore: make Binding a class (#15359)
Browse files Browse the repository at this point in the history
* chore: make Binding a class

* reorder

* regenerate
  • Loading branch information
Rich-Harris authored Feb 21, 2025
1 parent 1e1aea4 commit 3c4a8d4
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 86 deletions.
82 changes: 65 additions & 17 deletions packages/svelte/src/compiler/phases/scope.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/** @import { ClassDeclaration, Expression, FunctionDeclaration, Identifier, ImportDeclaration, MemberExpression, Node, Pattern, VariableDeclarator } from 'estree' */
/** @import { Context, Visitor } from 'zimmerframe' */
/** @import { AST, Binding, DeclarationKind } from '#compiler' */
/** @import { AST, BindingKind, DeclarationKind } from '#compiler' */
import is_reference from 'is-reference';
import { walk } from 'zimmerframe';
import { create_expression_metadata } from './nodes.js';
Expand All @@ -16,6 +16,69 @@ import { is_reserved, is_rune } from '../../utils.js';
import { determine_slot } from '../utils/slot.js';
import { validate_identifier_name } from './2-analyze/visitors/shared/utils.js';

export class Binding {
/** @type {Scope} */
scope;

/** @type {Identifier} */
node;

/** @type {BindingKind} */
kind;

/** @type {DeclarationKind} */
declaration_kind;

/**
* What the value was initialized with.
* For destructured props such as `let { foo = 'bar' } = $props()` this is `'bar'` and not `$props()`
* @type {null | Expression | FunctionDeclaration | ClassDeclaration | ImportDeclaration | AST.EachBlock | AST.SnippetBlock}
*/
initial;

/** @type {Array<{ node: Identifier; path: AST.SvelteNode[] }>} */
references = [];

/**
* For `legacy_reactive`: its reactive dependencies
* @type {Binding[]}
*/
legacy_dependencies = [];

/**
* Legacy props: the `class` in `{ export klass as class}`. $props(): The `class` in { class: klass } = $props()
* @type {string | null}
*/
prop_alias = null;

/**
* Additional metadata, varies per binding type
* @type {null | { inside_rest?: boolean }}
*/
metadata = null;

is_called = false;
mutated = false;
reassigned = false;
updated = false;

/**
*
* @param {Scope} scope
* @param {Identifier} node
* @param {BindingKind} kind
* @param {DeclarationKind} declaration_kind
* @param {Binding['initial']} initial
*/
constructor(scope, node, kind, declaration_kind, initial) {
this.scope = scope;
this.node = node;
this.initial = initial;
this.kind = kind;
this.declaration_kind = declaration_kind;
}
}

export class Scope {
/** @type {ScopeRoot} */
root;
Expand Down Expand Up @@ -100,22 +163,7 @@ export class Scope {
e.declaration_duplicate(node, node.name);
}

/** @type {Binding} */
const binding = {
node,
references: [],
legacy_dependencies: [],
initial,
reassigned: false,
mutated: false,
updated: false,
scope: this,
kind,
declaration_kind,
is_called: false,
prop_alias: null,
metadata: null
};
const binding = new Binding(this, node, kind, declaration_kind, initial);

validate_identifier_name(binding, this.function_depth);

Expand Down
85 changes: 17 additions & 68 deletions packages/svelte/src/compiler/types/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,5 @@
import type {
ClassDeclaration,
Expression,
FunctionDeclaration,
Identifier,
ImportDeclaration
} from 'estree';
import type { SourceMap } from 'magic-string';
import type { Scope } from '../phases/scope.js';
import type { Binding } from '../phases/scope.js';
import type { AST, Namespace } from './template.js';
import type { ICompileDiagnostic } from '../utils/compile_diagnostic.js';

Expand Down Expand Up @@ -241,6 +234,20 @@ export type ValidatedCompileOptions = ValidatedModuleCompileOptions &
hmr: CompileOptions['hmr'];
};

export type BindingKind =
| 'normal' // A variable that is not in any way special
| 'prop' // A normal prop (possibly reassigned or mutated)
| 'bindable_prop' // A prop one can `bind:` to (possibly reassigned or mutated)
| 'rest_prop' // A rest prop
| 'raw_state' // A state variable
| 'state' // A deeply reactive state variable
| 'derived' // A derived variable
| 'each' // An each block parameter
| 'snippet' // A snippet parameter
| 'store_sub' // A $store value
| 'legacy_reactive' // A `$:` declaration
| 'template'; // A binding declared in the template, e.g. in an `await` block or `const` tag

export type DeclarationKind =
| 'var'
| 'let'
Expand All @@ -251,66 +258,6 @@ export type DeclarationKind =
| 'rest_param'
| 'synthetic';

export interface Binding {
node: Identifier;
/**
* - `normal`: A variable that is not in any way special
* - `prop`: A normal prop (possibly reassigned or mutated)
* - `bindable_prop`: A prop one can `bind:` to (possibly reassigned or mutated)
* - `rest_prop`: A rest prop
* - `state`: A state variable
* - `derived`: A derived variable
* - `each`: An each block parameter
* - `snippet`: A snippet parameter
* - `store_sub`: A $store value
* - `legacy_reactive`: A `$:` declaration
* - `template`: A binding declared in the template, e.g. in an `await` block or `const` tag
*/
kind:
| 'normal'
| 'prop'
| 'bindable_prop'
| 'rest_prop'
| 'state'
| 'raw_state'
| 'derived'
| 'each'
| 'snippet'
| 'store_sub'
| 'legacy_reactive'
| 'template'
| 'snippet';
declaration_kind: DeclarationKind;
/**
* What the value was initialized with.
* For destructured props such as `let { foo = 'bar' } = $props()` this is `'bar'` and not `$props()`
*/
initial:
| null
| Expression
| FunctionDeclaration
| ClassDeclaration
| ImportDeclaration
| AST.EachBlock
| AST.SnippetBlock;
is_called: boolean;
references: { node: Identifier; path: AST.SvelteNode[] }[];
mutated: boolean;
reassigned: boolean;
/** `true` if mutated _or_ reassigned */
updated: boolean;
scope: Scope;
/** For `legacy_reactive`: its reactive dependencies */
legacy_dependencies: Binding[];
/** Legacy props: the `class` in `{ export klass as class}`. $props(): The `class` in { class: klass } = $props() */
prop_alias: string | null;
/** Additional metadata, varies per binding type */
metadata: {
/** `true` if is (inside) a rest parameter */
inside_rest?: boolean;
} | null;
}

export interface ExpressionMetadata {
/** All the bindings that are referenced inside this expression */
dependencies: Set<Binding>;
Expand All @@ -322,5 +269,7 @@ export interface ExpressionMetadata {

export * from './template.js';

export { Binding, Scope } from '../phases/scope.js';

// TODO this chain is a bit weird
export { ReactiveStatement } from '../phases/types.js';
2 changes: 1 addition & 1 deletion packages/svelte/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -622,8 +622,8 @@ declare module 'svelte/animate' {
}

declare module 'svelte/compiler' {
import type { Expression, Identifier, ArrayExpression, ArrowFunctionExpression, VariableDeclaration, VariableDeclarator, MemberExpression, Node, ObjectExpression, Pattern, Program, ChainExpression, SimpleCallExpression, SequenceExpression } from 'estree';
import type { SourceMap } from 'magic-string';
import type { ArrayExpression, ArrowFunctionExpression, VariableDeclaration, VariableDeclarator, Expression, Identifier, MemberExpression, Node, ObjectExpression, Pattern, Program, ChainExpression, SimpleCallExpression, SequenceExpression } from 'estree';
import type { Location } from 'locate-character';
/**
* `compile` converts your `.svelte` source code into a JavaScript module that exports a component
Expand Down

0 comments on commit 3c4a8d4

Please sign in to comment.