Skip to content

Commit 71945c9

Browse files
committed
fix: correctly parse each with loose parser
1 parent f3a7ded commit 71945c9

File tree

5 files changed

+367
-16
lines changed

5 files changed

+367
-16
lines changed
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: correctly parse `each` with loose parser

packages/svelte/src/compiler/phases/1-parse/read/expression.js

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,31 @@ import { find_matching_bracket } from '../utils/bracket.js';
88
/**
99
* @param {Parser} parser
1010
* @param {string} [opening_token]
11+
* @returns {Expression | undefined}
12+
*/
13+
export function get_loose_identifier(parser, opening_token) {
14+
// Find the next } and treat it as the end of the expression
15+
const end = find_matching_bracket(parser.template, parser.index, opening_token ?? '{');
16+
if (end) {
17+
const start = parser.index;
18+
parser.index = end;
19+
// We don't know what the expression is and signal this by returning an empty identifier
20+
return {
21+
type: 'Identifier',
22+
start,
23+
end,
24+
name: ''
25+
};
26+
}
27+
}
28+
29+
/**
30+
* @param {Parser} parser
31+
* @param {string} [opening_token]
32+
* @param {boolean} [is_each]
1133
* @returns {Expression}
1234
*/
13-
export default function read_expression(parser, opening_token) {
35+
export default function read_expression(parser, opening_token, is_each) {
1436
try {
1537
const node = parse_expression_at(parser.template, parser.ts, parser.index);
1638

@@ -41,19 +63,18 @@ export default function read_expression(parser, opening_token) {
4163

4264
return /** @type {Expression} */ (node);
4365
} catch (err) {
44-
if (parser.loose) {
66+
/**
67+
* If we are in an each loop we need the error to be thrown in cases like
68+
*
69+
* `as { y = z }`
70+
*
71+
* so we still throw and handle the error there
72+
*/
73+
if (parser.loose && !is_each) {
4574
// Find the next } and treat it as the end of the expression
46-
const end = find_matching_bracket(parser.template, parser.index, opening_token ?? '{');
47-
if (end) {
48-
const start = parser.index;
49-
parser.index = end;
50-
// We don't know what the expression is and signal this by returning an empty identifier
51-
return {
52-
type: 'Identifier',
53-
start,
54-
end,
55-
name: ''
56-
};
75+
const expression = get_loose_identifier(parser, opening_token);
76+
if (expression) {
77+
return expression;
5778
}
5879
}
5980

packages/svelte/src/compiler/phases/1-parse/state/tag.js

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22
/** @import { AST } from '#compiler' */
33
/** @import { Parser } from '../index.js' */
44
import read_pattern from '../read/context.js';
5-
import read_expression from '../read/expression.js';
5+
import read_expression, { get_loose_identifier } from '../read/expression.js';
66
import * as e from '../../../errors.js';
77
import { create_fragment } from '../utils/create.js';
88
import { walk } from 'zimmerframe';
99
import { parse_expression_at } from '../acorn.js';
1010
import { create_expression_metadata } from '../../nodes.js';
11+
import { find_matching_bracket } from '../utils/bracket.js';
12+
import exp from 'node:constants';
1113

1214
const regex_whitespace_with_closing_curly_brace = /^\s*}/;
1315

@@ -87,15 +89,23 @@ function open(parser) {
8789
// we get a valid expression
8890
while (!expression) {
8991
try {
90-
expression = read_expression(parser);
92+
expression = read_expression(parser, undefined, true);
9193
} catch (err) {
9294
end = /** @type {any} */ (err).position[0] - 2;
9395

9496
while (end > start && parser.template.slice(end, end + 2) !== 'as') {
9597
end -= 1;
9698
}
9799

98-
if (end <= start) throw err;
100+
if (end <= start) {
101+
if (parser.loose) {
102+
expression = get_loose_identifier(parser);
103+
if (expression) {
104+
break;
105+
}
106+
}
107+
throw err;
108+
}
99109

100110
// @ts-expect-error parser.template is meant to be readonly, this is a special case
101111
parser.template = template.slice(0, end);
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<script lang="ts">
2+
let arr = [];
3+
</script>
4+
5+
{#each arr as [key, value = 'default']}
6+
<div>{key}: {value}</div>
7+
{/each}

0 commit comments

Comments
 (0)