diff --git a/.changeset/serious-socks-cover.md b/.changeset/serious-socks-cover.md new file mode 100644 index 000000000000..709bb95beb07 --- /dev/null +++ b/.changeset/serious-socks-cover.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: ensure event handlers containing arguments are not hoisted diff --git a/packages/svelte/src/compiler/phases/2-analyze/index.js b/packages/svelte/src/compiler/phases/2-analyze/index.js index 71295b7059d5..517827f7e345 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/index.js +++ b/packages/svelte/src/compiler/phases/2-analyze/index.js @@ -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; } diff --git a/packages/svelte/src/compiler/phases/scope.js b/packages/svelte/src/compiler/phases/scope.js index f4b1265550a7..e8e56bfe78c3 100644 --- a/packages/svelte/src/compiler/phases/scope.js +++ b/packages/svelte/src/compiler/phases/scope.js @@ -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'); } } } diff --git a/packages/svelte/src/compiler/types/index.d.ts b/packages/svelte/src/compiler/types/index.d.ts index 2c5480eb7f58..04b4a9d1c3d7 100644 --- a/packages/svelte/src/compiler/types/index.d.ts +++ b/packages/svelte/src/compiler/types/index.d.ts @@ -237,6 +237,7 @@ export type DeclarationKind = | 'function' | 'import' | 'param' + | 'rest_param' | 'synthetic'; export interface Binding { diff --git a/packages/svelte/tests/runtime-runes/samples/event-arguments-2/_config.js b/packages/svelte/tests/runtime-runes/samples/event-arguments-2/_config.js new file mode 100644 index 000000000000..f2e6c67f8a39 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/event-arguments-2/_config.js @@ -0,0 +1,13 @@ +import { test } from '../../test'; + +export default test({ + html: ``, + + async test({ assert, target }) { + const [b1] = target.querySelectorAll('button'); + + b1?.click(); + await Promise.resolve(); + assert.htmlEqual(target.innerHTML, ''); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/event-arguments-2/main.svelte b/packages/svelte/tests/runtime-runes/samples/event-arguments-2/main.svelte new file mode 100644 index 000000000000..39333849f67b --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/event-arguments-2/main.svelte @@ -0,0 +1,8 @@ + + + diff --git a/packages/svelte/tests/runtime-runes/samples/event-arguments/_config.js b/packages/svelte/tests/runtime-runes/samples/event-arguments/_config.js new file mode 100644 index 000000000000..f2e6c67f8a39 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/event-arguments/_config.js @@ -0,0 +1,13 @@ +import { test } from '../../test'; + +export default test({ + html: ``, + + async test({ assert, target }) { + const [b1] = target.querySelectorAll('button'); + + b1?.click(); + await Promise.resolve(); + assert.htmlEqual(target.innerHTML, ''); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/event-arguments/main.svelte b/packages/svelte/tests/runtime-runes/samples/event-arguments/main.svelte new file mode 100644 index 000000000000..d6569e26fe1a --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/event-arguments/main.svelte @@ -0,0 +1,8 @@ + + +