diff --git a/.changeset/curly-dingos-smile.md b/.changeset/curly-dingos-smile.md new file mode 100644 index 0000000000..833d1f296a --- /dev/null +++ b/.changeset/curly-dingos-smile.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: preserve CSS comments when printing an AST diff --git a/packages/svelte/src/compiler/phases/1-parse/read/style.js b/packages/svelte/src/compiler/phases/1-parse/read/style.js index 160e5da277..1fd12d474b 100644 --- a/packages/svelte/src/compiler/phases/1-parse/read/style.js +++ b/packages/svelte/src/compiler/phases/1-parse/read/style.js @@ -48,13 +48,17 @@ export default function read_style(parser, start, attributes) { /** * @param {Parser} parser * @param {(parser: Parser) => boolean} finished - * @returns {Array} + * @returns {Array} */ function read_body(parser, finished) { - /** @type {Array} */ + /** @type {Array} */ const children = []; - while ((allow_comment_or_whitespace(parser), !finished(parser))) { + while (parser.index < parser.template.length) { + children.push(...read_comments_and_whitespace(parser)); + + if (finished(parser)) break; + if (parser.match('@')) { children.push(read_at_rule(parser)); } else { @@ -93,7 +97,8 @@ function read_at_rule(parser) { start, end: parser.index, name, - prelude, + prelude: prelude.value, + ...(prelude.raw && { raw: prelude.raw }), block }; } @@ -126,10 +131,10 @@ function read_rule(parser) { * @returns {AST.CSS.SelectorList} */ function read_selector_list(parser, inside_pseudo_class = false) { - /** @type {AST.CSS.ComplexSelector[]} */ + /** @type {Array} */ const children = []; - allow_comment_or_whitespace(parser); + children.push(...read_comments_and_whitespace(parser)); const start = parser.index; @@ -138,7 +143,7 @@ function read_selector_list(parser, inside_pseudo_class = false) { const end = parser.index; - allow_comment_or_whitespace(parser); + children.push(...read_comments_and_whitespace(parser)); if (inside_pseudo_class ? parser.match(')') : parser.match('{')) { return { @@ -149,7 +154,7 @@ function read_selector_list(parser, inside_pseudo_class = false) { }; } else { parser.eat(',', true); - allow_comment_or_whitespace(parser); + children.push(...read_comments_and_whitespace(parser)); } } @@ -323,7 +328,7 @@ function read_selector(parser, inside_pseudo_class = false) { } const index = parser.index; - allow_comment_or_whitespace(parser); + read_comments_and_whitespace(parser); if (parser.match(',') || (inside_pseudo_class ? parser.match(')') : parser.match('{'))) { // rewind, so we know whether to continue building the selector list @@ -412,11 +417,11 @@ function read_block(parser) { parser.eat('{', true); - /** @type {Array} */ + /** @type {Array} */ const children = []; while (parser.index < parser.template.length) { - allow_comment_or_whitespace(parser); + children.push(...read_comments_and_whitespace(parser)); if (parser.match('}')) { break; @@ -471,7 +476,7 @@ function read_declaration(parser) { const value = read_value(parser); - if (!value && !property.startsWith('--')) { + if (!value.value && !property.startsWith('--')) { e.css_empty_declaration({ start, end: index }); } @@ -486,16 +491,19 @@ function read_declaration(parser) { start, end, property, - value + value: value.value, + ...(value.raw && { raw: value.raw }) }; } /** * @param {Parser} parser - * @returns {string} + * @returns {{ value: string; raw: string | null }} */ function read_value(parser) { + const start = parser.index; let value = ''; + let has_comment = false; let escaped = false; let in_url = false; @@ -523,13 +531,19 @@ function read_value(parser) { } else if (char === '(' && value.slice(-3) === 'url') { in_url = true; } else if ((char === ';' || char === '{' || char === '}') && !in_url && !quote_mark) { - return value.trim(); + const normalized = value.trim(); + + return { + value: normalized, + raw: has_comment ? parser.template.slice(start, parser.index).trim() : null + }; } else if ( char === '/' && !in_url && !quote_mark && parser.template[parser.index + 1] === '*' ) { + has_comment = true; parser.index += 2; while (parser.index < parser.template.length) { if (parser.template[parser.index] === '*' && parser.template[parser.index + 1] === '/') { @@ -624,13 +638,26 @@ function read_identifier(parser) { return identifier; } -/** @param {Parser} parser */ -function allow_comment_or_whitespace(parser) { +/** + * @param {Parser} parser + * @returns {AST.CSS.CSSComment[]} + */ +function read_comments_and_whitespace(parser) { + /** @type {AST.CSS.CSSComment[]} */ + const comments = []; + parser.allow_whitespace(); while (parser.match('/*') || parser.match('