Skip to content

fix: escape a lone hyphen in identifier mode (CSSOM serialize)#37

Open
spokodev wants to merge 1 commit into
mathiasbynens:masterfrom
spokodev:fix/escape-lone-hyphen-identifier
Open

fix: escape a lone hyphen in identifier mode (CSSOM serialize)#37
spokodev wants to merge 1 commit into
mathiasbynens:masterfrom
spokodev:fix/escape-lone-hyphen-identifier

Conversation

@spokodev

Copy link
Copy Markdown

The bug

In identifier mode, a lone - is returned unescaped:

cssesc('-', { isIdentifier: true }); // => "-"

A bare - is not a valid CSS <ident-token>, so this output does not round-trip and produces an invalid selector when used as a class/ID body.

Why it is wrong

The CSSOM "serialize an identifier" algorithm (the one CSS.escape() implements) has an explicit rule: if the character is the first character and is a - (U+002D), and there is no second character, then escape it. So the reference output is \-:

CSS.escape('-'); // => "\\-"

The CSS Syntax Level 3 "would start an identifier" check agrees: a - only starts an identifier when the next code point is a name-start code point, another -, or a valid escape. A lone - does not start an <ident-token> at all.

Round-trip evidence with the css-tree parser:

csstree.parse('.-',  { context: 'selector' }); // Error: Identifier is expected   (cssesc output)
csstree.parse('.\\-', { context: 'selector' }); // OK -> ClassSelector             (CSSOM-correct)

The existing if (isIdentifier) block already special-cases the leading - followed by - or a digit (--foo, -0foo) to stay spec-compliant. The lone - is the one remaining case it misses.

The fix

One branch in the isIdentifier post-processing block: when the whole output is a single -, escape it to \-.

if (isIdentifier) {
	if (output == '-') {
		// A lone `-` is not a valid identifier; escape it.
		output = '\\-';
	} else if (/^-[-\d]/.test(output)) {
		...

String mode is unchanged: cssesc('-') still returns -, which is fine in a string context. Other inputs (--, -1, -a, --foo) are unchanged. Both src/cssesc.js (template source) and the built cssesc.js are updated, and npm run build reproduces the built file byte-for-byte.

Tests

Added two assertions next to the existing leading-hyphen cases: cssesc('-', { isIdentifier: false }) stays -, and cssesc('-', { isIdentifier: true }) is now \-. The new identifier assertion fails on the current code and passes with the fix; the full suite stays green.

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