fix: don't hoist function when already referenced in module scope

fixes #10279
pull/10229/head
Simon Holthausen 11 months ago
parent 14d7b26897
commit 1538264bd5

@ -0,0 +1,5 @@
---
"svelte": patch
---
fix: don't hoist function when already referenced in module scope

@ -94,6 +94,11 @@ function get_delegated_event(event_name, handler, context) {
} else if (handler.type === 'Identifier') { } else if (handler.type === 'Identifier') {
binding = context.state.scope.get(handler.name); binding = context.state.scope.get(handler.name);
if (context.state.analysis.module.scope.references.has(handler.name)) {
// If a binding with the same name is referenced in the module scope (even if not declared there), bail-out
return non_hoistable;
}
if (binding != null) { if (binding != null) {
for (const { path } of binding.references) { for (const { path } of binding.references) {
const parent = path.at(-1); const parent = path.at(-1);

@ -1,18 +1,25 @@
import { test } from '../../test'; import { test } from '../../test';
// Checks that event handlers are not hoisted when one of them is not delegateable
export default test({ export default test({
html: `<button>0</button>`, html: `<button>0</button><button>x0</button><button>y0</button>`,
async test({ assert, target }) { async test({ assert, target }) {
const [button] = target.querySelectorAll('button'); const [btn1, btn2, btn3] = target.querySelectorAll('button');
button.click(); btn1.click();
await Promise.resolve(); await Promise.resolve();
assert.htmlEqual(target.innerHTML, '<button>1</button>'); assert.htmlEqual(target.innerHTML, '<button>1</button><button>x0</button><button>y0</button>');
button.dispatchEvent(new MouseEvent('mouseenter')); btn1.dispatchEvent(new MouseEvent('mouseenter'));
await Promise.resolve(); await Promise.resolve();
assert.htmlEqual(target.innerHTML, '<button>2</button>'); assert.htmlEqual(target.innerHTML, '<button>2</button><button>x0</button><button>y0</button>');
btn2.click();
await Promise.resolve();
assert.htmlEqual(target.innerHTML, '<button>2</button><button>x1</button><button>y0</button>');
btn3.click();
await Promise.resolve();
assert.htmlEqual(target.innerHTML, '<button>2</button><button>x1</button><button>y1</button>');
} }
}); });

@ -1,11 +1,36 @@
<script context="module">
function declared_in_module_scope() {
return 'x';
}
let a = declared_in_module_scope();
let b = 'x';
try {
b = doesnt_exist();
} catch (e) {
b = 'y';
}
</script>
<script> <script>
let count = $state(0) let count1 = $state(0);
let count2 = $state(0);
let count3 = $state(0);
function increment() { function increment() {
count += 1 count1 += 1;
}
function declared_in_module_scope() {
count2 += 1;
}
function doesnt_exist() {
count3 += 1;
} }
</script> </script>
<button onclick={increment} onmouseenter={increment}> <!-- Checks that event handlers are not hoisted when one of them is not delegateable -->
{count} <button onclick={increment} onmouseenter={increment}>{count1}</button>
</button>
<!-- Checks that event handler is not hoisted if the same name is used in the module context -->
<button onclick={declared_in_module_scope}>{a}{count2}</button>
<button onclick={doesnt_exist}>{b}{count3}</button>

Loading…
Cancel
Save