fix: avoid creating unnnecessary deriveds for text nodes with call (#13206)

The build_template function was called but the result was potentially unused, causing unnecessary code output
Found in https://github.com/sveltejs/svelte/pull/13171#issuecomment-2343763030
pull/13217/head
Paolo Ricciuti 1 week ago committed by GitHub
parent 93f8329b1b
commit 3d5702aa8f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -31,7 +31,8 @@ import {
build_render_statement, build_render_statement,
build_template_literal, build_template_literal,
build_update, build_update,
build_update_assignment build_update_assignment,
get_states_and_calls
} from './shared/utils.js'; } from './shared/utils.js';
import { visit_event_attribute } from './shared/events.js'; import { visit_event_attribute } from './shared/events.js';
@ -315,14 +316,20 @@ export function RegularElement(node, context) {
// special case — if an element that only contains text, we don't need // special case — if an element that only contains text, we don't need
// to descend into it if the text is non-reactive // to descend into it if the text is non-reactive
const text_content = const states_and_calls =
trimmed.every((node) => node.type === 'Text' || node.type === 'ExpressionTag') && trimmed.every((node) => node.type === 'Text' || node.type === 'ExpressionTag') &&
trimmed.some((node) => node.type === 'ExpressionTag') && trimmed.some((node) => node.type === 'ExpressionTag') &&
build_template_literal(trimmed, context.visit, child_state); get_states_and_calls(trimmed);
if (text_content && !text_content.has_state) { if (states_and_calls && states_and_calls.states === 0) {
child_state.init.push( child_state.init.push(
b.stmt(b.assignment('=', b.member(context.state.node, 'textContent'), text_content.value)) b.stmt(
b.assignment(
'=',
b.member(context.state.node, 'textContent'),
build_template_literal(trimmed, context.visit, child_state).value
)
)
); );
} else { } else {
/** @type {Expression} */ /** @type {Expression} */

@ -10,6 +10,28 @@ import { create_derived } from '../../utils.js';
import is_reference from 'is-reference'; import is_reference from 'is-reference';
import { locator } from '../../../../../state.js'; import { locator } from '../../../../../state.js';
/**
* @param {Array<AST.Text | AST.ExpressionTag>} values
*/
export function get_states_and_calls(values) {
let states = 0;
let calls = 0;
for (let i = 0; i < values.length; i++) {
const node = values[i];
if (node.type === 'ExpressionTag') {
if (node.metadata.expression.has_call) {
calls++;
}
if (node.metadata.expression.has_state) {
states++;
}
}
}
return { states, calls };
}
/** /**
* @param {Array<AST.Text | AST.ExpressionTag>} values * @param {Array<AST.Text | AST.ExpressionTag>} values
* @param {(node: SvelteNode, state: any) => any} visit * @param {(node: SvelteNode, state: any) => any} visit
@ -22,24 +44,11 @@ export function build_template_literal(values, visit, state) {
let quasi = b.quasi(''); let quasi = b.quasi('');
const quasis = [quasi]; const quasis = [quasi];
let has_call = false; const { states, calls } = get_states_and_calls(values);
let has_state = false;
let contains_multiple_call_expression = false;
for (let i = 0; i < values.length; i++) { let has_call = calls > 0;
const node = values[i]; let has_state = states > 0;
let contains_multiple_call_expression = calls > 1;
if (node.type === 'ExpressionTag') {
if (node.metadata.expression.has_call) {
if (has_call) {
contains_multiple_call_expression = true;
}
has_call = true;
}
has_state ||= node.metadata.expression.has_state;
}
}
for (let i = 0; i < values.length; i++) { for (let i = 0; i < values.length; i++) {
const node = values[i]; const node = values[i];
@ -53,7 +62,6 @@ export function build_template_literal(values, visit, state) {
} else { } else {
if (contains_multiple_call_expression) { if (contains_multiple_call_expression) {
const id = b.id(state.scope.generate('stringified_text')); const id = b.id(state.scope.generate('stringified_text'));
state.init.push( state.init.push(
b.const( b.const(
id, id,

@ -0,0 +1,26 @@
import "svelte/internal/disclose-version";
import * as $ from "svelte/internal/client";
var root = $.template(`<p> </p>`);
export default function Text_nodes_deriveds($$anchor) {
let count1 = 0;
let count2 = 0;
function text1() {
return count1;
}
function text2() {
return count2;
}
var p = root();
const stringified_text = $.derived(() => text1() ?? "");
const stringified_text_1 = $.derived(() => text2() ?? "");
var text = $.child(p);
$.template_effect(() => $.set_text(text, `${$.get(stringified_text)}${$.get(stringified_text_1)}`));
$.reset(p);
$.append($$anchor, p);
}

@ -0,0 +1,16 @@
import * as $ from "svelte/internal/server";
export default function Text_nodes_deriveds($$payload) {
let count1 = 0;
let count2 = 0;
function text1() {
return count1;
}
function text2() {
return count2;
}
$$payload.out += `<p>${$.escape(text1())}${$.escape(text2())}</p>`;
}

@ -0,0 +1,14 @@
<script>
let count1=$state(0);
let count2=$state(0);
function text1(){
return count1;
}
function text2(){
return count2;
}
</script>
<p>{text1()}{text2()}</p>
Loading…
Cancel
Save