From 2b4410007dabcefc09aa24a2862476e6152f8810 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 26 Jun 2025 21:27:23 -0400 Subject: [PATCH] dry out --- .../3-transform/client/transform-client.js | 6 +- .../phases/3-transform/client/types.d.ts | 12 +--- .../3-transform/client/visitors/Fragment.js | 5 +- .../client/visitors/RegularElement.js | 31 +++------- .../3-transform/client/visitors/RenderTag.js | 27 +++------ .../client/visitors/SlotElement.js | 27 +++------ .../client/visitors/SvelteElement.js | 7 +-- .../client/visitors/shared/component.js | 40 ++++--------- .../client/visitors/shared/element.js | 52 ++++++----------- .../client/visitors/shared/utils.js | 56 +++++++++++-------- 10 files changed, 99 insertions(+), 164 deletions(-) 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 fcb9b47484..85fe359e20 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 @@ -59,6 +59,7 @@ import { UpdateExpression } from './visitors/UpdateExpression.js'; import { UseDirective } from './visitors/UseDirective.js'; import { AttachTag } from './visitors/AttachTag.js'; import { VariableDeclaration } from './visitors/VariableDeclaration.js'; +import { Memoizer } from './visitors/shared/utils.js'; /** @type {Visitors} */ const visitors = { @@ -170,10 +171,9 @@ export function client_component(analysis, options) { // these are set inside the `Fragment` visitor, and cannot be used until then 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) + template: /** @type {any} */ (null), + memoizer: /** @type {any} */ (null) }; const module = /** @type {ESTree.Program} */ ( 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 9ca16e6549..cf5c942268 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 @@ -12,6 +12,7 @@ import type { AST, Namespace, ValidatedCompileOptions } from '#compiler'; import type { TransformState } from '../types.js'; import type { ComponentAnalysis } from '../../types.js'; import type { Template } from './transform-template/template.js'; +import type { Memoizer } from './visitors/shared/utils.js'; export interface ClientTransformState extends TransformState { /** @@ -49,10 +50,8 @@ export interface ComponentClientTransformState extends ClientTransformState { readonly update: Statement[]; /** 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 }>; - /** Expressions used inside the render effect */ - readonly async_expressions: Array<{ id: Identifier; expression: Expression }>; + /** Memoized expressions */ + readonly memoizer: Memoizer; /** The HTML template string */ readonly template: Template; readonly metadata: { @@ -87,8 +86,3 @@ 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 122cae4501..0b10c02ffb 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 @@ -6,7 +6,7 @@ import * as b from '#compiler/builders'; import { clean_nodes, infer_namespace } from '../../utils.js'; import { transform_template } from '../transform-template/index.js'; import { process_children } from './shared/fragment.js'; -import { build_render_statement } from './shared/utils.js'; +import { build_render_statement, Memoizer } from './shared/utils.js'; import { Template } from '../transform-template/template.js'; /** @@ -62,9 +62,8 @@ export function Fragment(node, context) { ...context.state, init: [], update: [], - expressions: [], - async_expressions: [], after_update: [], + memoizer: new Memoizer(), template: new Template(), transform: { ...context.state.transform }, metadata: { 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 ad11af9216..0426ddc3b4 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 @@ -22,7 +22,7 @@ import { build_set_style } from './shared/element.js'; import { process_children } from './shared/fragment.js'; -import { build_render_statement, build_template_chunk, get_expression_id } from './shared/utils.js'; +import { build_render_statement, build_template_chunk, Memoizer } from './shared/utils.js'; import { visit_event_attribute } from './shared/events.js'; /** @@ -255,10 +255,7 @@ export function RegularElement(node, context) { context, (value, metadata) => metadata.has_call || metadata.has_await - ? get_expression_id( - metadata.has_await ? context.state.async_expressions : context.state.expressions, - value - ) + ? context.state.memoizer.add(value, metadata.has_await) : value ); @@ -465,15 +462,13 @@ function setup_select_synchronization(value_binding, context) { /** * @param {AST.ClassDirective[]} class_directives * @param {ComponentContext} context - * @param {MemoizedExpression[]} async_expressions - * @param {MemoizedExpression[]} expressions + * @param {Memoizer} memoizer * @return {ObjectExpression | Identifier} */ export function build_class_directives_object( class_directives, context, - async_expressions = context.state.async_expressions, - expressions = context.state.expressions + memoizer = context.state.memoizer ) { let properties = []; let has_call_or_state = false; @@ -488,23 +483,19 @@ export function build_class_directives_object( const directives = b.object(properties); - return has_call_or_state || has_await - ? get_expression_id(has_await ? async_expressions : expressions, directives) - : directives; + return has_call_or_state || has_await ? memoizer.add(directives, has_await) : directives; } /** * @param {AST.StyleDirective[]} style_directives * @param {ComponentContext} context - * @param {MemoizedExpression[]} async_expressions - * @param {MemoizedExpression[]} expressions + * @param {Memoizer} memoizer * @return {ObjectExpression | ArrayExpression | Identifier}} */ export function build_style_directives_object( style_directives, context, - async_expressions = context.state.async_expressions, - expressions = context.state.expressions + memoizer = context.state.memoizer ) { const normal = b.object([]); const important = b.object([]); @@ -527,9 +518,7 @@ export function build_style_directives_object( const directives = important.properties.length ? b.array([normal, important]) : normal; - return has_call_or_state || has_await - ? get_expression_id(has_await ? async_expressions : expressions, directives) - : directives; + return has_call_or_state || has_await ? memoizer.add(directives, has_await) : directives; } /** @@ -651,9 +640,7 @@ function build_element_special_value_attribute(element, node_id, attribute, cont element === 'select' && attribute.value !== true && !is_text_attribute(attribute); const { value, has_state } = build_attribute_value(attribute.value, context, (value, metadata) => - metadata.has_call || metadata.has_await - ? get_expression_id(metadata.has_await ? state.async_expressions : state.expressions, value) - : value + metadata.has_call || metadata.has_await ? state.memoizer.add(value, metadata.has_await) : value ); const evaluated = context.state.scope.evaluate(value); diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RenderTag.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RenderTag.js index b7187173e2..5255693fe3 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RenderTag.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RenderTag.js @@ -1,10 +1,10 @@ /** @import { Expression, Statement } from 'estree' */ /** @import { AST } from '#compiler' */ -/** @import { ComponentContext, MemoizedExpression } from '../types' */ +/** @import { ComponentContext } from '../types' */ import { unwrap_optional } from '../../../../utils/ast.js'; import * as b from '#compiler/builders'; import { create_derived } from '../utils.js'; -import { get_expression_id, build_expression } from './shared/utils.js'; +import { build_expression, Memoizer } from './shared/utils.js'; /** * @param {AST.RenderTag} node @@ -18,11 +18,7 @@ export function RenderTag(node, context) { /** @type {Expression[]} */ let args = []; - /** @type {MemoizedExpression[]} */ - const expressions = []; - - /** @type {MemoizedExpression[]} */ - const async_expressions = []; + const memoizer = new Memoizer(); for (let i = 0; i < call.arguments.length; i++) { const arg = /** @type {Expression} */ (call.arguments[i]); @@ -31,21 +27,16 @@ export function RenderTag(node, context) { let expression = build_expression(context, arg, metadata); if (metadata.has_await || metadata.has_call) { - expression = b.call( - '$.get', - get_expression_id(metadata.has_await ? async_expressions : expressions, expression) - ); + expression = b.call('$.get', memoizer.add(expression, metadata.has_await)); } args.push(b.thunk(expression)); } - [...async_expressions, ...expressions].forEach((memo, i) => { - memo.id.name = `$${i}`; - }); + memoizer.apply(); /** @type {Statement[]} */ - const statements = expressions.map((memo) => + const statements = memoizer.sync.map((memo) => b.var(memo.id, create_derived(context.state, b.thunk(memo.expression))) ); @@ -76,15 +67,15 @@ export function RenderTag(node, context) { ); } - if (async_expressions.length > 0) { + if (memoizer.async.length > 0) { context.state.init.push( b.stmt( b.call( '$.async', context.state.node, - b.array(async_expressions.map((memo) => b.thunk(memo.expression, true))), + b.array(memoizer.async.map((memo) => b.thunk(memo.expression, true))), b.arrow( - [context.state.node, ...async_expressions.map((memo) => memo.id)], + [context.state.node, ...memoizer.async.map((memo) => memo.id)], b.block(statements) ) ) diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/SlotElement.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/SlotElement.js index 072eca95f6..70de454c0e 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/SlotElement.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/SlotElement.js @@ -1,10 +1,10 @@ /** @import { BlockStatement, Expression, ExpressionStatement, Literal, Property, Statement } from 'estree' */ /** @import { AST } from '#compiler' */ -/** @import { ComponentContext, MemoizedExpression } from '../types' */ +/** @import { ComponentContext } from '../types' */ import * as b from '#compiler/builders'; import { create_derived } from '../utils.js'; import { build_attribute_value } from './shared/element.js'; -import { get_expression_id } from './shared/utils.js'; +import { Memoizer } from './shared/utils.js'; /** * @param {AST.SlotElement} node @@ -23,11 +23,7 @@ export function SlotElement(node, context) { /** @type {ExpressionStatement[]} */ const lets = []; - /** @type {MemoizedExpression[]} */ - const expressions = []; - - /** @type {MemoizedExpression[]} */ - const async_expressions = []; + const memoizer = new Memoizer(); let name = b.literal('default'); @@ -40,10 +36,7 @@ export function SlotElement(node, context) { context, (value, metadata) => metadata.has_call || metadata.has_await - ? b.call( - '$.get', - get_expression_id(metadata.has_await ? async_expressions : expressions, value) - ) + ? b.call('$.get', memoizer.add(value, metadata.has_await)) : value ); @@ -61,15 +54,13 @@ export function SlotElement(node, context) { } } - [...async_expressions, ...expressions].forEach((memo, i) => { - memo.id.name = `$${i}`; - }); + memoizer.apply(); // Let bindings first, they can be used on attributes context.state.init.push(...lets); /** @type {Statement[]} */ - const statements = expressions.map((memo) => + const statements = memoizer.sync.map((memo) => b.var(memo.id, create_derived(context.state, b.thunk(memo.expression))) ); @@ -85,15 +76,15 @@ export function SlotElement(node, context) { b.stmt(b.call('$.slot', context.state.node, b.id('$$props'), name, props_expression, fallback)) ); - if (async_expressions.length > 0) { + if (memoizer.async.length > 0) { context.state.init.push( b.stmt( b.call( '$.async', context.state.node, - b.array(async_expressions.map((memo) => b.thunk(memo.expression, true))), + b.array(memoizer.async.map((memo) => b.thunk(memo.expression, true))), b.arrow( - [context.state.node, ...async_expressions.map((memo) => memo.id)], + [context.state.node, ...memoizer.async.map((memo) => memo.id)], b.block(statements) ) ) 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 0534895fe1..2062519bb6 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 @@ -10,7 +10,7 @@ import { build_attribute_effect, build_set_class } from './shared/element.js'; -import { build_render_statement } from './shared/utils.js'; +import { build_render_statement, Memoizer } from './shared/utils.js'; /** * @param {AST.SvelteElement} node @@ -46,9 +46,8 @@ export function SvelteElement(node, context) { node: element_id, init: [], update: [], - expressions: [], - async_expressions: [], - after_update: [] + after_update: [], + memoizer: new Memoizer() } }; diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js index d14a60da67..77535cf16e 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js @@ -1,10 +1,10 @@ /** @import { BlockStatement, Expression, ExpressionStatement, Identifier, MemberExpression, Pattern, Property, SequenceExpression, Statement } from 'estree' */ /** @import { AST } from '#compiler' */ -/** @import { ComponentContext, MemoizedExpression } from '../../types.js' */ +/** @import { ComponentContext } from '../../types.js' */ import { dev, is_ignored } from '../../../../../state.js'; import { get_attribute_chunks, object } from '../../../../../utils/ast.js'; import * as b from '#compiler/builders'; -import { build_bind_this, get_expression_id, validate_binding } from '../shared/utils.js'; +import { build_bind_this, Memoizer, validate_binding } from '../shared/utils.js'; import { build_attribute_value } from '../shared/element.js'; import { build_event_handler } from './events.js'; import { determine_slot } from '../../../../../utils/slot.js'; @@ -44,11 +44,7 @@ export function build_component(node, component_name, context) { /** @type {Record} */ const events = {}; - /** @type {MemoizedExpression[]} */ - const expressions = []; - - /** @type {MemoizedExpression[]} */ - const async_expressions = []; + const memoizer = new Memoizer(); /** @type {Property[]} */ const custom_css_props = []; @@ -139,13 +135,7 @@ export function build_component(node, component_name, context) { props_and_spreads.push( b.thunk( attribute.metadata.expression.has_await || attribute.metadata.expression.has_call - ? b.call( - '$.get', - get_expression_id( - attribute.metadata.expression.has_await ? async_expressions : expressions, - expression - ) - ) + ? b.call('$.get', memoizer.add(expression, attribute.metadata.expression.has_await)) : expression ) ); @@ -160,10 +150,7 @@ export function build_component(node, component_name, context) { build_attribute_value(attribute.value, context, (value, metadata) => { // TODO put the derived in the local block return metadata.has_call || metadata.has_await - ? b.call( - '$.get', - get_expression_id(metadata.has_await ? async_expressions : expressions, value) - ) + ? b.call('$.get', memoizer.add(value, metadata.has_await)) : value; }).value ) @@ -199,10 +186,7 @@ export function build_component(node, component_name, context) { }); return should_wrap_in_derived - ? b.call( - '$.get', - get_expression_id(metadata.has_await ? async_expressions : expressions, value) - ) + ? b.call('$.get', memoizer.add(value, metadata.has_await)) : value; } ); @@ -465,7 +449,7 @@ export function build_component(node, component_name, context) { const statements = [ ...snippet_declarations, - ...expressions.map((memo) => + ...memoizer.sync.map((memo) => b.let(memo.id, create_derived(context.state, b.thunk(memo.expression))) ) ]; @@ -515,17 +499,15 @@ export function build_component(node, component_name, context) { statements.push(b.stmt(fn(anchor))); } - [...async_expressions, ...expressions].forEach((memo, i) => { - memo.id.name = `$${i}`; - }); + memoizer.apply(); - if (async_expressions.length > 0) { + if (memoizer.async.length > 0) { return b.stmt( b.call( '$.async', anchor, - b.array(async_expressions.map(({ expression }) => b.thunk(expression, true))), - b.arrow([b.id('$$anchor'), ...async_expressions.map(({ id }) => id)], b.block(statements)) + b.array(memoizer.async.map(({ expression }) => b.thunk(expression, true))), + b.arrow([b.id('$$anchor'), ...memoizer.async.map(({ id }) => id)], b.block(statements)) ) ); } 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 868cfe2946..8da489409b 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,13 +1,13 @@ -/** @import { ArrayExpression, Expression, Identifier, ObjectExpression } from 'estree' */ +/** @import { Expression, Identifier, ObjectExpression } from 'estree' */ /** @import { AST, ExpressionMetadata } from '#compiler' */ -/** @import { ComponentContext, MemoizedExpression } from '../../types' */ +/** @import { ComponentContext } from '../../types' */ import { escape_html } from '../../../../../../escaping.js'; import { normalize_attribute } from '../../../../../../utils.js'; import { is_ignored } from '../../../../../state.js'; 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, get_expression_id } from './utils.js'; +import { build_expression, build_template_chunk, Memoizer } from './utils.js'; /** * @param {Array} attributes @@ -28,18 +28,12 @@ export function build_attribute_effect( /** @type {ObjectExpression['properties']} */ const values = []; - /** @type {MemoizedExpression[]} */ - const async_expressions = []; - - /** @type {MemoizedExpression[]} */ - const expressions = []; + const memoizer = new Memoizer(); for (const attribute of attributes) { if (attribute.type === 'Attribute') { const { value } = build_attribute_value(attribute.value, context, (value, metadata) => - metadata.has_call || metadata.has_await - ? get_expression_id(metadata.has_await ? async_expressions : expressions, value) - : value + metadata.has_call || metadata.has_await ? memoizer.add(value, metadata.has_await) : value ); if ( @@ -57,10 +51,7 @@ export function build_attribute_effect( let value = /** @type {Expression} */ (context.visit(attribute)); if (attribute.metadata.expression.has_call || attribute.metadata.expression.has_await) { - value = get_expression_id( - attribute.metadata.expression.has_await ? async_expressions : expressions, - value - ); + value = memoizer.add(value, attribute.metadata.expression.has_await); } values.push(b.spread(value)); @@ -72,7 +63,7 @@ export function build_attribute_effect( b.prop( 'init', b.array([b.id('$.CLASS')]), - build_class_directives_object(class_directives, context, async_expressions, expressions) + build_class_directives_object(class_directives, context, memoizer) ) ); } @@ -82,16 +73,12 @@ export function build_attribute_effect( b.prop( 'init', b.array([b.id('$.STYLE')]), - build_style_directives_object(style_directives, context, async_expressions, expressions) + build_style_directives_object(style_directives, context, memoizer) ) ); } - const all = [...expressions, ...async_expressions]; - - for (let i = 0; i < all.length; i += 1) { - all[i].id.name = `$${i}`; - } + const all = memoizer.apply(); context.state.init.push( b.stmt( @@ -102,9 +89,10 @@ export function build_attribute_effect( all.map(({ id }) => id), b.object(values) ), - expressions.length > 0 && b.array(expressions.map(({ expression }) => b.thunk(expression))), - async_expressions.length > 0 && - b.array(async_expressions.map(({ expression }) => b.thunk(expression, true))), + memoizer.sync.length > 0 && + b.array(memoizer.sync.map(({ expression }) => b.thunk(expression))), + memoizer.async.length > 0 && + b.array(memoizer.async.map(({ expression }) => b.thunk(expression, true))), element.metadata.scoped && context.state.analysis.css.hash !== '' && b.literal(context.state.analysis.css.hash), @@ -170,10 +158,7 @@ export function build_set_class(element, node_id, attribute, class_directives, c } return metadata.has_call || metadata.has_await - ? get_expression_id( - metadata.has_await ? context.state.async_expressions : context.state.expressions, - value - ) + ? context.state.memoizer.add(value, metadata.has_await) : value; }); @@ -244,12 +229,7 @@ export function build_set_class(element, node_id, attribute, class_directives, c */ export function build_set_style(node_id, attribute, style_directives, context) { let { value, has_state } = build_attribute_value(attribute.value, context, (value, metadata) => - metadata.has_call - ? get_expression_id( - metadata.has_await ? context.state.async_expressions : context.state.expressions, - value - ) - : value + metadata.has_call ? context.state.memoizer.add(value, metadata.has_await) : value ); /** @type {Identifier | undefined} */ @@ -258,7 +238,7 @@ export function build_set_style(node_id, attribute, style_directives, context) { /** @type {ObjectExpression | Identifier | undefined} */ let prev; - /** @type {ArrayExpression | ObjectExpression | undefined} */ + /** @type {Expression | undefined} */ let next; if (style_directives.length) { 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 bbffefc152..ed9b8ad8a4 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 { AssignmentExpression, Expression, Identifier, MemberExpression, SequenceExpression, Literal, Super, UpdateExpression } from 'estree' */ /** @import { AST, ExpressionMetadata } from '#compiler' */ -/** @import { ComponentClientTransformState, ComponentContext, Context, MemoizedExpression } from '../../types' */ +/** @import { ComponentClientTransformState, ComponentContext, Context } from '../../types' */ import { walk } from 'zimmerframe'; import { object } from '../../../../../utils/ast.js'; import * as b from '#compiler/builders'; @@ -10,16 +10,34 @@ import is_reference from 'is-reference'; import { dev, is_ignored, locator } from '../../../../../state.js'; import { build_getter } from '../../utils.js'; -/** - * - * @param {MemoizedExpression[]} expressions - * @param {Expression} expression - */ -export function get_expression_id(expressions, expression) { - const id = b.id(`#`); // filled in later - expressions.push({ id, expression }); +export class Memoizer { + /** @type {Array<{ id: Identifier, expression: Expression }>} */ + sync = []; + + /** @type {Array<{ id: Identifier, expression: Expression }>} */ + async = []; + + /** + * @param {Expression} expression + * @param {boolean} has_await + */ + add(expression, has_await) { + const id = b.id(`#`); // filled in later + + (has_await ? this.async : this.sync).push({ id, expression }); - return id; + return id; + } + + apply() { + const all = [...this.async, ...this.sync]; + + all.forEach((memo, i) => { + memo.id.name = `$${i}`; + }); + + return all; + } } /** @@ -34,9 +52,7 @@ export function build_template_chunk( context, state = context.state, memoize = (value, metadata) => - metadata.has_call || metadata.has_await - ? get_expression_id(metadata.has_await ? state.async_expressions : state.expressions, value) - : value + metadata.has_call || metadata.has_await ? state.memoizer.add(value, metadata.has_await) : value ) { /** @type {Expression[]} */ const expressions = []; @@ -125,14 +141,9 @@ export function build_template_chunk( * @param {ComponentClientTransformState} state */ export function build_render_statement(state) { - const sync = state.expressions; - const async = state.async_expressions; - - const all = [...sync, ...async]; + const { memoizer } = state; - for (let i = 0; i < all.length; i += 1) { - all[i].id.name = `$${i}`; - } + const all = state.memoizer.apply(); return b.stmt( b.call( @@ -143,8 +154,9 @@ export function build_render_statement(state) { ? state.update[0].expression : b.block(state.update) ), - all.length > 0 && b.array(sync.map(({ expression }) => b.thunk(expression))), - async.length > 0 && b.array(async.map(({ expression }) => b.thunk(expression, true))) + all.length > 0 && b.array(memoizer.sync.map(({ expression }) => b.thunk(expression))), + memoizer.async.length > 0 && + b.array(memoizer.async.map(({ expression }) => b.thunk(expression, true))) ) ); }