From 03945f56b4faefed43377940a89765b5fe19058a Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti Date: Thu, 15 Aug 2024 14:42:10 +0200 Subject: [PATCH] fix: hoist reactive imports to the module (#12845) * fix: hoist reactive imports to the module * fix * Update .changeset/eleven-teachers-drive.md * beef up test * unused --------- Co-authored-by: Rich Harris Co-authored-by: Rich Harris --- .changeset/eleven-teachers-drive.md | 5 ++++ .../3-transform/client/transform-client.js | 2 +- .../3-transform/client/visitors/Program.js | 28 +++++++++++++------ .../_config.js | 17 +++++++++++ .../main.svelte | 9 ++++++ .../reactive-import-statement-module/state.js | 3 ++ 6 files changed, 54 insertions(+), 10 deletions(-) create mode 100644 .changeset/eleven-teachers-drive.md create mode 100644 packages/svelte/tests/runtime-legacy/samples/reactive-import-statement-module/_config.js create mode 100644 packages/svelte/tests/runtime-legacy/samples/reactive-import-statement-module/main.svelte create mode 100644 packages/svelte/tests/runtime-legacy/samples/reactive-import-statement-module/state.js diff --git a/.changeset/eleven-teachers-drive.md b/.changeset/eleven-teachers-drive.md new file mode 100644 index 0000000000..6a8129a733 --- /dev/null +++ b/.changeset/eleven-teachers-drive.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: treat module-level imports as non-reactive in legacy mode 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 5bc3e123cc..ca6763477e 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 @@ -197,7 +197,7 @@ export function client_component(analysis, options) { ) ); - instance.body.unshift(...state.legacy_reactive_imports); + module.body.unshift(...state.legacy_reactive_imports); /** @type {ESTree.Statement[]} */ const store_setup = []; diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/Program.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/Program.js index c222d74d6e..53e76f9a1d 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/Program.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/Program.js @@ -1,4 +1,4 @@ -/** @import { Expression, MemberExpression, Program } from 'estree' */ +/** @import { Expression, ImportDeclaration, MemberExpression, Program } from 'estree' */ /** @import { ComponentContext } from '../types' */ import { build_getter, is_prop_source } from '../utils.js'; import * as b from '../../../../utils/builders.js'; @@ -16,16 +16,26 @@ export function Program(_, context) { for (const [name, binding] of context.state.scope.declarations) { if (binding.declaration_kind === 'import' && binding.mutated) { - const id = b.id('$$_import_' + name); + // the declaration itself is hoisted to the module scope, so we need + // to resort to cruder measures to differentiate instance/module imports + const { start, end } = context.state.analysis.instance.ast; + const node = /** @type {ImportDeclaration} */ (binding.initial); + const is_instance_import = + /** @type {number} */ (node.start) > /** @type {number} */ (start) && + /** @type {number} */ (node.end) < /** @type {number} */ (end); - context.state.transform[name] = { - read: (_) => b.call(id), - mutate: (_, mutation) => b.call(id, mutation) - }; + if (is_instance_import) { + const id = b.id('$$_import_' + name); + + context.state.transform[name] = { + read: (_) => b.call(id), + mutate: (_, mutation) => b.call(id, mutation) + }; - context.state.legacy_reactive_imports.push( - b.var(id, b.call('$.reactive_import', b.thunk(b.id(name)))) - ); + context.state.legacy_reactive_imports.push( + b.var(id, b.call('$.reactive_import', b.thunk(b.id(name)))) + ); + } } } } diff --git a/packages/svelte/tests/runtime-legacy/samples/reactive-import-statement-module/_config.js b/packages/svelte/tests/runtime-legacy/samples/reactive-import-statement-module/_config.js new file mode 100644 index 0000000000..a5dedc15fc --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/reactive-import-statement-module/_config.js @@ -0,0 +1,17 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; +import state from './state.js'; + +export default test({ + html: '', + + before_test() { + state.count = 0; + }, + + test({ assert, target }) { + const button = target.querySelector('button'); + flushSync(() => button?.click()); + assert.htmlEqual(target.innerHTML, ''); + } +}); diff --git a/packages/svelte/tests/runtime-legacy/samples/reactive-import-statement-module/main.svelte b/packages/svelte/tests/runtime-legacy/samples/reactive-import-statement-module/main.svelte new file mode 100644 index 0000000000..beb2c406d1 --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/reactive-import-statement-module/main.svelte @@ -0,0 +1,9 @@ + + + diff --git a/packages/svelte/tests/runtime-legacy/samples/reactive-import-statement-module/state.js b/packages/svelte/tests/runtime-legacy/samples/reactive-import-statement-module/state.js new file mode 100644 index 0000000000..f0fc271f84 --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/reactive-import-statement-module/state.js @@ -0,0 +1,3 @@ +export default { + count: 0 +};