fix: align CSS escape consumption with the spec#1791
Conversation
The selector tokenizer and `funescape` diverged from CSS Syntax Level 3's "consume an escaped code point" in four ways: - Uppercase hex digits in an identifier (e.g. `#hel\6C o`) were mis-tokenized: `reName` lacked the `i` flag, so `\6C` only consumed `\6`, leaving `C` as a name char and the following space as a combinator. Added `i` to `reName` (`reEscape` already had it). - A `CRLF` after a hex escape was only half-consumed: `\s?` ate one of the two characters. Replaced with `(?:\r\n|\s)?` in both regexes so a CRLF pair is consumed as a single whitespace. - A trailing backslash at EOF (an empty escape) now resolves to U+FFFD instead of being dropped/throwing. Added `|$` to both regexes and an `escaped === ""` guard in `funescape`. - Surrogate code points (U+D800–U+DFFF) now resolve to U+FFFD. `fromCodePoint` accepts surrogate values and returns lone surrogates, so they previously leaked through. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughThis PR strengthens CSS selector parsing by refining escape sequence recognition and unescaping logic. Escape regex patterns are updated to handle broader forms, and the unescaping function now explicitly rejects surrogate code points and empty escapes at end-of-input, mapping them to U+FFFD. Four test cases validate these edge cases. ChangesCSS Escape Sequence Parsing
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
src/__fixtures__/tests.tsESLint skipped: missing config or dependency (missing-dependency). The ESLint configuration references a package that is not available in the sandbox. src/parse.tsESLint skipped: the ESLint configuration for this file references a package that is not available in the sandbox. 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. Comment |
Summary
funescapeand the identifier tokenizer (reName/reEscape) diverged from CSS Syntax Level 3 — "consume an escaped code point" in four ways. Each is fixed here, with regression tests.1. Uppercase hex digits in identifiers
reNamelacked theiflag, so a hex escape with uppercase letters was under-consumed. For example#hel\6C o:\6Conly matched\6(sinceCisn't in[\da-f]withouti), leavingCas a name char and the trailing space as a descendant combinator → parsed as idhell+ descendanto.i,\6Cis consumed as a two-digit hex escape and the following space is consumed as the escape's trailing whitespace → idhello, as intended.reEscapealready had theiflag, so this only needed to be mirrored onreName.2. CRLF after a hex escape
The spec consumes a single whitespace after a hex escape, and treats a
CRLFpair as one whitespace.\s?consumed only one of the two characters, leaving a stray\r/\nin the identifier. Replaced with(?:\r\n|\s)?in both regexes.3. Trailing backslash at EOF
A backslash at EOF is a parse error that yields U+FFFD per spec. Previously it was dropped (or threw, depending on the call path). Added a
|$alternative to both regexes and anescaped === ""guard infunescape.4. Surrogate code points → U+FFFD
The spec maps U+0000, surrogates, and out-of-range values to U+FFFD. The existing check handled U+0000 and
> 0x10FFFF, butString.fromCodePointaccepts surrogate values and returns lone surrogates, so escapes like\D800leaked through. Added the surrogate range to the check.Tests
Added fixtures in
src/__fixtures__/tests.tscovering each case (uppercase hex, CRLF consumption, trailing-backslash → U+FFFD, surrogate → U+FFFD). All existing tests continue to pass.🤖 Generated with Claude Code
Summary by CodeRabbit
Tests
Bug Fixes