diff --git a/.changeset/nine-cooks-join.md b/.changeset/nine-cooks-join.md new file mode 100644 index 000000000000..c2cc16965a54 --- /dev/null +++ b/.changeset/nine-cooks-join.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +breaking: warn on self-closing non-void HTML tags diff --git a/packages/svelte/src/compiler/phases/2-analyze/validation.js b/packages/svelte/src/compiler/phases/2-analyze/validation.js index ed840f530315..c02e9adf2188 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/validation.js +++ b/packages/svelte/src/compiler/phases/2-analyze/validation.js @@ -15,7 +15,12 @@ import { import { warn } from '../../warnings.js'; import fuzzymatch from '../1-parse/utils/fuzzymatch.js'; import { binding_properties } from '../bindings.js'; -import { ContentEditableBindings, EventModifiers, SVGElements } from '../constants.js'; +import { + ContentEditableBindings, + EventModifiers, + SVGElements, + VoidElements +} from '../constants.js'; import { is_custom_element_node } from '../nodes.js'; import { regex_illegal_attribute_character, @@ -565,6 +570,21 @@ const validation = { } } + if ( + context.state.analysis.source[node.end - 2] === '/' && + context.state.options.namespace !== 'foreign' && + !VoidElements.includes(node.name) && + !SVGElements.includes(node.name) + ) { + warn( + context.state.analysis.warnings, + node, + context.path, + 'invalid-self-closing-tag', + node.name + ); + } + context.next({ ...context.state, parent_element: node.name diff --git a/packages/svelte/src/compiler/warnings.js b/packages/svelte/src/compiler/warnings.js index 8f0e81d9c021..e8f48864b257 100644 --- a/packages/svelte/src/compiler/warnings.js +++ b/packages/svelte/src/compiler/warnings.js @@ -243,6 +243,12 @@ const options = { "The 'customElement' option is used when generating a custom element. Did you forget the 'customElement: true' compile option?" }; +const misc = { + /** @param {string} name */ + 'invalid-self-closing-tag': (name) => + `Self-closing HTML tags for non-void elements are ambiguous — use <${name} ...> rather than <${name} ... />` +}; + /** @satisfies {Warnings} */ const warnings = { ...css, @@ -254,7 +260,8 @@ const warnings = { ...components, ...legacy, ...block, - ...options + ...options, + ...misc }; /** @typedef {typeof warnings} AllWarnings */ diff --git a/packages/svelte/tests/compiler-errors/samples/component-slot-nested-error-2/main.svelte b/packages/svelte/tests/compiler-errors/samples/component-slot-nested-error-2/main.svelte index f0ba192c4f70..7143fdda50e5 100644 --- a/packages/svelte/tests/compiler-errors/samples/component-slot-nested-error-2/main.svelte +++ b/packages/svelte/tests/compiler-errors/samples/component-slot-nested-error-2/main.svelte @@ -5,7 +5,7 @@
-
+
diff --git a/packages/svelte/tests/compiler-errors/samples/component-slot-nested-error-3/main.svelte b/packages/svelte/tests/compiler-errors/samples/component-slot-nested-error-3/main.svelte index 3031292d0402..c36659274c58 100644 --- a/packages/svelte/tests/compiler-errors/samples/component-slot-nested-error-3/main.svelte +++ b/packages/svelte/tests/compiler-errors/samples/component-slot-nested-error-3/main.svelte @@ -5,7 +5,7 @@
-
+
diff --git a/packages/svelte/tests/compiler-errors/samples/component-slot-nested-error/main.svelte b/packages/svelte/tests/compiler-errors/samples/component-slot-nested-error/main.svelte index 5f251ea62520..b0ecfcf31eca 100644 --- a/packages/svelte/tests/compiler-errors/samples/component-slot-nested-error/main.svelte +++ b/packages/svelte/tests/compiler-errors/samples/component-slot-nested-error/main.svelte @@ -4,6 +4,6 @@
-
+
diff --git a/packages/svelte/tests/css/samples/general-siblings-combinator-await-not-exhaustive/input.svelte b/packages/svelte/tests/css/samples/general-siblings-combinator-await-not-exhaustive/input.svelte index fadfc914ed4f..a3e4230161a8 100644 --- a/packages/svelte/tests/css/samples/general-siblings-combinator-await-not-exhaustive/input.svelte +++ b/packages/svelte/tests/css/samples/general-siblings-combinator-await-not-exhaustive/input.svelte @@ -18,24 +18,24 @@ .c ~ .g { color: green; } -
+
{#await promise then value} -
+
{:catch error} -
+
{/await} {#await promise} -
+
{:catch error} -
+
{/await} {#await promise} -
+
{:then error} -
+
{/await} -
+
diff --git a/packages/svelte/tests/css/samples/general-siblings-combinator-await/input.svelte b/packages/svelte/tests/css/samples/general-siblings-combinator-await/input.svelte index 8aeadab170e0..11bd358c01b9 100644 --- a/packages/svelte/tests/css/samples/general-siblings-combinator-await/input.svelte +++ b/packages/svelte/tests/css/samples/general-siblings-combinator-await/input.svelte @@ -17,14 +17,14 @@ .b ~ .d { color: green; } -
+
{#await promise} -
+
{:then value} -
+
{:catch error} -
+
{/await} -
\ No newline at end of file +
diff --git a/packages/svelte/tests/css/samples/general-siblings-combinator-each-2/input.svelte b/packages/svelte/tests/css/samples/general-siblings-combinator-each-2/input.svelte index 5bbdbdef669f..b719ce407771 100644 --- a/packages/svelte/tests/css/samples/general-siblings-combinator-each-2/input.svelte +++ b/packages/svelte/tests/css/samples/general-siblings-combinator-each-2/input.svelte @@ -27,11 +27,11 @@ } -
+
{#each array as item} -
-
+
+
{/each} -
\ No newline at end of file +
diff --git a/packages/svelte/tests/css/samples/general-siblings-combinator-each-else-nested/input.svelte b/packages/svelte/tests/css/samples/general-siblings-combinator-each-else-nested/input.svelte index 63a11237089a..3030f3e9ab7a 100644 --- a/packages/svelte/tests/css/samples/general-siblings-combinator-each-else-nested/input.svelte +++ b/packages/svelte/tests/css/samples/general-siblings-combinator-each-else-nested/input.svelte @@ -35,43 +35,43 @@ .e ~ .f { color: green; } -
+
{#each array as a} -
+
{#each array as b} -
+
{:else} -
+
{/each} {/each} {#each array as c} {#each array as d} -
+
{/each} {:else} -
+
{/each} {#each array as x} -
+
{#each array as y} {#each array as z} -
+
{/each} {:else} -
+
{/each} -
+
{/each} -
+
{#each array as item} {#each array as item} -
+
{:else} -
+
{/each} -{/each} \ No newline at end of file +{/each} diff --git a/packages/svelte/tests/css/samples/general-siblings-combinator-each-else/input.svelte b/packages/svelte/tests/css/samples/general-siblings-combinator-each-else/input.svelte index d2916b263de1..04e0d64468b0 100644 --- a/packages/svelte/tests/css/samples/general-siblings-combinator-each-else/input.svelte +++ b/packages/svelte/tests/css/samples/general-siblings-combinator-each-else/input.svelte @@ -13,12 +13,12 @@ .b ~ .c { color: green; } -
+
{#each array as item} -
+
{:else} -
+
{/each} -
+
diff --git a/packages/svelte/tests/css/samples/general-siblings-combinator-each-nested/input.svelte b/packages/svelte/tests/css/samples/general-siblings-combinator-each-nested/input.svelte index b7c7377015ab..014b9ab83fd1 100644 --- a/packages/svelte/tests/css/samples/general-siblings-combinator-each-nested/input.svelte +++ b/packages/svelte/tests/css/samples/general-siblings-combinator-each-nested/input.svelte @@ -65,39 +65,39 @@ .g ~ .i { color: green; } -
+
{#each array as item} -
-
+
+
{/each} {#each array as item} {#each array as item} {#each array as item} -
+
{/each} -
+
{/each} -
+
{/each} {#each array as item} -
+
{#each array as item} -
+
{#each array as item} -
+
{/each} {/each} {/each} {#each array as item} -
+
{#each array as item} -
+
{#each array as item} -
+
{/each} {/each} {/each} @@ -105,9 +105,9 @@ {#each array as item} {#each array as item} {#each array as item} -
+
{/each} -
+
{/each} -
-{/each} \ No newline at end of file +
+{/each} diff --git a/packages/svelte/tests/css/samples/general-siblings-combinator-each/input.svelte b/packages/svelte/tests/css/samples/general-siblings-combinator-each/input.svelte index ce65da109da7..9b4925e08ce5 100644 --- a/packages/svelte/tests/css/samples/general-siblings-combinator-each/input.svelte +++ b/packages/svelte/tests/css/samples/general-siblings-combinator-each/input.svelte @@ -8,13 +8,13 @@ } -
+
{#each array as item} - -
- -
+ +
+ +
{/each} - \ No newline at end of file + diff --git a/packages/svelte/tests/css/samples/general-siblings-combinator-if-not-exhaustive-with-each/input.svelte b/packages/svelte/tests/css/samples/general-siblings-combinator-if-not-exhaustive-with-each/input.svelte index 68a6825b8227..1f86b2bbf63d 100644 --- a/packages/svelte/tests/css/samples/general-siblings-combinator-if-not-exhaustive-with-each/input.svelte +++ b/packages/svelte/tests/css/samples/general-siblings-combinator-if-not-exhaustive-with-each/input.svelte @@ -18,14 +18,14 @@ .b ~ .c { color: green; } -
+
{#if foo} -
+
{:else} {#each array as item} -
+
{/each} {/if} -
+
diff --git a/packages/svelte/tests/css/samples/general-siblings-combinator-if-not-exhaustive/input.svelte b/packages/svelte/tests/css/samples/general-siblings-combinator-if-not-exhaustive/input.svelte index e5703a09fdfd..50de4745b63e 100644 --- a/packages/svelte/tests/css/samples/general-siblings-combinator-if-not-exhaustive/input.svelte +++ b/packages/svelte/tests/css/samples/general-siblings-combinator-if-not-exhaustive/input.svelte @@ -14,12 +14,12 @@ .b ~ .c { color: green; } -
+
{#if foo} -
+
{:else if bar} -
+
{/if} -
+
diff --git a/packages/svelte/tests/css/samples/general-siblings-combinator-if/input.svelte b/packages/svelte/tests/css/samples/general-siblings-combinator-if/input.svelte index fca5499f2e64..d89c9de74174 100644 --- a/packages/svelte/tests/css/samples/general-siblings-combinator-if/input.svelte +++ b/packages/svelte/tests/css/samples/general-siblings-combinator-if/input.svelte @@ -18,14 +18,14 @@ .c ~ .d { color: green; } -
+
{#if foo} -
+
{:else if bar} -
+
{:else} -
+
{/if} -
\ No newline at end of file +
diff --git a/packages/svelte/tests/css/samples/general-siblings-combinator-slot/input.svelte b/packages/svelte/tests/css/samples/general-siblings-combinator-slot/input.svelte index 93b2cd73a751..afb3b5226ca4 100644 --- a/packages/svelte/tests/css/samples/general-siblings-combinator-slot/input.svelte +++ b/packages/svelte/tests/css/samples/general-siblings-combinator-slot/input.svelte @@ -15,16 +15,16 @@ .b ~ .g { color: green; } -
+
-
+
-
-
+
+
-
+
-
+
diff --git a/packages/svelte/tests/css/samples/general-siblings-combinator-star/input.svelte b/packages/svelte/tests/css/samples/general-siblings-combinator-star/input.svelte index a069685d4f1e..39698972eab2 100644 --- a/packages/svelte/tests/css/samples/general-siblings-combinator-star/input.svelte +++ b/packages/svelte/tests/css/samples/general-siblings-combinator-star/input.svelte @@ -8,10 +8,10 @@
-
+
-
-
-
\ No newline at end of file +
+
+
diff --git a/packages/svelte/tests/css/samples/general-siblings-combinator/input.svelte b/packages/svelte/tests/css/samples/general-siblings-combinator/input.svelte index 533702a3a317..c526151ce2a7 100644 --- a/packages/svelte/tests/css/samples/general-siblings-combinator/input.svelte +++ b/packages/svelte/tests/css/samples/general-siblings-combinator/input.svelte @@ -16,9 +16,9 @@
- - + +

-
\ No newline at end of file +
diff --git a/packages/svelte/tests/css/samples/global-with-child-combinator-2/input.svelte b/packages/svelte/tests/css/samples/global-with-child-combinator-2/input.svelte index 146f302633cd..c0f2f6967890 100644 --- a/packages/svelte/tests/css/samples/global-with-child-combinator-2/input.svelte +++ b/packages/svelte/tests/css/samples/global-with-child-combinator-2/input.svelte @@ -5,5 +5,5 @@
-
-
\ No newline at end of file +
+
diff --git a/packages/svelte/tests/css/samples/global-with-child-combinator/input.svelte b/packages/svelte/tests/css/samples/global-with-child-combinator/input.svelte index 3b5ff160c394..35b8774e640f 100644 --- a/packages/svelte/tests/css/samples/global-with-child-combinator/input.svelte +++ b/packages/svelte/tests/css/samples/global-with-child-combinator/input.svelte @@ -5,5 +5,5 @@
-
-
\ No newline at end of file +
+
diff --git a/packages/svelte/tests/css/samples/quote-mark-inside-string/input.svelte b/packages/svelte/tests/css/samples/quote-mark-inside-string/input.svelte index 052d5949bcab..e3355cd24668 100644 --- a/packages/svelte/tests/css/samples/quote-mark-inside-string/input.svelte +++ b/packages/svelte/tests/css/samples/quote-mark-inside-string/input.svelte @@ -1,4 +1,4 @@ -
+
-
+
{#await promise then value} -
+
{:catch error} -
+
{/await} {#await promise} -
+
{:catch error} -
+
{/await} {#await promise} -
+
{:then error} -
+
{/await} -
+
diff --git a/packages/svelte/tests/css/samples/siblings-combinator-await/input.svelte b/packages/svelte/tests/css/samples/siblings-combinator-await/input.svelte index 07698c2a3000..eab88894acb2 100644 --- a/packages/svelte/tests/css/samples/siblings-combinator-await/input.svelte +++ b/packages/svelte/tests/css/samples/siblings-combinator-await/input.svelte @@ -17,14 +17,14 @@ .b + .d { color: green; } -
+
{#await promise} -
+
{:then value} -
+
{:catch error} -
+
{/await} -
\ No newline at end of file +
diff --git a/packages/svelte/tests/css/samples/siblings-combinator-each-2/input.svelte b/packages/svelte/tests/css/samples/siblings-combinator-each-2/input.svelte index bbad045fbc6d..11876e5baca5 100644 --- a/packages/svelte/tests/css/samples/siblings-combinator-each-2/input.svelte +++ b/packages/svelte/tests/css/samples/siblings-combinator-each-2/input.svelte @@ -28,11 +28,11 @@ } -
+
{#each array as item} -
-
+
+
{/each} -
\ No newline at end of file +
diff --git a/packages/svelte/tests/css/samples/siblings-combinator-each-else-nested/input.svelte b/packages/svelte/tests/css/samples/siblings-combinator-each-else-nested/input.svelte index d44a53c8fcbf..94da7d87aca0 100644 --- a/packages/svelte/tests/css/samples/siblings-combinator-each-else-nested/input.svelte +++ b/packages/svelte/tests/css/samples/siblings-combinator-each-else-nested/input.svelte @@ -32,43 +32,43 @@ .g + .h + .i + .j { color: green; } -
+
{#each array as a} -
+
{#each array as b} -
+
{:else} -
+
{/each} {/each} {#each array as c} {#each array as d} -
+
{/each} {:else} -
+
{/each} {#each array as item} -
+
{#each array as item} {#each array as item} -
+
{/each} {:else} -
+
{/each} -
+
{/each} -
+
{#each array as item} {#each array as item} -
+
{:else} -
+
{/each} {/each} diff --git a/packages/svelte/tests/css/samples/siblings-combinator-each-else/input.svelte b/packages/svelte/tests/css/samples/siblings-combinator-each-else/input.svelte index 04d194546102..5ea5bb0f3732 100644 --- a/packages/svelte/tests/css/samples/siblings-combinator-each-else/input.svelte +++ b/packages/svelte/tests/css/samples/siblings-combinator-each-else/input.svelte @@ -13,12 +13,12 @@ .b + .c { color: green; } -
+
{#each array as item} -
+
{:else} -
+
{/each} -
+
diff --git a/packages/svelte/tests/css/samples/siblings-combinator-each-nested/input.svelte b/packages/svelte/tests/css/samples/siblings-combinator-each-nested/input.svelte index b5b92426750c..fcc9bbd2e886 100644 --- a/packages/svelte/tests/css/samples/siblings-combinator-each-nested/input.svelte +++ b/packages/svelte/tests/css/samples/siblings-combinator-each-nested/input.svelte @@ -65,39 +65,39 @@ .g + .i { color: green; } -
+
{#each array as item} -
-
+
+
{/each} {#each array as item} {#each array as item} {#each array as item} -
+
{/each} -
+
{/each} -
+
{/each} {#each array as item} -
+
{#each array as item} -
+
{#each array as item} -
+
{/each} {/each} {/each} {#each array as item} -
+
{#each array as item} -
+
{#each array as item} -
+
{/each} {/each} {/each} @@ -105,9 +105,9 @@ {#each array as item} {#each array as item} {#each array as item} -
+
{/each} -
+
{/each} -
-{/each} \ No newline at end of file +
+{/each} diff --git a/packages/svelte/tests/css/samples/siblings-combinator-each/input.svelte b/packages/svelte/tests/css/samples/siblings-combinator-each/input.svelte index de77a2518006..e28cb5ef2de6 100644 --- a/packages/svelte/tests/css/samples/siblings-combinator-each/input.svelte +++ b/packages/svelte/tests/css/samples/siblings-combinator-each/input.svelte @@ -8,13 +8,13 @@ } -
+
{#each array as item} - -
- -
+ +
+ +
{/each} - \ No newline at end of file + diff --git a/packages/svelte/tests/css/samples/siblings-combinator-global/input.svelte b/packages/svelte/tests/css/samples/siblings-combinator-global/input.svelte index ec06f3c0156c..5838977b1399 100644 --- a/packages/svelte/tests/css/samples/siblings-combinator-global/input.svelte +++ b/packages/svelte/tests/css/samples/siblings-combinator-global/input.svelte @@ -17,5 +17,5 @@
{#each [] as _} -

-{/each} \ No newline at end of file +

+{/each} diff --git a/packages/svelte/tests/css/samples/siblings-combinator-if-not-exhaustive-with-each/input.svelte b/packages/svelte/tests/css/samples/siblings-combinator-if-not-exhaustive-with-each/input.svelte index 71009d0286c7..2a0d31710381 100644 --- a/packages/svelte/tests/css/samples/siblings-combinator-if-not-exhaustive-with-each/input.svelte +++ b/packages/svelte/tests/css/samples/siblings-combinator-if-not-exhaustive-with-each/input.svelte @@ -18,14 +18,14 @@ .b + .c { color: green; } -
+
{#if foo} -
+
{:else} {#each array as item} -
+
{/each} {/if} -
+
diff --git a/packages/svelte/tests/css/samples/siblings-combinator-if-not-exhaustive/input.svelte b/packages/svelte/tests/css/samples/siblings-combinator-if-not-exhaustive/input.svelte index 41901d285ec5..552881f8695b 100644 --- a/packages/svelte/tests/css/samples/siblings-combinator-if-not-exhaustive/input.svelte +++ b/packages/svelte/tests/css/samples/siblings-combinator-if-not-exhaustive/input.svelte @@ -14,12 +14,12 @@ .b + .c { color: green; } -
+
{#if foo} -
+
{:else if bar} -
+
{/if} -
+
diff --git a/packages/svelte/tests/css/samples/siblings-combinator-if/input.svelte b/packages/svelte/tests/css/samples/siblings-combinator-if/input.svelte index 6cfc436876a4..be4c3932299b 100644 --- a/packages/svelte/tests/css/samples/siblings-combinator-if/input.svelte +++ b/packages/svelte/tests/css/samples/siblings-combinator-if/input.svelte @@ -18,14 +18,14 @@ .c + .d { color: green; } -
+
{#if foo} -
+
{:else if bar} -
+
{:else} -
+
{/if} -
\ No newline at end of file +
diff --git a/packages/svelte/tests/css/samples/siblings-combinator-slot/input.svelte b/packages/svelte/tests/css/samples/siblings-combinator-slot/input.svelte index 6e0df3f4979b..630bc2fe9704 100644 --- a/packages/svelte/tests/css/samples/siblings-combinator-slot/input.svelte +++ b/packages/svelte/tests/css/samples/siblings-combinator-slot/input.svelte @@ -11,14 +11,14 @@ .c + .f { color: green; } -
+
-
+
-
-
+
+
-
+
diff --git a/packages/svelte/tests/css/samples/siblings-combinator-star/input.svelte b/packages/svelte/tests/css/samples/siblings-combinator-star/input.svelte index ca837f223958..5bcc38f1bf87 100644 --- a/packages/svelte/tests/css/samples/siblings-combinator-star/input.svelte +++ b/packages/svelte/tests/css/samples/siblings-combinator-star/input.svelte @@ -8,10 +8,10 @@
-
+
-
-
-
\ No newline at end of file +
+
+
diff --git a/packages/svelte/tests/css/samples/siblings-combinator/input.svelte b/packages/svelte/tests/css/samples/siblings-combinator/input.svelte index 3e22076d52fa..87682163acd7 100644 --- a/packages/svelte/tests/css/samples/siblings-combinator/input.svelte +++ b/packages/svelte/tests/css/samples/siblings-combinator/input.svelte @@ -29,7 +29,7 @@
- - + +
-
\ No newline at end of file +
diff --git a/packages/svelte/tests/parser-legacy/samples/attribute-shorthand/input.svelte b/packages/svelte/tests/parser-legacy/samples/attribute-shorthand/input.svelte index 35468de00697..cd76c59e4be1 100644 --- a/packages/svelte/tests/parser-legacy/samples/attribute-shorthand/input.svelte +++ b/packages/svelte/tests/parser-legacy/samples/attribute-shorthand/input.svelte @@ -1 +1 @@ -
\ No newline at end of file +
diff --git a/packages/svelte/tests/parser-legacy/samples/attribute-shorthand/output.json b/packages/svelte/tests/parser-legacy/samples/attribute-shorthand/output.json index 5daf29018c3b..2ae3acfdc7eb 100644 --- a/packages/svelte/tests/parser-legacy/samples/attribute-shorthand/output.json +++ b/packages/svelte/tests/parser-legacy/samples/attribute-shorthand/output.json @@ -2,12 +2,12 @@ "html": { "type": "Fragment", "start": 0, - "end": 11, + "end": 16, "children": [ { "type": "Element", "start": 0, - "end": 11, + "end": 16, "name": "div", "attributes": [ { diff --git a/packages/svelte/tests/runtime-browser/custom-elements-samples/escaped-css/main.svelte b/packages/svelte/tests/runtime-browser/custom-elements-samples/escaped-css/main.svelte index 4da86e90ca98..5bc8d9ad8d6b 100644 --- a/packages/svelte/tests/runtime-browser/custom-elements-samples/escaped-css/main.svelte +++ b/packages/svelte/tests/runtime-browser/custom-elements-samples/escaped-css/main.svelte @@ -1,6 +1,6 @@ - + -
\ No newline at end of file +
diff --git a/packages/svelte/tests/validator/samples/css-mismatched-quotes/input.svelte b/packages/svelte/tests/validator/samples/css-mismatched-quotes/input.svelte index 964aa62d7b03..75a4e0de598e 100644 --- a/packages/svelte/tests/validator/samples/css-mismatched-quotes/input.svelte +++ b/packages/svelte/tests/validator/samples/css-mismatched-quotes/input.svelte @@ -1,4 +1,4 @@ -
+