diff --git a/packages/svelte/src/compiler/phases/1-parse/state/element.js b/packages/svelte/src/compiler/phases/1-parse/state/element.js index b0db9ce178..7b0950ae82 100644 --- a/packages/svelte/src/compiler/phases/1-parse/state/element.js +++ b/packages/svelte/src/compiler/phases/1-parse/state/element.js @@ -9,11 +9,10 @@ import { decode_character_references } from '../utils/html.js'; import * as e from '../../../errors.js'; import * as w from '../../../warnings.js'; import { create_fragment } from '../utils/create.js'; -import { create_attribute, create_expression_metadata, is_element_node } from '../../nodes.js'; +import { create_attribute, ExpressionMetadata, is_element_node } from '../../nodes.js'; import { get_attribute_expression, is_expression_attribute } from '../../../utils/ast.js'; import { closing_tag_omitted } from '../../../../html-tree-validation.js'; import { list } from '../../../utils/string.js'; -import { regex_whitespace } from '../../patterns.js'; const regex_invalid_unquoted_attribute_value = /^(\/>|[\s"'=<>`])/; const regex_closing_textarea_tag = /^<\/textarea(\s[^>]*)?>/i; @@ -297,7 +296,7 @@ export default function element(parser) { element.tag = get_attribute_expression(definition); } - element.metadata.expression = create_expression_metadata(); + element.metadata.expression = new ExpressionMetadata(); } if (is_top_level_script_or_style) { @@ -508,7 +507,7 @@ function read_attribute(parser) { end: parser.index, expression, metadata: { - expression: create_expression_metadata() + expression: new ExpressionMetadata() } }; @@ -528,7 +527,7 @@ function read_attribute(parser) { end: parser.index, expression, metadata: { - expression: create_expression_metadata() + expression: new ExpressionMetadata() } }; @@ -568,7 +567,7 @@ function read_attribute(parser) { name }, metadata: { - expression: create_expression_metadata() + expression: new ExpressionMetadata() } }; @@ -628,7 +627,7 @@ function read_attribute(parser) { modifiers: /** @type {Array<'important'>} */ (modifiers), value, metadata: { - expression: create_expression_metadata() + expression: new ExpressionMetadata() } }; } @@ -658,7 +657,7 @@ function read_attribute(parser) { name: directive_name, expression, metadata: { - expression: create_expression_metadata() + expression: new ExpressionMetadata() } }; @@ -824,7 +823,7 @@ function read_sequence(parser, done, location) { end: parser.index, expression, metadata: { - expression: create_expression_metadata() + expression: new ExpressionMetadata() } }; diff --git a/packages/svelte/src/compiler/phases/1-parse/state/tag.js b/packages/svelte/src/compiler/phases/1-parse/state/tag.js index ba091ef7ec..4ff948e165 100644 --- a/packages/svelte/src/compiler/phases/1-parse/state/tag.js +++ b/packages/svelte/src/compiler/phases/1-parse/state/tag.js @@ -3,7 +3,7 @@ /** @import { Parser } from '../index.js' */ import { walk } from 'zimmerframe'; import * as e from '../../../errors.js'; -import { create_expression_metadata } from '../../nodes.js'; +import { ExpressionMetadata } from '../../nodes.js'; import { parse_expression_at } from '../acorn.js'; import read_pattern from '../read/context.js'; import read_expression, { get_loose_identifier } from '../read/expression.js'; @@ -42,7 +42,7 @@ export default function tag(parser) { end: parser.index, expression, metadata: { - expression: create_expression_metadata() + expression: new ExpressionMetadata() } }); } @@ -65,7 +65,7 @@ function open(parser) { consequent: create_fragment(), alternate: null, metadata: { - expression: create_expression_metadata() + expression: new ExpressionMetadata() } }); @@ -249,7 +249,7 @@ function open(parser) { then: null, catch: null, metadata: { - expression: create_expression_metadata() + expression: new ExpressionMetadata() } }); @@ -334,7 +334,7 @@ function open(parser) { expression, fragment: create_fragment(), metadata: { - expression: create_expression_metadata() + expression: new ExpressionMetadata() } }); @@ -477,7 +477,7 @@ function next(parser) { consequent: create_fragment(), alternate: null, metadata: { - expression: create_expression_metadata() + expression: new ExpressionMetadata() } }); @@ -643,7 +643,7 @@ function special(parser) { end: parser.index, expression, metadata: { - expression: create_expression_metadata() + expression: new ExpressionMetadata() } }); @@ -721,7 +721,7 @@ function special(parser) { end: parser.index - 1 }, metadata: { - expression: create_expression_metadata() + expression: new ExpressionMetadata() } }); } @@ -748,7 +748,7 @@ function special(parser) { end: parser.index, expression: /** @type {AST.RenderTag['expression']} */ (expression), metadata: { - expression: create_expression_metadata(), + expression: new ExpressionMetadata(), dynamic: false, arguments: [], path: [], diff --git a/packages/svelte/src/compiler/phases/2-analyze/types.d.ts b/packages/svelte/src/compiler/phases/2-analyze/types.d.ts index bad6c7d613..9d24f9dbac 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/types.d.ts +++ b/packages/svelte/src/compiler/phases/2-analyze/types.d.ts @@ -1,6 +1,7 @@ import type { Scope } from '../scope.js'; import type { ComponentAnalysis, ReactiveStatement } from '../types.js'; -import type { AST, ExpressionMetadata, StateField, ValidatedCompileOptions } from '#compiler'; +import type { AST, StateField, ValidatedCompileOptions } from '#compiler'; +import type { ExpressionMetadata } from '../nodes.js'; export interface AnalysisState { scope: Scope; diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js index 4b66abe1d1..52eba8c735 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js @@ -7,7 +7,7 @@ import { get_parent } from '../../../utils/ast.js'; import { is_pure, is_safe_identifier } from './shared/utils.js'; import { dev, locate_node, source } from '../../../state.js'; import * as b from '#compiler/builders'; -import { create_expression_metadata } from '../../nodes.js'; +import { ExpressionMetadata } from '../../nodes.js'; /** * @param {CallExpression} node @@ -243,7 +243,7 @@ export function CallExpression(node, context) { // `$inspect(foo)` or `$derived(foo) should not trigger the `static-state-reference` warning if (rune === '$derived') { - const expression = create_expression_metadata(); + const expression = new ExpressionMetadata(); context.next({ ...context.state, diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/RenderTag.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/RenderTag.js index 1230ef6b04..d0c994b7a4 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/RenderTag.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/RenderTag.js @@ -5,7 +5,7 @@ import * as e from '../../../errors.js'; import { validate_opening_tag } from './shared/utils.js'; import { mark_subtree_dynamic } from './shared/fragment.js'; import { is_resolved_snippet } from './shared/snippets.js'; -import { create_expression_metadata } from '../../nodes.js'; +import { ExpressionMetadata } from '../../nodes.js'; /** * @param {AST.RenderTag} node @@ -57,7 +57,7 @@ export function RenderTag(node, context) { context.visit(callee, { ...context.state, expression: node.metadata.expression }); for (const arg of expression.arguments) { - const metadata = create_expression_metadata(); + const metadata = new ExpressionMetadata(); node.metadata.arguments.push(metadata); context.visit(arg, { diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/element.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/element.js index 4b32dab82a..dd390c99da 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/element.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/element.js @@ -1,5 +1,5 @@ /** @import { Expression, Identifier, ObjectExpression } from 'estree' */ -/** @import { AST, ExpressionMetadata } from '#compiler' */ +/** @import { AST } from '#compiler' */ /** @import { ComponentContext } from '../../types' */ import { escape_html } from '../../../../../../escaping.js'; import { normalize_attribute } from '../../../../../../utils.js'; @@ -8,6 +8,7 @@ import { is_event_attribute } from '../../../../../utils/ast.js'; import * as b from '#compiler/builders'; import { build_class_directives_object, build_style_directives_object } from '../RegularElement.js'; import { build_expression, build_template_chunk, Memoizer } from './utils.js'; +import { ExpressionMetadata } from '../../../../nodes.js'; /** * @param {Array} attributes diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/events.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/events.js index d4d6721960..3ab1506eb3 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/events.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/events.js @@ -1,9 +1,10 @@ /** @import { Expression } from 'estree' */ -/** @import { AST, ExpressionMetadata } from '#compiler' */ +/** @import { AST } from '#compiler' */ /** @import { ComponentContext } from '../../types' */ import { is_capture_event, is_passive_event } from '../../../../../../utils.js'; import { dev, locator } from '../../../../../state.js'; import * as b from '#compiler/builders'; +import { ExpressionMetadata } from '../../../../nodes.js'; /** * @param {AST.Attribute} node diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js index a42063b2e2..46d2f2b777 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js @@ -1,5 +1,5 @@ /** @import { AssignmentExpression, Expression, Identifier, MemberExpression, SequenceExpression, Literal, Super, UpdateExpression, ExpressionStatement } from 'estree' */ -/** @import { AST, ExpressionMetadata } from '#compiler' */ +/** @import { AST } from '#compiler' */ /** @import { ComponentClientTransformState, ComponentContext, Context } from '../../types' */ import { walk } from 'zimmerframe'; import { object } from '../../../../../utils/ast.js'; @@ -9,6 +9,7 @@ import { regex_is_valid_identifier } from '../../../../patterns.js'; import is_reference from 'is-reference'; import { dev, is_ignored, locator, component_name } from '../../../../../state.js'; import { build_getter } from '../../utils.js'; +import { ExpressionMetadata } from '../../../../nodes.js'; /** * A utility for extracting complex expressions (such as call expressions) diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/element.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/element.js index cfb87b0ce7..b7607f3fb8 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/element.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/element.js @@ -1,13 +1,9 @@ /** @import { ArrayExpression, Expression, Literal, ObjectExpression } from 'estree' */ -/** @import { AST, ExpressionMetadata } from '#compiler' */ +/** @import { AST } from '#compiler' */ /** @import { ComponentContext, ComponentServerTransformState } from '../../types.js' */ import { is_event_attribute, is_text_attribute } from '../../../../../utils/ast.js'; import { binding_properties } from '../../../../bindings.js'; -import { - create_attribute, - create_expression_metadata, - is_custom_element_node -} from '../../../../nodes.js'; +import { create_attribute, ExpressionMetadata, is_custom_element_node } from '../../../../nodes.js'; import { regex_starts_with_newline } from '../../../../patterns.js'; import * as b from '#compiler/builders'; import { @@ -160,7 +156,7 @@ export function build_element_attributes(node, context, transform) { build_attribute_value(value_attribute.value, context, transform) ), metadata: { - expression: create_expression_metadata() + expression: new ExpressionMetadata() } } ]) @@ -174,7 +170,7 @@ export function build_element_attributes(node, context, transform) { end: -1, expression, metadata: { - expression: create_expression_metadata() + expression: new ExpressionMetadata() } } ]) diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/utils.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/utils.js index 92653ed73c..09a854670c 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/utils.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/utils.js @@ -1,5 +1,5 @@ -/** @import { AssignmentOperator, Expression, Identifier, Node, Statement, BlockStatement } from 'estree' */ -/** @import { AST, ExpressionMetadata } from '#compiler' */ +/** @import { Expression, Identifier, Node, Statement, BlockStatement } from 'estree' */ +/** @import { AST } from '#compiler' */ /** @import { ComponentContext, ServerTransformState } from '../../types.js' */ import { escape_html } from '../../../../../../escaping.js'; @@ -13,6 +13,7 @@ import * as b from '#compiler/builders'; import { sanitize_template_string } from '../../../../../utils/sanitize_template_string.js'; import { regex_whitespaces_strict } from '../../../../patterns.js'; import { has_await_expression } from '../../../../../utils/ast.js'; +import { ExpressionMetadata } from '../../../../nodes.js'; /** Opens an if/each block, so that we can remove nodes in the case of a mismatch */ export const block_open = b.literal(BLOCK_OPEN); diff --git a/packages/svelte/src/compiler/phases/nodes.js b/packages/svelte/src/compiler/phases/nodes.js index 13188681d2..bca9c29a65 100644 --- a/packages/svelte/src/compiler/phases/nodes.js +++ b/packages/svelte/src/compiler/phases/nodes.js @@ -1,5 +1,5 @@ /** @import { Expression, PrivateIdentifier } from 'estree' */ -/** @import { AST, ExpressionMetadata } from '#compiler' */ +/** @import { AST, Binding } from '#compiler' */ /** * All nodes that can appear elsewhere than the top level, have attributes and can contain children @@ -64,20 +64,33 @@ export function create_attribute(name, start, end, value) { } }; } +export class ExpressionMetadata { + /** True if the expression references state directly, or _might_ (via member/call expressions) */ + has_state = false; -/** - * @returns {ExpressionMetadata} - */ -export function create_expression_metadata() { - return { - dependencies: new Set(), - references: new Set(), - has_state: false, - has_call: false, - has_member_expression: false, - has_assignment: false, - has_await: false - }; + /** True if the expression involves a call expression (often, it will need to be wrapped in a derived) */ + has_call = false; + + /** True if the expression contains `await` */ + has_await = false; + + /** True if the expression includes a member expression */ + has_member_expression = false; + + /** True if the expression includes an assignment or an update */ + has_assignment = false; + + /** + * All the bindings that are referenced eagerly (not inside functions) in this expression + * @type {Set} + */ + dependencies = new Set(); + + /** + * True if the expression references state directly, or _might_ (via member/call expressions) + * @type {Set} + */ + references = new Set(); } /** diff --git a/packages/svelte/src/compiler/phases/scope.js b/packages/svelte/src/compiler/phases/scope.js index f7d3dac0f7..7dbdf47967 100644 --- a/packages/svelte/src/compiler/phases/scope.js +++ b/packages/svelte/src/compiler/phases/scope.js @@ -3,7 +3,7 @@ /** @import { AST, BindingKind, DeclarationKind } from '#compiler' */ import is_reference from 'is-reference'; import { walk } from 'zimmerframe'; -import { create_expression_metadata } from './nodes.js'; +import { ExpressionMetadata } from './nodes.js'; import * as b from '#compiler/builders'; import * as e from '../errors.js'; import { @@ -1201,7 +1201,7 @@ export function create_scopes(ast, root, allow_reactive_declarations, parent) { if (node.fallback) visit(node.fallback, { scope }); node.metadata = { - expression: create_expression_metadata(), + expression: new ExpressionMetadata(), keyed: false, contains_group_binding: false, index: scope.root.unique('$$index'), diff --git a/packages/svelte/src/compiler/types/index.d.ts b/packages/svelte/src/compiler/types/index.d.ts index 9bd4b91d58..fce3f62c5c 100644 --- a/packages/svelte/src/compiler/types/index.d.ts +++ b/packages/svelte/src/compiler/types/index.d.ts @@ -294,23 +294,6 @@ export type DeclarationKind = | 'using' | 'await using'; -export interface ExpressionMetadata { - /** All the bindings that are referenced eagerly (not inside functions) in this expression */ - dependencies: Set; - /** All the bindings that are referenced inside this expression, including inside functions */ - references: Set; - /** True if the expression references state directly, or _might_ (via member/call expressions) */ - has_state: boolean; - /** True if the expression involves a call expression (often, it will need to be wrapped in a derived) */ - has_call: boolean; - /** True if the expression contains `await` */ - has_await: boolean; - /** True if the expression includes a member expression */ - has_member_expression: boolean; - /** True if the expression includes an assignment or an update */ - has_assignment: boolean; -} - export interface StateField { type: StateCreationRuneName; node: PropertyDefinition | AssignmentExpression; diff --git a/packages/svelte/src/compiler/types/template.d.ts b/packages/svelte/src/compiler/types/template.d.ts index f38706d075..fa7484e523 100644 --- a/packages/svelte/src/compiler/types/template.d.ts +++ b/packages/svelte/src/compiler/types/template.d.ts @@ -1,4 +1,4 @@ -import type { Binding, ExpressionMetadata } from '#compiler'; +import type { Binding } from '#compiler'; import type { ArrayExpression, ArrowFunctionExpression, @@ -17,6 +17,7 @@ import type { } from 'estree'; import type { Scope } from '../phases/scope'; import type { _CSS } from './css'; +import type { ExpressionMetadata } from '../phases/nodes'; /** * - `html` — the default, for e.g. `
` or ``