Skip to content

Commit 6087705

Browse files
committed
fix: align identifier parsing with CSS standards and browser behaviour
1 parent 927cebf commit 6087705

File tree

3 files changed

+76
-3
lines changed

3 files changed

+76
-3
lines changed

src/parser.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,17 @@ export function createParser(
269269
return null;
270270
}
271271
let result = '';
272+
while (is('-')) {
273+
result += chr;
274+
next();
275+
}
276+
if (strict && result.length >= 2) {
277+
// Checking this only for strict mode since browsers work fine with these identifiers.
278+
fail('Identifiers cannot start with two hyphens with strict mode on.');
279+
}
280+
if (digitsChars[chr]) {
281+
fail('Identifiers cannot start with hyphens followed by digits.');
282+
}
272283
while (pos < l) {
273284
if (isIdent(chr)) {
274285
result += readAndNext();
@@ -281,7 +292,7 @@ export function createParser(
281292
result += readAndNext();
282293
}
283294
} else {
284-
return result;
295+
break;
285296
}
286297
}
287298
return result;

src/utils.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
11
export function isIdentStart(c: string) {
2-
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c === '-' || c === '_' || c === '\\';
2+
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c === '-' || c === '_' || c === '\\' || c >= '\u00a0';
33
}
44

55
export function isIdent(c: string) {
6-
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c === '-' || c === '_';
6+
return (
7+
(c >= 'a' && c <= 'z') ||
8+
(c >= 'A' && c <= 'Z') ||
9+
(c >= '0' && c <= '9') ||
10+
c === '-' ||
11+
c === '_' ||
12+
c >= '\u00a0'
13+
);
714
}
815

916
export function isHex(c: string) {

test/parser.test.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,61 @@ describe('parse()', () => {
55
syntax: 'progressive'
66
});
77

8+
describe('Identifiers', () => {
9+
it('should parse a regular valid identifier', () => {
10+
expect(parse('#id')).toEqual(
11+
ast.selector({
12+
rules: [
13+
ast.rule({
14+
items: [ast.id({name: 'id'})]
15+
})
16+
]
17+
})
18+
);
19+
});
20+
it('should parse an identifier starting with a hyphen', () => {
21+
expect(parse('#-id')).toEqual(
22+
ast.selector({
23+
rules: [
24+
ast.rule({
25+
items: [ast.id({name: '-id'})]
26+
})
27+
]
28+
})
29+
);
30+
});
31+
it('should fail on an identifier starting with multiple hyphens', () => {
32+
expect(() => parse('#--id')).toThrow('Identifiers cannot start with two hyphens with strict mode on.');
33+
});
34+
it('should parse an identifier starting with multiple hyphens in case of strict: false', () => {
35+
expect(createParser({strict: false})('#--id')).toEqual(
36+
ast.selector({
37+
rules: [
38+
ast.rule({
39+
items: [ast.id({name: '--id'})]
40+
})
41+
]
42+
})
43+
);
44+
});
45+
it('should fail on an identifier starting with a hyphen and followed with a digit', () => {
46+
expect(() => parse('#-1')).toThrow('Identifiers cannot start with hyphens followed by digits.');
47+
expect(() => createParser({strict: false})('#--1')).toThrow(
48+
'Identifiers cannot start with hyphens followed by digits.'
49+
);
50+
});
51+
it('should parse an identifier consisting unicode characters', () => {
52+
expect(parse('#ÈÈ')).toEqual(
53+
ast.selector({
54+
rules: [
55+
ast.rule({
56+
items: [ast.id({name: 'ÈÈ'})]
57+
})
58+
]
59+
})
60+
);
61+
});
62+
});
863
describe('Tag Names', () => {
964
it('should parse a tag name', () => {
1065
expect(parse('div')).toEqual(

0 commit comments

Comments
 (0)