From 3146e93bbb34fff06dad9c87c9fd53b3865896f7 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Tue, 17 Dec 2024 16:53:33 +0100 Subject: [PATCH] chore: loose parser improvements (#14733) * handle invalid expression inside each key * handle invalid expression inside each expression * handle invalid expression inside await block * handle "in the middle of typing" components with starting lowercase and dot at the end * changeset --- .changeset/tiny-kings-serve.md | 5 + .../phases/1-parse/read/expression.js | 5 +- .../compiler/phases/1-parse/state/element.js | 9 +- .../src/compiler/phases/1-parse/state/tag.js | 55 +++- .../compiler/phases/1-parse/utils/bracket.js | 5 + .../loose-invalid-expression/input.svelte | 12 + .../loose-invalid-expression/output.json | 268 +++++++++++++++++- .../samples/loose-unclosed-tag/input.svelte | 8 + .../samples/loose-unclosed-tag/output.json | 146 +++++++--- .../loose-invalid-expression/input.svelte | 12 + .../loose-invalid-expression/output.json | 234 ++++++++++++++- .../samples/loose-unclosed-tag/input.svelte | 8 + .../samples/loose-unclosed-tag/output.json | 168 ++++++++--- 13 files changed, 836 insertions(+), 99 deletions(-) create mode 100644 .changeset/tiny-kings-serve.md diff --git a/.changeset/tiny-kings-serve.md b/.changeset/tiny-kings-serve.md new file mode 100644 index 000000000000..b88376713261 --- /dev/null +++ b/.changeset/tiny-kings-serve.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +chore: more loose parser improvements 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 021bd95a6e84..907608b11be6 100644 --- a/packages/svelte/src/compiler/phases/1-parse/read/expression.js +++ b/packages/svelte/src/compiler/phases/1-parse/read/expression.js @@ -7,9 +7,10 @@ import { find_matching_bracket } from '../utils/bracket.js'; /** * @param {Parser} parser + * @param {string} [opening_token] * @returns {Expression} */ -export default function read_expression(parser) { +export default function read_expression(parser, opening_token) { try { const node = parse_expression_at(parser.template, parser.ts, parser.index); @@ -42,7 +43,7 @@ export default function read_expression(parser) { } catch (err) { if (parser.loose) { // Find the next } and treat it as the end of the expression - const end = find_matching_bracket(parser.template, parser.index, '{'); + const end = find_matching_bracket(parser.template, parser.index, opening_token ?? '{'); if (end) { const start = parser.index; parser.index = end; 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 c112cf678987..66946a8f8d22 100644 --- a/packages/svelte/src/compiler/phases/1-parse/state/element.js +++ b/packages/svelte/src/compiler/phases/1-parse/state/element.js @@ -123,8 +123,11 @@ export default function element(parser) { } if (!regex_valid_element_name.test(name) && !regex_valid_component_name.test(name)) { - const bounds = { start: start + 1, end: start + 1 + name.length }; - e.tag_invalid_name(bounds); + // in the middle of typing -> allow in loose mode + if (!parser.loose || !name.endsWith('.')) { + const bounds = { start: start + 1, end: start + 1 + name.length }; + e.tag_invalid_name(bounds); + } } if (root_only_meta_tags.has(name)) { @@ -141,7 +144,7 @@ export default function element(parser) { const type = meta_tags.has(name) ? meta_tags.get(name) - : regex_valid_component_name.test(name) + : regex_valid_component_name.test(name) || (parser.loose && name.endsWith('.')) ? 'Component' : name === 'title' && parent_is_head(parser.stack) ? 'TitleElement' diff --git a/packages/svelte/src/compiler/phases/1-parse/state/tag.js b/packages/svelte/src/compiler/phases/1-parse/state/tag.js index 423ada792cb7..7996d64ded78 100644 --- a/packages/svelte/src/compiler/phases/1-parse/state/tag.js +++ b/packages/svelte/src/compiler/phases/1-parse/state/tag.js @@ -174,13 +174,30 @@ function open(parser) { if (parser.eat('(')) { parser.allow_whitespace(); - key = read_expression(parser); + key = read_expression(parser, '('); parser.allow_whitespace(); parser.eat(')', true); parser.allow_whitespace(); } - parser.eat('}', true); + const matches = parser.eat('}', true, false); + + if (!matches) { + // Parser may have read the `as` as part of the expression (e.g. in `{#each foo. as x}`) + if (parser.template.slice(parser.index - 4, parser.index) === ' as ') { + const prev_index = parser.index; + context = read_pattern(parser); + parser.eat('}', true); + expression = { + type: 'Identifier', + name: '', + start: expression.start, + end: prev_index - 4 + }; + } else { + parser.eat('}', true); // rerun to produce the parser error + } + } /** @type {AST.EachBlock} */ const block = parser.append({ @@ -246,7 +263,39 @@ function open(parser) { parser.fragments.push(block.pending); } - parser.eat('}', true); + const matches = parser.eat('}', true, false); + + // Parser may have read the `then/catch` as part of the expression (e.g. in `{#await foo. then x}`) + if (!matches) { + if (parser.template.slice(parser.index - 6, parser.index) === ' then ') { + const prev_index = parser.index; + block.value = read_pattern(parser); + parser.eat('}', true); + block.expression = { + type: 'Identifier', + name: '', + start: expression.start, + end: prev_index - 6 + }; + block.then = block.pending; + block.pending = null; + } else if (parser.template.slice(parser.index - 7, parser.index) === ' catch ') { + const prev_index = parser.index; + block.error = read_pattern(parser); + parser.eat('}', true); + block.expression = { + type: 'Identifier', + name: '', + start: expression.start, + end: prev_index - 7 + }; + block.catch = block.pending; + block.pending = null; + } else { + parser.eat('}', true); // rerun to produce the parser error + } + } + parser.stack.push(block); return; diff --git a/packages/svelte/src/compiler/phases/1-parse/utils/bracket.js b/packages/svelte/src/compiler/phases/1-parse/utils/bracket.js index 4e02f06de61b..e7576af9550f 100644 --- a/packages/svelte/src/compiler/phases/1-parse/utils/bracket.js +++ b/packages/svelte/src/compiler/phases/1-parse/utils/bracket.js @@ -4,6 +4,8 @@ const SQUARE_BRACKET_OPEN = '['.charCodeAt(0); const SQUARE_BRACKET_CLOSE = ']'.charCodeAt(0); const CURLY_BRACKET_OPEN = '{'.charCodeAt(0); const CURLY_BRACKET_CLOSE = '}'.charCodeAt(0); +const PARENTHESES_OPEN = '('.charCodeAt(0); +const PARENTHESES_CLOSE = ')'.charCodeAt(0); /** @param {number} code */ export function is_bracket_open(code) { @@ -34,6 +36,9 @@ export function get_bracket_close(open) { if (open === CURLY_BRACKET_OPEN) { return CURLY_BRACKET_CLOSE; } + if (open === PARENTHESES_OPEN) { + return PARENTHESES_CLOSE; + } } /** 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 index a0977b9a6346..b64f15c5c1ba 100644 --- a/packages/svelte/tests/parser-legacy/samples/loose-invalid-expression/input.svelte +++ b/packages/svelte/tests/parser-legacy/samples/loose-invalid-expression/input.svelte @@ -9,3 +9,15 @@ asd{a.}asd {foo[bar.]} + +{#if x.}{/if} + +{#each array as item (item.)}{/each} + +{#each obj. as item}{/each} + +{#await x.}{/await} + +{#await x. then y}{/await} + +{#await x. catch y}{/await} diff --git a/packages/svelte/tests/parser-legacy/samples/loose-invalid-expression/output.json b/packages/svelte/tests/parser-legacy/samples/loose-invalid-expression/output.json index d27b6cd914b5..0564d6d29517 100644 --- a/packages/svelte/tests/parser-legacy/samples/loose-invalid-expression/output.json +++ b/packages/svelte/tests/parser-legacy/samples/loose-invalid-expression/output.json @@ -2,7 +2,7 @@ "html": { "type": "Fragment", "start": 0, - "end": 164, + "end": 324, "children": [ { "type": "Element", @@ -236,6 +236,272 @@ "end": 163, "name": "" } + }, + { + "type": "Text", + "start": 164, + "end": 166, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "IfBlock", + "start": 166, + "end": 179, + "expression": { + "type": "Identifier", + "start": 171, + "end": 173, + "name": "" + }, + "children": [] + }, + { + "type": "Text", + "start": 179, + "end": 181, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "EachBlock", + "start": 181, + "end": 217, + "children": [], + "context": { + "type": "Identifier", + "name": "item", + "start": 197, + "loc": { + "start": { + "line": 15, + "column": 16, + "character": 197 + }, + "end": { + "line": 15, + "column": 20, + "character": 201 + } + }, + "end": 201 + }, + "expression": { + "type": "Identifier", + "start": 188, + "end": 193, + "loc": { + "start": { + "line": 15, + "column": 7 + }, + "end": { + "line": 15, + "column": 12 + } + }, + "name": "array" + }, + "key": { + "type": "Identifier", + "start": 203, + "end": 208, + "name": "" + } + }, + { + "type": "Text", + "start": 217, + "end": 219, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "EachBlock", + "start": 219, + "end": 246, + "children": [], + "context": { + "type": "Identifier", + "name": "item", + "start": 234, + "loc": { + "start": { + "line": 17, + "column": 15, + "character": 234 + }, + "end": { + "line": 17, + "column": 19, + "character": 238 + } + }, + "end": 238 + }, + "expression": { + "type": "Identifier", + "name": "", + "start": 226, + "end": 230 + } + }, + { + "type": "Text", + "start": 246, + "end": 248, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "AwaitBlock", + "start": 248, + "end": 267, + "expression": { + "type": "Identifier", + "start": 256, + "end": 258, + "name": "" + }, + "value": null, + "error": null, + "pending": { + "type": "PendingBlock", + "start": 259, + "end": 259, + "children": [], + "skip": false + }, + "then": { + "type": "ThenBlock", + "start": null, + "end": null, + "children": [], + "skip": true + }, + "catch": { + "type": "CatchBlock", + "start": null, + "end": null, + "children": [], + "skip": true + } + }, + { + "type": "Text", + "start": 267, + "end": 269, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "AwaitBlock", + "start": 269, + "end": 295, + "expression": { + "type": "Identifier", + "name": "", + "start": 277, + "end": 279 + }, + "value": { + "type": "Identifier", + "name": "y", + "start": 285, + "loc": { + "start": { + "line": 21, + "column": 16, + "character": 285 + }, + "end": { + "line": 21, + "column": 17, + "character": 286 + } + }, + "end": 286 + }, + "error": null, + "pending": { + "type": "PendingBlock", + "start": null, + "end": null, + "children": [], + "skip": true + }, + "then": { + "type": "ThenBlock", + "start": 287, + "end": 267, + "children": [], + "skip": false + }, + "catch": { + "type": "CatchBlock", + "start": null, + "end": null, + "children": [], + "skip": true + } + }, + { + "type": "Text", + "start": 295, + "end": 297, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "AwaitBlock", + "start": 297, + "end": 324, + "expression": { + "type": "Identifier", + "name": "", + "start": 305, + "end": 307 + }, + "value": null, + "error": { + "type": "Identifier", + "name": "y", + "start": 314, + "loc": { + "start": { + "line": 23, + "column": 17, + "character": 314 + }, + "end": { + "line": 23, + "column": 18, + "character": 315 + } + }, + "end": 315 + }, + "pending": { + "type": "PendingBlock", + "start": null, + "end": null, + "children": [], + "skip": true + }, + "then": { + "type": "ThenBlock", + "start": null, + "end": null, + "children": [], + "skip": true + }, + "catch": { + "type": "CatchBlock", + "start": 316, + "end": 295, + "children": [], + "skip": false + } } ] } diff --git a/packages/svelte/tests/parser-legacy/samples/loose-unclosed-tag/input.svelte b/packages/svelte/tests/parser-legacy/samples/loose-unclosed-tag/input.svelte index 10a3876b7c0f..83017c79aa0e 100644 --- a/packages/svelte/tests/parser-legacy/samples/loose-unclosed-tag/input.svelte +++ b/packages/svelte/tests/parser-legacy/samples/loose-unclosed-tag/input.svelte @@ -10,6 +10,14 @@ +
+ + +
+ + {#if foo}
{/if} diff --git a/packages/svelte/tests/parser-legacy/samples/loose-unclosed-tag/output.json b/packages/svelte/tests/parser-legacy/samples/loose-unclosed-tag/output.json index d91cc68098c3..2205a00e203a 100644 --- a/packages/svelte/tests/parser-legacy/samples/loose-unclosed-tag/output.json +++ b/packages/svelte/tests/parser-legacy/samples/loose-unclosed-tag/output.json @@ -2,7 +2,7 @@ "html": { "type": "Fragment", "start": 0, - "end": 160, + "end": 204, "children": [ { "type": "Element", @@ -136,20 +136,82 @@ "data": "\n\n" }, { - "type": "IfBlock", + "type": "Element", "start": 74, + "end": 94, + "name": "div", + "attributes": [], + "children": [ + { + "type": "Text", + "start": 79, + "end": 81, + "raw": "\n\t", + "data": "\n\t" + }, + { + "type": "InlineComponent", + "start": 81, + "end": 88, + "name": "Comp.", + "attributes": [], + "children": [] + } + ] + }, + { + "type": "Text", + "start": 94, "end": 96, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "Element", + "start": 96, + "end": 116, + "name": "div", + "attributes": [], + "children": [ + { + "type": "Text", + "start": 101, + "end": 103, + "raw": "\n\t", + "data": "\n\t" + }, + { + "type": "InlineComponent", + "start": 103, + "end": 110, + "name": "comp.", + "attributes": [], + "children": [] + } + ] + }, + { + "type": "Text", + "start": 116, + "end": 118, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "IfBlock", + "start": 118, + "end": 140, "expression": { "type": "Identifier", - "start": 79, - "end": 82, + "start": 123, + "end": 126, "loc": { "start": { - "line": 13, + "line": 21, "column": 5 }, "end": { - "line": 13, + "line": 21, "column": 8 } }, @@ -158,15 +220,15 @@ "children": [ { "type": "Element", - "start": 85, - "end": 91, + "start": 129, + "end": 135, "name": "div", "attributes": [], "children": [ { "type": "Text", - "start": 90, - "end": 91, + "start": 134, + "end": 135, "raw": "\n", "data": "\n" } @@ -176,26 +238,26 @@ }, { "type": "Text", - "start": 96, - "end": 98, + "start": 140, + "end": 142, "raw": "\n\n", "data": "\n\n" }, { "type": "IfBlock", - "start": 98, - "end": 130, + "start": 142, + "end": 174, "expression": { "type": "Identifier", - "start": 103, - "end": 106, + "start": 147, + "end": 150, "loc": { "start": { - "line": 17, + "line": 25, "column": 5 }, "end": { - "line": 17, + "line": 25, "column": 8 } }, @@ -204,31 +266,31 @@ "children": [ { "type": "InlineComponent", - "start": 109, - "end": 125, + "start": 153, + "end": 169, "name": "Comp", "attributes": [ { "type": "Attribute", - "start": 115, - "end": 124, + "start": 159, + "end": 168, "name": "foo", "value": [ { "type": "MustacheTag", - "start": 119, - "end": 124, + "start": 163, + "end": 168, "expression": { "type": "Identifier", - "start": 120, - "end": 123, + "start": 164, + "end": 167, "loc": { "start": { - "line": 18, + "line": 26, "column": 12 }, "end": { - "line": 18, + "line": 26, "column": 15 } }, @@ -244,36 +306,36 @@ }, { "type": "Text", - "start": 130, - "end": 132, + "start": 174, + "end": 176, "raw": "\n\n", "data": "\n\n" }, { "type": "Element", - "start": 132, - "end": 160, + "start": 176, + "end": 204, "name": "div", "attributes": [], "children": [ { "type": "Text", - "start": 137, - "end": 138, + "start": 181, + "end": 182, "raw": "\n", "data": "\n" }, { "type": "Element", - "start": 138, - "end": 147, + "start": 182, + "end": 191, "name": "p", "attributes": [], "children": [ { "type": "Text", - "start": 141, - "end": 143, + "start": 185, + "end": 187, "raw": "hi", "data": "hi" } @@ -281,15 +343,15 @@ }, { "type": "Text", - "start": 147, - "end": 149, + "start": 191, + "end": 193, "raw": "\n\n", "data": "\n\n" }, { "type": "Element", - "start": 149, - "end": 160, + "start": 193, + "end": 204, "name": "open-ended", "attributes": [], "children": [] diff --git a/packages/svelte/tests/parser-modern/samples/loose-invalid-expression/input.svelte b/packages/svelte/tests/parser-modern/samples/loose-invalid-expression/input.svelte index a0977b9a6346..b64f15c5c1ba 100644 --- a/packages/svelte/tests/parser-modern/samples/loose-invalid-expression/input.svelte +++ b/packages/svelte/tests/parser-modern/samples/loose-invalid-expression/input.svelte @@ -9,3 +9,15 @@ asd{a.}asd {foo[bar.]} + +{#if x.}{/if} + +{#each array as item (item.)}{/each} + +{#each obj. as item}{/each} + +{#await x.}{/await} + +{#await x. then y}{/await} + +{#await x. catch y}{/await} diff --git a/packages/svelte/tests/parser-modern/samples/loose-invalid-expression/output.json b/packages/svelte/tests/parser-modern/samples/loose-invalid-expression/output.json index cdb7f66c5807..56fa4286ddae 100644 --- a/packages/svelte/tests/parser-modern/samples/loose-invalid-expression/output.json +++ b/packages/svelte/tests/parser-modern/samples/loose-invalid-expression/output.json @@ -2,7 +2,7 @@ "css": null, "js": [], "start": 0, - "end": 164, + "end": 324, "type": "Root", "fragment": { "type": "Fragment", @@ -247,6 +247,238 @@ "end": 163, "name": "" } + }, + { + "type": "Text", + "start": 164, + "end": 166, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "IfBlock", + "elseif": false, + "start": 166, + "end": 179, + "test": { + "type": "Identifier", + "start": 171, + "end": 173, + "name": "" + }, + "consequent": { + "type": "Fragment", + "nodes": [] + }, + "alternate": null + }, + { + "type": "Text", + "start": 179, + "end": 181, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "EachBlock", + "start": 181, + "end": 217, + "expression": { + "type": "Identifier", + "start": 188, + "end": 193, + "loc": { + "start": { + "line": 15, + "column": 7 + }, + "end": { + "line": 15, + "column": 12 + } + }, + "name": "array" + }, + "body": { + "type": "Fragment", + "nodes": [] + }, + "context": { + "type": "Identifier", + "name": "item", + "start": 197, + "loc": { + "start": { + "line": 15, + "column": 16, + "character": 197 + }, + "end": { + "line": 15, + "column": 20, + "character": 201 + } + }, + "end": 201 + }, + "key": { + "type": "Identifier", + "start": 203, + "end": 208, + "name": "" + } + }, + { + "type": "Text", + "start": 217, + "end": 219, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "EachBlock", + "start": 219, + "end": 246, + "expression": { + "type": "Identifier", + "name": "", + "start": 226, + "end": 230 + }, + "body": { + "type": "Fragment", + "nodes": [] + }, + "context": { + "type": "Identifier", + "name": "item", + "start": 234, + "loc": { + "start": { + "line": 17, + "column": 15, + "character": 234 + }, + "end": { + "line": 17, + "column": 19, + "character": 238 + } + }, + "end": 238 + } + }, + { + "type": "Text", + "start": 246, + "end": 248, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "AwaitBlock", + "start": 248, + "end": 267, + "expression": { + "type": "Identifier", + "start": 256, + "end": 258, + "name": "" + }, + "value": null, + "error": null, + "pending": { + "type": "Fragment", + "nodes": [] + }, + "then": null, + "catch": null + }, + { + "type": "Text", + "start": 267, + "end": 269, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "AwaitBlock", + "start": 269, + "end": 295, + "expression": { + "type": "Identifier", + "name": "", + "start": 277, + "end": 279 + }, + "value": { + "type": "Identifier", + "name": "y", + "start": 285, + "loc": { + "start": { + "line": 21, + "column": 16, + "character": 285 + }, + "end": { + "line": 21, + "column": 17, + "character": 286 + } + }, + "end": 286 + }, + "error": null, + "pending": null, + "then": { + "type": "Fragment", + "nodes": [] + }, + "catch": null + }, + { + "type": "Text", + "start": 295, + "end": 297, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "AwaitBlock", + "start": 297, + "end": 324, + "expression": { + "type": "Identifier", + "name": "", + "start": 305, + "end": 307 + }, + "value": null, + "error": { + "type": "Identifier", + "name": "y", + "start": 314, + "loc": { + "start": { + "line": 23, + "column": 17, + "character": 314 + }, + "end": { + "line": 23, + "column": 18, + "character": 315 + } + }, + "end": 315 + }, + "pending": null, + "then": null, + "catch": { + "type": "Fragment", + "nodes": [] + } } ] }, diff --git a/packages/svelte/tests/parser-modern/samples/loose-unclosed-tag/input.svelte b/packages/svelte/tests/parser-modern/samples/loose-unclosed-tag/input.svelte index 10a3876b7c0f..83017c79aa0e 100644 --- a/packages/svelte/tests/parser-modern/samples/loose-unclosed-tag/input.svelte +++ b/packages/svelte/tests/parser-modern/samples/loose-unclosed-tag/input.svelte @@ -10,6 +10,14 @@ +
+ + +
+ + {#if foo}
{/if} diff --git a/packages/svelte/tests/parser-modern/samples/loose-unclosed-tag/output.json b/packages/svelte/tests/parser-modern/samples/loose-unclosed-tag/output.json index ec47bfc738f0..cf9138c02629 100644 --- a/packages/svelte/tests/parser-modern/samples/loose-unclosed-tag/output.json +++ b/packages/svelte/tests/parser-modern/samples/loose-unclosed-tag/output.json @@ -2,7 +2,7 @@ "css": null, "js": [], "start": 0, - "end": 160, + "end": 204, "type": "Root", "fragment": { "type": "Fragment", @@ -155,21 +155,95 @@ "data": "\n\n" }, { - "type": "IfBlock", - "elseif": false, + "type": "RegularElement", "start": 74, + "end": 94, + "name": "div", + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 79, + "end": 81, + "raw": "\n\t", + "data": "\n\t" + }, + { + "type": "Component", + "start": 81, + "end": 88, + "name": "Comp.", + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [] + } + } + ] + } + }, + { + "type": "Text", + "start": 94, "end": 96, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "RegularElement", + "start": 96, + "end": 116, + "name": "div", + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 101, + "end": 103, + "raw": "\n\t", + "data": "\n\t" + }, + { + "type": "Component", + "start": 103, + "end": 110, + "name": "comp.", + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [] + } + } + ] + } + }, + { + "type": "Text", + "start": 116, + "end": 118, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "IfBlock", + "elseif": false, + "start": 118, + "end": 140, "test": { "type": "Identifier", - "start": 79, - "end": 82, + "start": 123, + "end": 126, "loc": { "start": { - "line": 13, + "line": 21, "column": 5 }, "end": { - "line": 13, + "line": 21, "column": 8 } }, @@ -180,15 +254,15 @@ "nodes": [ { "type": "Text", - "start": 83, - "end": 85, + "start": 127, + "end": 129, "raw": "\n\t", "data": "\n\t" }, { "type": "RegularElement", - "start": 85, - "end": 91, + "start": 129, + "end": 135, "name": "div", "attributes": [], "fragment": { @@ -196,8 +270,8 @@ "nodes": [ { "type": "Text", - "start": 90, - "end": 91, + "start": 134, + "end": 135, "raw": "\n", "data": "\n" } @@ -210,27 +284,27 @@ }, { "type": "Text", - "start": 96, - "end": 98, + "start": 140, + "end": 142, "raw": "\n\n", "data": "\n\n" }, { "type": "IfBlock", "elseif": false, - "start": 98, - "end": 130, + "start": 142, + "end": 174, "test": { "type": "Identifier", - "start": 103, - "end": 106, + "start": 147, + "end": 150, "loc": { "start": { - "line": 17, + "line": 25, "column": 5 }, "end": { - "line": 17, + "line": 25, "column": 8 } }, @@ -241,37 +315,37 @@ "nodes": [ { "type": "Text", - "start": 107, - "end": 109, + "start": 151, + "end": 153, "raw": "\n\t", "data": "\n\t" }, { "type": "Component", - "start": 109, - "end": 125, + "start": 153, + "end": 169, "name": "Comp", "attributes": [ { "type": "Attribute", - "start": 115, - "end": 124, + "start": 159, + "end": 168, "name": "foo", "value": { "type": "ExpressionTag", - "start": 119, - "end": 124, + "start": 163, + "end": 168, "expression": { "type": "Identifier", - "start": 120, - "end": 123, + "start": 164, + "end": 167, "loc": { "start": { - "line": 18, + "line": 26, "column": 12 }, "end": { - "line": 18, + "line": 26, "column": 15 } }, @@ -291,15 +365,15 @@ }, { "type": "Text", - "start": 130, - "end": 132, + "start": 174, + "end": 176, "raw": "\n\n", "data": "\n\n" }, { "type": "RegularElement", - "start": 132, - "end": 160, + "start": 176, + "end": 204, "name": "div", "attributes": [], "fragment": { @@ -307,15 +381,15 @@ "nodes": [ { "type": "Text", - "start": 137, - "end": 138, + "start": 181, + "end": 182, "raw": "\n", "data": "\n" }, { "type": "RegularElement", - "start": 138, - "end": 147, + "start": 182, + "end": 191, "name": "p", "attributes": [], "fragment": { @@ -323,8 +397,8 @@ "nodes": [ { "type": "Text", - "start": 141, - "end": 143, + "start": 185, + "end": 187, "raw": "hi", "data": "hi" } @@ -333,15 +407,15 @@ }, { "type": "Text", - "start": 147, - "end": 149, + "start": 191, + "end": 193, "raw": "\n\n", "data": "\n\n" }, { "type": "RegularElement", - "start": 149, - "end": 160, + "start": 193, + "end": 204, "name": "open-ended", "attributes": [], "fragment": {