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 <rich.harris@vercel.com>
pull/14328/head
Dominic Gannaway 3 days ago committed by GitHub
parent 95ab85fad7
commit 3a69b4c415
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: treat property accesses of literals as pure

@ -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 { AST, Binding } from '#compiler' */
/** @import { AnalysisState, Context } from '../../types' */ /** @import { AnalysisState, Context } from '../../types' */
/** @import { Scope } from '../../../scope' */ /** @import { Scope } from '../../../scope' */
@ -176,21 +176,42 @@ export function is_safe_identifier(expression, scope) {
} }
/** /**
* @param {Expression | Super} node * @param {Expression | Literal | Super} node
* @param {Context} context * @param {Context} context
* @returns {boolean} * @returns {boolean}
*/ */
export function is_pure(node, context) { 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') { if (node.type !== 'Identifier' && node.type !== 'MemberExpression') {
return false; 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) return false;
if (left.type === 'Identifier') { if (left.type === 'Identifier') {
const binding = context.state.scope.get(left.name); const binding = context.state.scope.get(left.name);
if (binding === null) return true; // globals are assumed to be safe 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) // TODO add more cases (safe Svelte imports, etc)

@ -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>`
});

@ -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>
Loading…
Cancel
Save