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 <rich.harris@vercel.com>
Co-authored-by: Rich Harris <hello@rich-harris.dev>
pull/12862/head
Paolo Ricciuti 4 months ago committed by GitHub
parent 45da5a426f
commit 03945f56b4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: treat module-level imports as non-reactive in legacy mode

@ -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 = [];

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

@ -0,0 +1,17 @@
import { flushSync } from 'svelte';
import { test } from '../../test';
import state from './state.js';
export default test({
html: '<button>0</button>',
before_test() {
state.count = 0;
},
test({ assert, target }) {
const button = target.querySelector('button');
flushSync(() => button?.click());
assert.htmlEqual(target.innerHTML, '<button>0</button>');
}
});

@ -0,0 +1,9 @@
<script context="module">
import state from './state.js';
function update() {
state.count += 1;
}
</script>
<button on:click={update}>{state.count}</button>
Loading…
Cancel
Save