From 2bc7c03241087a0a6c1031a3a1abdf0eaa238ce2 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 4 Dec 2023 08:19:20 -0500 Subject: [PATCH] only create source for state that is reassigned --- .../3-transform/client/transform-client.js | 7 ++--- .../phases/3-transform/client/utils.js | 3 ++- .../client/visitors/javascript-runes.js | 27 ++++++++++++------- .../_expected/client/main.svelte.js | 18 ++++++------- .../_expected/client/index.svelte.js | 4 +++ .../_expected/server/index.svelte.js | 5 ++++ .../samples/export-state/index.svelte.js | 3 +++ 7 files changed, 44 insertions(+), 23 deletions(-) create mode 100644 packages/svelte/tests/snapshot/samples/export-state/_expected/client/index.svelte.js create mode 100644 packages/svelte/tests/snapshot/samples/export-state/_expected/server/index.svelte.js create mode 100644 packages/svelte/tests/snapshot/samples/export-state/index.svelte.js diff --git a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js index d4e2550c41..d69b70ec08 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js @@ -240,11 +240,12 @@ export function client_component(source, analysis, options) { const properties = analysis.exports.map(({ name, alias }) => { const binding = analysis.instance.scope.get(name); + const is_source = + binding?.kind === 'state' && (!state.analysis.immutable || binding.reassigned); + // TODO This is always a getter because the `renamed-instance-exports` test wants it that way. // Should we for code size reasons make it an init in runes mode and/or non-dev mode? - return b.get(alias ?? name, [ - b.return(binding?.kind === 'state' ? b.call('$.get', b.id(name)) : b.id(name)) - ]); + return b.get(alias ?? name, [b.return(is_source ? b.call('$.get', b.id(name)) : b.id(name))]); }); if (analysis.accessors) { 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 c58f0bb484..d6700add11 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/utils.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/utils.js @@ -79,7 +79,8 @@ export function serialize_get_binding(node, state) { } if ( - binding.kind === 'state' || + (binding.kind === 'state' && + (!state.analysis.immutable || state.analysis.accessors || binding.reassigned)) || binding.kind === 'derived' || binding.kind === 'prop' || binding.kind === 'rest_prop' || 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 ae343f3ef3..01a6ec7df3 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 @@ -211,21 +211,28 @@ export const javascript_visitors_runes = { } const args = /** @type {import('estree').CallExpression} */ (init).arguments; - const value = + let value = args.length === 0 ? b.id('undefined') : /** @type {import('estree').Expression} */ (visit(args[0])); - const opts = args[1] && /** @type {import('estree').Expression} */ (visit(args[1])); if (declarator.id.type === 'Identifier') { - const callee = rune === '$state' ? '$.source' : '$.derived'; - const arg = - rune === '$state' - ? should_proxy(value) - ? b.call('$.proxy', value) - : value - : b.thunk(value); - declarations.push(b.declarator(declarator.id, b.call(callee, arg, opts))); + if (rune === '$state') { + const binding = /** @type {import('#compiler').Binding} */ ( + state.scope.get(declarator.id.name) + ); + if (should_proxy(value)) { + value = b.call('$.proxy', value); + } + + if (!state.analysis.immutable || state.analysis.accessors || binding.reassigned) { + value = b.call('$.source', value); + } + } else { + value = b.call('$.derived', b.thunk(value)); + } + + declarations.push(b.declarator(declarator.id, value)); continue; } diff --git a/packages/svelte/tests/snapshot/samples/dynamic-attributes-casing/_expected/client/main.svelte.js b/packages/svelte/tests/snapshot/samples/dynamic-attributes-casing/_expected/client/main.svelte.js index 6f53521673..713b4e1337 100644 --- a/packages/svelte/tests/snapshot/samples/dynamic-attributes-casing/_expected/client/main.svelte.js +++ b/packages/svelte/tests/snapshot/samples/dynamic-attributes-casing/_expected/client/main.svelte.js @@ -9,8 +9,8 @@ export default function Main($$anchor, $$props) { $.push($$props, true); // needs to be a snapshot test because jsdom does auto-correct the attribute casing - let x = $.source('test'); - let y = $.source(() => 'test'); + let x = 'test'; + let y = () => 'test'; /* Init */ var fragment = $.open_frag($$anchor, false, frag); var node = $.child_frag(fragment); @@ -21,28 +21,28 @@ export default function Main($$anchor, $$props) { var custom_element_1 = $.sibling($.sibling(svg_1)); /* Update */ - $.attr_effect(div, "foobar", () => $.get(y)()); - $.attr_effect(svg_1, "viewBox", () => $.get(y)()); - $.set_custom_element_data_effect(custom_element_1, "fooBar", () => $.get(y)()); + $.attr_effect(div, "foobar", () => y()); + $.attr_effect(svg_1, "viewBox", () => y()); + $.set_custom_element_data_effect(custom_element_1, "fooBar", () => y()); var node_foobar; var svg_viewBox; var custom_element_fooBar; $.render_effect(() => { - if (node_foobar !== (node_foobar = $.get(x))) { + if (node_foobar !== (node_foobar = x)) { $.attr(node, "foobar", node_foobar); } - if (svg_viewBox !== (svg_viewBox = $.get(x))) { + if (svg_viewBox !== (svg_viewBox = x)) { $.attr(svg, "viewBox", svg_viewBox); } - if (custom_element_fooBar !== (custom_element_fooBar = $.get(x))) { + if (custom_element_fooBar !== (custom_element_fooBar = x)) { $.set_custom_element_data(custom_element, "fooBar", custom_element_fooBar); } }); $.close_frag($$anchor, fragment); $.pop(); -} \ No newline at end of file +} diff --git a/packages/svelte/tests/snapshot/samples/export-state/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/export-state/_expected/client/index.svelte.js new file mode 100644 index 0000000000..c9ba1e7c73 --- /dev/null +++ b/packages/svelte/tests/snapshot/samples/export-state/_expected/client/index.svelte.js @@ -0,0 +1,4 @@ +/* index.svelte.js generated by Svelte VERSION */ +import * as $ from "svelte/internal"; + +export const object = $.proxy({ ok: true }); diff --git a/packages/svelte/tests/snapshot/samples/export-state/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/export-state/_expected/server/index.svelte.js new file mode 100644 index 0000000000..770f2e5e4f --- /dev/null +++ b/packages/svelte/tests/snapshot/samples/export-state/_expected/server/index.svelte.js @@ -0,0 +1,5 @@ +/* index.svelte.js generated by Svelte VERSION */ +import * as $ from "svelte/internal/server"; + +export const object = { ok: true }; + diff --git a/packages/svelte/tests/snapshot/samples/export-state/index.svelte.js b/packages/svelte/tests/snapshot/samples/export-state/index.svelte.js new file mode 100644 index 0000000000..023fcb9b35 --- /dev/null +++ b/packages/svelte/tests/snapshot/samples/export-state/index.svelte.js @@ -0,0 +1,3 @@ +export const object = $state({ + ok: true +});