From 2ebeac9dcca01cad8d33276e7def115ae4335e62 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 1 Oct 2026 08:05:17 +0000 Subject: [PATCH 1/4] Fix on_comment not firing for comments right after a declaration colon skip_whitespace_in_range skipped comments without invoking on_comment, so 'color: /* c */ green' lost its comment. Regression from #279. --- src/parse-options.test.ts | 31 +++++++++++++++++++++++++++++++ src/tokenize.ts | 17 ++--------------- 2 files changed, 33 insertions(+), 15 deletions(-) diff --git a/src/parse-options.test.ts b/src/parse-options.test.ts index a948113..7e357ac 100644 --- a/src/parse-options.test.ts +++ b/src/parse-options.test.ts @@ -375,6 +375,37 @@ describe('Parser Options', () => { expect(comments[0].column).toBe(2) }) + test('should find comments in selectors, values and at-rule blocks', () => { + const comments: Array<{ length: number }> = [] + const css = ` + /* comment 1 */ + test1, + /* comment 2 */ + test2 { + /* comment 3 */ + color: /* comment 4 */ green; + background: + red, + /* comment 5 */ + yellow + ; + } + + @media all { + /* comment 6 */ + } + ` + + parse(css, { + on_comment: (info) => { + comments.push(info) + }, + }) + + expect(comments).toHaveLength(6) + expect(comments.reduce((sum, c) => sum + c.length, 0)).toBe(90) + }) + test('should not call on_comment when no comments present', () => { let called = false const css = 'body { color: red; }' diff --git a/src/tokenize.ts b/src/tokenize.ts index 96b1a6f..559c0b2 100644 --- a/src/tokenize.ts +++ b/src/tokenize.ts @@ -1027,26 +1027,13 @@ export class Lexer { continue } - // Skip comments /*...*/ + // Skip comments /*...*/ (fires on_comment) if ( ch === CHAR_FORWARD_SLASH && this.pos + 1 < end && this.source.charCodeAt(this.pos + 1) === CHAR_ASTERISK ) { - this.advance() // skip / - this.advance() // skip * - while (this.pos < end) { - if ( - this.source.charCodeAt(this.pos) === CHAR_ASTERISK && - this.pos + 1 < end && - this.source.charCodeAt(this.pos + 1) === CHAR_FORWARD_SLASH - ) { - this.advance() // skip * - this.advance() // skip / - break - } - this.advance() - } + this._skip_comment() continue } From 2c10a7be12d41f56c6331868c64c8f727c0a554e Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 1 Oct 2026 08:06:49 +0000 Subject: [PATCH 2/4] Add on_comment test for comments in at-rule preludes --- src/parse-options.test.ts | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/parse-options.test.ts b/src/parse-options.test.ts index 7e357ac..ad02597 100644 --- a/src/parse-options.test.ts +++ b/src/parse-options.test.ts @@ -406,6 +406,35 @@ describe('Parser Options', () => { expect(comments.reduce((sum, c) => sum + c.length, 0)).toBe(90) }) + test('should find comments in at-rule preludes', () => { + const css = ` + @media /* c1 */ screen and /* c2 */ (min-width: 100px) {} + @supports /* c3 */ (display: grid) {} + @layer /* c4 */ base, /* c5 */ theme; + @import /* c6 */ url("a.css") /* c7 */ layer(base); + @container /* c8 */ sidebar /* c9 */ (min-width: 1px) {} + ` + const found: string[] = [] + + parse(css, { + on_comment: (info) => { + found.push(css.slice(info.start, info.end)) + }, + }) + + expect(found).toEqual([ + '/* c1 */', + '/* c2 */', + '/* c3 */', + '/* c4 */', + '/* c5 */', + '/* c6 */', + '/* c7 */', + '/* c8 */', + '/* c9 */', + ]) + }) + test('should not call on_comment when no comments present', () => { let called = false const css = 'body { color: red; }' From 14b75e29b955ad154234769bd6f71c3dc39878cb Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 1 Oct 2026 08:07:17 +0000 Subject: [PATCH 3/4] Use vi.fn spy in comments test --- src/parse-options.test.ts | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/parse-options.test.ts b/src/parse-options.test.ts index ad02597..95952f8 100644 --- a/src/parse-options.test.ts +++ b/src/parse-options.test.ts @@ -1,4 +1,4 @@ -import { describe, test, expect } from 'vitest' +import { describe, test, expect, vi } from 'vitest' import { parse } from './parse' import { SELECTOR_LIST, STYLE_RULE, DECLARATION, VALUE, AT_RULE, RAW } from './arena' import { PlainCSSNode } from './css-node' @@ -376,7 +376,7 @@ describe('Parser Options', () => { }) test('should find comments in selectors, values and at-rule blocks', () => { - const comments: Array<{ length: number }> = [] + const on_comment = vi.fn() const css = ` /* comment 1 */ test1, @@ -396,14 +396,11 @@ describe('Parser Options', () => { } ` - parse(css, { - on_comment: (info) => { - comments.push(info) - }, - }) + parse(css, { on_comment }) - expect(comments).toHaveLength(6) - expect(comments.reduce((sum, c) => sum + c.length, 0)).toBe(90) + expect(on_comment).toHaveBeenCalledTimes(6) + const total_length = on_comment.mock.calls.reduce((sum, [info]) => sum + info.length, 0) + expect(total_length).toBe(90) }) test('should find comments in at-rule preludes', () => { From 8abcf15cf4aa219e70a032ed5cccd11eecf71ed3 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 1 Oct 2026 08:09:21 +0000 Subject: [PATCH 4/4] Type the on_comment mock to satisfy lint --- src/parse-options.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/parse-options.test.ts b/src/parse-options.test.ts index 95952f8..96e621a 100644 --- a/src/parse-options.test.ts +++ b/src/parse-options.test.ts @@ -2,6 +2,7 @@ import { describe, test, expect, vi } from 'vitest' import { parse } from './parse' import { SELECTOR_LIST, STYLE_RULE, DECLARATION, VALUE, AT_RULE, RAW } from './arena' import { PlainCSSNode } from './css-node' +import type { CommentInfo } from './tokenize' import type { Rule, Atrule, Declaration, CSSNode } from './node-types' describe('Parser Options', () => { @@ -376,7 +377,7 @@ describe('Parser Options', () => { }) test('should find comments in selectors, values and at-rule blocks', () => { - const on_comment = vi.fn() + const on_comment = vi.fn<(info: CommentInfo) => void>() const css = ` /* comment 1 */ test1,