Skip to content

Commit 3d5702a

Browse files
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 #13171 (comment)
1 parent 93f8329 commit 3d5702a

File tree

5 files changed

+94
-23
lines changed

5 files changed

+94
-23
lines changed

packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ import {
3131
build_render_statement,
3232
build_template_literal,
3333
build_update,
34-
build_update_assignment
34+
build_update_assignment,
35+
get_states_and_calls
3536
} from './shared/utils.js';
3637
import { visit_event_attribute } from './shared/events.js';
3738

@@ -315,14 +316,20 @@ export function RegularElement(node, context) {
315316

316317
// special case — if an element that only contains text, we don't need
317318
// to descend into it if the text is non-reactive
318-
const text_content =
319+
const states_and_calls =
319320
trimmed.every((node) => node.type === 'Text' || node.type === 'ExpressionTag') &&
320321
trimmed.some((node) => node.type === 'ExpressionTag') &&
321-
build_template_literal(trimmed, context.visit, child_state);
322+
get_states_and_calls(trimmed);
322323

323-
if (text_content && !text_content.has_state) {
324+
if (states_and_calls && states_and_calls.states === 0) {
324325
child_state.init.push(
325-
b.stmt(b.assignment('=', b.member(context.state.node, 'textContent'), text_content.value))
326+
b.stmt(
327+
b.assignment(
328+
'=',
329+
b.member(context.state.node, 'textContent'),
330+
build_template_literal(trimmed, context.visit, child_state).value
331+
)
332+
)
326333
);
327334
} else {
328335
/** @type {Expression} */

packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,28 @@ import { create_derived } from '../../utils.js';
1010
import is_reference from 'is-reference';
1111
import { locator } from '../../../../../state.js';
1212

13+
/**
14+
* @param {Array<AST.Text | AST.ExpressionTag>} values
15+
*/
16+
export function get_states_and_calls(values) {
17+
let states = 0;
18+
let calls = 0;
19+
for (let i = 0; i < values.length; i++) {
20+
const node = values[i];
21+
22+
if (node.type === 'ExpressionTag') {
23+
if (node.metadata.expression.has_call) {
24+
calls++;
25+
}
26+
if (node.metadata.expression.has_state) {
27+
states++;
28+
}
29+
}
30+
}
31+
32+
return { states, calls };
33+
}
34+
1335
/**
1436
* @param {Array<AST.Text | AST.ExpressionTag>} values
1537
* @param {(node: SvelteNode, state: any) => any} visit
@@ -22,24 +44,11 @@ export function build_template_literal(values, visit, state) {
2244
let quasi = b.quasi('');
2345
const quasis = [quasi];
2446

25-
let has_call = false;
26-
let has_state = false;
27-
let contains_multiple_call_expression = false;
47+
const { states, calls } = get_states_and_calls(values);
2848

29-
for (let i = 0; i < values.length; i++) {
30-
const node = values[i];
31-
32-
if (node.type === 'ExpressionTag') {
33-
if (node.metadata.expression.has_call) {
34-
if (has_call) {
35-
contains_multiple_call_expression = true;
36-
}
37-
has_call = true;
38-
}
39-
40-
has_state ||= node.metadata.expression.has_state;
41-
}
42-
}
49+
let has_call = calls > 0;
50+
let has_state = states > 0;
51+
let contains_multiple_call_expression = calls > 1;
4352

4453
for (let i = 0; i < values.length; i++) {
4554
const node = values[i];
@@ -53,7 +62,6 @@ export function build_template_literal(values, visit, state) {
5362
} else {
5463
if (contains_multiple_call_expression) {
5564
const id = b.id(state.scope.generate('stringified_text'));
56-
5765
state.init.push(
5866
b.const(
5967
id,
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import "svelte/internal/disclose-version";
2+
import * as $ from "svelte/internal/client";
3+
4+
var root = $.template(`<p> </p>`);
5+
6+
export default function Text_nodes_deriveds($$anchor) {
7+
let count1 = 0;
8+
let count2 = 0;
9+
10+
function text1() {
11+
return count1;
12+
}
13+
14+
function text2() {
15+
return count2;
16+
}
17+
18+
var p = root();
19+
const stringified_text = $.derived(() => text1() ?? "");
20+
const stringified_text_1 = $.derived(() => text2() ?? "");
21+
var text = $.child(p);
22+
23+
$.template_effect(() => $.set_text(text, `${$.get(stringified_text)}${$.get(stringified_text_1)}`));
24+
$.reset(p);
25+
$.append($$anchor, p);
26+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import * as $ from "svelte/internal/server";
2+
3+
export default function Text_nodes_deriveds($$payload) {
4+
let count1 = 0;
5+
let count2 = 0;
6+
7+
function text1() {
8+
return count1;
9+
}
10+
11+
function text2() {
12+
return count2;
13+
}
14+
15+
$$payload.out += `<p>${$.escape(text1())}${$.escape(text2())}</p>`;
16+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<script>
2+
let count1=$state(0);
3+
let count2=$state(0);
4+
5+
function text1(){
6+
return count1;
7+
}
8+
9+
function text2(){
10+
return count2;
11+
}
12+
</script>
13+
14+
<p>{text1()}{text2()}</p>

0 commit comments

Comments
 (0)