handle invalid expressions

pull/14691/head
Simon Holthausen 2 years ago
parent 553912d088
commit 72850e5ce9

@ -39,6 +39,38 @@ export default function read_expression(parser) {
return /** @type {Expression} */ (node); return /** @type {Expression} */ (node);
} catch (err) { } 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); parser.acorn_error(err);
} }
} }

@ -481,7 +481,7 @@ function read_attribute(parser) {
return spread; return spread;
} else { } else {
const value_start = parser.index; const value_start = parser.index;
const name = parser.read_identifier(); let name = parser.read_identifier();
if (name === null) { if (name === null) {
if ( if (
@ -491,9 +491,13 @@ function read_attribute(parser) {
// We're likely in an unclosed opening tag and did read part of a block. // 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 to not crash the parser so it can continue with closing the tag.
return null; 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(); parser.allow_whitespace();
parser.eat('}', true); parser.eat('}', true);

@ -0,0 +1,8 @@
<div {}></div>
<div foo={}></div>
<div foo={a.}></div>
<div foo={'hi}'.}></div>
<Component onclick={() => x.} />
<input bind:value={a.} />

@ -0,0 +1,206 @@
{
"html": {
"type": "Fragment",
"start": 0,
"end": 140,
"children": [
{
"type": "Element",
"start": 0,
"end": 14,
"name": "div",
"attributes": [
{
"type": "Attribute",
"start": 5,
"end": 7,
"name": "",
"value": [
{
"type": "AttributeShorthand",
"start": 6,
"end": 6,
"expression": {
"start": 6,
"end": 6,
"type": "Identifier",
"name": ""
}
}
]
}
],
"children": []
},
{
"type": "Text",
"start": 14,
"end": 15,
"raw": "\n",
"data": "\n"
},
{
"type": "Element",
"start": 15,
"end": 33,
"name": "div",
"attributes": [
{
"type": "Attribute",
"start": 20,
"end": 26,
"name": "foo",
"value": [
{
"type": "MustacheTag",
"start": 24,
"end": 26,
"expression": {
"type": "Identifier",
"start": 25,
"end": 25,
"name": ""
}
}
]
}
],
"children": []
},
{
"type": "Text",
"start": 33,
"end": 35,
"raw": "\n\n",
"data": "\n\n"
},
{
"type": "Element",
"start": 35,
"end": 55,
"name": "div",
"attributes": [
{
"type": "Attribute",
"start": 40,
"end": 48,
"name": "foo",
"value": [
{
"type": "MustacheTag",
"start": 44,
"end": 48,
"expression": {
"type": "Identifier",
"start": 45,
"end": 47,
"name": ""
}
}
]
}
],
"children": []
},
{
"type": "Text",
"start": 55,
"end": 56,
"raw": "\n",
"data": "\n"
},
{
"type": "Element",
"start": 56,
"end": 80,
"name": "div",
"attributes": [
{
"type": "Attribute",
"start": 61,
"end": 73,
"name": "foo",
"value": [
{
"type": "MustacheTag",
"start": 65,
"end": 73,
"expression": {
"type": "Identifier",
"start": 66,
"end": 72,
"name": ""
}
}
]
}
],
"children": []
},
{
"type": "Text",
"start": 80,
"end": 81,
"raw": "\n",
"data": "\n"
},
{
"type": "InlineComponent",
"start": 81,
"end": 113,
"name": "Component",
"attributes": [
{
"type": "Attribute",
"start": 92,
"end": 110,
"name": "onclick",
"value": [
{
"type": "MustacheTag",
"start": 100,
"end": 110,
"expression": {
"type": "Identifier",
"start": 101,
"end": 109,
"name": ""
}
}
]
}
],
"children": []
},
{
"type": "Text",
"start": 113,
"end": 115,
"raw": "\n\n",
"data": "\n\n"
},
{
"type": "Element",
"start": 115,
"end": 140,
"name": "input",
"attributes": [
{
"start": 122,
"end": 137,
"type": "Binding",
"name": "value",
"expression": {
"type": "Identifier",
"start": 134,
"end": 136,
"name": ""
},
"modifiers": []
}
],
"children": []
}
]
}
}

@ -0,0 +1,8 @@
<div {}></div>
<div foo={}></div>
<div foo={a.}></div>
<div foo={'hi}'.}></div>
<Component onclick={() => x.} />
<input bind:value={a.} />

@ -0,0 +1,218 @@
{
"css": null,
"js": [],
"start": 0,
"end": 140,
"type": "Root",
"fragment": {
"type": "Fragment",
"nodes": [
{
"type": "RegularElement",
"start": 0,
"end": 14,
"name": "div",
"attributes": [
{
"type": "Attribute",
"start": 5,
"end": 7,
"name": "",
"value": {
"type": "ExpressionTag",
"start": 6,
"end": 6,
"expression": {
"start": 6,
"end": 6,
"type": "Identifier",
"name": ""
}
}
}
],
"fragment": {
"type": "Fragment",
"nodes": []
}
},
{
"type": "Text",
"start": 14,
"end": 15,
"raw": "\n",
"data": "\n"
},
{
"type": "RegularElement",
"start": 15,
"end": 33,
"name": "div",
"attributes": [
{
"type": "Attribute",
"start": 20,
"end": 26,
"name": "foo",
"value": {
"type": "ExpressionTag",
"start": 24,
"end": 26,
"expression": {
"type": "Identifier",
"start": 25,
"end": 25,
"name": ""
}
}
}
],
"fragment": {
"type": "Fragment",
"nodes": []
}
},
{
"type": "Text",
"start": 33,
"end": 35,
"raw": "\n\n",
"data": "\n\n"
},
{
"type": "RegularElement",
"start": 35,
"end": 55,
"name": "div",
"attributes": [
{
"type": "Attribute",
"start": 40,
"end": 48,
"name": "foo",
"value": {
"type": "ExpressionTag",
"start": 44,
"end": 48,
"expression": {
"type": "Identifier",
"start": 45,
"end": 47,
"name": ""
}
}
}
],
"fragment": {
"type": "Fragment",
"nodes": []
}
},
{
"type": "Text",
"start": 55,
"end": 56,
"raw": "\n",
"data": "\n"
},
{
"type": "RegularElement",
"start": 56,
"end": 80,
"name": "div",
"attributes": [
{
"type": "Attribute",
"start": 61,
"end": 73,
"name": "foo",
"value": {
"type": "ExpressionTag",
"start": 65,
"end": 73,
"expression": {
"type": "Identifier",
"start": 66,
"end": 72,
"name": ""
}
}
}
],
"fragment": {
"type": "Fragment",
"nodes": []
}
},
{
"type": "Text",
"start": 80,
"end": 81,
"raw": "\n",
"data": "\n"
},
{
"type": "Component",
"start": 81,
"end": 113,
"name": "Component",
"attributes": [
{
"type": "Attribute",
"start": 92,
"end": 110,
"name": "onclick",
"value": {
"type": "ExpressionTag",
"start": 100,
"end": 110,
"expression": {
"type": "Identifier",
"start": 101,
"end": 109,
"name": ""
}
}
}
],
"fragment": {
"type": "Fragment",
"nodes": []
}
},
{
"type": "Text",
"start": 113,
"end": 115,
"raw": "\n\n",
"data": "\n\n"
},
{
"type": "RegularElement",
"start": 115,
"end": 140,
"name": "input",
"attributes": [
{
"start": 122,
"end": 137,
"type": "BindDirective",
"name": "value",
"expression": {
"type": "Identifier",
"start": 134,
"end": 136,
"name": ""
},
"modifiers": []
}
],
"fragment": {
"type": "Fragment",
"nodes": []
}
}
]
},
"options": null
}
Loading…
Cancel
Save