Skip to content

fix: strip // comments from unknown at-rule preludes - #4544

Open
dchaudhari7177 wants to merge 2 commits into
less:masterfrom
dchaudhari7177:fix/3527-supports-line-comments
Open

dchaudhari7177 wants to merge 2 commits into
less:masterfrom
dchaudhari7177:fix/3527-supports-line-comments

Conversation

@dchaudhari7177

@dchaudhari7177 dchaudhari7177 commented Sep 20, 2026 •

Copy link
Copy Markdown

Closes #3527.

An unknown at-rule's prelude is scanned as raw text by
parserInput.$parseUntil, which has a case for /* */ but none for //. So
a line comment inside @supports selector(...) was copied straight into the
CSS:

@supports selector(
  :focus-visible // *"a"
) { a { color: red; } }
@supports selector(
    :focus-visible // *"a") {   /* comment leaked, and ate the newline */

The " inside the comment also got picked up by the quote handling as a real
string, which is what mangles the rest of the line.

Telling a comment from a URL

The obvious fix — skip from // to end of line — breaks URLs, and that
turned out to be a live concern:

@document url-prefix(http://example.com/) { ... }
a { background: url(//cdn.example.com/x.png); }   /* protocol-relative */

So ( cannot be treated as a comment-start context either.

So a // is not a comment in two places, and is one everywhere else in the prelude:

  • Inside a URL function (url(, url-prefix(, domain(). The value parser turns comment absorption off inside url() too, so this matches how Less already reads the same text in a declaration. It covers url(//cdn…) and url( //cdn…).
  • Right after a :, which is a scheme, as in a bare http://host.

(An earlier revision required whitespace before the //. The review bot pointed out two holes in that: url( //cdn…) lost its address, and (display: flex)// comment leaked. Both are now fixture cases.)

Scoped to at-rule preludes

My first attempt applied this to every permissive value, and the existing
permissive-parse fixture caught it:

--this: () => {
  basically anything until final semi-colon;
  even other stuff; // i\'m serious;
};

That is a custom property, whose value is preserved verbatim — the //
there is content, not a comment. So the behaviour is gated behind a new
stripLineComments flag that only atruleUnknown passes.

Verification

New fixture tests-unit/at-rules-unknown-line-comments covers the issue's
case, a trailing comment before the block, http:// in both @supports and
@document url-prefix, a protocol-relative url(//…) with and without a space after (, a // with no space before it, and a /* */ comment
that must survive.

grunt test:node — exit 0, zero failures.

One thing I deliberately did not change: the issue's expected output shows
@supports selector(:focus-visible) with the whitespace collapsed. Less
preserves prelude whitespace for unknown at-rules even when no comment is
involved, so the remaining newline is existing behaviour and not part of this
bug. Output here is @supports selector(\n :focus-visible ) {.

Summary by CodeRabbit

  • Bug Fixes

    • Fixed parsing of line comments within unknown at-rule values so comments no longer appear in generated CSS or consume the remainder of a line.
    • Preserved valid URL syntax, including http:// and protocol-relative // URLs.
    • Continued preserving CSS block comments within at-rule preludes.
  • Tests

    • Added coverage for comments, URLs, and varied unknown at-rule formatting.

An unknown at-rule's prelude is scanned as raw text by $parseUntil, which
handled /* */ but had no case for //. A line comment inside `@supports
selector(...)` was therefore copied into the CSS, taking the rest of the line
with it -- and any quote inside the comment was picked up as a real string,
mangling the output further.

Skip from // to the end of the line, but only where whitespace precedes it.
That is what separates a comment from a URL: the scanner absorbs comments
while skipping whitespace, which is why `http://host` and a protocol-relative
`url(//host/x.png)` are not comments, and both must keep working.

Gate it on a new stripLineComments flag rather than applying it to every
permissive value. A custom property's value is preserved verbatim, so a //
inside `--this: () => { ... }` is content, not a comment.

Closes less#3527
@coderabbitai

coderabbitai Bot commented Sep 20, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: less/less.js/.coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: e3a7940a-8dd0-4ea5-892d-40c026d9835a

📥 Commits

Reviewing files that changed from the base of the PR and between 4cc5680 and 3c7bf86.

📒 Files selected for processing (3)
  • packages/less/lib/less/parser/parser-input.js
  • packages/test-data/tests-unit/at-rules-unknown-line-comments/at-rules-unknown-line-comments.css
  • packages/test-data/tests-unit/at-rules-unknown-line-comments/at-rules-unknown-line-comments.less

Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review.


📝 Walkthrough

Walkthrough

Unknown at-rule parsing now strips recognized // line comments when scanning values. The scanner tracks URL-function parentheses and does not treat // inside them or immediately after a colon as a comment. Added fixtures cover comment, URL, and block-comment cases.

Changes

Unknown at-rule line comment handling

Layer / File(s) Summary
Line-comment scanning support
packages/less/lib/less/parser/parser-input.js
$parseUntil accepts stripLineComments, tracks URL-function parentheses, and skips recognized line comments when enabled.
Unknown at-rule integration
packages/less/lib/less/parser/parser.js
permissiveValue forwards the option, and unknown at-rule parsing enables line-comment stripping.
Parser behavior fixtures
packages/test-data/tests-unit/at-rules-unknown-line-comments/*
Fixtures cover line comments, absolute and protocol-relative URLs, block comments, and unknown at-rule output.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Merge Risk: ⚪ Minimal · up to 3c7bf

Unknown at-rule line comments are removed while supported URL forms and block comments remain preserved. The change is ready to merge.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Issue #3527 requires removal of // comments from unknown @supports selector(...) preludes. At the reviewed head, $parseUntil supports stripLineComments, and unknown at-rule parsing enables it.…
Out of Scope Changes check ✅ Passed The changes remain within issue #3527. The parser flag limits line-comment removal to callers that request it, and unknown at-rule preludes are the stated target. URL and block-comment cases test boun…
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 2…
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: stripping // comments from unknown at-rule preludes.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@greptile-apps

greptile-apps Bot commented Sep 20, 2026

Copy link
Copy Markdown

RetriggerConfidence Score: 4/5

This PR is not yet safe to merge because the new discriminator can corrupt valid unknown at-rule preludes and leaves some ordinary line comments unstripped.

Summary

This PR adds opt-in stripping of Less line comments while scanning unknown at-rule preludes and introduces a fixture covering comments and common URL forms. The new whitespace-based discriminator is ambiguous, however: it strips valid whitespace-prefixed protocol-relative URLs while missing adjacent Less comments.

  • Threads a stripLineComments option through permissiveValue and enables it for unknown at-rules.
  • Removes matching comment text while preserving the terminating newline.
  • Adds fixture coverage for comments, absolute URLs, protocol-relative URLs, and CSS block comments.

Reviews (1) · Last reviewed commit: "fix: strip `//` comments from unknown at..."

Comment on lines +281 to +283
} else if (stripLineComments &&
input.charAt(i + 1) === '/' &&
/\s/.test(input.charAt(i - 1))) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Whitespace Misclassifies URLs

The preceding-whitespace check cannot reliably distinguish comments from URLs. For example, a valid protocol-relative URL with whitespace after url(, such as @supports (background: url( //cdn.example/x)), meets this condition, so the scanner removes the URL and the rest of the line, including its closing delimiters. Conversely, a Less comment directly adjacent to prelude content, such as @supports (display: grid)// comment, fails the condition and remains in the generated CSS. Unknown at-rule preludes are therefore corrupted for both realistic forms.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both cases were real, thanks. Fixed in 3c7bf86. The whitespace test is gone. A // is now left alone only inside url(, url-prefix( or domain( (the value parser also stops absorbing comments there), or right after a : (a scheme). url( //cdn.example.com/y.png) keeps its address, and (display: flex)// comment is stripped. Both are in the fixture now, and the previous parser fails them. grunt test:node passes.

Requiring whitespace before `//` kept `url( //host/x.png)` wrong (the
URL was stripped) and let `(display: flex)// comment` through. Track
whether the scanner is inside `url(`, `url-prefix(` or `domain(`,
where the value parser also stops absorbing comments, and treat a `//`
after a `:` as a scheme. Every other `//` in an unknown at-rule
prelude is a comment.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Line comments aren't stripped from @supports selector(...)

1 participant