From 9100120a3389b3ea79d372dba6342750cfc77a73 Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Tue, 17 Dec 2024 12:25:30 +0100 Subject: [PATCH 1/5] handle invalid expression inside each key --- .../phases/1-parse/read/expression.js | 5 +- .../src/compiler/phases/1-parse/state/tag.js | 2 +- .../compiler/phases/1-parse/utils/bracket.js | 5 ++ .../loose-invalid-expression/input.svelte | 4 + .../loose-invalid-expression/output.json | 74 ++++++++++++++++- .../loose-invalid-expression/input.svelte | 4 + .../loose-invalid-expression/output.json | 82 ++++++++++++++++++- 7 files changed, 171 insertions(+), 5 deletions(-) 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/tag.js b/packages/svelte/src/compiler/phases/1-parse/state/tag.js index 423ada792cb7..5718500613a9 100644 --- a/packages/svelte/src/compiler/phases/1-parse/state/tag.js +++ b/packages/svelte/src/compiler/phases/1-parse/state/tag.js @@ -174,7 +174,7 @@ 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(); 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..62b8eb26a1a0 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,7 @@ asd{a.}asd {foo[bar.]} + +{#if x.}{/if} + +{#each array as item (item.)}{/each} 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..2d7e81cbe40c 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": 217, "children": [ { "type": "Element", @@ -236,6 +236,78 @@ "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": "" + } } ] } 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..62b8eb26a1a0 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,7 @@ asd{a.}asd {foo[bar.]} + +{#if x.}{/if} + +{#each array as item (item.)}{/each} 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..3e17bee890aa 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": 217, "type": "Root", "fragment": { "type": "Fragment", @@ -247,6 +247,86 @@ "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": "" + } } ] }, From 2ba4b95dd6f98e0d13959ea51f8c6af09381440d Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Tue, 17 Dec 2024 12:40:26 +0100 Subject: [PATCH 2/5] handle invalid expression inside each expression --- .../src/compiler/phases/1-parse/state/tag.js | 15 ++++++- .../loose-invalid-expression/input.svelte | 2 + .../loose-invalid-expression/output.json | 39 ++++++++++++++++- .../loose-invalid-expression/input.svelte | 2 + .../loose-invalid-expression/output.json | 42 ++++++++++++++++++- 5 files changed, 97 insertions(+), 3 deletions(-) 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 5718500613a9..0fc6135bf245 100644 --- a/packages/svelte/src/compiler/phases/1-parse/state/tag.js +++ b/packages/svelte/src/compiler/phases/1-parse/state/tag.js @@ -180,7 +180,20 @@ function open(parser) { parser.allow_whitespace(); } - parser.eat('}', true); + const matches = parser.eat('}', true, false); + + // Parser may have read the `as` as part of the expression (e.g. in `{#each foo. as x}`) + if (!matches && 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 + }; + } /** @type {AST.EachBlock} */ const block = parser.append({ 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 62b8eb26a1a0..3f44de96a7b8 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 @@ -13,3 +13,5 @@ asd{a.}asd {#if x.}{/if} {#each array as item (item.)}{/each} + +{#each obj. as item}{/each} 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 2d7e81cbe40c..57fb01c77dbe 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": 217, + "end": 246, "children": [ { "type": "Element", @@ -308,6 +308,43 @@ "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 + } } ] } 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 62b8eb26a1a0..3f44de96a7b8 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 @@ -13,3 +13,5 @@ asd{a.}asd {#if x.}{/if} {#each array as item (item.)}{/each} + +{#each obj. as item}{/each} 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 3e17bee890aa..a1a7148e024b 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": 217, + "end": 246, "type": "Root", "fragment": { "type": "Fragment", @@ -327,6 +327,46 @@ "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 + } } ] }, From c677df696b059891d57ca23e63ef45892ce12b21 Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Tue, 17 Dec 2024 12:55:43 +0100 Subject: [PATCH 3/5] handle invalid expression inside await block --- .../src/compiler/phases/1-parse/state/tag.js | 60 +++++-- .../loose-invalid-expression/input.svelte | 6 + .../loose-invalid-expression/output.json | 159 +++++++++++++++++- .../loose-invalid-expression/input.svelte | 6 + .../loose-invalid-expression/output.json | 114 ++++++++++++- 5 files changed, 331 insertions(+), 14 deletions(-) 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 0fc6135bf245..7996d64ded78 100644 --- a/packages/svelte/src/compiler/phases/1-parse/state/tag.js +++ b/packages/svelte/src/compiler/phases/1-parse/state/tag.js @@ -182,17 +182,21 @@ function open(parser) { const matches = parser.eat('}', true, false); - // Parser may have read the `as` as part of the expression (e.g. in `{#each foo. as x}`) - if (!matches && 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 - }; + 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} */ @@ -259,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/tests/parser-legacy/samples/loose-invalid-expression/input.svelte b/packages/svelte/tests/parser-legacy/samples/loose-invalid-expression/input.svelte index 3f44de96a7b8..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 @@ -15,3 +15,9 @@ asd{a.}asd {#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 57fb01c77dbe..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": 246, + "end": 324, "children": [ { "type": "Element", @@ -345,6 +345,163 @@ "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-modern/samples/loose-invalid-expression/input.svelte b/packages/svelte/tests/parser-modern/samples/loose-invalid-expression/input.svelte index 3f44de96a7b8..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 @@ -15,3 +15,9 @@ asd{a.}asd {#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 a1a7148e024b..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": 246, + "end": 324, "type": "Root", "fragment": { "type": "Fragment", @@ -367,6 +367,118 @@ }, "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": [] + } } ] }, From bfb2f265a83ad11299d8055d56e98a31a1da1517 Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Tue, 17 Dec 2024 13:09:12 +0100 Subject: [PATCH 4/5] handle "in the middle of typing" components with starting lowercase and dot at the end --- .../compiler/phases/1-parse/state/element.js | 9 +- .../samples/loose-unclosed-tag/input.svelte | 8 + .../samples/loose-unclosed-tag/output.json | 146 ++++++++++----- .../samples/loose-unclosed-tag/input.svelte | 8 + .../samples/loose-unclosed-tag/output.json | 168 +++++++++++++----- 5 files changed, 247 insertions(+), 92 deletions(-) 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/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-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": { From e8b6f3c5cac7286a522522723eb615d0b943bba6 Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Tue, 17 Dec 2024 13:09:35 +0100 Subject: [PATCH 5/5] changeset --- .changeset/tiny-kings-serve.md | 5 +++++ 1 file changed, 5 insertions(+) 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