Skip to content

Commit 48e3db2

Browse files
fix: default values in object destructuring (#14554)
* fix: enhance string/expression length check and fix closing character issues * docs: add documentation for unterminated_string_constant error * test: add tests for object destructuring with default values in "each" blocks * Update .changeset/clean-planets-rush.md * refactor: clean up unnecessary comments and whitespace * fix: resolve formatting issues * simplify * tweak * regenerate --------- Co-authored-by: Rich Harris <rich.harris@vercel.com>
1 parent 4aadb34 commit 48e3db2

File tree

9 files changed

+1806
-25
lines changed

9 files changed

+1806
-25
lines changed

.changeset/clean-planets-rush.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': patch
3+
---
4+
5+
fix: handle default values in object destructuring within "each" blocks when using characters like "}" and "]"

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

+6
Original file line numberDiff line numberDiff line change
@@ -1012,6 +1012,12 @@ Unexpected end of input
10121012
'%word%' is a reserved word in JavaScript and cannot be used here
10131013
```
10141014

1015+
### unterminated_string_constant
1016+
1017+
```
1018+
Unterminated string constant
1019+
```
1020+
10151021
### void_element_invalid_content
10161022

10171023
```

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

+4
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,10 @@ See https://html.spec.whatwg.org/multipage/custom-elements.html#valid-custom-ele
418418

419419
> '%word%' is a reserved word in JavaScript and cannot be used here
420420
421+
## unterminated_string_constant
422+
423+
> Unterminated string constant
424+
421425
## void_element_invalid_content
422426

423427
> Void elements cannot have children or closing tags

packages/svelte/src/compiler/errors.js

+9
Original file line numberDiff line numberDiff line change
@@ -1574,6 +1574,15 @@ export function unexpected_reserved_word(node, word) {
15741574
e(node, "unexpected_reserved_word", `'${word}' is a reserved word in JavaScript and cannot be used here\nhttps://svelte.dev/e/unexpected_reserved_word`);
15751575
}
15761576

1577+
/**
1578+
* Unterminated string constant
1579+
* @param {null | number | NodeLike} node
1580+
* @returns {never}
1581+
*/
1582+
export function unterminated_string_constant(node) {
1583+
e(node, "unterminated_string_constant", `Unterminated string constant\nhttps://svelte.dev/e/unterminated_string_constant`);
1584+
}
1585+
15771586
/**
15781587
* Void elements cannot have children or closing tags
15791588
* @param {null | number | NodeLike} node

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

+71-24
Original file line numberDiff line numberDiff line change
@@ -37,33 +37,11 @@ export default function read_pattern(parser) {
3737
e.expected_pattern(i);
3838
}
3939

40-
/** @type {string[]} */
41-
const bracket_stack = [];
42-
43-
while (i < parser.template.length) {
44-
const char = parser.template[i];
45-
46-
if (is_bracket_open(char)) {
47-
bracket_stack.push(char);
48-
} else if (is_bracket_close(char)) {
49-
const popped = /** @type {string} */ (bracket_stack.pop());
50-
const expected = /** @type {string} */ (get_bracket_close(popped));
51-
52-
if (char !== expected) {
53-
e.expected_token(i, expected);
54-
}
55-
56-
if (bracket_stack.length === 0) {
57-
i += 1;
58-
break;
59-
}
60-
}
61-
i += 1;
62-
}
63-
40+
i = match_bracket(parser, start);
6441
parser.index = i;
6542

6643
const pattern_string = parser.template.slice(start, i);
44+
6745
try {
6846
// the length of the `space_with_newline` has to be start - 1
6947
// because we added a `(` in front of the pattern_string,
@@ -93,6 +71,75 @@ export default function read_pattern(parser) {
9371
}
9472
}
9573

74+
/**
75+
* @param {Parser} parser
76+
* @param {number} start
77+
*/
78+
function match_bracket(parser, start) {
79+
const bracket_stack = [];
80+
81+
let i = start;
82+
83+
while (i < parser.template.length) {
84+
let char = parser.template[i++];
85+
86+
if (char === "'" || char === '"' || char === '`') {
87+
i = match_quote(parser, i, char);
88+
continue;
89+
}
90+
91+
if (is_bracket_open(char)) {
92+
bracket_stack.push(char);
93+
} else if (is_bracket_close(char)) {
94+
const popped = /** @type {string} */ (bracket_stack.pop());
95+
const expected = /** @type {string} */ (get_bracket_close(popped));
96+
97+
if (char !== expected) {
98+
e.expected_token(i - 1, expected);
99+
}
100+
101+
if (bracket_stack.length === 0) {
102+
return i;
103+
}
104+
}
105+
}
106+
107+
e.unexpected_eof(parser.template.length);
108+
}
109+
110+
/**
111+
* @param {Parser} parser
112+
* @param {number} start
113+
* @param {string} quote
114+
*/
115+
function match_quote(parser, start, quote) {
116+
let is_escaped = false;
117+
let i = start;
118+
119+
while (i < parser.template.length) {
120+
const char = parser.template[i++];
121+
122+
if (is_escaped) {
123+
is_escaped = false;
124+
continue;
125+
}
126+
127+
if (char === quote) {
128+
return i;
129+
}
130+
131+
if (char === '\\') {
132+
is_escaped = true;
133+
}
134+
135+
if (quote === '`' && char === '$' && parser.template[i] === '{') {
136+
i = match_bracket(parser, i);
137+
}
138+
}
139+
140+
e.unterminated_string_constant(start);
141+
}
142+
96143
/**
97144
* @param {Parser} parser
98145
* @returns {any}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{#each x as { y = 'z' }}{/each}
2+
3+
{#each x as { y = '{' }}{/each}
4+
5+
{#each x as { y = ']' }}{/each}
6+
7+
{#each x as { y = `${`"`}` }}{/each}
8+
9+
{#each x as { y = `${`John`}` }}{/each}

0 commit comments

Comments
 (0)