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 5255693fe3..528f867e01 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 @@ -3,7 +3,6 @@ /** @import { ComponentContext } from '../types' */ import { unwrap_optional } from '../../../../utils/ast.js'; import * as b from '#compiler/builders'; -import { create_derived } from '../utils.js'; import { build_expression, Memoizer } from './shared/utils.js'; /** @@ -36,9 +35,7 @@ export function RenderTag(node, context) { memoizer.apply(); /** @type {Statement[]} */ - const statements = memoizer.sync.map((memo) => - b.var(memo.id, create_derived(context.state, b.thunk(memo.expression))) - ); + const statements = memoizer.deriveds(); let snippet_function = build_expression( context, @@ -67,17 +64,16 @@ export function RenderTag(node, context) { ); } - if (memoizer.async.length > 0) { + const async_values = memoizer.async_values(); + + if (async_values) { context.state.init.push( b.stmt( b.call( '$.async', context.state.node, - b.array(memoizer.async.map((memo) => b.thunk(memo.expression, true))), - b.arrow( - [context.state.node, ...memoizer.async.map((memo) => memo.id)], - b.block(statements) - ) + async_values, + b.arrow([context.state.node, ...memoizer.async_ids()], 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 70de454c0e..fce445c626 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 @@ -2,7 +2,6 @@ /** @import { AST } from '#compiler' */ /** @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 { Memoizer } from './shared/utils.js'; @@ -60,9 +59,7 @@ export function SlotElement(node, context) { context.state.init.push(...lets); /** @type {Statement[]} */ - const statements = memoizer.sync.map((memo) => - b.var(memo.id, create_derived(context.state, b.thunk(memo.expression))) - ); + const statements = memoizer.deriveds(); const props_expression = spreads.length === 0 ? b.object(props) : b.call('$.spread_props', b.object(props), ...spreads); @@ -76,17 +73,16 @@ export function SlotElement(node, context) { b.stmt(b.call('$.slot', context.state.node, b.id('$$props'), name, props_expression, fallback)) ); - if (memoizer.async.length > 0) { + const async_values = memoizer.async_values(); + + if (async_values) { context.state.init.push( b.stmt( b.call( '$.async', context.state.node, - b.array(memoizer.async.map((memo) => b.thunk(memo.expression, true))), - b.arrow( - [context.state.node, ...memoizer.async.map((memo) => memo.id)], - b.block(statements) - ) + async_values, + b.arrow([context.state.node, ...memoizer.async_ids()], b.block(statements)) ) ) ); 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 77535cf16e..582b57a7ad 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 @@ -8,7 +8,6 @@ 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'; -import { create_derived } from '../../utils.js'; /** * @param {AST.Component | AST.SvelteComponent | AST.SvelteSelf} node @@ -447,12 +446,7 @@ export function build_component(node, component_name, context) { }; } - const statements = [ - ...snippet_declarations, - ...memoizer.sync.map((memo) => - b.let(memo.id, create_derived(context.state, b.thunk(memo.expression))) - ) - ]; + const statements = [...snippet_declarations, ...memoizer.deriveds()]; if (is_component_dynamic) { const prev = fn; @@ -501,13 +495,15 @@ export function build_component(node, component_name, context) { memoizer.apply(); - if (memoizer.async.length > 0) { + const async_values = memoizer.async_values(); + + if (async_values) { return b.stmt( b.call( '$.async', anchor, - b.array(memoizer.async.map(({ expression }) => b.thunk(expression, true))), - b.arrow([b.id('$$anchor'), ...memoizer.async.map(({ id }) => id)], b.block(statements)) + async_values, + b.arrow([b.id('$$anchor'), ...memoizer.async_ids()], 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 8da489409b..d63658b481 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 @@ -78,21 +78,16 @@ export function build_attribute_effect( ); } - const all = memoizer.apply(); + memoizer.apply(); context.state.init.push( b.stmt( b.call( '$.attribute_effect', element_id, - b.arrow( - all.map(({ id }) => id), - b.object(values) - ), - 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))), + b.arrow(memoizer.all_ids(), b.object(values)), + memoizer.sync_values(), + memoizer.async_values(), element.metadata.scoped && context.state.analysis.css.hash !== '' && b.literal(context.state.analysis.css.hash), 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 ed9b8ad8a4..f032f49a48 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 @@ -12,10 +12,10 @@ import { build_getter } from '../../utils.js'; export class Memoizer { /** @type {Array<{ id: Identifier, expression: Expression }>} */ - sync = []; + #sync = []; /** @type {Array<{ id: Identifier, expression: Expression }>} */ - async = []; + #async = []; /** * @param {Expression} expression @@ -24,19 +24,39 @@ export class Memoizer { add(expression, has_await) { const id = b.id(`#`); // filled in later - (has_await ? this.async : this.sync).push({ id, expression }); + (has_await ? this.#async : this.#sync).push({ id, expression }); return id; } apply() { - const all = [...this.async, ...this.sync]; - - all.forEach((memo, i) => { + [...this.#async, ...this.#sync].forEach((memo, i) => { memo.id.name = `$${i}`; }); + } + + all_ids() { + return [...this.#async, ...this.#sync].map((memo) => memo.id); + } + + async_ids() { + return this.#async.map((memo) => memo.id); + } + + async_values() { + if (this.#async.length === 0) return; + return b.array(this.#async.map((memo) => b.thunk(memo.expression, true))); + } + + deriveds(runes = true) { + return this.#sync.map((memo) => + b.let(memo.id, b.call(runes ? '$.derived' : '$.derived_safe_equal', b.thunk(memo.expression))) + ); + } - return all; + sync_values() { + if (this.#sync.length === 0) return; + return b.array(this.#sync.map((memo) => b.thunk(memo.expression))); } } @@ -143,20 +163,19 @@ export function build_template_chunk( export function build_render_statement(state) { const { memoizer } = state; - const all = state.memoizer.apply(); + state.memoizer.apply(); return b.stmt( b.call( '$.template_effect', b.arrow( - all.map(({ id }) => id), + memoizer.all_ids(), state.update.length === 1 && state.update[0].type === 'ExpressionStatement' ? state.update[0].expression : b.block(state.update) ), - 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))) + memoizer.sync_values(), + memoizer.async_values() ) ); }