Skip to content

Commit 1e0b874

Browse files
authored
feat: add support {#each} without as (svelte v5.4.0) (#617)
1 parent 1cede6c commit 1e0b874

17 files changed

+4071
-19
lines changed

.changeset/witty-hairs-judge.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"svelte-eslint-parser": minor
3+
---
4+
5+
feat: add support `{#each}` without `as` (svelte v5.4.0)

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@
105105
"prettier-plugin-svelte": "^3.3.2",
106106
"rimraf": "^6.0.1",
107107
"semver": "^7.6.3",
108-
"svelte": "^5.3.1",
108+
"svelte": "^5.9.0",
109109
"svelte2tsx": "^0.7.28",
110110
"tsx": "^4.19.2",
111111
"typescript": "~5.7.2",

src/ast/html.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -347,7 +347,7 @@ export interface SvelteElseBlockElseIf extends BaseSvelteElseBlock {
347347
export interface SvelteEachBlock extends BaseNode {
348348
type: "SvelteEachBlock";
349349
expression: ESTree.Expression;
350-
context: ESTree.Pattern;
350+
context: ESTree.Pattern | null;
351351
index: ESTree.Identifier | null;
352352
key: ESTree.Expression | null;
353353
children: Child[];

src/context/script-let.ts

+23-9
Original file line numberDiff line numberDiff line change
@@ -433,22 +433,26 @@ export class ScriptLetContext {
433433

434434
public nestEachBlock(
435435
expression: ESTree.Expression,
436-
context: ESTree.Pattern,
436+
context: ESTree.Pattern | null,
437437
indexRange: { start: number; end: number } | null,
438438
eachBlock: SvelteEachBlock,
439439
callback: (
440440
expr: ESTree.Expression,
441-
ctx: ESTree.Pattern,
441+
ctx: ESTree.Pattern | null,
442442
index: ESTree.Identifier | null,
443443
) => void,
444444
): void {
445445
const exprRange = getNodeRange(expression);
446-
const ctxRange = getNodeRange(context);
446+
const ctxRange = context && getNodeRange(context);
447447
let source = "Array.from(";
448448
const exprOffset = source.length;
449449
source += `${this.ctx.code.slice(...exprRange)}).forEach((`;
450450
const ctxOffset = source.length;
451-
source += this.ctx.code.slice(...ctxRange);
451+
if (ctxRange) {
452+
source += this.ctx.code.slice(...ctxRange);
453+
} else {
454+
source += "__$ctx__";
455+
}
452456
let idxOffset: number | null = null;
453457
if (indexRange) {
454458
source += ",";
@@ -473,7 +477,7 @@ export class ScriptLetContext {
473477
const scope = result.getScope(fn.body);
474478

475479
// Process for nodes
476-
callback(expr, ctx, idx);
480+
callback(expr, context ? ctx : null, idx);
477481

478482
// Process for scope
479483
result.registerNodeToScope(eachBlock, scope);
@@ -484,6 +488,10 @@ export class ScriptLetContext {
484488
}
485489
}
486490
}
491+
if (!context) {
492+
// remove `__$ctx__` variable
493+
removeIdentifierVariable(ctx, scope);
494+
}
487495
// remove Array reference
488496
const arrayId = (callArrayFrom.callee as ESTree.MemberExpression)
489497
.object;
@@ -512,18 +520,24 @@ export class ScriptLetContext {
512520
tokens.pop(); // )
513521
tokens.pop(); // ;
514522

515-
const map = [
523+
const map: {
524+
offset: number;
525+
range: [number, number];
526+
newNode: ESTree.Expression | ESTree.Pattern;
527+
}[] = [
516528
{
517529
offset: exprOffset,
518530
range: exprRange,
519531
newNode: expr,
520532
},
521-
{
533+
];
534+
if (ctxRange) {
535+
map.push({
522536
offset: ctxOffset,
523537
range: ctxRange,
524538
newNode: ctx,
525-
},
526-
];
539+
});
540+
}
527541
if (indexRange) {
528542
map.push({
529543
offset: idxOffset!,

src/parser/converts/block.ts

+13-8
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ export function convertEachBlock(
269269
const eachBlock: SvelteEachBlock = {
270270
type: "SvelteEachBlock",
271271
expression: null as any,
272-
context: null as any,
272+
context: null,
273273
index: null,
274274
key: null,
275275
children: [],
@@ -281,7 +281,10 @@ export function convertEachBlock(
281281
let indexRange: null | { start: number; end: number } = null;
282282

283283
if (node.index) {
284-
const start = ctx.code.indexOf(node.index, getWithLoc(node.context).end);
284+
const start = ctx.code.indexOf(
285+
node.index,
286+
getWithLoc(node.context ?? node.expression).end,
287+
);
285288
indexRange = {
286289
start,
287290
end: start + node.index.length,
@@ -300,11 +303,13 @@ export function convertEachBlock(
300303
},
301304
);
302305

303-
const asStart = ctx.code.indexOf("as", getWithLoc(node.expression).end);
304-
ctx.addToken("Keyword", {
305-
start: asStart,
306-
end: asStart + 2,
307-
});
306+
if (node.context) {
307+
const asStart = ctx.code.indexOf("as", getWithLoc(node.expression).end);
308+
ctx.addToken("Keyword", {
309+
start: asStart,
310+
end: asStart + 2,
311+
});
312+
}
308313

309314
if (node.key) {
310315
ctx.scriptLet.addExpression(node.key, eachBlock, null, (key) => {
@@ -335,7 +340,7 @@ export function convertEachBlock(
335340
const elseStart = startBlockIndexForElse(
336341
fallbackFragment,
337342
body,
338-
node.key || indexRange || node.context,
343+
node.key || indexRange || node.context || node.expression,
339344
ctx,
340345
);
341346

Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{#each expression}...{/each}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[
2+
{
3+
"ruleId": "no-undef",
4+
"code": "expression",
5+
"line": 1,
6+
"column": 8
7+
}
8+
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
{
2+
"type": "Program",
3+
"body": [
4+
{
5+
"type": "SvelteEachBlock",
6+
"expression": {
7+
"type": "Identifier",
8+
"name": "expression",
9+
"range": [
10+
7,
11+
17
12+
],
13+
"loc": {
14+
"start": {
15+
"line": 1,
16+
"column": 7
17+
},
18+
"end": {
19+
"line": 1,
20+
"column": 17
21+
}
22+
}
23+
},
24+
"context": null,
25+
"index": null,
26+
"key": null,
27+
"children": [
28+
{
29+
"type": "SvelteText",
30+
"value": "...",
31+
"range": [
32+
18,
33+
21
34+
],
35+
"loc": {
36+
"start": {
37+
"line": 1,
38+
"column": 18
39+
},
40+
"end": {
41+
"line": 1,
42+
"column": 21
43+
}
44+
}
45+
}
46+
],
47+
"else": null,
48+
"range": [
49+
0,
50+
28
51+
],
52+
"loc": {
53+
"start": {
54+
"line": 1,
55+
"column": 0
56+
},
57+
"end": {
58+
"line": 1,
59+
"column": 28
60+
}
61+
}
62+
}
63+
],
64+
"sourceType": "module",
65+
"comments": [],
66+
"tokens": [
67+
{
68+
"type": "Punctuator",
69+
"value": "{",
70+
"range": [
71+
0,
72+
1
73+
],
74+
"loc": {
75+
"start": {
76+
"line": 1,
77+
"column": 0
78+
},
79+
"end": {
80+
"line": 1,
81+
"column": 1
82+
}
83+
}
84+
},
85+
{
86+
"type": "MustacheKeyword",
87+
"value": "#each",
88+
"range": [
89+
1,
90+
6
91+
],
92+
"loc": {
93+
"start": {
94+
"line": 1,
95+
"column": 1
96+
},
97+
"end": {
98+
"line": 1,
99+
"column": 6
100+
}
101+
}
102+
},
103+
{
104+
"type": "Identifier",
105+
"value": "expression",
106+
"range": [
107+
7,
108+
17
109+
],
110+
"loc": {
111+
"start": {
112+
"line": 1,
113+
"column": 7
114+
},
115+
"end": {
116+
"line": 1,
117+
"column": 17
118+
}
119+
}
120+
},
121+
{
122+
"type": "Punctuator",
123+
"value": "}",
124+
"range": [
125+
17,
126+
18
127+
],
128+
"loc": {
129+
"start": {
130+
"line": 1,
131+
"column": 17
132+
},
133+
"end": {
134+
"line": 1,
135+
"column": 18
136+
}
137+
}
138+
},
139+
{
140+
"type": "HTMLText",
141+
"value": "...",
142+
"range": [
143+
18,
144+
21
145+
],
146+
"loc": {
147+
"start": {
148+
"line": 1,
149+
"column": 18
150+
},
151+
"end": {
152+
"line": 1,
153+
"column": 21
154+
}
155+
}
156+
},
157+
{
158+
"type": "Punctuator",
159+
"value": "{",
160+
"range": [
161+
21,
162+
22
163+
],
164+
"loc": {
165+
"start": {
166+
"line": 1,
167+
"column": 21
168+
},
169+
"end": {
170+
"line": 1,
171+
"column": 22
172+
}
173+
}
174+
},
175+
{
176+
"type": "MustacheKeyword",
177+
"value": "/each",
178+
"range": [
179+
22,
180+
27
181+
],
182+
"loc": {
183+
"start": {
184+
"line": 1,
185+
"column": 22
186+
},
187+
"end": {
188+
"line": 1,
189+
"column": 27
190+
}
191+
}
192+
},
193+
{
194+
"type": "Punctuator",
195+
"value": "}",
196+
"range": [
197+
27,
198+
28
199+
],
200+
"loc": {
201+
"start": {
202+
"line": 1,
203+
"column": 27
204+
},
205+
"end": {
206+
"line": 1,
207+
"column": 28
208+
}
209+
}
210+
}
211+
],
212+
"range": [
213+
0,
214+
29
215+
],
216+
"loc": {
217+
"start": {
218+
"line": 1,
219+
"column": 0
220+
},
221+
"end": {
222+
"line": 2,
223+
"column": 0
224+
}
225+
}
226+
}

0 commit comments

Comments
 (0)