Skip to content

Commit 9d12fd1

Browse files
trueadmRich-Harris
andauthored
chore: remove template expression inlining (#14374)
* chore: remove template expression inlining * missed some * fix * feedback * feedback * Update packages/svelte/src/compiler/phases/3-transform/client/utils.js Co-authored-by: Rich Harris <[email protected]> * Update packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js Co-authored-by: Rich Harris <[email protected]> * Update packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js Co-authored-by: Rich Harris <[email protected]> * Update packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js Co-authored-by: Rich Harris <[email protected]> * Update packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js Co-authored-by: Rich Harris <[email protected]> * Update packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js Co-authored-by: Rich Harris <[email protected]> * fix * Update .changeset/calm-mice-perform.md Co-authored-by: Rich Harris <[email protected]> --------- Co-authored-by: Rich Harris <[email protected]>
1 parent f616c22 commit 9d12fd1

File tree

20 files changed

+97
-261
lines changed

20 files changed

+97
-261
lines changed

.changeset/calm-mice-perform.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': patch
3+
---
4+
5+
fix: remove template expression inlining

packages/svelte/src/compiler/phases/2-analyze/visitors/Attribute.js

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/** @import { ArrowFunctionExpression, Expression, FunctionDeclaration, FunctionExpression } from 'estree' */
22
/** @import { AST, DelegatedEvent, SvelteNode } from '#compiler' */
33
/** @import { Context } from '../types' */
4-
import { is_boolean_attribute, is_capture_event, is_delegated } from '../../../../utils.js';
4+
import { cannot_be_set_statically, is_capture_event, is_delegated } from '../../../../utils.js';
55
import {
66
get_attribute_chunks,
77
get_attribute_expression,
@@ -30,12 +30,12 @@ export function Attribute(node, context) {
3030
}
3131
}
3232

33-
if (node.name.startsWith('on')) {
33+
if (is_event_attribute(node)) {
3434
mark_subtree_dynamic(context.path);
3535
}
3636

37-
if (parent.type === 'RegularElement' && is_boolean_attribute(node.name.toLowerCase())) {
38-
node.metadata.expression.can_inline = false;
37+
if (cannot_be_set_statically(node.name)) {
38+
mark_subtree_dynamic(context.path);
3939
}
4040

4141
if (node.value !== true) {
@@ -51,7 +51,6 @@ export function Attribute(node, context) {
5151

5252
node.metadata.expression.has_state ||= chunk.metadata.expression.has_state;
5353
node.metadata.expression.has_call ||= chunk.metadata.expression.has_call;
54-
node.metadata.expression.can_inline &&= chunk.metadata.expression.can_inline;
5554
}
5655

5756
if (is_event_attribute(node)) {

packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -179,8 +179,6 @@ export function CallExpression(node, context) {
179179
if (!is_pure(node.callee, context) || context.state.expression.dependencies.size > 0) {
180180
context.state.expression.has_call = true;
181181
context.state.expression.has_state = true;
182-
context.state.expression.can_inline = false;
183-
mark_subtree_dynamic(context.path);
184182
}
185183
}
186184
}

packages/svelte/src/compiler/phases/2-analyze/visitors/ExpressionTag.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
/** @import { Context } from '../types' */
33
import { is_tag_valid_with_parent } from '../../../../html-tree-validation.js';
44
import * as e from '../../../errors.js';
5+
import { mark_subtree_dynamic } from './shared/fragment.js';
56

67
/**
78
* @param {AST.ExpressionTag} node
@@ -14,5 +15,9 @@ export function ExpressionTag(node, context) {
1415
}
1516
}
1617

18+
// TODO ideally we wouldn't do this here, we'd just do it on encountering
19+
// an `Identifier` within the tag. But we currently need to handle `{42}` etc
20+
mark_subtree_dynamic(context.path);
21+
1722
context.next({ ...context.state, expression: node.metadata.expression });
1823
}

packages/svelte/src/compiler/phases/2-analyze/visitors/Identifier.js

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
/** @import { Expression, Identifier } from 'estree' */
2+
/** @import { EachBlock } from '#compiler' */
23
/** @import { Context } from '../types' */
34
import is_reference from 'is-reference';
45
import { should_proxy } from '../../3-transform/client/utils.js';
@@ -19,6 +20,8 @@ export function Identifier(node, context) {
1920
return;
2021
}
2122

23+
mark_subtree_dynamic(context.path);
24+
2225
// If we are using arguments outside of a function, then throw an error
2326
if (
2427
node.name === 'arguments' &&
@@ -84,12 +87,6 @@ export function Identifier(node, context) {
8487
}
8588
}
8689

87-
// no binding means global, and we can't inline e.g. `<span>{location}</span>`
88-
// because it could change between component renders. if there _is_ a
89-
// binding and it is outside module scope, the expression cannot
90-
// be inlined (TODO allow inlining in more cases - e.g. primitive consts)
91-
let can_inline = !!binding && !binding.scope.parent && binding.kind === 'normal';
92-
9390
if (binding) {
9491
if (context.state.expression) {
9592
context.state.expression.dependencies.add(binding);
@@ -125,12 +122,4 @@ export function Identifier(node, context) {
125122
w.reactive_declaration_module_script_dependency(node);
126123
}
127124
}
128-
129-
if (!can_inline) {
130-
if (context.state.expression) {
131-
context.state.expression.can_inline = false;
132-
}
133-
134-
mark_subtree_dynamic(context.path);
135-
}
136125
}

packages/svelte/src/compiler/phases/2-analyze/visitors/MemberExpression.js

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,6 @@ export function MemberExpression(node, context) {
2020

2121
if (context.state.expression && !is_pure(node, context)) {
2222
context.state.expression.has_state = true;
23-
context.state.expression.can_inline = false;
24-
25-
mark_subtree_dynamic(context.path);
2623
}
2724

2825
if (!is_safe_identifier(node, context.state.scope)) {

packages/svelte/src/compiler/phases/2-analyze/visitors/RegularElement.js

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/** @import { AST } from '#compiler' */
22
/** @import { Context } from '../types' */
3-
import { cannot_be_set_statically, is_mathml, is_svg, is_void } from '../../../../utils.js';
3+
import { is_mathml, is_svg, is_void } from '../../../../utils.js';
44
import {
55
is_tag_valid_with_ancestor,
66
is_tag_valid_with_parent
@@ -75,14 +75,6 @@ export function RegularElement(node, context) {
7575
node.attributes.push(create_attribute('value', child.start, child.end, [child]));
7676
}
7777

78-
if (
79-
node.attributes.some(
80-
(attribute) => attribute.type === 'Attribute' && cannot_be_set_statically(attribute.name)
81-
)
82-
) {
83-
mark_subtree_dynamic(context.path);
84-
}
85-
8678
const binding = context.state.scope.get(node.name);
8779
if (
8880
binding !== null &&

packages/svelte/src/compiler/phases/2-analyze/visitors/TaggedTemplateExpression.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ export function TaggedTemplateExpression(node, context) {
1010
if (context.state.expression && !is_pure(node.tag, context)) {
1111
context.state.expression.has_call = true;
1212
context.state.expression.has_state = true;
13-
context.state.expression.can_inline = false;
1413
}
1514

1615
if (node.tag.type === 'Identifier') {

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,16 @@
33
/** @import { ClientTransformState, ComponentClientTransformState, ComponentContext } from './types.js' */
44
/** @import { Analysis } from '../../types.js' */
55
/** @import { Scope } from '../../scope.js' */
6+
import * as b from '../../../utils/builders.js';
7+
import { extract_identifiers, is_simple_expression } from '../../../utils/ast.js';
68
import {
7-
PROPS_IS_BINDABLE,
8-
PROPS_IS_IMMUTABLE,
99
PROPS_IS_LAZY_INITIAL,
10+
PROPS_IS_IMMUTABLE,
1011
PROPS_IS_RUNES,
11-
PROPS_IS_UPDATED
12+
PROPS_IS_UPDATED,
13+
PROPS_IS_BINDABLE
1214
} from '../../../../constants.js';
1315
import { dev } from '../../../state.js';
14-
import { extract_identifiers, is_simple_expression } from '../../../utils/ast.js';
15-
import * as b from '../../../utils/builders.js';
1616
import { get_value } from './visitors/shared/declarations.js';
1717

1818
/**

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -141,14 +141,14 @@ export function Fragment(node, context) {
141141
const id = b.id(context.state.scope.generate('fragment'));
142142

143143
const use_space_template =
144-
trimmed.every((node) => node.type === 'Text' || node.type === 'ExpressionTag') &&
145-
trimmed.some((node) => node.type === 'ExpressionTag' && !node.metadata.expression.can_inline);
144+
trimmed.some((node) => node.type === 'ExpressionTag') &&
145+
trimmed.every((node) => node.type === 'Text' || node.type === 'ExpressionTag');
146146

147147
if (use_space_template) {
148148
// special case — we can use `$.text` instead of creating a unique template
149149
const id = b.id(context.state.scope.generate('text'));
150150

151-
process_children(trimmed, () => id, null, {
151+
process_children(trimmed, () => id, false, {
152152
...context,
153153
state
154154
});
@@ -158,12 +158,12 @@ export function Fragment(node, context) {
158158
} else {
159159
if (is_standalone) {
160160
// no need to create a template, we can just use the existing block's anchor
161-
process_children(trimmed, () => b.id('$$anchor'), null, { ...context, state });
161+
process_children(trimmed, () => b.id('$$anchor'), false, { ...context, state });
162162
} else {
163163
/** @type {(is_text: boolean) => Expression} */
164164
const expression = (is_text) => b.call('$.first_child', id, is_text && b.true);
165165

166-
process_children(trimmed, expression, null, { ...context, state });
166+
process_children(trimmed, expression, false, { ...context, state });
167167

168168
let flags = TEMPLATE_FRAGMENT;
169169

0 commit comments

Comments
 (0)