diff --git a/CHANGELOG.md b/CHANGELOG.md index bd070862f3..6eda3003ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ * Prevent duplicate invalidation with certain two-way component bindings ([#3180](https://github.com/sveltejs/svelte/issues/3180), [#5117](https://github.com/sveltejs/svelte/issues/5117), [#5144](https://github.com/sveltejs/svelte/issues/5144)) * Fix reactivity when passing `$$props` to a `` ([#3364](https://github.com/sveltejs/svelte/issues/3364)) * Fix unneeded invalidation of `$$props` and `$$restProps` ([#4993](https://github.com/sveltejs/svelte/issues/4993), [#5118](https://github.com/sveltejs/svelte/issues/5118)) +* Provide better compiler error message when mismatched tags are due to autoclosing of tags ([#5049](https://github.com/sveltejs/svelte/issues/5049)) ## 3.24.0 diff --git a/src/compiler/parse/index.ts b/src/compiler/parse/index.ts index a809eeebeb..52795c02a9 100644 --- a/src/compiler/parse/index.ts +++ b/src/compiler/parse/index.ts @@ -8,6 +8,12 @@ import error from '../utils/error'; type ParserState = (parser: Parser) => (ParserState | void); +interface LastAutoClosedTag { + tag: string; + reason: string; + depth: number; +} + export class Parser { readonly template: string; readonly filename?: string; @@ -20,6 +26,7 @@ export class Parser { css: Style[] = []; js: Script[] = []; meta_tags = {}; + last_auto_closed_tag?: LastAutoClosedTag; constructor(template: string, options: ParserOptions) { if (typeof template !== 'string') { diff --git a/src/compiler/parse/state/tag.ts b/src/compiler/parse/state/tag.ts index ce8a0a9aa7..311bf852e2 100644 --- a/src/compiler/parse/state/tag.ts +++ b/src/compiler/parse/state/tag.ts @@ -133,11 +133,15 @@ export default function tag(parser: Parser) { // close any elements that don't have their own closing tags, e.g.

while (parent.name !== name) { - if (parent.type !== 'Element') + if (parent.type !== 'Element') { + const message = parser.last_auto_closed_tag && parser.last_auto_closed_tag.tag === name + ? ` attempted to close <${name}> that was already automatically closed by <${parser.last_auto_closed_tag.reason}>` + : ` attempted to close an element that was not open`; parser.error({ code: `invalid-closing-tag`, - message: ` attempted to close an element that was not open` + message }, start); + } parent.end = start; parser.stack.pop(); @@ -148,10 +152,19 @@ export default function tag(parser: Parser) { parent.end = parser.index; parser.stack.pop(); + if (parser.last_auto_closed_tag && parser.stack.length < parser.last_auto_closed_tag.depth) { + parser.last_auto_closed_tag = null; + } + return; } else if (closing_tag_omitted(parent.name, name)) { parent.end = start; parser.stack.pop(); + parser.last_auto_closed_tag = { + tag: parent.name, + reason: name, + depth: parser.stack.length, + }; } const unique_names: Set = new Set(); diff --git a/test/parser/samples/error-unmatched-closing-tag-autoclose-2/error.json b/test/parser/samples/error-unmatched-closing-tag-autoclose-2/error.json new file mode 100644 index 0000000000..d24296bd96 --- /dev/null +++ b/test/parser/samples/error-unmatched-closing-tag-autoclose-2/error.json @@ -0,0 +1,10 @@ +{ + "code": "invalid-closing-tag", + "message": "

attempted to close an element that was not open", + "pos": 38, + "start": { + "character": 38, + "column": 0, + "line": 5 + } +} diff --git a/test/parser/samples/error-unmatched-closing-tag-autoclose-2/input.svelte b/test/parser/samples/error-unmatched-closing-tag-autoclose-2/input.svelte new file mode 100644 index 0000000000..5182577921 --- /dev/null +++ b/test/parser/samples/error-unmatched-closing-tag-autoclose-2/input.svelte @@ -0,0 +1,5 @@ +
+

+

pre tag
+
+

\ No newline at end of file diff --git a/test/parser/samples/error-unmatched-closing-tag-autoclose/error.json b/test/parser/samples/error-unmatched-closing-tag-autoclose/error.json new file mode 100644 index 0000000000..e6532d747e --- /dev/null +++ b/test/parser/samples/error-unmatched-closing-tag-autoclose/error.json @@ -0,0 +1,10 @@ +{ + "code": "invalid-closing-tag", + "message": "

attempted to close

that was already automatically closed by

",
+	"pos": 24,
+	"start": {
+		"character": 24,
+		"column": 0,
+		"line": 3
+	}
+}
diff --git a/test/parser/samples/error-unmatched-closing-tag-autoclose/input.svelte b/test/parser/samples/error-unmatched-closing-tag-autoclose/input.svelte
new file mode 100644
index 0000000000..0bfd609736
--- /dev/null
+++ b/test/parser/samples/error-unmatched-closing-tag-autoclose/input.svelte
@@ -0,0 +1,3 @@
+

+

pre tag
+

\ No newline at end of file