handle unclosed opening tags

pull/14691/head
Simon Holthausen 2 years ago
parent 7337612af4
commit 553912d088

@ -247,6 +247,7 @@ export class Parser {
/** @param {RegExp} pattern */
read_until(pattern) {
if (this.index >= this.template.length) {
if (this.loose) return '';
e.unexpected_eof(this.template.length);
}

@ -319,10 +319,30 @@ export default function element(parser) {
parser.append(element);
const self_closing = parser.eat('/') || is_void(name);
const closed = parser.eat('>', true, false);
// Loose parsing mode
if (!closed) {
// We may have eaten an opening `<` of the next element and treated is as an attribute...
const last = element.attributes.at(-1);
if (last?.type === 'Attribute' && last.name === '<') {
parser.index = last.start;
element.attributes.pop();
}
// ... or we may have eaten part of a following block
else {
const prev_1 = parser.template[parser.index - 1];
const prev_2 = parser.template[parser.index - 2];
const current = parser.template[parser.index];
if (prev_2 === '{' && prev_1 === '/') {
parser.index -= 2;
} else if (prev_1 === '{' && (current === '#' || current === '@' || current === ':')) {
parser.index -= 1;
}
}
}
parser.eat('>', true);
if (self_closing) {
if (self_closing || !closed) {
// don't push self-closing elements onto the stack
element.end = parser.index;
} else if (name === 'textarea') {
@ -464,6 +484,14 @@ function read_attribute(parser) {
const name = parser.read_identifier();
if (name === null) {
if (
parser.loose &&
(parser.match('#') || parser.match('/') || parser.match('@') || parser.match(':'))
) {
// 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;
}
e.attribute_empty_shorthand(start);
}
@ -740,5 +768,9 @@ function read_sequence(parser, done, location) {
}
}
e.unexpected_eof(parser.template.length);
if (parser.loose) {
return chunks;
} else {
e.unexpected_eof(parser.template.length);
}
}

@ -0,0 +1,20 @@
<div>
<Comp foo={bar}
</div>
<div>
<span foo={bar}
</div>
{#if foo}
<Comp foo={bar}
{/if}
{#if foo}
<Comp foo={bar}
{#if bar}
{bar}
{/if}
{/if}
<div foo={bar}

@ -0,0 +1,349 @@
{
"html": {
"type": "Fragment",
"start": 0,
"end": 170,
"children": [
{
"type": "Element",
"start": 0,
"end": 29,
"name": "div",
"attributes": [],
"children": [
{
"type": "Text",
"start": 5,
"end": 7,
"raw": "\n\t",
"data": "\n\t"
},
{
"type": "InlineComponent",
"start": 7,
"end": 23,
"name": "Comp",
"attributes": [
{
"type": "Attribute",
"start": 13,
"end": 22,
"name": "foo",
"value": [
{
"type": "MustacheTag",
"start": 17,
"end": 22,
"expression": {
"type": "Identifier",
"start": 18,
"end": 21,
"loc": {
"start": {
"line": 2,
"column": 12
},
"end": {
"line": 2,
"column": 15
}
},
"name": "bar"
}
}
]
}
],
"children": []
}
]
},
{
"type": "Text",
"start": 29,
"end": 31,
"raw": "\n\n",
"data": "\n\n"
},
{
"type": "Element",
"start": 31,
"end": 60,
"name": "div",
"attributes": [],
"children": [
{
"type": "Text",
"start": 36,
"end": 38,
"raw": "\n\t",
"data": "\n\t"
},
{
"type": "Element",
"start": 38,
"end": 54,
"name": "span",
"attributes": [
{
"type": "Attribute",
"start": 44,
"end": 53,
"name": "foo",
"value": [
{
"type": "MustacheTag",
"start": 48,
"end": 53,
"expression": {
"type": "Identifier",
"start": 49,
"end": 52,
"loc": {
"start": {
"line": 6,
"column": 12
},
"end": {
"line": 6,
"column": 15
}
},
"name": "bar"
}
}
]
}
],
"children": []
}
]
},
{
"type": "Text",
"start": 60,
"end": 62,
"raw": "\n\n",
"data": "\n\n"
},
{
"type": "IfBlock",
"start": 62,
"end": 94,
"expression": {
"type": "Identifier",
"start": 67,
"end": 70,
"loc": {
"start": {
"line": 9,
"column": 5
},
"end": {
"line": 9,
"column": 8
}
},
"name": "foo"
},
"children": [
{
"type": "InlineComponent",
"start": 73,
"end": 89,
"name": "Comp",
"attributes": [
{
"type": "Attribute",
"start": 79,
"end": 88,
"name": "foo",
"value": [
{
"type": "MustacheTag",
"start": 83,
"end": 88,
"expression": {
"type": "Identifier",
"start": 84,
"end": 87,
"loc": {
"start": {
"line": 10,
"column": 12
},
"end": {
"line": 10,
"column": 15
}
},
"name": "bar"
}
}
]
}
],
"children": []
}
]
},
{
"type": "Text",
"start": 94,
"end": 96,
"raw": "\n\n",
"data": "\n\n"
},
{
"type": "IfBlock",
"start": 96,
"end": 154,
"expression": {
"type": "Identifier",
"start": 101,
"end": 104,
"loc": {
"start": {
"line": 13,
"column": 5
},
"end": {
"line": 13,
"column": 8
}
},
"name": "foo"
},
"children": [
{
"type": "InlineComponent",
"start": 107,
"end": 124,
"name": "Comp",
"attributes": [
{
"type": "Attribute",
"start": 113,
"end": 122,
"name": "foo",
"value": [
{
"type": "MustacheTag",
"start": 117,
"end": 122,
"expression": {
"type": "Identifier",
"start": 118,
"end": 121,
"loc": {
"start": {
"line": 14,
"column": 12
},
"end": {
"line": 14,
"column": 15
}
},
"name": "bar"
}
}
]
}
],
"children": []
},
{
"type": "IfBlock",
"start": 124,
"end": 148,
"expression": {
"type": "Identifier",
"start": 129,
"end": 132,
"loc": {
"start": {
"line": 15,
"column": 6
},
"end": {
"line": 15,
"column": 9
}
},
"name": "bar"
},
"children": [
{
"type": "MustacheTag",
"start": 136,
"end": 141,
"expression": {
"type": "Identifier",
"start": 137,
"end": 140,
"loc": {
"start": {
"line": 16,
"column": 3
},
"end": {
"line": 16,
"column": 6
}
},
"name": "bar"
}
}
]
}
]
},
{
"type": "Text",
"start": 154,
"end": 156,
"raw": "\n\n",
"data": "\n\n"
},
{
"type": "Element",
"start": 156,
"end": 170,
"name": "div",
"attributes": [
{
"type": "Attribute",
"start": 161,
"end": 170,
"name": "foo",
"value": [
{
"type": "MustacheTag",
"start": 165,
"end": 170,
"expression": {
"type": "Identifier",
"start": 166,
"end": 169,
"loc": {
"start": {
"line": 20,
"column": 10
},
"end": {
"line": 20,
"column": 13
}
},
"name": "bar"
}
}
]
}
],
"children": []
}
]
}
}

@ -0,0 +1,20 @@
<div>
<Comp foo={bar}
</div>
<div>
<span foo={bar}
</div>
{#if foo}
<Comp foo={bar}
{/if}
{#if foo}
<Comp foo={bar}
{#if bar}
{bar}
{/if}
{/if}
<div foo={bar}

@ -0,0 +1,414 @@
{
"css": null,
"js": [],
"start": 0,
"end": 170,
"type": "Root",
"fragment": {
"type": "Fragment",
"nodes": [
{
"type": "RegularElement",
"start": 0,
"end": 29,
"name": "div",
"attributes": [],
"fragment": {
"type": "Fragment",
"nodes": [
{
"type": "Text",
"start": 5,
"end": 7,
"raw": "\n\t",
"data": "\n\t"
},
{
"type": "Component",
"start": 7,
"end": 23,
"name": "Comp",
"attributes": [
{
"type": "Attribute",
"start": 13,
"end": 22,
"name": "foo",
"value": {
"type": "ExpressionTag",
"start": 17,
"end": 22,
"expression": {
"type": "Identifier",
"start": 18,
"end": 21,
"loc": {
"start": {
"line": 2,
"column": 12
},
"end": {
"line": 2,
"column": 15
}
},
"name": "bar"
}
}
}
],
"fragment": {
"type": "Fragment",
"nodes": []
}
}
]
}
},
{
"type": "Text",
"start": 29,
"end": 31,
"raw": "\n\n",
"data": "\n\n"
},
{
"type": "RegularElement",
"start": 31,
"end": 60,
"name": "div",
"attributes": [],
"fragment": {
"type": "Fragment",
"nodes": [
{
"type": "Text",
"start": 36,
"end": 38,
"raw": "\n\t",
"data": "\n\t"
},
{
"type": "RegularElement",
"start": 38,
"end": 54,
"name": "span",
"attributes": [
{
"type": "Attribute",
"start": 44,
"end": 53,
"name": "foo",
"value": {
"type": "ExpressionTag",
"start": 48,
"end": 53,
"expression": {
"type": "Identifier",
"start": 49,
"end": 52,
"loc": {
"start": {
"line": 6,
"column": 12
},
"end": {
"line": 6,
"column": 15
}
},
"name": "bar"
}
}
}
],
"fragment": {
"type": "Fragment",
"nodes": []
}
}
]
}
},
{
"type": "Text",
"start": 60,
"end": 62,
"raw": "\n\n",
"data": "\n\n"
},
{
"type": "IfBlock",
"elseif": false,
"start": 62,
"end": 94,
"test": {
"type": "Identifier",
"start": 67,
"end": 70,
"loc": {
"start": {
"line": 9,
"column": 5
},
"end": {
"line": 9,
"column": 8
}
},
"name": "foo"
},
"consequent": {
"type": "Fragment",
"nodes": [
{
"type": "Text",
"start": 71,
"end": 73,
"raw": "\n\t",
"data": "\n\t"
},
{
"type": "Component",
"start": 73,
"end": 89,
"name": "Comp",
"attributes": [
{
"type": "Attribute",
"start": 79,
"end": 88,
"name": "foo",
"value": {
"type": "ExpressionTag",
"start": 83,
"end": 88,
"expression": {
"type": "Identifier",
"start": 84,
"end": 87,
"loc": {
"start": {
"line": 10,
"column": 12
},
"end": {
"line": 10,
"column": 15
}
},
"name": "bar"
}
}
}
],
"fragment": {
"type": "Fragment",
"nodes": []
}
}
]
},
"alternate": null
},
{
"type": "Text",
"start": 94,
"end": 96,
"raw": "\n\n",
"data": "\n\n"
},
{
"type": "IfBlock",
"elseif": false,
"start": 96,
"end": 154,
"test": {
"type": "Identifier",
"start": 101,
"end": 104,
"loc": {
"start": {
"line": 13,
"column": 5
},
"end": {
"line": 13,
"column": 8
}
},
"name": "foo"
},
"consequent": {
"type": "Fragment",
"nodes": [
{
"type": "Text",
"start": 105,
"end": 107,
"raw": "\n\t",
"data": "\n\t"
},
{
"type": "Component",
"start": 107,
"end": 124,
"name": "Comp",
"attributes": [
{
"type": "Attribute",
"start": 113,
"end": 122,
"name": "foo",
"value": {
"type": "ExpressionTag",
"start": 117,
"end": 122,
"expression": {
"type": "Identifier",
"start": 118,
"end": 121,
"loc": {
"start": {
"line": 14,
"column": 12
},
"end": {
"line": 14,
"column": 15
}
},
"name": "bar"
}
}
}
],
"fragment": {
"type": "Fragment",
"nodes": []
}
},
{
"type": "IfBlock",
"elseif": false,
"start": 124,
"end": 148,
"test": {
"type": "Identifier",
"start": 129,
"end": 132,
"loc": {
"start": {
"line": 15,
"column": 6
},
"end": {
"line": 15,
"column": 9
}
},
"name": "bar"
},
"consequent": {
"type": "Fragment",
"nodes": [
{
"type": "Text",
"start": 133,
"end": 136,
"raw": "\n\t\t",
"data": "\n\t\t"
},
{
"type": "ExpressionTag",
"start": 136,
"end": 141,
"expression": {
"type": "Identifier",
"start": 137,
"end": 140,
"loc": {
"start": {
"line": 16,
"column": 3
},
"end": {
"line": 16,
"column": 6
}
},
"name": "bar"
}
},
{
"type": "Text",
"start": 141,
"end": 143,
"raw": "\n\t",
"data": "\n\t"
}
]
},
"alternate": null
},
{
"type": "Text",
"start": 148,
"end": 149,
"raw": "\n",
"data": "\n"
}
]
},
"alternate": null
},
{
"type": "Text",
"start": 154,
"end": 156,
"raw": "\n\n",
"data": "\n\n"
},
{
"type": "RegularElement",
"start": 156,
"end": 170,
"name": "div",
"attributes": [
{
"type": "Attribute",
"start": 161,
"end": 170,
"name": "foo",
"value": {
"type": "ExpressionTag",
"start": 165,
"end": 170,
"expression": {
"type": "Identifier",
"start": 166,
"end": 169,
"loc": {
"start": {
"line": 20,
"column": 10
},
"end": {
"line": 20,
"column": 13
}
},
"name": "bar"
}
}
}
],
"fragment": {
"type": "Fragment",
"nodes": []
}
}
]
},
"options": null
}
Loading…
Cancel
Save