Skip to content

Commit c4e9faa

Browse files
fix: correctly parse each with loose parser (#14887)
* fix: correctly parse `each` with loose parser * chore: fix lint * chore: remove unused imports * Apply suggestions from code review * chore: fix lint --------- Co-authored-by: Simon H <[email protected]>
1 parent ed26c3f commit c4e9faa

File tree

5 files changed

+364
-21
lines changed

5 files changed

+364
-21
lines changed

Diff for: .changeset/famous-scissors-begin.md

+5
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

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

+29-14
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} [disallow_loose]
1133
* @returns {Expression}
1234
*/
13-
export default function read_expression(parser, opening_token) {
35+
export default function read_expression(parser, opening_token, disallow_loose) {
1436
try {
1537
const node = parse_expression_at(parser.template, parser.ts, parser.index);
1638

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

4264
return /** @type {Expression} */ (node);
4365
} catch (err) {
44-
if (parser.loose) {
45-
// 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-
};
66+
// If we are in an each loop we need the error to be thrown in cases like
67+
// `as { y = z }` so we still throw and handle the error there
68+
if (parser.loose && !disallow_loose) {
69+
const expression = get_loose_identifier(parser, opening_token);
70+
if (expression) {
71+
return expression;
5772
}
5873
}
5974

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

+15-7
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
/** @import { ArrowFunctionExpression, Expression, Identifier, Pattern } from 'estree' */
22
/** @import { AST } from '#compiler' */
33
/** @import { Parser } from '../index.js' */
4-
import read_pattern from '../read/context.js';
5-
import read_expression from '../read/expression.js';
6-
import * as e from '../../../errors.js';
7-
import { create_fragment } from '../utils/create.js';
84
import { walk } from 'zimmerframe';
9-
import { parse_expression_at } from '../acorn.js';
5+
import * as e from '../../../errors.js';
106
import { create_expression_metadata } from '../../nodes.js';
7+
import { parse_expression_at } from '../acorn.js';
8+
import read_pattern from '../read/context.js';
9+
import read_expression, { get_loose_identifier } from '../read/expression.js';
10+
import { create_fragment } from '../utils/create.js';
1111

1212
const regex_whitespace_with_closing_curly_brace = /^\s*}/;
1313

@@ -87,15 +87,23 @@ function open(parser) {
8787
// we get a valid expression
8888
while (!expression) {
8989
try {
90-
expression = read_expression(parser);
90+
expression = read_expression(parser, undefined, true);
9191
} catch (err) {
9292
end = /** @type {any} */ (err).position[0] - 2;
9393

9494
while (end > start && parser.template.slice(end, end + 2) !== 'as') {
9595
end -= 1;
9696
}
9797

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

100108
// @ts-expect-error parser.template is meant to be readonly, this is a special case
101109
parser.template = template.slice(0, end);
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)