Skip to content

Commit 3a69b4c

Browse files
trueadmRich-Harris
andauthored
fix: ensure inline object literals are correctly serialised (#14325)
* fix: ensure inline object literals are correctly serialised * Apply suggestions from code review * address feedback * address feedback --------- Co-authored-by: Rich Harris <[email protected]>
1 parent 95ab85f commit 3a69b4c

File tree

4 files changed

+46
-3
lines changed

4 files changed

+46
-3
lines changed

.changeset/wicked-readers-knock.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: treat property accesses of literals as pure

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

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/** @import { AssignmentExpression, Expression, Identifier, Pattern, PrivateIdentifier, Super, UpdateExpression, VariableDeclarator } from 'estree' */
1+
/** @import { AssignmentExpression, Expression, Literal, Pattern, PrivateIdentifier, Super, UpdateExpression, VariableDeclarator } from 'estree' */
22
/** @import { AST, Binding } from '#compiler' */
33
/** @import { AnalysisState, Context } from '../../types' */
44
/** @import { Scope } from '../../../scope' */
@@ -176,21 +176,42 @@ export function is_safe_identifier(expression, scope) {
176176
}
177177

178178
/**
179-
* @param {Expression | Super} node
179+
* @param {Expression | Literal | Super} node
180180
* @param {Context} context
181181
* @returns {boolean}
182182
*/
183183
export function is_pure(node, context) {
184+
if (node.type === 'Literal') {
185+
return true;
186+
}
187+
if (node.type === 'CallExpression') {
188+
if (!is_pure(node.callee, context)) {
189+
return false;
190+
}
191+
for (let arg of node.arguments) {
192+
if (!is_pure(arg.type === 'SpreadElement' ? arg.argument : arg, context)) {
193+
return false;
194+
}
195+
}
196+
return true;
197+
}
184198
if (node.type !== 'Identifier' && node.type !== 'MemberExpression') {
185199
return false;
186200
}
187201

188-
const left = object(node);
202+
/** @type {Expression | Super | null} */
203+
let left = node;
204+
while (left.type === 'MemberExpression') {
205+
left = left.object;
206+
}
207+
189208
if (!left) return false;
190209

191210
if (left.type === 'Identifier') {
192211
const binding = context.state.scope.get(left.name);
193212
if (binding === null) return true; // globals are assumed to be safe
213+
} else if (is_pure(left, context)) {
214+
return true;
194215
}
195216

196217
// TODO add more cases (safe Svelte imports, etc)
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { test } from '../../test';
2+
3+
export default test({
4+
html: `
5+
<p>Without text expression: 7.36</p>
6+
<p>With text expression: 7.36</p>
7+
<p>With text expression and function call: 7.36</p>
8+
<p>With text expression and property access: 4</p>
9+
<h1>Hello name!</h1>
10+
<p>4</p>`
11+
});
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<p>Without text expression: 7.36</p>
2+
<p>With text expression: {7.36}</p>
3+
<p>With text expression and function call: {(7.36).toLocaleString()}</p>
4+
<p>With text expression and property access: {"test".length}</p>
5+
<h1>Hello {('name').toUpperCase().toLowerCase()}!</h1>
6+
<p>{"test".length}</p>

0 commit comments

Comments
 (0)