Skip to content

Commit

Permalink
fix: ensure inline object literals are correctly serialised (#14325)
Browse files Browse the repository at this point in the history
* fix: ensure inline object literals are correctly serialised

* Apply suggestions from code review

* address feedback

* address feedback

---------

Co-authored-by: Rich Harris <rich.harris@vercel.com>
  • Loading branch information
trueadm and Rich-Harris authored Nov 16, 2024
1 parent 95ab85f commit 3a69b4c
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 3 deletions.
5 changes: 5 additions & 0 deletions .changeset/wicked-readers-knock.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'svelte': patch
---

fix: treat property accesses of literals as pure
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/** @import { AssignmentExpression, Expression, Identifier, Pattern, PrivateIdentifier, Super, UpdateExpression, VariableDeclarator } from 'estree' */
/** @import { AssignmentExpression, Expression, Literal, Pattern, PrivateIdentifier, Super, UpdateExpression, VariableDeclarator } from 'estree' */
/** @import { AST, Binding } from '#compiler' */
/** @import { AnalysisState, Context } from '../../types' */
/** @import { Scope } from '../../../scope' */
Expand Down Expand Up @@ -176,21 +176,42 @@ export function is_safe_identifier(expression, scope) {
}

/**
* @param {Expression | Super} node
* @param {Expression | Literal | Super} node
* @param {Context} context
* @returns {boolean}
*/
export function is_pure(node, context) {
if (node.type === 'Literal') {
return true;
}
if (node.type === 'CallExpression') {
if (!is_pure(node.callee, context)) {
return false;
}
for (let arg of node.arguments) {
if (!is_pure(arg.type === 'SpreadElement' ? arg.argument : arg, context)) {
return false;
}
}
return true;
}
if (node.type !== 'Identifier' && node.type !== 'MemberExpression') {
return false;
}

const left = object(node);
/** @type {Expression | Super | null} */
let left = node;
while (left.type === 'MemberExpression') {
left = left.object;
}

if (!left) return false;

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

// TODO add more cases (safe Svelte imports, etc)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { test } from '../../test';

export default test({
html: `
<p>Without text expression: 7.36</p>
<p>With text expression: 7.36</p>
<p>With text expression and function call: 7.36</p>
<p>With text expression and property access: 4</p>
<h1>Hello name!</h1>
<p>4</p>`
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<p>Without text expression: 7.36</p>
<p>With text expression: {7.36}</p>
<p>With text expression and function call: {(7.36).toLocaleString()}</p>
<p>With text expression and property access: {"test".length}</p>
<h1>Hello {('name').toUpperCase().toLowerCase()}!</h1>
<p>{"test".length}</p>

0 comments on commit 3a69b4c

Please sign in to comment.