diff --git a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js index 0861a7735c..c1c8170e30 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js @@ -175,6 +175,7 @@ export function client_component(analysis, options) { init: /** @type {any} */ (null), update: /** @type {any} */ (null), expressions: /** @type {any} */ (null), + async_expressions: /** @type {any} */ (null), after_update: /** @type {any} */ (null), template: /** @type {any} */ (null), locations: /** @type {any} */ (null) diff --git a/packages/svelte/src/compiler/phases/3-transform/client/types.d.ts b/packages/svelte/src/compiler/phases/3-transform/client/types.d.ts index 51c6f428d4..9cfcd718c5 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/types.d.ts +++ b/packages/svelte/src/compiler/phases/3-transform/client/types.d.ts @@ -53,7 +53,9 @@ export interface ComponentClientTransformState extends ClientTransformState { /** Stuff that happens after the render effect (control blocks, dynamic elements, bindings, actions, etc) */ readonly after_update: Statement[]; /** Expressions used inside the render effect */ - readonly expressions: Array<{ id: Identifier; expression: Expression; is_async: boolean }>; + readonly expressions: Array<{ id: Identifier; expression: Expression }>; + /** Expressions used inside the render effect */ + readonly async_expressions: Array<{ id: Identifier; expression: Expression }>; /** The HTML template string */ readonly template: Array; readonly locations: SourceLocation[]; @@ -113,3 +115,8 @@ export type ComponentVisitors = import('zimmerframe').Visitors< AST.SvelteNode, ComponentClientTransformState >; + +export interface MemoizedExpression { + id: Identifier; + expression: Expression; +} diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/Fragment.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/Fragment.js index da65862fd9..2d15435199 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/Fragment.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/Fragment.js @@ -64,6 +64,7 @@ export function Fragment(node, context) { init: [], update: [], expressions: [], + async_expressions: [], after_update: [], template: [], locations: [], diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js index 3c306b241f..7c22f3c7bc 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js @@ -543,7 +543,7 @@ function build_element_attribute_update_assignment( let { value, has_state } = build_attribute_value(attribute.value, context, (value, metadata) => metadata.has_call || metadata.is_async - ? get_expression_id(state, value, metadata.is_async) + ? get_expression_id(metadata.is_async ? state.async_expressions : state.expressions, value) : value ); @@ -673,7 +673,7 @@ function build_element_special_value_attribute(element, node_id, attribute, cont const state = context.state; const { value, has_state } = build_attribute_value(attribute.value, context, (value, metadata) => metadata.has_call || metadata.is_async - ? get_expression_id(state, value, metadata.is_async) + ? get_expression_id(metadata.is_async ? state.async_expressions : state.expressions, value) : value ); diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteElement.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteElement.js index e275283655..ccf08dc423 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteElement.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteElement.js @@ -48,6 +48,7 @@ export function SvelteElement(node, context) { init: [], update: [], expressions: [], + async_expressions: [], after_update: [] } }; 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 097b309345..79cc8f531c 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 @@ -40,7 +40,10 @@ export function build_set_attributes( context, (value, metadata) => metadata.has_call || metadata.is_async - ? get_expression_id(context.state, value, metadata.is_async) + ? get_expression_id( + metadata.is_async ? context.state.async_expressions : context.state.expressions, + value + ) : value ); @@ -64,7 +67,12 @@ export function build_set_attributes( let value = /** @type {Expression} */ (context.visit(attribute)); if (attribute.metadata.expression.has_call || attribute.metadata.expression.is_async) { - value = get_expression_id(context.state, value, attribute.metadata.expression.is_async); + value = get_expression_id( + attribute.metadata.expression.is_async + ? context.state.async_expressions + : context.state.expressions, + value + ); } values.push(b.spread(value)); @@ -117,7 +125,10 @@ export function build_style_directives( ? build_getter({ name: directive.name, type: 'Identifier' }, context.state) : build_attribute_value(directive.value, context, (value, metadata) => metadata.has_call || metadata.is_async - ? get_expression_id(context.state, value, metadata.is_async) + ? get_expression_id( + metadata.is_async ? context.state.async_expressions : context.state.expressions, + value + ) : value ).value; @@ -159,7 +170,7 @@ export function build_class_directives( let value = /** @type {Expression} */ (context.visit(directive.expression)); if (has_call || is_async) { - value = get_expression_id(state, value, is_async); + value = get_expression_id(is_async ? state.async_expressions : state.expressions, value); } const update = b.stmt(b.call('$.toggle_class', element_id, b.literal(directive.name), value)); 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 2bfbc5ff8a..077ced10c2 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,6 +1,6 @@ -/** @import { Expression, ExpressionStatement, Identifier, MemberExpression, SequenceExpression, Statement, Super } from 'estree' */ +/** @import { Expression, ExpressionStatement, Identifier, MemberExpression, SequenceExpression, Super } from 'estree' */ /** @import { AST, ExpressionMetadata } from '#compiler' */ -/** @import { ComponentClientTransformState } from '../../types' */ +/** @import { ComponentClientTransformState, MemoizedExpression } from '../../types' */ import { walk } from 'zimmerframe'; import { object } from '../../../../../utils/ast.js'; import * as b from '../../../../../utils/builders.js'; @@ -22,19 +22,18 @@ export function memoize_expression(state, value) { /** * - * @param {ComponentClientTransformState} state + * @param {MemoizedExpression[]} expressions * @param {Expression} expression - * @param {boolean} is_async */ -export function get_expression_id(state, expression, is_async) { - for (let i = 0; i < state.expressions.length; i += 1) { - if (compare_expressions(state.expressions[i].expression, expression)) { - return state.expressions[i].id; +export function get_expression_id(expressions, expression) { + for (let i = 0; i < expressions.length; i += 1) { + if (compare_expressions(expressions[i].expression, expression)) { + return expressions[i].id; } } - const id = b.id(''); // filled in later - state.expressions.push({ id, expression, is_async }); + const id = b.id('~'); // filled in later + expressions.push({ id, expression }); return id; } @@ -92,7 +91,7 @@ export function build_template_chunk( state, memoize = (value, metadata) => metadata.has_call || metadata.is_async - ? get_expression_id(state, value, metadata.is_async) + ? get_expression_id(metadata.is_async ? state.async_expressions : state.expressions, value) : value ) { /** @type {Expression[]} */ @@ -163,8 +162,8 @@ export function build_template_chunk( * @param {ComponentClientTransformState} state */ export function build_render_statement(state) { - const sync = state.expressions.filter(({ is_async }) => !is_async); - const async = state.expressions.filter(({ is_async }) => is_async); + const sync = state.expressions; + const async = state.async_expressions; const all = [...sync, ...async];