Skip to content

Commit 9b5fb3f

Browse files
authored
fix: error if each block has key but no as clause (#16966)
1 parent 7d977fa commit 9b5fb3f

File tree

9 files changed

+49
-6
lines changed

9 files changed

+49
-6
lines changed

.changeset/thirty-rules-dance.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: error if `each` block has `key` but no `as` clause

documentation/docs/98-reference/.generated/compile-errors.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ Cyclical dependency detected: %cycle%
199199
### const_tag_invalid_reference
200200

201201
```
202-
The `{@const %name% = ...}` declaration is not available in this snippet
202+
The `{@const %name% = ...}` declaration is not available in this snippet
203203
```
204204

205205
The following is an error:
@@ -453,6 +453,12 @@ This turned out to be buggy and unpredictable, particularly when working with de
453453
{/each}
454454
```
455455

456+
### each_key_without_as
457+
458+
```
459+
An `{#each ...}` block without an `as` clause cannot have a key
460+
```
461+
456462
### effect_invalid_placement
457463

458464
```

packages/svelte/messages/compile-errors/template.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@
126126
127127
## const_tag_invalid_reference
128128

129-
> The `{@const %name% = ...}` declaration is not available in this snippet
129+
> The `{@const %name% = ...}` declaration is not available in this snippet
130130
131131
The following is an error:
132132

@@ -179,6 +179,10 @@ The same applies to components:
179179

180180
> `%type%` name cannot be empty
181181
182+
## each_key_without_as
183+
184+
> An `{#each ...}` block without an `as` clause cannot have a key
185+
182186
## element_invalid_closing_tag
183187

184188
> `</%name%>` attempted to close an element that was not open

packages/svelte/src/compiler/errors.js

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -986,13 +986,13 @@ export function const_tag_invalid_placement(node) {
986986
}
987987

988988
/**
989-
* The `{@const %name% = ...}` declaration is not available in this snippet
989+
* The `{@const %name% = ...}` declaration is not available in this snippet
990990
* @param {null | number | NodeLike} node
991991
* @param {string} name
992992
* @returns {never}
993993
*/
994994
export function const_tag_invalid_reference(node, name) {
995-
e(node, 'const_tag_invalid_reference', `The \`{@const ${name} = ...}\` declaration is not available in this snippet \nhttps://svelte.dev/e/const_tag_invalid_reference`);
995+
e(node, 'const_tag_invalid_reference', `The \`{@const ${name} = ...}\` declaration is not available in this snippet\nhttps://svelte.dev/e/const_tag_invalid_reference`);
996996
}
997997

998998
/**
@@ -1023,6 +1023,15 @@ export function directive_missing_name(node, type) {
10231023
e(node, 'directive_missing_name', `\`${type}\` name cannot be empty\nhttps://svelte.dev/e/directive_missing_name`);
10241024
}
10251025

1026+
/**
1027+
* An `{#each ...}` block without an `as` clause cannot have a key
1028+
* @param {null | number | NodeLike} node
1029+
* @returns {never}
1030+
*/
1031+
export function each_key_without_as(node) {
1032+
e(node, 'each_key_without_as', `An \`{#each ...}\` block without an \`as\` clause cannot have a key\nhttps://svelte.dev/e/each_key_without_as`);
1033+
}
1034+
10261035
/**
10271036
* `</%name%>` attempted to close an element that was not open
10281037
* @param {null | number | NodeLike} node

packages/svelte/src/compiler/phases/2-analyze/visitors/EachBlock.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ export function EachBlock(node, context) {
2828
node.key.type !== 'Identifier' || !node.index || node.key.name !== node.index;
2929
}
3030

31+
if (node.metadata.keyed && !node.context) {
32+
e.each_key_without_as(node);
33+
}
34+
3135
// evaluate expression in parent scope
3236
context.visit(node.expression, {
3337
...context.state,

packages/svelte/tests/compiler-errors/samples/const-tag-snippet-invalid-reference-1/_config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ export default test({
44
async: true,
55
error: {
66
code: 'const_tag_invalid_reference',
7-
message: 'The `{@const foo = ...}` declaration is not available in this snippet ',
7+
message: 'The `{@const foo = ...}` declaration is not available in this snippet',
88
position: [376, 379]
99
}
1010
});

packages/svelte/tests/compiler-errors/samples/const-tag-snippet-invalid-reference-2/_config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ export default test({
44
async: true,
55
error: {
66
code: 'const_tag_invalid_reference',
7-
message: 'The `{@const foo = ...}` declaration is not available in this snippet ',
7+
message: 'The `{@const foo = ...}` declaration is not available in this snippet',
88
position: [298, 301]
99
}
1010
});
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { test } from '../../test';
2+
3+
export default test({
4+
error: {
5+
code: 'each_key_without_as',
6+
message: 'An `{#each ...}` block without an `as` clause cannot have a key'
7+
}
8+
});
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<script>
2+
let items = [{ id: 3 }, { id: 2 }, { id: 1 }];
3+
</script>
4+
5+
{#each items, i (items[i].id)}
6+
<p>{items[i].id}</p>
7+
{/each}

0 commit comments

Comments
 (0)