Skip to content

Commit 1538264

Browse files
committed
fix: don't hoist function when already referenced in module scope
fixes #10279
1 parent 14d7b26 commit 1538264

File tree

4 files changed

+54
-12
lines changed

4 files changed

+54
-12
lines changed

.changeset/many-trees-fix.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"svelte": patch
3+
---
4+
5+
fix: don't hoist function when already referenced in module scope

packages/svelte/src/compiler/phases/2-analyze/index.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,11 @@ function get_delegated_event(event_name, handler, context) {
9494
} else if (handler.type === 'Identifier') {
9595
binding = context.state.scope.get(handler.name);
9696

97+
if (context.state.analysis.module.scope.references.has(handler.name)) {
98+
// If a binding with the same name is referenced in the module scope (even if not declared there), bail-out
99+
return non_hoistable;
100+
}
101+
97102
if (binding != null) {
98103
for (const { path } of binding.references) {
99104
const parent = path.at(-1);
Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,25 @@
11
import { test } from '../../test';
22

3-
// Checks that event handlers are not hoisted when one of them is not delegateable
43
export default test({
5-
html: `<button>0</button>`,
4+
html: `<button>0</button><button>x0</button><button>y0</button>`,
65

76
async test({ assert, target }) {
8-
const [button] = target.querySelectorAll('button');
7+
const [btn1, btn2, btn3] = target.querySelectorAll('button');
98

10-
button.click();
9+
btn1.click();
1110
await Promise.resolve();
12-
assert.htmlEqual(target.innerHTML, '<button>1</button>');
11+
assert.htmlEqual(target.innerHTML, '<button>1</button><button>x0</button><button>y0</button>');
1312

14-
button.dispatchEvent(new MouseEvent('mouseenter'));
13+
btn1.dispatchEvent(new MouseEvent('mouseenter'));
1514
await Promise.resolve();
16-
assert.htmlEqual(target.innerHTML, '<button>2</button>');
15+
assert.htmlEqual(target.innerHTML, '<button>2</button><button>x0</button><button>y0</button>');
16+
17+
btn2.click();
18+
await Promise.resolve();
19+
assert.htmlEqual(target.innerHTML, '<button>2</button><button>x1</button><button>y0</button>');
20+
21+
btn3.click();
22+
await Promise.resolve();
23+
assert.htmlEqual(target.innerHTML, '<button>2</button><button>x1</button><button>y1</button>');
1724
}
1825
});
Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,36 @@
1+
<script context="module">
2+
function declared_in_module_scope() {
3+
return 'x';
4+
}
5+
let a = declared_in_module_scope();
6+
7+
let b = 'x';
8+
try {
9+
b = doesnt_exist();
10+
} catch (e) {
11+
b = 'y';
12+
}
13+
</script>
14+
115
<script>
2-
let count = $state(0)
16+
let count1 = $state(0);
17+
let count2 = $state(0);
18+
let count3 = $state(0);
319
420
function increment() {
5-
count += 1
21+
count1 += 1;
22+
}
23+
function declared_in_module_scope() {
24+
count2 += 1;
25+
}
26+
function doesnt_exist() {
27+
count3 += 1;
628
}
729
</script>
830

9-
<button onclick={increment} onmouseenter={increment}>
10-
{count}
11-
</button>
31+
<!-- Checks that event handlers are not hoisted when one of them is not delegateable -->
32+
<button onclick={increment} onmouseenter={increment}>{count1}</button>
33+
34+
<!-- Checks that event handler is not hoisted if the same name is used in the module context -->
35+
<button onclick={declared_in_module_scope}>{a}{count2}</button>
36+
<button onclick={doesnt_exist}>{b}{count3}</button>

0 commit comments

Comments
 (0)