fix: $ dropped from attribute names - #336
Open
theRizwan wants to merge 1 commit into
Open
Conversation
`attribute()` handled `$` in the value position, and treated it as the suffix
match operator when followed by `=`, but had no branch for a `$` that belongs to
the attribute name. Those fell through to the `tokens.caret` case, which only
acts when the next token is `=`, so the character was silently discarded:
parser().astSync("[#{$attr}]").toString() // "[#{attr}]"
parser().astSync("[$attr]").toString() // "[$attr]" -> "[attr]"
Preprocessors reach this through interpolation, which is how both reports arrived.
The value position already worked, so `[lang=#{$locale}]` round-tripped while
`[#{$attr}]` did not.
The new branch mirrors the existing value-position handling and is guarded on the
next token not being `=`, which keeps the suffix match operator intact. That guard
is what allows a name and an operator to coexist:
[$attr$="x"] attribute "$attr", operator "$=", value "x"
Compared against main across 4,800 generated attribute selectors: 991 newly
round-trip, 0 regressions, and none that previously parsed now throw. A trailing
`$` such as `[a$]` also stops raising a raw TypeError as a side effect, since the
new branch returns before the unguarded lookahead is reached.
Fixes postcss#211
Fixes postcss#306
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.
Fixes #211. Fixes #306.
Both reports are the same defect seen through different syntax.
The bug
The
$is dropped when it belongs to the attribute name. The value position already worked, which is why[lang=#{$locale}]round-trips in the existing tests while[#{$attr}]does not.Cause
attribute()has a branch for$in the value position, and thetokens.caretcase immediately below turns$into the suffix match operator when the next token is=. Nothing handled a$that is part of the attribute name, so it fell through totokens.caret, failed the=check, and was discarded without being recorded anywhere.The new branch mirrors the value-position handling, and is guarded on the next token not being
=.Why the guard matters
Without it,
$=stops parsing as an operator. With it, a name and an operator can coexist:[a$=x]and[data-weird-attr$="Something=weird"]are unaffected, and both remain covered by the existing tests.Verification
Compared against
mainacross 4,800 generated attribute selectors, varying the name, namespace, operator, value, quoting, insensitivity flag and padding:TypeErrorThe
TypeErrorreduction is a side effect rather than the aim: a trailing$such as[a$]now returns before reaching the unguardednext[TOKEN.TYPE]lookahead intokens.caret. That lookahead is the subject of #334 and is not otherwise touched here.npm testpasses, includingoxlint, the type check and the coverage thresholds: 94.99% lines, 95.41% branches, 97.71% functions against gates of 94/94/96. Test count 789 to 799.Tests
Added to
nonstandard.mjs, next to the existingsass escapescases, since these arrive through preprocessor interpolation: interpolated attribute name, bare$name, interpolation on both sides, a$name combined with$=, and a namespaced$name.Not addressed
[#{$attr} i]still drops the insensitivity flag, both before and after this change. The flag survives only when an operator is present, which is a separate defect and left alone.