Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/famous-scissors-begin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'svelte': patch
---

fix: correctly parse `each` with loose parser
47 changes: 34 additions & 13 deletions packages/svelte/src/compiler/phases/1-parse/read/expression.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,31 @@ import { find_matching_bracket } from '../utils/bracket.js';
/**
* @param {Parser} parser
* @param {string} [opening_token]
* @returns {Expression | undefined}
*/
export function get_loose_identifier(parser, opening_token) {
// Find the next } and treat it as the end of the expression
const end = find_matching_bracket(parser.template, parser.index, opening_token ?? '{');
if (end) {
const start = parser.index;
parser.index = end;
// We don't know what the expression is and signal this by returning an empty identifier
return {
type: 'Identifier',
start,
end,
name: ''
};
}
}

/**
* @param {Parser} parser
* @param {string} [opening_token]
* @param {boolean} [is_each]
* @returns {Expression}
*/
export default function read_expression(parser, opening_token) {
export default function read_expression(parser, opening_token, is_each) {
try {
const node = parse_expression_at(parser.template, parser.ts, parser.index);

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

return /** @type {Expression} */ (node);
} catch (err) {
if (parser.loose) {
/**
* If we are in an each loop we need the error to be thrown in cases like
*
* `as { y = z }`
*
* so we still throw and handle the error there
*/
if (parser.loose && !is_each) {
// Find the next } and treat it as the end of the expression
const end = find_matching_bracket(parser.template, parser.index, opening_token ?? '{');
if (end) {
const start = parser.index;
parser.index = end;
// We don't know what the expression is and signal this by returning an empty identifier
return {
type: 'Identifier',
start,
end,
name: ''
};
const expression = get_loose_identifier(parser, opening_token);
if (expression) {
return expression;
}
}

Expand Down
22 changes: 15 additions & 7 deletions packages/svelte/src/compiler/phases/1-parse/state/tag.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
/** @import { ArrowFunctionExpression, Expression, Identifier, Pattern } from 'estree' */
/** @import { AST } from '#compiler' */
/** @import { Parser } from '../index.js' */
import read_pattern from '../read/context.js';
import read_expression from '../read/expression.js';
import * as e from '../../../errors.js';
import { create_fragment } from '../utils/create.js';
import { walk } from 'zimmerframe';
import { parse_expression_at } from '../acorn.js';
import * as e from '../../../errors.js';
import { create_expression_metadata } from '../../nodes.js';
import { parse_expression_at } from '../acorn.js';
import read_pattern from '../read/context.js';
import read_expression, { get_loose_identifier } from '../read/expression.js';
import { create_fragment } from '../utils/create.js';

const regex_whitespace_with_closing_curly_brace = /^\s*}/;

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

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

if (end <= start) throw err;
if (end <= start) {
if (parser.loose) {
expression = get_loose_identifier(parser);
if (expression) {
break;
}
}
throw err;
}

// @ts-expect-error parser.template is meant to be readonly, this is a special case
parser.template = template.slice(0, end);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<script lang="ts">
let arr = [];
</script>

{#each arr as [key, value = 'default']}
<div>{key}: {value}</div>
{/each}
Loading
Loading