Skip to content

fix: escape all special characters in identifiers when stringifying#1802

Open
spokodev wants to merge 3 commits into
fb55:masterfrom
spokodev:fix/stringify-escape-special-chars
Open

fix: escape all special characters in identifiers when stringifying#1802
spokodev wants to merge 3 commits into
fb55:masterfrom
spokodev:fix/stringify-escape-special-chars

Conversation

@spokodev

@spokodev spokodev commented Jun 17, 2026

Copy link
Copy Markdown

Bug

charsToEscapeInName is missing several characters that are special in selector syntax, so stringify() produces selectors that re-parse to a different AST (or fail to parse). parse(stringify(x)) is no longer equal to x:

import { parse, stringify } from "css-what";

stringify(parse(String.raw`.x\&y`));         // ".x&y"        -> parses as 2 selectors (`&` nesting)
stringify(parse(String.raw`[data-foo\=bar]`)); // "[data-foo=bar]" -> name `data-foo` + operator `=`
stringify(parse(String.raw`.a\{b\}c`));      // ".a{b}c"      -> unparseable

This breaks round-tripping of modern class names that contain these characters — e.g. Tailwind arbitrary values and CSS-nesting fragments (grid-cols-[&], data-[x=y], …).

Fix

Add =, &, ?, @, `, { and } to charsToEscapeInName, so every special ASCII character in an identifier is escaped — completing the set alongside the # > < / , ; etc. that are already handled. Escaping is transparent to parse, so existing round-trips are unaffected; only previously-corrupted ones are fixed.

Tests

Added cases to the "Stringify CSS spec compliance" block in stringify.spec.ts (a=ba\=b, &, {}, ?, @). They fail on main (the characters are emitted unescaped) and pass with this change. Full suite: 860 passing, no regressions.

Same class as #1056 (which added %); distinct from #1791, which fixes escape consumption on the parse side — this is escape production on the stringify side.

Summary by CodeRabbit

  • Bug Fixes

    • Improved CSS selector stringification/escaping for identifiers containing =, &, ?, @, backticks, {, and } to generate valid selector output.
  • Tests

    • Expanded test coverage for these special-character escaping cases.
    • Added round-trip verification to ensure selectors containing escaped characters preserve the same selector AST across parse and stringify.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Credits must be used to enable repository wide code reviews.

@coderabbitai

coderabbitai Bot commented Jun 17, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 1186f12c-6f02-4ef3-ba9b-14efbd751eb0

📥 Commits

Reviewing files that changed from the base of the PR and between b4e3f20 and a1e1321.

📒 Files selected for processing (1)
  • src/stringify.spec.ts

📝 Walkthrough

Walkthrough

stringify.ts now escapes seven additional special characters in selector names. stringify.spec.ts adds direct escape-output cases and a round-trip test suite covering selectors with those escaped characters.

Changes

Escape character expansion

Layer / File(s) Summary
Expand escape character set and test outputs
src/stringify.ts, src/stringify.spec.ts
Adds =, &, ?, @, `, {, and } to the escape set in escapeName, and extends escapeCases with expected escaped selector strings for those characters.
Round-trip validation tests
src/stringify.spec.ts
Adds a parse/stringify/parse test block for selectors containing escaped special characters and compares the resulting AST to the original parse.

Estimated code review effort: 1 (Trivial) | ~3 minutes

Poem

🐇 I hopped through braces, ticks, and gleaming signs,
@ and ? now nest in tidy lines.
An ampersand and equals took a bow today—
The selector trail is safely tucked away.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: expanding escaping for special characters during selector stringification.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/stringify.spec.ts`:
- Around line 31-35: The test data array starting at line 31 is missing coverage
for the backtick character and specific round-trip selector contexts. Add test
cases to this array for the backtick escape (backtick paired with its escaped
equivalent string), and add three additional test cases for the exact round-trip
selector contexts mentioned in the PR: the class selector with escaped ampersand
(`.x\&y`), the data attribute selector with escaped equals (` [data-foo\=bar]`),
and the class selector with escaped braces (`.a\{b\}c`). Each test case should
follow the existing pattern of providing the input string, the expected escaped
output using String.raw, and a descriptive label.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 902c7b00-7785-47ad-87c4-7401897ac6e0

📥 Commits

Reviewing files that changed from the base of the PR and between 2d552b9 and af3afa9.

📒 Files selected for processing (2)
  • src/stringify.spec.ts
  • src/stringify.ts

Comment thread src/stringify.spec.ts
@spokodev
spokodev force-pushed the fix/stringify-escape-special-chars branch from c24aff0 to f6438f1 Compare July 1, 2026 16:43

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/stringify.spec.ts`:
- Around line 62-66: The `it.each(selectors)` test currently nests
`parse(stringify(parse(selector)))` too deeply and violates the
`unicorn/max-nested-calls` rule. In `stringify.spec.ts`, split the chained
`parse`/`stringify`/`parse` flow into intermediate variables inside the test
callback, then compare the final parsed value against the original
`parse(selector)` result to keep the assertion behavior unchanged while reducing
nesting.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 403712ba-b24e-454c-8eae-7d600752d7c3

📥 Commits

Reviewing files that changed from the base of the PR and between c24aff0 and f6438f1.

📒 Files selected for processing (2)
  • src/stringify.spec.ts
  • src/stringify.ts

Comment thread src/stringify.spec.ts
spokodev added 2 commits July 2, 2026 16:50
`charsToEscapeInName` was missing several characters that are special in
selector syntax, so `stringify()` emitted selectors that re-parse to a
different AST or fail to parse. This breaks round-tripping of modern
class names (arbitrary values, CSS nesting), e.g.:

  stringify(parse(".x\\&y"))        // => ".x&y"      (two selectors)
  stringify(parse("[data-foo\\=x]")) // => "[data-foo=x]" (name+operator)
  stringify(parse(".a\\{b\\}c"))    // => ".a{b}c"

Add `=`, `&`, `?`, `@`, `` ` ``, `{` and `}` so every special ASCII
character in an identifier is escaped, matching the existing handling of
`# > < / , ;` etc.
@spokodev
spokodev force-pushed the fix/stringify-escape-special-chars branch from f6438f1 to b4e3f20 Compare July 2, 2026 18:22

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/stringify.spec.ts`:
- Around line 53-67: The stringify round-trip test in Stringify round-trips
special characters in identifiers is missing coverage for the newly escaped @
character. Update the selectors array in the describe block to include an
`@-containing` selector (for example one using parse and stringify with a
data-attribute style selector), so the round-trip assertion exercises the same
code paths as the existing cases for &, =, {}, /, and ?.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: f3b18178-8cec-4b37-ac03-5657f61e1d41

📥 Commits

Reviewing files that changed from the base of the PR and between f6438f1 and b4e3f20.

📒 Files selected for processing (2)
  • src/stringify.spec.ts
  • src/stringify.ts

Comment thread src/stringify.spec.ts
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.

1 participant