11import { Selector , SelectorType , AttributeAction } from "./types" ;
22
3- const charsToEscape = new Set (
3+ const attribValChars = [ "\\" , '"' ] ;
4+ const pseudoValChars = [ ...attribValChars , "(" , ")" ] ;
5+
6+ const charsToEscapeInAttributeValue = new Set (
7+ attribValChars . map ( ( c ) => c . charCodeAt ( 0 ) )
8+ ) ;
9+ const charsToEscapeInPseudoValue = new Set (
10+ pseudoValChars . map ( ( c ) => c . charCodeAt ( 0 ) )
11+ ) ;
12+ const charsToEscapeInName = new Set (
413 [
14+ ...pseudoValChars ,
515 "~" ,
616 "^" ,
717 "$" ,
@@ -14,10 +24,6 @@ const charsToEscape = new Set(
1424 "]" ,
1525 " " ,
1626 "." ,
17- "\\" ,
18- "(" ,
19- ")" ,
20- '"' ,
2127 ] . map ( ( c ) => c . charCodeAt ( 0 ) )
2228) ;
2329
@@ -54,14 +60,20 @@ function stringifyToken(token: Selector): string {
5460 return getNamespacedName ( token ) ;
5561
5662 case SelectorType . PseudoElement :
57- return `::${ escapeName ( token . name ) } ` ;
63+ return `::${ escapeName ( token . name , charsToEscapeInName ) } ` ;
5864
5965 case SelectorType . Pseudo :
60- if ( token . data === null ) return `:${ escapeName ( token . name ) } ` ;
66+ if ( token . data === null )
67+ return `:${ escapeName ( token . name , charsToEscapeInName ) } ` ;
6168 if ( typeof token . data === "string" ) {
62- return `:${ escapeName ( token . name ) } (${ escapeName ( token . data ) } )` ;
69+ return `:${ escapeName (
70+ token . name ,
71+ charsToEscapeInName
72+ ) } (${ escapeName ( token . data , charsToEscapeInPseudoValue ) } )`;
6373 }
64- return `:${ escapeName ( token . name ) } (${ stringify ( token . data ) } )` ;
74+ return `:${ escapeName ( token . name , charsToEscapeInName ) } (${ stringify (
75+ token . data
76+ ) } )`;
6577
6678 case SelectorType . Attribute : {
6779 if (
@@ -70,15 +82,15 @@ function stringifyToken(token: Selector): string {
7082 token . ignoreCase === "quirks" &&
7183 ! token . namespace
7284 ) {
73- return `#${ escapeName ( token . value ) } ` ;
85+ return `#${ escapeName ( token . value , charsToEscapeInName ) } ` ;
7486 }
7587 if (
7688 token . name === "class" &&
7789 token . action === AttributeAction . Element &&
7890 token . ignoreCase === "quirks" &&
7991 ! token . namespace
8092 ) {
81- return `.${ escapeName ( token . value ) } ` ;
93+ return `.${ escapeName ( token . value , charsToEscapeInName ) } ` ;
8294 }
8395
8496 const name = getNamespacedName ( token ) ;
@@ -88,7 +100,8 @@ function stringifyToken(token: Selector): string {
88100 }
89101
90102 return `[${ name } ${ getActionValue ( token . action ) } ="${ escapeName (
91- token . value
103+ token . value ,
104+ charsToEscapeInAttributeValue
92105 ) } "${
93106 token . ignoreCase === null ? "" : token . ignoreCase ? " i" : " s"
94107 } ]`;
@@ -121,16 +134,23 @@ function getNamespacedName(token: {
121134 name : string ;
122135 namespace : string | null ;
123136} ) : string {
124- return `${ getNamespace ( token . namespace ) } ${ escapeName ( token . name ) } ` ;
137+ return `${ getNamespace ( token . namespace ) } ${ escapeName (
138+ token . name ,
139+ charsToEscapeInName
140+ ) } `;
125141}
126142
127143function getNamespace ( namespace : string | null ) : string {
128144 return namespace !== null
129- ? `${ namespace === "*" ? "*" : escapeName ( namespace ) } |`
145+ ? `${
146+ namespace === "*"
147+ ? "*"
148+ : escapeName ( namespace , charsToEscapeInName )
149+ } |`
130150 : "" ;
131151}
132152
133- function escapeName ( str : string ) : string {
153+ function escapeName ( str : string , charsToEscape : Set < number > ) : string {
134154 let lastIdx = 0 ;
135155 let ret = "" ;
136156
0 commit comments