From 5f3fcaf88a7068e9427ce96bf2a4bc7ff4209d62 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Tue, 2 Jan 2024 23:56:30 +0000 Subject: [PATCH] fix: bail-out event handler referencing each index (#10063) * fix: bail-out event handler referencing each index * lint * ts * types --- .changeset/tall-garlics-try.md | 5 +++++ packages/svelte/src/compiler/phases/2-analyze/index.js | 4 ++++ packages/svelte/src/compiler/phases/scope.js | 6 +++--- packages/svelte/src/compiler/types/index.d.ts | 10 ++++++++-- packages/svelte/types/index.d.ts | 10 ++++++++-- 5 files changed, 28 insertions(+), 7 deletions(-) create mode 100644 .changeset/tall-garlics-try.md diff --git a/.changeset/tall-garlics-try.md b/.changeset/tall-garlics-try.md new file mode 100644 index 000000000..a7b45bdf6 --- /dev/null +++ b/.changeset/tall-garlics-try.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: bail-out event handler referencing each index diff --git a/packages/svelte/src/compiler/phases/2-analyze/index.js b/packages/svelte/src/compiler/phases/2-analyze/index.js index e7f9e1a72..dc98fb31e 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/index.js +++ b/packages/svelte/src/compiler/phases/2-analyze/index.js @@ -183,6 +183,10 @@ function get_delegated_event(node, context) { ) { return non_hoistable; } + // If we referebnce the index within an each block, then bail-out. + if (binding !== null && binding.initial?.type === 'EachBlock') { + return non_hoistable; + } if ( binding !== null && diff --git a/packages/svelte/src/compiler/phases/scope.js b/packages/svelte/src/compiler/phases/scope.js index ad3e8fdb3..053053508 100644 --- a/packages/svelte/src/compiler/phases/scope.js +++ b/packages/svelte/src/compiler/phases/scope.js @@ -65,7 +65,7 @@ export class Scope { * @param {import('estree').Identifier} node * @param {import('#compiler').Binding['kind']} kind * @param {import('#compiler').DeclarationKind} declaration_kind - * @param {null | import('estree').Expression | import('estree').FunctionDeclaration | import('estree').ClassDeclaration | import('estree').ImportDeclaration} initial + * @param {null | import('estree').Expression | import('estree').FunctionDeclaration | import('estree').ClassDeclaration | import('estree').ImportDeclaration | import('../types/template.js').EachBlock} initial * @returns {import('#compiler').Binding} */ declare(node, kind, declaration_kind, initial = null) { @@ -523,7 +523,7 @@ export function create_scopes(ast, root, allow_reactive_declarations, parent) { const is_keyed = node.key && (node.key.type !== 'Identifier' || !node.index || node.key.name !== node.index); - scope.declare(b.id(node.index), is_keyed ? 'derived' : 'normal', 'const'); + scope.declare(b.id(node.index), is_keyed ? 'derived' : 'normal', 'const', node); } if (node.key) visit(node.key, { scope }); @@ -680,7 +680,7 @@ export function set_scope(scopes) { /** * Returns the name of the rune if the given expression is a `CallExpression` using a rune. - * @param {import('estree').Node | null | undefined} node + * @param {import('estree').Node | import('../types/template.js').EachBlock | null | undefined} node * @param {Scope} scope * @returns {Runes[number] | null} */ diff --git a/packages/svelte/src/compiler/types/index.d.ts b/packages/svelte/src/compiler/types/index.d.ts index fcb960847..b62b47bf0 100644 --- a/packages/svelte/src/compiler/types/index.d.ts +++ b/packages/svelte/src/compiler/types/index.d.ts @@ -11,7 +11,7 @@ import type { SourceMap } from 'magic-string'; import type { Context } from 'zimmerframe'; import type { Scope } from '../phases/scope.js'; import * as Css from './css.js'; -import type { Namespace, SvelteNode } from './template.js'; +import type { EachBlock, Namespace, SvelteNode } from './template.js'; /** The return value of `compile` from `svelte/compiler` */ export interface CompileResult { @@ -269,7 +269,13 @@ export interface Binding { * What the value was initialized with. * For destructured props such as `let { foo = 'bar' } = $props()` this is `'bar'` and not `$props()` */ - initial: null | Expression | FunctionDeclaration | ClassDeclaration | ImportDeclaration; + initial: + | null + | Expression + | FunctionDeclaration + | ClassDeclaration + | ImportDeclaration + | EachBlock; is_called: boolean; references: { node: Identifier; path: SvelteNode[] }[]; mutated: boolean; diff --git a/packages/svelte/types/index.d.ts b/packages/svelte/types/index.d.ts index 4db820970..7baeab418 100644 --- a/packages/svelte/types/index.d.ts +++ b/packages/svelte/types/index.d.ts @@ -746,7 +746,13 @@ declare module 'svelte/compiler' { * What the value was initialized with. * For destructured props such as `let { foo = 'bar' } = $props()` this is `'bar'` and not `$props()` */ - initial: null | Expression | FunctionDeclaration | ClassDeclaration | ImportDeclaration; + initial: + | null + | Expression + | FunctionDeclaration + | ClassDeclaration + | ImportDeclaration + | EachBlock; is_called: boolean; references: { node: Identifier; path: SvelteNode[] }[]; mutated: boolean; @@ -1021,7 +1027,7 @@ declare module 'svelte/compiler' { */ function_depth: number; - declare(node: import('estree').Identifier, kind: Binding['kind'], declaration_kind: DeclarationKind, initial?: null | import('estree').Expression | import('estree').FunctionDeclaration | import('estree').ClassDeclaration | import('estree').ImportDeclaration): Binding; + declare(node: import('estree').Identifier, kind: Binding['kind'], declaration_kind: DeclarationKind, initial?: null | import('estree').Expression | import('estree').FunctionDeclaration | import('estree').ClassDeclaration | import('estree').ImportDeclaration | EachBlock): Binding; child(porous?: boolean): Scope; generate(preferred_name: string): string;