Do not terminate a hack-prefixed property before a comment - #2126
Merged
ai merged 2 commits intoAug 6, 2026
Conversation
A declaration is read as a custom property when the *first token* of the
declaration starts with `--`, but the stringifier decided it from `prop`.
The `*`/`_` hack prefix is moved out of `prop` into `raws.before` after that
decision, so for `*--x:red` the parser builds a normal declaration -- whose
value stops at the first comment -- while the stringifier saw a custom
property and terminated it:
postcss.parse('a{*--x:red/*c*/}').toString()
// => 'a{*--x:red;/*c*/}', semicolon invented
The same applies to any declaration with something other than spaces in
`before`. Check `before` alongside `prop` so the two stay in sync: only a
declaration that will re-parse as a custom property can swallow a following
comment, and only that one needs the semicolon.
ai
reviewed
Aug 5, 2026
| // is moved from `prop` into `before` after that decision, so `*--x` is a normal | ||
| // declaration that stops at the first comment. Re-checking `before` here keeps | ||
| // this in sync: with anything but spaces in front of the property, the output | ||
| // will not re-parse as a custom property and needs no terminating semicolon. |
Member
There was a problem hiding this comment.
Nice hack. Just ask LLM to be very minimalistic in comment.
Nowadays be smart is mean use less words.
Member
|
Thanks |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
PostCSS aims for byte-to-byte equal output, but since 8.5.22 the stringifier invents a semicolon in a declaration list:
_--xbehaves the same. The output stays valid CSS and re-parses to the same AST, so nothing breaks downstream — but a tool that only means to touch what it edits now emits an unrelated;.Why
The parser decides whether a declaration is a custom property from the first token of the declaration:
#2117re-derived that decision in the stringifier fromprop:Those two agree almost always, but
decl()strips the*/_hack prefix out ofpropand intoraws.beforeafter the parser has made its choice:So
*--x:redis parsed as a normal declaration — its value stops at the first comment, and the comment is already a separate node — while the stringifier seesprop === '--x'and terminates it. The semicolon#2117adds is there to stop a custom property's value from swallowing a following comment; here there is nothing to protect, because*--x:redwill not re-parse as a custom property either. The same mismatch happens whenever anything but spaces precedes the property, sincedecl()collects those tokens intobeforetoo (a{'s'--x:1/*c*/}).Fix
Check
beforealongsideprop, so the stringifier asks the same question the parser did — will this re-parse as a custom property? — and terminates only the declarations that need it.Tests
keeps hack-prefixed property before a comment unchanged—*--x/_--xround-trip byte-for-byte, and still re-parse todecl,comment.terminates indented custom property followed by a comment— a whitespace-onlybeforeis still a custom property and still gets its semicolon, so the narrowing can't be over-read asbefore === ''.The two tests from
#2115/#2117pass unchanged.pnpm unitis 683/683, types and size-limit pass, and lint is clean apart from the pre-existingRootExitsort warning onmain.I also re-ran a round-trip check over ~320k generated stylesheets (
parse(css).toString() === css, plus AST-shape stability across a re-parse) at both root level and nested in a rule/at-rule: every failure it reported was this one cause, and after the change it reports none.