diff --git a/packages/svelte/src/compiler/phases/1-parse/read/expression.js b/packages/svelte/src/compiler/phases/1-parse/read/expression.js
index 4e33c23f28..2930dc5b0a 100644
--- a/packages/svelte/src/compiler/phases/1-parse/read/expression.js
+++ b/packages/svelte/src/compiler/phases/1-parse/read/expression.js
@@ -39,6 +39,38 @@ export default function read_expression(parser) {
return /** @type {Expression} */ (node);
} catch (err) {
+ if (parser.loose) {
+ // Find the next } and treat it as the end of the expression
+ let index = parser.index;
+ let num_braces = 0;
+ while (index < parser.template.length) {
+ const char = parser.template[index];
+ if (char === '{') num_braces += 1;
+ if (char === '}') {
+ if (num_braces === 0) {
+ // We assume that there's some kind of whitespace or the start of the closing tag after the closing brace,
+ // else this hints at a wrong counting of braces (e.g. in the case of foo={'hi}'})
+ if (!/[\s>/]/.test(parser.template[index + 1])) {
+ num_braces += 1;
+ continue;
+ }
+
+ const start = parser.index;
+ parser.index = index;
+ // We don't know what the expression is and signal this by returning an empty identifier
+ return {
+ type: 'Identifier',
+ start,
+ end: index,
+ name: ''
+ };
+ }
+ num_braces -= 1;
+ }
+ index += 1;
+ }
+ }
+
parser.acorn_error(err);
}
}
diff --git a/packages/svelte/src/compiler/phases/1-parse/state/element.js b/packages/svelte/src/compiler/phases/1-parse/state/element.js
index b7473d073a..b778e19748 100644
--- a/packages/svelte/src/compiler/phases/1-parse/state/element.js
+++ b/packages/svelte/src/compiler/phases/1-parse/state/element.js
@@ -481,7 +481,7 @@ function read_attribute(parser) {
return spread;
} else {
const value_start = parser.index;
- const name = parser.read_identifier();
+ let name = parser.read_identifier();
if (name === null) {
if (
@@ -491,8 +491,12 @@ function read_attribute(parser) {
// We're likely in an unclosed opening tag and did read part of a block.
// Return null to not crash the parser so it can continue with closing the tag.
return null;
+ } else if (parser.loose && parser.match('}')) {
+ // Likely in the middle of typing, just created the shorthand
+ name = '';
+ } else {
+ e.attribute_empty_shorthand(start);
}
- e.attribute_empty_shorthand(start);
}
parser.allow_whitespace();
diff --git a/packages/svelte/tests/parser-legacy/samples/loose-invalid-expression/input.svelte b/packages/svelte/tests/parser-legacy/samples/loose-invalid-expression/input.svelte
new file mode 100644
index 0000000000..bd1db72f53
--- /dev/null
+++ b/packages/svelte/tests/parser-legacy/samples/loose-invalid-expression/input.svelte
@@ -0,0 +1,8 @@
+