fix: ensure event handlers containing arguments are not hoisted (#9758)

* fix: ensure event handlers containing arguments are not hoisted

* add test

* handle rest arguments
pull/9759/head
Dominic Gannaway 12 months ago committed by GitHub
parent 2017af407d
commit ef5bcfe542
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: ensure event handlers containing arguments are not hoisted

@ -168,17 +168,23 @@ function get_delegated_event(node, context) {
const scope = target_function.metadata.scope;
for (const [reference] of scope.references) {
// Bail-out if the arguments keyword is used
if (reference === 'arguments') {
return non_hoistable;
}
const binding = scope.get(reference);
if (
binding !== null &&
// Bail-out if we reference anything from the EachBlock (for now) that mutates in non-runes mode,
((!context.state.analysis.runes && binding.kind === 'each') ||
// or any normal not reactive bindings that are mutated.
binding.kind === 'normal' ||
// or any reactive imports (those are rewritten) (can only happen in legacy mode)
(binding.kind === 'state' && binding.declaration_kind === 'import')) &&
binding.mutated
// Bail-out if the the binding is a rest param
(binding.declaration_kind === 'rest_param' ||
// Bail-out if we reference anything from the EachBlock (for now) that mutates in non-runes mode,
(((!context.state.analysis.runes && binding.kind === 'each') ||
// or any normal not reactive bindings that are mutated.
binding.kind === 'normal' ||
// or any reactive imports (those are rewritten) (can only happen in legacy mode)
(binding.kind === 'state' && binding.declaration_kind === 'import')) &&
binding.mutated))
) {
return non_hoistable;
}

@ -255,7 +255,7 @@ export function create_scopes(ast, root, allow_reactive_declarations, parent) {
function add_params(scope, params) {
for (const param of params) {
for (const node of extract_identifiers(param)) {
scope.declare(node, 'normal', 'param');
scope.declare(node, 'normal', param.type === 'RestElement' ? 'rest_param' : 'param');
}
}
}

@ -237,6 +237,7 @@ export type DeclarationKind =
| 'function'
| 'import'
| 'param'
| 'rest_param'
| 'synthetic';
export interface Binding {

@ -0,0 +1,13 @@
import { test } from '../../test';
export default test({
html: `<button>0</button>`,
async test({ assert, target }) {
const [b1] = target.querySelectorAll('button');
b1?.click();
await Promise.resolve();
assert.htmlEqual(target.innerHTML, '<button>1</button>');
}
});

@ -0,0 +1,8 @@
<script>
let count = $state(0);
function increment(...args) {
count += args.length;
}
</script>
<button on:click={increment}>{count}</button>

@ -0,0 +1,13 @@
import { test } from '../../test';
export default test({
html: `<button>0</button>`,
async test({ assert, target }) {
const [b1] = target.querySelectorAll('button');
b1?.click();
await Promise.resolve();
assert.htmlEqual(target.innerHTML, '<button>1</button>');
}
});

@ -0,0 +1,8 @@
<script>
let count = $state(0);
function increment() {
count += arguments.length;
}
</script>
<button on:click={increment}>{count}</button>
Loading…
Cancel
Save