Skip to content

Commit 4875cd6

Browse files
authored
fix: correctly parse escaped unicode characters in css selector (#15976)
Co-authored-by: 7nik <kifiranet@gmail.com>
1 parent 8860ca6 commit 4875cd6

File tree

6 files changed

+151
-9
lines changed

6 files changed

+151
-9
lines changed

.changeset/lazy-singers-pretend.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': patch
3+
---
4+
5+
fix: correctly parse escaped unicode characters in css selector

packages/svelte/src/compiler/phases/1-parse/read/style.js

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ const REGEX_NTH_OF =
1212
const REGEX_WHITESPACE_OR_COLON = /[\s:]/;
1313
const REGEX_LEADING_HYPHEN_OR_DIGIT = /-?\d/;
1414
const REGEX_VALID_IDENTIFIER_CHAR = /[a-zA-Z0-9_-]/;
15+
const REGEX_UNICODE_SEQUENCE = /^\\[0-9a-fA-F]{1,6}(\r\n|\s)?/;
1516
const REGEX_COMMENT_CLOSE = /\*\//;
1617
const REGEX_HTML_COMMENT_CLOSE = /-->/;
1718

@@ -580,25 +581,26 @@ function read_identifier(parser) {
580581
e.css_expected_identifier(start);
581582
}
582583

583-
let escaped = false;
584-
585584
while (parser.index < parser.template.length) {
586585
const char = parser.template[parser.index];
587-
if (escaped) {
588-
identifier += '\\' + char;
589-
escaped = false;
590-
} else if (char === '\\') {
591-
escaped = true;
586+
if (char === '\\') {
587+
const sequence = parser.match_regex(REGEX_UNICODE_SEQUENCE);
588+
if (sequence) {
589+
identifier += String.fromCodePoint(parseInt(sequence.slice(1), 16));
590+
parser.index += sequence.length;
591+
} else {
592+
identifier += '\\' + parser.template[parser.index + 1];
593+
parser.index += 2;
594+
}
592595
} else if (
593596
/** @type {number} */ (char.codePointAt(0)) >= 160 ||
594597
REGEX_VALID_IDENTIFIER_CHAR.test(char)
595598
) {
596599
identifier += char;
600+
parser.index++;
597601
} else {
598602
break;
599603
}
600-
601-
parser.index++;
602604
}
603605

604606
if (identifier === '') {
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import { test } from '../../test';
2+
3+
export default test({
4+
warnings: [
5+
{
6+
code: 'css_unused_selector',
7+
message: 'Unused CSS selector ".\\61 sdf"',
8+
start: {
9+
line: 22,
10+
column: 1,
11+
character: 465
12+
},
13+
end: {
14+
line: 22,
15+
column: 10,
16+
character: 474
17+
}
18+
},
19+
{
20+
code: 'css_unused_selector',
21+
message: 'Unused CSS selector ".\\61\n\tsdf"',
22+
start: {
23+
line: 23,
24+
column: 1,
25+
character: 492
26+
},
27+
end: {
28+
line: 24,
29+
column: 4,
30+
character: 501
31+
}
32+
},
33+
{
34+
code: 'css_unused_selector',
35+
message: 'Unused CSS selector ".\\61\n sdf"',
36+
start: {
37+
line: 25,
38+
column: 1,
39+
character: 519
40+
},
41+
end: {
42+
line: 26,
43+
column: 4,
44+
character: 528
45+
}
46+
},
47+
{
48+
code: 'css_unused_selector',
49+
message: 'Unused CSS selector "#\\31span"',
50+
start: {
51+
line: 28,
52+
column: 1,
53+
character: 547
54+
},
55+
end: {
56+
line: 28,
57+
column: 9,
58+
character: 555
59+
}
60+
},
61+
{
62+
code: 'css_unused_selector',
63+
message: 'Unused CSS selector "#\\31 span"',
64+
start: {
65+
line: 29,
66+
column: 1,
67+
character: 573
68+
},
69+
end: {
70+
line: 29,
71+
column: 10,
72+
character: 582
73+
}
74+
}
75+
]
76+
});
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#\31\32\33 .svelte-xyz{ color: green; }
2+
#\31 23.svelte-xyz { color: green; }
3+
#line\a break.svelte-xyz { color: green; }
4+
#line\a
5+
break.svelte-xyz { color: green; }
6+
#line\00000abreak.svelte-xyz { color: green; }
7+
#line\00000a break.svelte-xyz { color: green; }
8+
#line\00000a break.svelte-xyz { color: green; }
9+
.a\1f642 b.svelte-xyz { color: green; }
10+
11+
.\61sdf.svelte-xyz { color: green; }
12+
13+
/* (unused) .\61 sdf { color: red; }*/
14+
/* (unused) .\61
15+
sdf { color: red; }*/
16+
/* (unused) .\61
17+
sdf { color: red; }*/
18+
19+
/* (unused) #\31span { color: red; }*/
20+
/* (unused) #\31 span { color: red; }*/
21+
#\31 .svelte-xyz span:where(.svelte-xyz) { color: green; }
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<div id="123" class="svelte-xyz"></div>
2+
<div class="svelte-xyz" id="line
3+
break"></div>
4+
<div class="a🙂b svelte-xyz"></div>
5+
<div class="asdf svelte-xyz"></div>
6+
<div class="&#97;sdf svelte-xyz"></div>
7+
<div id="1" class="svelte-xyz"><span class="svelte-xyz"></span></div>
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<div id="123"></div>
2+
<div id="line
3+
break"></div>
4+
<div class="a🙂b"></div>
5+
<div class="asdf"></div>
6+
<div class="&#97;sdf"></div>
7+
<div id="1"><span></span></div>
8+
9+
<style>
10+
#\31\32\33 { color: green; }
11+
#\31 23 { color: green; }
12+
#line\a break { color: green; }
13+
#line\a
14+
break { color: green; }
15+
#line\00000abreak { color: green; }
16+
#line\00000a break { color: green; }
17+
#line\00000a break { color: green; }
18+
.a\1f642 b { color: green; }
19+
20+
.\61sdf { color: green; }
21+
22+
.\61 sdf { color: red; }
23+
.\61
24+
sdf { color: red; }
25+
.\61
26+
sdf { color: red; }
27+
28+
#\31span { color: red; }
29+
#\31 span { color: red; }
30+
#\31 span { color: green; }
31+
</style>

0 commit comments

Comments
 (0)