diff --git a/.changeset/popular-walls-hunt.md b/.changeset/popular-walls-hunt.md new file mode 100644 index 000000000000..9dd06c2d4e72 --- /dev/null +++ b/.changeset/popular-walls-hunt.md @@ -0,0 +1,5 @@ +--- +"svelte": patch +--- + +fix: improve compiled output of multiple call expression in single text node 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 1d8b7acb7194..2ff6fe17157e 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 @@ -1396,7 +1396,7 @@ function process_children(nodes, expression, is_element, { visit, state }) { state.template.push(' '); - const [contains_call_expression, value] = serialize_template_literal(sequence, visit); + const [contains_call_expression, value] = serialize_template_literal(sequence, visit, state); const update = b.stmt(b.call('$.set_text', text_id, value)); @@ -1511,25 +1511,39 @@ function serialize_attribute_value(attribute_value, context) { } } - return serialize_template_literal(attribute_value, context.visit); + return serialize_template_literal(attribute_value, context.visit, context.state); } /** * @param {Array} values * @param {(node: import('#compiler').SvelteNode) => any} visit + * @param {import("../types.js").ComponentClientTransformState} state * @returns {[boolean, import('estree').TemplateLiteral]} */ -function serialize_template_literal(values, visit) { +function serialize_template_literal(values, visit, state) { /** @type {import('estree').TemplateElement[]} */ const quasis = []; /** @type {import('estree').Expression[]} */ const expressions = []; let contains_call_expression = false; + let contains_multiple_call_expression = false; quasis.push(b.quasi('')); for (let i = 0; i < values.length; i++) { const node = values[i]; + + if (node.type === 'ExpressionTag' && node.metadata.contains_call_expression) { + if (contains_call_expression) { + contains_multiple_call_expression = true; + } + contains_call_expression = true; + } + } + + for (let i = 0; i < values.length; i++) { + const node = values[i]; + if (node.type === 'Text') { const last = /** @type {import('estree').TemplateElement} */ (quasis.at(-1)); last.value.raw += sanitize_template_string(node.data); @@ -1539,11 +1553,23 @@ function serialize_template_literal(values, visit) { last.value.raw += sanitize_template_string(node.expression.value + ''); } } else { - if (node.type === 'ExpressionTag' && node.metadata.contains_call_expression) { - contains_call_expression = true; - } + if (contains_multiple_call_expression) { + const id = b.id(state.scope.generate('stringified_text')); - expressions.push(b.call('$.stringify', visit(node.expression))); + state.init.push( + b.const( + id, + b.call( + // In runes mode, we want things to be fine-grained - but not in legacy mode + state.analysis.runes ? '$.derived' : '$.derived_safe_equal', + b.thunk(/** @type {import('estree').Expression} */ (visit(node.expression))) + ) + ) + ); + expressions.push(b.call('$.get', id)); + } else { + expressions.push(b.call('$.stringify', visit(node.expression))); + } quasis.push(b.quasi('', i + 1 === values.length)); } } @@ -1586,7 +1612,7 @@ export const template_visitors = { declaration.id, b.call( // In runes mode, we want things to be fine-grained - but not in legacy mode - state.options.runes ? '$.derived' : '$.derived_safe_equal', + state.analysis.runes ? '$.derived' : '$.derived_safe_equal', b.thunk(/** @type {import('estree').Expression} */ (visit(declaration.init))) ) ) @@ -1623,7 +1649,7 @@ export const template_visitors = { state.init.push( // In runes mode, we want things to be fine-grained - but not in legacy mode - b.const(tmp, b.call(state.options.runes ? '$.derived' : '$.derived_safe_equal', fn)) + b.const(tmp, b.call(state.analysis.runes ? '$.derived' : '$.derived_safe_equal', fn)) ); // we need to eagerly evaluate the expression in order to hit any @@ -2972,7 +2998,7 @@ export const template_visitors = { b.assignment( '=', b.member(b.id('$.document'), b.id('title')), - serialize_template_literal(/** @type {any} */ (node.fragment.nodes), visit)[1] + serialize_template_literal(/** @type {any} */ (node.fragment.nodes), visit, state)[1] ) ) ); diff --git a/packages/svelte/tests/runtime-legacy/samples/reactive-value-function/main.svelte b/packages/svelte/tests/runtime-legacy/samples/reactive-value-function/main.svelte index 5b132d638e03..4830070e062c 100644 --- a/packages/svelte/tests/runtime-legacy/samples/reactive-value-function/main.svelte +++ b/packages/svelte/tests/runtime-legacy/samples/reactive-value-function/main.svelte @@ -1,7 +1,7 @@ + + + + +{getX()}|{getY()}