handle unclosed blocks

pull/14691/head
Simon Holthausen 2 years ago
parent 6994021ef9
commit 7337612af4

@ -164,14 +164,15 @@ export class Parser {
/** /**
* @param {string} str * @param {string} str
* @param {boolean} required * @param {boolean} required
* @param {boolean} required_in_loose
*/ */
eat(str, required = false) { eat(str, required = false, required_in_loose = true) {
if (this.match(str)) { if (this.match(str)) {
this.index += str.length; this.index += str.length;
return true; return true;
} }
if (required) { if (required && (!this.loose || required_in_loose)) {
e.expected_token(this.index, str); e.expected_token(this.index, str);
} }

@ -454,43 +454,66 @@ function close(parser) {
const start = parser.index - 1; const start = parser.index - 1;
let block = parser.current(); let block = parser.current();
/** Only relevant/reached for loose parsing mode */
let matched;
switch (block.type) { switch (block.type) {
case 'IfBlock': case 'IfBlock':
parser.eat('if', true); matched = parser.eat('if', true, false);
if (!matched) {
block.end = start - 1;
parser.pop();
close(parser);
return;
}
parser.allow_whitespace(); parser.allow_whitespace();
parser.eat('}', true); parser.eat('}', true);
while (block.elseif) { while (block.elseif) {
block.end = parser.index; block.end = parser.index;
parser.stack.pop(); parser.stack.pop();
block = /** @type {AST.IfBlock} */ (parser.current()); block = /** @type {AST.IfBlock} */ (parser.current());
} }
block.end = parser.index; block.end = parser.index;
parser.pop(); parser.pop();
return; return;
case 'EachBlock': case 'EachBlock':
parser.eat('each', true); matched = parser.eat('each', true, false);
break; break;
case 'KeyBlock': case 'KeyBlock':
parser.eat('key', true); matched = parser.eat('key', true, false);
break; break;
case 'AwaitBlock': case 'AwaitBlock':
parser.eat('await', true); matched = parser.eat('await', true, false);
break; break;
case 'SnippetBlock': case 'SnippetBlock':
parser.eat('snippet', true); matched = parser.eat('snippet', true, false);
break; break;
case 'RegularElement': case 'RegularElement':
// TODO handle implicitly closed elements if (parser.loose) {
e.block_unexpected_close(start); matched = false;
} else {
// TODO handle implicitly closed elements
e.block_unexpected_close(start);
}
break; break;
default: default:
e.block_unexpected_close(start); e.block_unexpected_close(start);
} }
if (!matched) {
block.end = start - 1;
parser.pop();
close(parser);
return;
}
parser.allow_whitespace(); parser.allow_whitespace();
parser.eat('}', true); parser.eat('}', true);
block.end = parser.index; block.end = parser.index;

@ -0,0 +1,20 @@
<div>
{#if foo}
</div>
<Comp>
{#key bar}
</Comp>
<div>
{#if foo}
{#if bar}
{/if}
</div>
{#if foo}
{#key bar}
{/if}
{#each x as y}
<p>hi</p>

@ -0,0 +1,276 @@
{
"html": {
"type": "Fragment",
"start": 0,
"end": 150,
"children": [
{
"type": "Element",
"start": 0,
"end": 23,
"name": "div",
"attributes": [],
"children": [
{
"type": "Text",
"start": 5,
"end": 7,
"raw": "\n\t",
"data": "\n\t"
},
{
"type": "IfBlock",
"start": 7,
"end": 17,
"expression": {
"type": "Identifier",
"start": 12,
"end": 15,
"loc": {
"start": {
"line": 2,
"column": 6
},
"end": {
"line": 2,
"column": 9
}
},
"name": "foo"
},
"children": []
}
]
},
{
"type": "Text",
"start": 23,
"end": 25,
"raw": "\n\n",
"data": "\n\n"
},
{
"type": "InlineComponent",
"start": 25,
"end": 51,
"name": "Comp",
"attributes": [],
"children": [
{
"type": "Text",
"start": 31,
"end": 33,
"raw": "\n\t",
"data": "\n\t"
},
{
"type": "KeyBlock",
"start": 33,
"end": 44,
"expression": {
"type": "Identifier",
"start": 39,
"end": 42,
"loc": {
"start": {
"line": 6,
"column": 7
},
"end": {
"line": 6,
"column": 10
}
},
"name": "bar"
},
"children": []
}
]
},
{
"type": "Text",
"start": 51,
"end": 53,
"raw": "\n\n",
"data": "\n\n"
},
{
"type": "Element",
"start": 53,
"end": 95,
"name": "div",
"attributes": [],
"children": [
{
"type": "Text",
"start": 58,
"end": 60,
"raw": "\n\t",
"data": "\n\t"
},
{
"type": "IfBlock",
"start": 60,
"end": 89,
"expression": {
"type": "Identifier",
"start": 65,
"end": 68,
"loc": {
"start": {
"line": 10,
"column": 6
},
"end": {
"line": 10,
"column": 9
}
},
"name": "foo"
},
"children": [
{
"type": "IfBlock",
"start": 72,
"end": 88,
"expression": {
"type": "Identifier",
"start": 77,
"end": 80,
"loc": {
"start": {
"line": 11,
"column": 7
},
"end": {
"line": 11,
"column": 10
}
},
"name": "bar"
},
"children": []
}
]
}
]
},
{
"type": "Text",
"start": 95,
"end": 97,
"raw": "\n\n",
"data": "\n\n"
},
{
"type": "IfBlock",
"start": 97,
"end": 124,
"expression": {
"type": "Identifier",
"start": 102,
"end": 105,
"loc": {
"start": {
"line": 15,
"column": 5
},
"end": {
"line": 15,
"column": 8
}
},
"name": "foo"
},
"children": [
{
"type": "KeyBlock",
"start": 108,
"end": 119,
"expression": {
"type": "Identifier",
"start": 114,
"end": 117,
"loc": {
"start": {
"line": 16,
"column": 7
},
"end": {
"line": 16,
"column": 10
}
},
"name": "bar"
},
"children": []
}
]
},
{
"type": "Text",
"start": 124,
"end": 126,
"raw": "\n\n",
"data": "\n\n"
},
{
"type": "EachBlock",
"start": 126,
"end": 150,
"children": [
{
"type": "Element",
"start": 141,
"end": 150,
"name": "p",
"attributes": [],
"children": [
{
"type": "Text",
"start": 144,
"end": 146,
"raw": "hi",
"data": "hi"
}
]
}
],
"context": {
"type": "Identifier",
"name": "y",
"start": 138,
"loc": {
"start": {
"line": 19,
"column": 12,
"character": 138
},
"end": {
"line": 19,
"column": 13,
"character": 139
}
},
"end": 139
},
"expression": {
"type": "Identifier",
"start": 133,
"end": 134,
"loc": {
"start": {
"line": 19,
"column": 7
},
"end": {
"line": 19,
"column": 8
}
},
"name": "x"
}
}
]
}
}

@ -6,5 +6,9 @@
<span> <span>
</div> </div>
{#if foo}
<div>
{/if}
<div> <div>
<p>hi</p> <p>hi</p>

@ -2,7 +2,7 @@
"html": { "html": {
"type": "Fragment", "type": "Fragment",
"start": 0, "start": 0,
"end": 59, "end": 83,
"children": [ "children": [
{ {
"type": "Element", "type": "Element",
@ -83,30 +83,76 @@
"data": "\n\n" "data": "\n\n"
}, },
{ {
"type": "Element", "type": "IfBlock",
"start": 44, "start": 44,
"end": 59, "end": 66,
"expression": {
"type": "Identifier",
"start": 49,
"end": 52,
"loc": {
"start": {
"line": 9,
"column": 5
},
"end": {
"line": 9,
"column": 8
}
},
"name": "foo"
},
"children": [
{
"type": "Element",
"start": 55,
"end": 61,
"name": "div",
"attributes": [],
"children": [
{
"type": "Text",
"start": 60,
"end": 61,
"raw": "\n",
"data": "\n"
}
]
}
]
},
{
"type": "Text",
"start": 66,
"end": 68,
"raw": "\n\n",
"data": "\n\n"
},
{
"type": "Element",
"start": 68,
"end": 83,
"name": "div", "name": "div",
"attributes": [], "attributes": [],
"children": [ "children": [
{ {
"type": "Text", "type": "Text",
"start": 49, "start": 73,
"end": 50, "end": 74,
"raw": "\n", "raw": "\n",
"data": "\n" "data": "\n"
}, },
{ {
"type": "Element", "type": "Element",
"start": 50, "start": 74,
"end": 59, "end": 83,
"name": "p", "name": "p",
"attributes": [], "attributes": [],
"children": [ "children": [
{ {
"type": "Text", "type": "Text",
"start": 53, "start": 77,
"end": 55, "end": 79,
"raw": "hi", "raw": "hi",
"data": "hi" "data": "hi"
} }

Loading…
Cancel
Save