From 4e727ed900aa483835c62f53cd03d6a63203671b Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Wed, 11 Dec 2024 15:03:46 +0100 Subject: [PATCH] handle invalid blocks --- .../src/compiler/phases/1-parse/state/tag.js | 35 ++-- .../samples/loose-invalid-block/input.svelte | 13 ++ .../samples/loose-invalid-block/output.json | 107 +++++++++++ .../samples/loose-invalid-block/input.svelte | 13 ++ .../samples/loose-invalid-block/output.json | 171 ++++++++++++++++++ 5 files changed, 326 insertions(+), 13 deletions(-) create mode 100644 packages/svelte/tests/parser-legacy/samples/loose-invalid-block/input.svelte create mode 100644 packages/svelte/tests/parser-legacy/samples/loose-invalid-block/output.json create mode 100644 packages/svelte/tests/parser-modern/samples/loose-invalid-block/input.svelte create mode 100644 packages/svelte/tests/parser-modern/samples/loose-invalid-block/output.json 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 bacd56d53d..423ada792c 100644 --- a/packages/svelte/src/compiler/phases/1-parse/state/tag.js +++ b/packages/svelte/src/compiler/phases/1-parse/state/tag.js @@ -279,34 +279,43 @@ function open(parser) { parser.require_whitespace(); const name_start = parser.index; - const name = parser.read_identifier(); + let name = parser.read_identifier(); const name_end = parser.index; if (name === null) { - e.expected_identifier(parser.index); + if (parser.loose) { + name = ''; + } else { + e.expected_identifier(parser.index); + } } parser.allow_whitespace(); const params_start = parser.index; - parser.eat('(', true); - let parentheses = 1; + const matched = parser.eat('(', true, false); - while (parser.index < parser.template.length && (!parser.match(')') || parentheses !== 1)) { - if (parser.match('(')) parentheses++; - if (parser.match(')')) parentheses--; - parser.index += 1; - } + if (matched) { + let parentheses = 1; - parser.eat(')', true); + while (parser.index < parser.template.length && (!parser.match(')') || parentheses !== 1)) { + if (parser.match('(')) parentheses++; + if (parser.match(')')) parentheses--; + parser.index += 1; + } + + parser.eat(')', true); + } const prelude = parser.template.slice(0, params_start).replace(/\S/g, ' '); const params = parser.template.slice(params_start, parser.index); - let function_expression = /** @type {ArrowFunctionExpression} */ ( - parse_expression_at(prelude + `${params} => {}`, parser.ts, params_start) - ); + let function_expression = matched + ? /** @type {ArrowFunctionExpression} */ ( + parse_expression_at(prelude + `${params} => {}`, parser.ts, params_start) + ) + : { params: [] }; parser.allow_whitespace(); parser.eat('}', true); diff --git a/packages/svelte/tests/parser-legacy/samples/loose-invalid-block/input.svelte b/packages/svelte/tests/parser-legacy/samples/loose-invalid-block/input.svelte new file mode 100644 index 0000000000..4489a3eb27 --- /dev/null +++ b/packages/svelte/tests/parser-legacy/samples/loose-invalid-block/input.svelte @@ -0,0 +1,13 @@ +{#if } +{:else if } +{:else } +{/if} + +{#each } +{/each} + +{#snippet } +{/snippet} + +{#snippet foo} +{/snippet} diff --git a/packages/svelte/tests/parser-legacy/samples/loose-invalid-block/output.json b/packages/svelte/tests/parser-legacy/samples/loose-invalid-block/output.json new file mode 100644 index 0000000000..480fcf2edc --- /dev/null +++ b/packages/svelte/tests/parser-legacy/samples/loose-invalid-block/output.json @@ -0,0 +1,107 @@ +{ + "html": { + "type": "Fragment", + "start": 0, + "end": 102, + "children": [ + { + "type": "IfBlock", + "start": 0, + "end": 33, + "expression": { + "type": "Identifier", + "start": 5, + "end": 5, + "name": "" + }, + "children": [], + "else": { + "type": "ElseBlock", + "start": 18, + "end": 28, + "children": [ + { + "type": "IfBlock", + "start": 18, + "end": 33, + "expression": { + "type": "Identifier", + "start": 17, + "end": 17, + "name": "" + }, + "children": [], + "else": { + "type": "ElseBlock", + "start": 27, + "end": 28, + "children": [] + }, + "elseif": true + } + ] + } + }, + { + "type": "Text", + "start": 33, + "end": 35, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "EachBlock", + "start": 35, + "end": 51, + "children": [], + "context": null, + "expression": { + "type": "Identifier", + "start": 42, + "end": 42, + "name": "" + } + }, + { + "type": "Text", + "start": 51, + "end": 53, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "SnippetBlock", + "start": 53, + "end": 75, + "expression": { + "type": "Identifier", + "start": 63, + "end": 63, + "name": "" + }, + "parameters": [], + "children": [] + }, + { + "type": "Text", + "start": 75, + "end": 77, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "SnippetBlock", + "start": 77, + "end": 102, + "expression": { + "type": "Identifier", + "start": 87, + "end": 90, + "name": "foo" + }, + "parameters": [], + "children": [] + } + ] + } +} diff --git a/packages/svelte/tests/parser-modern/samples/loose-invalid-block/input.svelte b/packages/svelte/tests/parser-modern/samples/loose-invalid-block/input.svelte new file mode 100644 index 0000000000..4489a3eb27 --- /dev/null +++ b/packages/svelte/tests/parser-modern/samples/loose-invalid-block/input.svelte @@ -0,0 +1,13 @@ +{#if } +{:else if } +{:else } +{/if} + +{#each } +{/each} + +{#snippet } +{/snippet} + +{#snippet foo} +{/snippet} diff --git a/packages/svelte/tests/parser-modern/samples/loose-invalid-block/output.json b/packages/svelte/tests/parser-modern/samples/loose-invalid-block/output.json new file mode 100644 index 0000000000..46aad16b21 --- /dev/null +++ b/packages/svelte/tests/parser-modern/samples/loose-invalid-block/output.json @@ -0,0 +1,171 @@ +{ + "css": null, + "js": [], + "start": 0, + "end": 102, + "type": "Root", + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "IfBlock", + "elseif": false, + "start": 0, + "end": 33, + "test": { + "type": "Identifier", + "start": 5, + "end": 5, + "name": "" + }, + "consequent": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 6, + "end": 7, + "raw": "\n", + "data": "\n" + } + ] + }, + "alternate": { + "type": "Fragment", + "nodes": [ + { + "start": 7, + "end": 33, + "type": "IfBlock", + "elseif": true, + "test": { + "type": "Identifier", + "start": 17, + "end": 17, + "name": "" + }, + "consequent": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 18, + "end": 19, + "raw": "\n", + "data": "\n" + } + ] + }, + "alternate": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 27, + "end": 28, + "raw": "\n", + "data": "\n" + } + ] + } + } + ] + } + }, + { + "type": "Text", + "start": 33, + "end": 35, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "EachBlock", + "start": 35, + "end": 51, + "expression": { + "type": "Identifier", + "start": 42, + "end": 42, + "name": "" + }, + "body": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 43, + "end": 44, + "raw": "\n", + "data": "\n" + } + ] + }, + "context": null + }, + { + "type": "Text", + "start": 51, + "end": 53, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "SnippetBlock", + "start": 53, + "end": 75, + "expression": { + "type": "Identifier", + "start": 63, + "end": 63, + "name": "" + }, + "parameters": [], + "body": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 64, + "end": 65, + "raw": "\n", + "data": "\n" + } + ] + } + }, + { + "type": "Text", + "start": 75, + "end": 77, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "SnippetBlock", + "start": 77, + "end": 102, + "expression": { + "type": "Identifier", + "start": 87, + "end": 90, + "name": "foo" + }, + "parameters": [], + "body": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 91, + "end": 92, + "raw": "\n", + "data": "\n" + } + ] + } + } + ] + }, + "options": null +}