From 945ee19d74c06e065bb8e89dbd7084411cd3f410 Mon Sep 17 00:00:00 2001 From: eug-L Date: Sun, 29 Sep 2024 14:34:12 +0800 Subject: [PATCH] fix: allow unquoted slash while attribute value is empty --- .../compiler/phases/1-parse/state/element.js | 14 +++++-- .../samples/attribute-unquoted/input.svelte | 3 +- .../samples/attribute-unquoted/output.json | 41 ++++++++++++++++++- 3 files changed, 52 insertions(+), 6 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 d94d8180e7a9..a1b955fb3e1d 100644 --- a/packages/svelte/src/compiler/phases/1-parse/state/element.js +++ b/packages/svelte/src/compiler/phases/1-parse/state/element.js @@ -14,7 +14,8 @@ import { get_attribute_expression, is_expression_attribute } from '../../../util import { closing_tag_omitted } from '../../../../html-tree-validation.js'; import { list } from '../../../utils/string.js'; -const regex_invalid_unquoted_attribute_value = /^(\/>|[\s"'=<>`])/; +const regex_invalid_unquoted_attribute_value_or_self_closing_tag = /^(\/>|[\s"'=<>`])/; +const regex_invalid_unquoted_attribute_value = /^[\s"'=<>`]/; const regex_closing_textarea_tag = /^<\/textarea(\s[^>]*)?>/i; const regex_closing_comment = /-->/; const regex_whitespace_or_slash_or_closing_tag = /(\s|\/|>)/; @@ -633,9 +634,14 @@ function read_attribute_value(parser) { try { value = read_sequence( parser, - () => { + /** @param {number} chunk_length */ + (chunk_length) => { // handle common case of quote marks existing outside of regex for performance reasons if (quote_mark) return parser.match(quote_mark); + + if (chunk_length > 0) + return !!parser.match_regex(regex_invalid_unquoted_attribute_value_or_self_closing_tag); + return !!parser.match_regex(regex_invalid_unquoted_attribute_value); }, 'in attribute value' @@ -669,7 +675,7 @@ function read_attribute_value(parser) { /** * @param {Parser} parser - * @param {() => boolean} done + * @param {(value: number) => boolean} done * @param {string} location * @returns {any[]} */ @@ -699,7 +705,7 @@ function read_sequence(parser, done, location) { while (parser.index < parser.template.length) { const index = parser.index; - if (done()) { + if (done(current_chunk.raw.length)) { flush(parser.index); return chunks; } else if (parser.eat('{')) { diff --git a/packages/svelte/tests/parser-legacy/samples/attribute-unquoted/input.svelte b/packages/svelte/tests/parser-legacy/samples/attribute-unquoted/input.svelte index 4bab0df72f3e..1258815cb9da 100644 --- a/packages/svelte/tests/parser-legacy/samples/attribute-unquoted/input.svelte +++ b/packages/svelte/tests/parser-legacy/samples/attribute-unquoted/input.svelte @@ -1 +1,2 @@ -
\ No newline at end of file +
+home diff --git a/packages/svelte/tests/parser-legacy/samples/attribute-unquoted/output.json b/packages/svelte/tests/parser-legacy/samples/attribute-unquoted/output.json index 5df4d66ab668..c73f4d5c4d6a 100644 --- a/packages/svelte/tests/parser-legacy/samples/attribute-unquoted/output.json +++ b/packages/svelte/tests/parser-legacy/samples/attribute-unquoted/output.json @@ -2,7 +2,7 @@ "html": { "type": "Fragment", "start": 0, - "end": 21, + "end": 40, "children": [ { "type": "Element", @@ -27,6 +27,45 @@ } ], "children": [] + }, + { + "type": "Text", + "start": 21, + "end": 22, + "raw": "\n", + "data": "\n" + }, + { + "type": "Element", + "start": 22, + "end": 40, + "name": "a", + "attributes": [ + { + "type": "Attribute", + "start": 25, + "end": 31, + "name": "href", + "value": [ + { + "start": 30, + "end": 31, + "type": "Text", + "raw": "/", + "data": "/" + } + ] + } + ], + "children": [ + { + "type": "Text", + "start": 32, + "end": 36, + "raw": "home", + "data": "home" + } + ] } ] }