Skip to content

Commit 42ce088

Browse files
committed
don't hoist identifiers when one of them is used in an event that is not delegateable
1 parent 59e26bf commit 42ce088

File tree

3 files changed

+60
-14
lines changed

3 files changed

+60
-14
lines changed

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

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -104,19 +104,28 @@ function get_delegated_event(node, context) {
104104
return non_hoistable;
105105
}
106106

107-
const element =
108-
parent.type === 'OnDirective'
109-
? path.at(-2)
110-
: parent.type === 'ExpressionTag' &&
111-
is_event_attribute(/** @type {import('#compiler').Attribute} */ (path.at(-2)))
112-
? path.at(-3)
113-
: null;
114-
115-
if (element) {
107+
/** @type {import('#compiler').RegularElement | null} */
108+
let element = null;
109+
/** @type {string | null} */
110+
let event_name = null;
111+
if (parent.type === 'OnDirective') {
112+
element = /** @type {import('#compiler').RegularElement} */ (path.at(-2));
113+
event_name = parent.name;
114+
} else if (
115+
parent.type === 'ExpressionTag' &&
116+
is_event_attribute(/** @type {import('#compiler').Attribute} */ (path.at(-2)))
117+
) {
118+
element = /** @type {import('#compiler').RegularElement} */ (path.at(-3));
119+
const attribute = /** @type {import('#compiler').Attribute} */ (path.at(-2));
120+
event_name = get_attribute_event_name(attribute.name);
121+
}
122+
123+
if (element && event_name) {
116124
if (
117125
element.type !== 'RegularElement' ||
118126
!determine_element_spread_and_delegatable(element).metadata.can_delegate_events ||
119-
(element.metadata.has_spread && node.type === 'Attribute')
127+
(element.metadata.has_spread && node.type === 'Attribute') ||
128+
!DelegatedEvents.includes(event_name)
120129
) {
121130
return non_hoistable;
122131
}
@@ -1036,10 +1045,7 @@ function determine_element_spread_and_delegatable(node) {
10361045
) {
10371046
let event_name = attribute.name;
10381047
if (attribute.type === 'Attribute') {
1039-
if (is_capture_event(event_name)) {
1040-
event_name = event_name.slice(0, -7);
1041-
}
1042-
event_name = event_name.slice(2);
1048+
event_name = get_attribute_event_name(event_name);
10431049
}
10441050
events.set(event_name, (events.get(event_name) || 0) + 1);
10451051
if (!has_on && attribute.type === 'OnDirective') {
@@ -1066,6 +1072,17 @@ function determine_element_spread_and_delegatable(node) {
10661072
return node;
10671073
}
10681074

1075+
/**
1076+
* @param {string} event_name
1077+
*/
1078+
function get_attribute_event_name(event_name) {
1079+
if (is_capture_event(event_name)) {
1080+
event_name = event_name.slice(0, -7);
1081+
}
1082+
event_name = event_name.slice(2);
1083+
return event_name;
1084+
}
1085+
10691086
/**
10701087
* @param {string} name
10711088
* @returns boolean
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { test } from '../../test';
2+
3+
// Checks that event handlers are not hoisted when one of them is not delegateable
4+
export default test({
5+
html: `<button>0</button>`,
6+
7+
async test({ assert, target }) {
8+
const [button] = target.querySelectorAll('button');
9+
10+
button.click();
11+
await Promise.resolve();
12+
assert.htmlEqual(target.innerHTML, '<button>1</button>');
13+
14+
button.dispatchEvent(new MouseEvent('mouseenter'));
15+
await Promise.resolve();
16+
assert.htmlEqual(target.innerHTML, '<button>2</button>');
17+
}
18+
});
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<script>
2+
let count = $state(0)
3+
4+
function increment() {
5+
count += 1
6+
}
7+
</script>
8+
9+
<button onclick={increment} onmouseenter={increment}>
10+
{count}
11+
</button>

0 commit comments

Comments
 (0)