From 37154706d3009237332f60a8678751ac6e5e9c26 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Fri, 19 Jan 2024 22:28:50 +0000 Subject: [PATCH] further improvements --- .../compiler/phases/2-analyze/validation.js | 2 +- .../phases/3-transform/client/utils.js | 32 +++++++++++++++---- .../client/visitors/javascript-runes.js | 9 ++++-- .../runes-wrong-state-placement-3/_config.js | 10 ++++++ .../runes-wrong-state-placement-3/main.svelte | 11 +++++++ 5 files changed, 53 insertions(+), 11 deletions(-) create mode 100644 packages/svelte/tests/compiler-errors/samples/runes-wrong-state-placement-3/_config.js create mode 100644 packages/svelte/tests/compiler-errors/samples/runes-wrong-state-placement-3/main.svelte diff --git a/packages/svelte/src/compiler/phases/2-analyze/validation.js b/packages/svelte/src/compiler/phases/2-analyze/validation.js index b3bc67c8cb..33bbda0f52 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/validation.js +++ b/packages/svelte/src/compiler/phases/2-analyze/validation.js @@ -721,7 +721,7 @@ function validate_call_expression(node, scope, path) { if ( node.arguments.length === 1 && node.arguments[0].type !== 'SpreadElement' && - should_proxy_or_freeze(node.arguments[0]) + should_proxy_or_freeze(node.arguments[0], scope) ) { return; } diff --git a/packages/svelte/src/compiler/phases/3-transform/client/utils.js b/packages/svelte/src/compiler/phases/3-transform/client/utils.js index c5ff3866d1..49ca618ada 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/utils.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/utils.js @@ -293,7 +293,7 @@ export function serialize_set_binding(node, context, fallback, options) { if ( context.state.analysis.runes && !options?.skip_proxy_and_freeze && - should_proxy_or_freeze(value) + should_proxy_or_freeze(value, context.state.scope) ) { const assignment = fallback(); if (assignment.type === 'AssignmentExpression') { @@ -310,7 +310,7 @@ export function serialize_set_binding(node, context, fallback, options) { left, context.state.analysis.runes && !options?.skip_proxy_and_freeze && - should_proxy_or_freeze(value) + should_proxy_or_freeze(value, context.state.scope) ? private_state.kind === 'frozen_state' ? b.call('$.freeze', value) : b.call('$.proxy', value) @@ -330,7 +330,7 @@ export function serialize_set_binding(node, context, fallback, options) { context.state.analysis.runes && public_state !== undefined && !options?.skip_proxy_and_freeze && - should_proxy_or_freeze(value) + should_proxy_or_freeze(value, context.state.scope) ) { const assignment = fallback(); if (assignment.type === 'AssignmentExpression') { @@ -398,7 +398,7 @@ export function serialize_set_binding(node, context, fallback, options) { b.id(left_name), context.state.analysis.runes && !options?.skip_proxy_and_freeze && - should_proxy_or_freeze(value) + should_proxy_or_freeze(value, context.state.scope) ? b.call('$.proxy', value) : value ); @@ -408,7 +408,7 @@ export function serialize_set_binding(node, context, fallback, options) { b.id(left_name), context.state.analysis.runes && !options?.skip_proxy_and_freeze && - should_proxy_or_freeze(value) + should_proxy_or_freeze(value, context.state.scope) ? b.call('$.freeze', value) : value ); @@ -623,8 +623,11 @@ export function get_prop_source(binding, state, name, initial) { return b.call('$.prop', ...args); } -/** @param {import('estree').Expression} node */ -export function should_proxy_or_freeze(node) { +/** + * @param {import('estree').Expression} node + * @param {import("../../scope.js").Scope | null} scope + */ +export function should_proxy_or_freeze(node, scope) { if ( !node || node.type === 'Literal' || @@ -637,5 +640,20 @@ export function should_proxy_or_freeze(node) { ) { return false; } + if (node.type === 'Identifier' && scope !== null) { + const binding = scope.get(node.name); + // Let's see if the reference is something that can be proxied or frozen + if ( + binding !== null && + !binding.reassigned && + binding.initial !== null && + binding.initial.type !== 'FunctionDeclaration' && + binding.initial.type !== 'ClassDeclaration' && + binding.initial.type !== 'ImportDeclaration' && + binding.initial.type !== 'EachBlock' + ) { + return should_proxy_or_freeze(binding.initial, null); + } + } return true; } diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/javascript-runes.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/javascript-runes.js index d75021e1fd..5ae3813212 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/javascript-runes.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/javascript-runes.js @@ -85,11 +85,14 @@ export const javascript_visitors_runes = { value = field.kind === 'state' - ? b.call('$.source', should_proxy_or_freeze(init) ? b.call('$.proxy', init) : init) + ? b.call( + '$.source', + should_proxy_or_freeze(init, state.scope) ? b.call('$.proxy', init) : init + ) : field.kind === 'frozen_state' ? b.call( '$.source', - should_proxy_or_freeze(init) ? b.call('$.freeze', init) : init + should_proxy_or_freeze(init, state.scope) ? b.call('$.freeze', init) : init ) : b.call('$.derived', b.thunk(init)); } else { @@ -238,7 +241,7 @@ export const javascript_visitors_runes = { */ const create_state_declarator = (id, value) => { const binding = /** @type {import('#compiler').Binding} */ (state.scope.get(id.name)); - if (should_proxy_or_freeze(value)) { + if (should_proxy_or_freeze(value, state.scope)) { value = b.call(rune === '$state' ? '$.proxy' : '$.freeze', value); } if (is_state_source(binding, state)) { diff --git a/packages/svelte/tests/compiler-errors/samples/runes-wrong-state-placement-3/_config.js b/packages/svelte/tests/compiler-errors/samples/runes-wrong-state-placement-3/_config.js new file mode 100644 index 0000000000..77dea8dffa --- /dev/null +++ b/packages/svelte/tests/compiler-errors/samples/runes-wrong-state-placement-3/_config.js @@ -0,0 +1,10 @@ +import { test } from '../../test'; + +export default test({ + error: { + code: 'invalid-state-location', + message: + '$state() can only be used as a variable declaration initializer, a class field or if passed an object or array, can be used as an expression', + position: [113, 124] + } +}); diff --git a/packages/svelte/tests/compiler-errors/samples/runes-wrong-state-placement-3/main.svelte b/packages/svelte/tests/compiler-errors/samples/runes-wrong-state-placement-3/main.svelte new file mode 100644 index 0000000000..c57e5204ee --- /dev/null +++ b/packages/svelte/tests/compiler-errors/samples/runes-wrong-state-placement-3/main.svelte @@ -0,0 +1,11 @@ +