From 7dc674525efc18d7734ddc3d1911732de60412d9 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sun, 28 Jul 2024 22:23:31 -0400 Subject: [PATCH] use unified reactivity metadata --- .../compiler/phases/1-parse/state/element.js | 34 ++++------ .../src/compiler/phases/1-parse/state/tag.js | 4 +- .../src/compiler/phases/2-analyze/index.js | 35 +++++----- .../src/compiler/phases/2-analyze/types.d.ts | 3 +- .../3-transform/client/visitors/template.js | 64 +++++++++++-------- .../server/visitors/shared/component.js | 3 +- .../server/visitors/shared/element.js | 16 +++-- packages/svelte/src/compiler/phases/nodes.js | 13 +++- packages/svelte/src/compiler/types/index.d.ts | 3 + .../svelte/src/compiler/types/template.d.ts | 21 +++--- .../parser-modern/samples/options/output.json | 19 ++++-- 11 files changed, 123 insertions(+), 92 deletions(-) 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 1d8671a6d3..52adf57459 100644 --- a/packages/svelte/src/compiler/phases/1-parse/state/element.js +++ b/packages/svelte/src/compiler/phases/1-parse/state/element.js @@ -9,7 +9,7 @@ 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 } from '../../nodes.js'; +import { create_attribute, create_expression_metadata } from '../../nodes.js'; import { get_attribute_expression, is_expression_attribute } from '../../../utils/ast.js'; import { closing_tag_omitted } from '../../../../html-tree-validation.js'; @@ -127,7 +127,7 @@ export default function element(parser) { const type = meta_tags.has(name) ? meta_tags.get(name) - : regex_capital_letter.test(name[0]) || name === 'svelte:self' || name === 'svelte:component' + : regex_capital_letter.test(name[0]) ? 'Component' : name === 'title' && parent_is_head(parser.stack) ? 'TitleElement' @@ -140,7 +140,7 @@ export default function element(parser) { const element = type === 'RegularElement' ? { - type: type, + type, start, end: -1, name, @@ -163,7 +163,7 @@ export default function element(parser) { fragment: create_fragment(true), parent: null, metadata: { - svg: false + expression: create_expression_metadata() } }); @@ -507,10 +507,8 @@ function read_attribute(parser) { end: parser.index, expression, parent: null, - effect: { - is_dynamic: false, - has_call_expression: false, - bindings: new Set() + metadata: { + expression: create_expression_metadata() } }; @@ -538,10 +536,8 @@ function read_attribute(parser) { name }, parent: null, - effect: { - has_call_expression: false, - is_dynamic: false, - bindings: new Set() + metadata: { + expression: create_expression_metadata() } }; @@ -586,7 +582,7 @@ function read_attribute(parser) { value, parent: null, metadata: { - dynamic: false + expression: create_expression_metadata() } }; } @@ -618,17 +614,10 @@ function read_attribute(parser) { modifiers, expression, metadata: { - dynamic: false, - contains_call_expression: false + expression: create_expression_metadata() } }; - if (directive.type === 'ClassDirective') { - directive.metadata = { - dynamic: false - }; - } - if (directive.type === 'TransitionDirective') { const direction = name.slice(0, colon_index); directive.intro = direction === 'in' || direction === 'transition'; @@ -791,8 +780,7 @@ function read_sequence(parser, done, location) { expression, parent: null, metadata: { - contains_call_expression: false, - dynamic: false + expression: create_expression_metadata() } }; 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 4ccfe2cb5f..aacaf29440 100644 --- a/packages/svelte/src/compiler/phases/1-parse/state/tag.js +++ b/packages/svelte/src/compiler/phases/1-parse/state/tag.js @@ -7,6 +7,7 @@ import * as e from '../../../errors.js'; import { create_fragment } from '../utils/create.js'; import { walk } from 'zimmerframe'; import { parse_expression_at } from '../acorn.js'; +import { create_expression_metadata } from '../../nodes.js'; const regex_whitespace_with_closing_curly_brace = /^\s*}/; @@ -39,8 +40,7 @@ export default function tag(parser) { end: parser.index, expression, metadata: { - contains_call_expression: false, - dynamic: false + expression: create_expression_metadata() } }); } diff --git a/packages/svelte/src/compiler/phases/2-analyze/index.js b/packages/svelte/src/compiler/phases/2-analyze/index.js index aac76faa60..00a140bf82 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/index.js +++ b/packages/svelte/src/compiler/phases/2-analyze/index.js @@ -1178,7 +1178,7 @@ const common_visitors = { context.next(); - node.metadata.dynamic = get_attribute_chunks(node.value).some((chunk) => { + node.metadata.expression.has_state = get_attribute_chunks(node.value).some((chunk) => { if (chunk.type !== 'ExpressionTag') { return false; } @@ -1190,7 +1190,7 @@ const common_visitors = { return false; } - return chunk.metadata.dynamic || chunk.metadata.contains_call_expression; + return chunk.metadata.expression.has_state; }); if (is_event_attribute(node)) { @@ -1211,10 +1211,10 @@ const common_visitors = { } }, ClassDirective(node, context) { - context.next({ ...context.state, expression: node }); + context.next({ ...context.state, expression: node.metadata.expression }); }, SpreadAttribute(node, context) { - context.next({ ...context.state, expression: node }); + context.next({ ...context.state, expression: node.metadata.expression }); }, SlotElement(node, context) { let name = 'default'; @@ -1230,17 +1230,17 @@ const common_visitors = { if (node.value === true) { const binding = context.state.scope.get(node.name); if (binding?.kind !== 'normal') { - node.metadata.dynamic = true; + node.metadata.expression.has_state = true; } } else { context.next(); - node.metadata.dynamic = get_attribute_chunks(node.value).some( - (node) => node.type === 'ExpressionTag' && node.metadata.dynamic + node.metadata.expression.has_state = get_attribute_chunks(node.value).some( + (node) => node.type === 'ExpressionTag' && node.metadata.expression.has_state ); } }, ExpressionTag(node, context) { - context.next({ ...context.state, expression: node }); + context.next({ ...context.state, expression: node.metadata.expression }); }, Identifier(node, context) { const parent = /** @type {Node} */ (context.path.at(-1)); @@ -1264,7 +1264,7 @@ const common_visitors = { // if no binding, means some global variable if (binding && binding.kind !== 'normal') { if (context.state.expression) { - context.state.expression.metadata.dynamic = true; + context.state.expression.has_state = true; } // TODO it would be better to just bail out when we hit the ExportSpecifier node but that's @@ -1297,13 +1297,10 @@ const common_visitors = { }, CallExpression(node, context) { const { expression, render_tag } = context.state; - if ( - (expression?.type === 'ExpressionTag' || - expression?.type === 'SpreadAttribute' || - expression?.type === 'OnDirective') && - !is_known_safe_call(node, context) - ) { - expression.metadata.contains_call_expression = true; + + if (expression && !is_known_safe_call(node, context)) { + expression.has_call = true; + expression.has_state = true; } if (render_tag) { @@ -1354,7 +1351,7 @@ const common_visitors = { }, MemberExpression(node, context) { if (context.state.expression) { - context.state.expression.metadata.dynamic = true; + context.state.expression.has_state = true; } if (!is_safe_identifier(node, context.state.scope)) { @@ -1368,7 +1365,7 @@ const common_visitors = { if (parent?.type === 'SvelteElement' || parent?.type === 'RegularElement') { state.analysis.event_directive_node ??= node; } - next({ ...state, expression: node }); + next({ ...state, expression: node.metadata.expression }); }, BindDirective(node, context) { let i = context.path.length; @@ -1543,7 +1540,7 @@ const common_visitors = { node.name.includes('.') ? node.name.slice(0, node.name.indexOf('.')) : node.name ); - node.metadata.dynamic = binding !== null && binding.kind !== 'normal'; + node.metadata.expression.has_state = binding !== null && binding.kind !== 'normal'; }, RenderTag(node, context) { context.next({ ...context.state, render_tag: node }); 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 feacbdedcc..044de78527 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/types.d.ts +++ b/packages/svelte/src/compiler/phases/2-analyze/types.d.ts @@ -2,6 +2,7 @@ import type { Scope } from '../scope.js'; import type { ComponentAnalysis, ReactiveStatement } from '../types.js'; import type { ClassDirective, + ExpressionMetadata, ExpressionTag, OnDirective, RenderTag, @@ -21,7 +22,7 @@ export interface AnalysisState { /** Which slots the current parent component has */ component_slots: Set; /** The current {expression}, if any */ - expression: ExpressionTag | ClassDirective | OnDirective | SpreadAttribute | null; + expression: ExpressionMetadata | null; /** The current {@render ...} tag, if any */ render_tag: null | RenderTag; private_derived_state: string[]; diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js index ca78a33a1c..b48e891e90 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js @@ -1,5 +1,5 @@ /** @import { BlockStatement, CallExpression, Expression, ExpressionStatement, Identifier, Literal, MemberExpression, ObjectExpression, Pattern, Property, Statement, Super, TemplateElement, TemplateLiteral } from 'estree' */ -/** @import { Attribute, BindDirective, Binding, ClassDirective, Component, DelegatedEvent, EachBlock, ExpressionTag, Namespace, OnDirective, RegularElement, SpreadAttribute, StyleDirective, SvelteComponent, SvelteElement, SvelteNode, SvelteSelf, TemplateNode, Text } from '#compiler' */ +/** @import { Attribute, BindDirective, Binding, ClassDirective, Component, DelegatedEvent, EachBlock, ExpressionMetadata, ExpressionTag, Namespace, OnDirective, RegularElement, SpreadAttribute, StyleDirective, SvelteComponent, SvelteElement, SvelteNode, SvelteSelf, TemplateNode, Text } from '#compiler' */ /** @import { SourceLocation } from '#shared' */ /** @import { Scope } from '../../../scope.js' */ /** @import { ComponentClientTransformState, ComponentContext, ComponentVisitors } from '../types.js' */ @@ -104,12 +104,16 @@ function serialize_style_directives(style_directives, element_id, context, is_at ); const contains_call_expression = get_attribute_chunks(directive.value).some( - (v) => v.type === 'ExpressionTag' && v.metadata.contains_call_expression + (v) => v.type === 'ExpressionTag' && v.metadata.expression.has_call ); if (!is_attributes_reactive && contains_call_expression) { state.init.push(serialize_update(update)); - } else if (is_attributes_reactive || directive.metadata.dynamic || contains_call_expression) { + } else if ( + is_attributes_reactive || + directive.metadata.expression.has_state || + contains_call_expression + ) { state.update.push(update); } else { state.init.push(update); @@ -155,7 +159,11 @@ function serialize_class_directives(class_directives, element_id, context, is_at if (!is_attributes_reactive && contains_call_expression) { state.init.push(serialize_update(update)); - } else if (is_attributes_reactive || directive.metadata.dynamic || contains_call_expression) { + } else if ( + is_attributes_reactive || + directive.metadata.expression.has_state || + contains_call_expression + ) { state.update.push(update); } else { state.init.push(update); @@ -306,7 +314,7 @@ function serialize_element_spread_attributes( } needs_isolation ||= - attribute.type === 'SpreadAttribute' && attribute.metadata.contains_call_expression; + attribute.type === 'SpreadAttribute' && attribute.metadata.expression.has_call; } const lowercase_attributes = @@ -405,11 +413,11 @@ function serialize_dynamic_element_attributes(attributes, context, element_id) { } is_reactive ||= - attribute.metadata.dynamic || + attribute.metadata.expression.has_state || // objects could contain reactive getters -> play it safe and always assume spread attributes are reactive attribute.type === 'SpreadAttribute'; needs_isolation ||= - attribute.type === 'SpreadAttribute' && attribute.metadata.contains_call_expression; + attribute.type === 'SpreadAttribute' && attribute.metadata.expression.has_call; } if (needs_isolation || is_reactive) { @@ -492,7 +500,7 @@ function serialize_element_attribute_update_assignment(element, node_id, attribu if (context.state.metadata.namespace === 'foreign') { const statement = b.stmt(b.call('$.set_attribute', node_id, b.literal(name), value)); - if (attribute.metadata.dynamic) { + if (attribute.metadata.expression.has_state) { const id = state.scope.generate(`${node_id.name}_${name}`); serialize_update_assignment(state, id, undefined, value, statement); return true; @@ -529,7 +537,7 @@ function serialize_element_attribute_update_assignment(element, node_id, attribu update = b.stmt(b.call(callee, node_id, b.literal(name), value)); } - if (attribute.metadata.dynamic) { + if (attribute.metadata.expression.has_state) { if (contains_call_expression) { state.init.push(serialize_update(update)); } else { @@ -556,7 +564,7 @@ function serialize_custom_element_attribute_update_assignment(node_id, attribute const update = b.stmt(b.call('$.set_custom_element_data', node_id, b.literal(name), value)); - if (attribute.metadata.dynamic) { + if (attribute.metadata.expression.has_state) { if (contains_call_expression) { state.init.push(serialize_update(update)); } else { @@ -592,7 +600,7 @@ function serialize_element_special_value_attribute(element, node_id, attribute, value ) ); - const is_reactive = attribute.metadata.dynamic; + const is_reactive = attribute.metadata.expression.has_state; const is_select_with_value = // attribute.metadata.dynamic would give false negatives because even if the value does not change, // the inner options could still change, so we need to always treat it as reactive @@ -723,10 +731,10 @@ function serialize_inline_component(node, component_name, context, anchor = cont events[attribute.name].push(handler); } else if (attribute.type === 'SpreadAttribute') { const expression = /** @type {Expression} */ (context.visit(attribute)); - if (attribute.metadata.dynamic) { + if (attribute.metadata.expression.has_state) { let value = expression; - if (attribute.metadata.contains_call_expression) { + if (attribute.metadata.expression.has_call) { const id = b.id(context.state.scope.generate('spread_element')); context.state.init.push(b.var(id, b.call('$.derived', b.thunk(value)))); value = b.call('$.get', id); @@ -754,7 +762,7 @@ function serialize_inline_component(node, component_name, context, anchor = cont const [, value] = serialize_attribute_value(attribute.value, context); - if (attribute.metadata.dynamic) { + if (attribute.metadata.expression.has_state) { let arg = value; // When we have a non-simple computation, anything other than an Identifier or Member expression, @@ -1118,7 +1126,7 @@ function serialize_render_stmt(update) { /** * Serializes the event handler function of the `on:` directive * @param {Pick} node - * @param {null | { contains_call_expression: boolean; dynamic: boolean; } | null} metadata + * @param {null | ExpressionMetadata} metadata * @param {ComponentContext} context */ function serialize_event_handler(node, metadata, { state, visit }) { @@ -1145,7 +1153,7 @@ function serialize_event_handler(node, metadata, { state, visit }) { ); if ( - metadata?.contains_call_expression && + metadata?.has_call && !( (handler.type === 'ArrowFunctionExpression' || handler.type === 'FunctionExpression') && handler.metadata.hoistable @@ -1227,7 +1235,7 @@ function serialize_event_handler(node, metadata, { state, visit }) { /** * Serializes an event handler function of the `on:` directive or an attribute starting with `on` * @param {{name: string;modifiers: string[];expression: Expression | null;delegated?: DelegatedEvent | null;}} node - * @param {null | { contains_call_expression: boolean; dynamic: boolean; }} metadata + * @param {null | ExpressionMetadata} metadata * @param {ComponentContext} context */ function serialize_event(node, metadata, context) { @@ -1353,7 +1361,9 @@ function serialize_event_attribute(node, context) { modifiers, delegated: node.metadata.delegated }, - !Array.isArray(node.value) && node.value?.type === 'ExpressionTag' ? node.value.metadata : null, + !Array.isArray(node.value) && node.value?.type === 'ExpressionTag' + ? node.value.metadata.expression + : null, context ); } @@ -1397,9 +1407,9 @@ function process_children(nodes, expression, is_element, { visit, state }) { b.call('$.set_text', text_id, /** @type {Expression} */ (visit(node.expression, state))) ); - if (node.metadata.contains_call_expression && !within_bound_contenteditable) { + if (node.metadata.expression.has_call && !within_bound_contenteditable) { state.init.push(serialize_update(update)); - } else if (node.metadata.dynamic && !within_bound_contenteditable) { + } else if (node.metadata.expression.has_state && !within_bound_contenteditable) { state.update.push(update); } else { state.init.push( @@ -1427,7 +1437,9 @@ function process_children(nodes, expression, is_element, { visit, state }) { if (contains_call_expression && !within_bound_contenteditable) { state.init.push(serialize_update(update)); } else if ( - sequence.some((node) => node.type === 'ExpressionTag' && node.metadata.dynamic) && + sequence.some( + (node) => node.type === 'ExpressionTag' && node.metadata.expression.has_state + ) && !within_bound_contenteditable ) { state.update.push(update); @@ -1528,7 +1540,7 @@ function serialize_attribute_value(value, context) { } return [ - chunk.metadata.contains_call_expression, + chunk.metadata.expression.has_call, /** @type {Expression} */ (context.visit(chunk.expression)) ]; } @@ -1555,7 +1567,7 @@ function serialize_template_literal(values, visit, state) { for (let i = 0; i < values.length; i++) { const node = values[i]; - if (node.type === 'ExpressionTag' && node.metadata.contains_call_expression) { + if (node.type === 'ExpressionTag' && node.metadata.expression.has_call) { if (contains_call_expression) { contains_multiple_call_expression = true; } @@ -2851,7 +2863,7 @@ export const template_visitors = { context.next({ ...context.state, in_constructor: false }); }, OnDirective(node, context) { - serialize_event(node, node.metadata, context); + serialize_event(node, node.metadata.expression, context); }, UseDirective(node, { state, next, visit }) { const params = [b.id('$$node')]; @@ -3115,7 +3127,7 @@ export const template_visitors = { } }, Component(node, context) { - if (node.metadata.dynamic) { + if (node.metadata.expression.has_state) { // Handle dynamic references to what seems like static inline components const component = serialize_inline_component(node, '$$component', context, b.id('$$anchor')); context.state.init.push( @@ -3234,7 +3246,7 @@ export const template_visitors = { name = value; is_default = false; } else if (attribute.name !== 'slot') { - if (attribute.metadata.dynamic) { + if (attribute.metadata.expression.has_state) { props.push(b.get(attribute.name, [b.return(value)])); } else { props.push(b.init(attribute.name, value)); diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/component.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/component.js index 19e0fd6e43..84fec05665 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/component.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/component.js @@ -233,7 +233,8 @@ export function serialize_inline_component(node, expression, context) { } const dynamic = - node.type === 'SvelteComponent' || (node.type === 'Component' && node.metadata.dynamic); + node.type === 'SvelteComponent' || + (node.type === 'Component' && node.metadata.expression.has_state); if (custom_css_props.length > 0) { context.state.template.push( 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 fbfa4497b0..052b3c5d6f 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 @@ -12,7 +12,11 @@ import { LoadErrorElements, WhitespaceInsensitiveAttributes } from '../../../../constants.js'; -import { create_attribute, is_custom_element_node } from '../../../../nodes.js'; +import { + create_attribute, + create_expression_metadata, + is_custom_element_node +} from '../../../../nodes.js'; import { regex_starts_with_newline } from '../../../../patterns.js'; import * as b from '../../../../../utils/builders.js'; import { @@ -142,8 +146,7 @@ export function serialize_element_attributes(node, context) { serialize_attribute_value(value_attribute.value, context) ), metadata: { - contains_call_expression: false, - dynamic: false + expression: create_expression_metadata() } } ]) @@ -158,8 +161,7 @@ export function serialize_element_attributes(node, context) { parent: attribute, expression: attribute.expression, metadata: { - contains_call_expression: false, - dynamic: false + expression: create_expression_metadata() } } ]) @@ -399,7 +401,9 @@ function serialize_class_directives(class_directives, class_attribute) { ), b.literal(' ') ), - metadata: { contains_call_expression: false, dynamic: false } + metadata: { + expression: create_expression_metadata() + } }); class_attribute.value = chunks; diff --git a/packages/svelte/src/compiler/phases/nodes.js b/packages/svelte/src/compiler/phases/nodes.js index bb69b9749a..6237f22899 100644 --- a/packages/svelte/src/compiler/phases/nodes.js +++ b/packages/svelte/src/compiler/phases/nodes.js @@ -45,8 +45,19 @@ export function create_attribute(name, start, end, value) { value, parent: null, metadata: { - dynamic: false, + expression: create_expression_metadata(), delegated: null } }; } + +/** + * @returns {Compiler.ExpressionMetadata} + */ +export function create_expression_metadata() { + return { + dependencies: new Set(), + has_state: false, + has_call: false + }; +} diff --git a/packages/svelte/src/compiler/types/index.d.ts b/packages/svelte/src/compiler/types/index.d.ts index af03530207..c7bf2ff3e1 100644 --- a/packages/svelte/src/compiler/types/index.d.ts +++ b/packages/svelte/src/compiler/types/index.d.ts @@ -317,8 +317,11 @@ export interface Binding { } export interface ExpressionMetadata { + /** All the bindings that are referenced inside this expression */ dependencies: 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; } diff --git a/packages/svelte/src/compiler/types/template.d.ts b/packages/svelte/src/compiler/types/template.d.ts index 69d5585b50..191042d1cc 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, Css, EffectMetadata } from '#compiler'; +import type { Binding, Css, ExpressionMetadata } from '#compiler'; import type { ArrayExpression, ArrowFunctionExpression, @@ -110,7 +110,9 @@ export interface Text extends BaseNode { export interface ExpressionTag extends BaseNode { type: 'ExpressionTag'; expression: Expression; - effect: EffectMetadata; + metadata: { + expression: ExpressionMetadata; + }; } /** A (possibly reactive) HTML template expression — `{@html ...}` */ @@ -183,7 +185,7 @@ export interface ClassDirective extends BaseNode { /** The 'y' in `class:x={y}`, or the `x` in `class:x` */ expression: Expression; metadata: { - dynamic: false; + expression: ExpressionMetadata; }; } @@ -205,8 +207,7 @@ export interface OnDirective extends BaseNode { expression: null | Expression; modifiers: string[]; // TODO specify metadata: { - contains_call_expression: boolean; - dynamic: boolean; + expression: ExpressionMetadata; }; } @@ -226,7 +227,7 @@ export interface StyleDirective extends BaseNode { value: true | ExpressionTag | Array; modifiers: Array<'important'>; metadata: { - dynamic: boolean; + expression: ExpressionMetadata; }; } @@ -273,7 +274,7 @@ interface BaseElement extends BaseNode { export interface Component extends BaseElement { type: 'Component'; metadata: { - dynamic: boolean; + expression: ExpressionMetadata; }; } @@ -447,7 +448,7 @@ export interface Attribute extends BaseNode { name: string; value: true | ExpressionTag | Array; metadata: { - dynamic: boolean; + expression: ExpressionMetadata; /** May be set if this is an event attribute */ delegated: null | DelegatedEvent; }; @@ -456,7 +457,9 @@ export interface Attribute extends BaseNode { export interface SpreadAttribute extends BaseNode { type: 'SpreadAttribute'; expression: Expression; - effect: EffectMetadata; + metadata: { + expression: ExpressionMetadata; + }; } export type TemplateNode = diff --git a/packages/svelte/tests/parser-modern/samples/options/output.json b/packages/svelte/tests/parser-modern/samples/options/output.json index 708bf0cd1a..fd02d5da56 100644 --- a/packages/svelte/tests/parser-modern/samples/options/output.json +++ b/packages/svelte/tests/parser-modern/samples/options/output.json @@ -45,7 +45,11 @@ ], "parent": null, "metadata": { - "dynamic": false, + "expression": { + "dependencies": {}, + "has_state": false, + "has_call": false + }, "delegated": null } }, @@ -77,13 +81,20 @@ }, "parent": null, "metadata": { - "contains_call_expression": false, - "dynamic": false + "expression": { + "dependencies": {}, + "has_state": false, + "has_call": false + } } }, "parent": null, "metadata": { - "dynamic": false, + "expression": { + "dependencies": {}, + "has_state": false, + "has_call": false + }, "delegated": null } }