11import path from 'path' ;
2- import htmltags from './htmltags ' ;
3- import { ClassesObjectType } from '.. ' ;
2+ import type { ClassesObjectType , HasEPE , HasECE , HasCPC , HasCCC } from '.. ' ;
3+ import htmlTags from './html-tags ' ;
44
55export const isWindowDefined = typeof window !== 'undefined' ;
66export const isDocumentDefined = typeof document !== 'undefined' ;
77export const isInDevelopment = process . env . NODE_ENV === 'development' ;
88
9- export const isClassesObjectType = ( object : any ) : object is ClassesObjectType => {
9+ export const isClassesObjectType = ( object : object ) : object is ClassesObjectType => {
1010 return typeof object === 'object' && ! Array . isArray ( object ) ;
1111} ;
1212
13+ export const toPascalCase = ( str : string ) => {
14+ return str . charAt ( 0 ) . toUpperCase ( ) + str . slice ( 1 ) . toLowerCase ( ) ;
15+ } ;
16+
17+ const pascalCaseHtmlTags = htmlTags . map ( ( code ) => toPascalCase ( code ) ) ;
18+
19+ const isHasEPEType = ( property : string ) : property is HasEPE => {
20+ const regex = new RegExp ( `^has(${ pascalCaseHtmlTags . join ( '|' ) } )Plus(${ pascalCaseHtmlTags . join ( '|' ) } )?(.*?)$` ) ;
21+ return regex . test ( property ) ;
22+ } ;
23+ const isHasECEType = ( property : string ) : property is HasECE => {
24+ const regex = new RegExp ( `^has(${ pascalCaseHtmlTags . join ( '|' ) } )Child(${ pascalCaseHtmlTags . join ( '|' ) } )?(.*?)$` ) ;
25+ return regex . test ( property ) ;
26+ } ;
27+
28+ function isHasCPCTypeKey ( key : string ) : key is HasCPC {
29+ const regex = / ^ h a s C l a s s .* P l u s .* $ / ;
30+ return regex . test ( key ) ;
31+ }
32+
33+ function isHasCPCType ( property : string ) : property is HasCPC {
34+ return isHasCPCTypeKey ( property ) ;
35+ }
36+
37+ function isHasCCCTypeKey ( key : string ) : key is HasCPC {
38+ const regex = / ^ h a s C l a s s .* C h i l d .* $ / ;
39+ return regex . test ( key ) ;
40+ }
41+
42+ function isHasCCCType ( property : string ) : property is HasCCC {
43+ return isHasCCCTypeKey ( property ) ;
44+ }
45+
1346// Block the bug selector if the property is only numbers.
1447export const isNumeric = ( value : string ) : boolean => {
1548 return / ^ \d + $ / . test ( value ) ;
@@ -23,25 +56,104 @@ export const get = {
2356 dir,
2457} ;
2558
26- export const toPascalCase = ( str : string ) => {
27- return str . charAt ( 0 ) . toUpperCase ( ) + str . slice ( 1 ) . toLowerCase ( ) ;
28- } ;
29-
3059export const camelToKebabCase = ( property : string ) => {
31- const pseudoCamelPropWithArgs = [ 'nthChild' , 'nthLastChild' , 'nthLastOfType' , 'nthOfType' , 'lang' , 'not' ] ;
60+ const pseudoCamelPropWithArgs = [
61+ 'nthChild' ,
62+ 'nthLastChild' ,
63+ 'nthLastOfType' ,
64+ 'nthOfType' ,
65+ 'lang' ,
66+ 'notClass' ,
67+ 'not' ,
68+ 'hasClassChild' ,
69+ 'hasClassPlus' ,
70+ 'hasClass' ,
71+ 'hasChild' ,
72+ 'hasPlus' ,
73+ 'has' ,
74+ ] ;
3275
3376 const toKebabCase = ( str : string ) => str . replace ( / ( [ A - Z ] ) / g, '-$1' ) . toLowerCase ( ) ;
3477
3578 for ( const prop of pseudoCamelPropWithArgs ) {
3679 const index = property . indexOf ( prop ) ;
3780 if ( index !== - 1 ) {
3881 const afterProp = property . slice ( index + prop . length ) ;
82+ const afterNotKebab = afterProp . replace ( / _ / g, '-' ) . toLowerCase ( ) ;
83+
84+ if ( prop === 'notClass' ) {
85+ return `:not(.${ afterNotKebab } )` ;
86+ }
3987
4088 if ( prop === 'not' ) {
41- const afterNot = afterProp . charAt ( 0 ) . toUpperCase ( ) + afterProp . slice ( 1 ) . toLowerCase ( ) ;
42- const afterNotKebab = afterProp . replace ( / _ / g, '-' ) . toLowerCase ( ) ;
43- if ( ! htmltags . includes ( afterNot ) ) {
44- return `:not(.${ afterNotKebab } )` ;
89+ if ( property . includes ( 'not(' ) ) {
90+ return `:not${ afterProp . toLowerCase ( ) } ` ;
91+ } else {
92+ return `:not(${ afterProp . toLowerCase ( ) } )` ;
93+ }
94+ }
95+
96+ if ( isHasCCCType ( property ) ) {
97+ const regex = / ^ h a s C l a s s ( .* ?) C h i l d ( .* ?) $ / ;
98+ const matches = property . match ( regex ) ;
99+ if ( matches ) {
100+ const [ , class1 , class2 ] = matches ;
101+ return `:has(.${ class1 . replace ( / _ / g, '-' ) . toLowerCase ( ) } > ${ pascalCaseHtmlTags . includes ( class2 ) ? class2 . toLowerCase ( ) : '.' + class2 . replace ( / _ / g, '-' ) . toLowerCase ( ) } )` ;
102+ }
103+ }
104+
105+ if ( isHasCPCType ( property ) ) {
106+ const regex = / ^ h a s C l a s s ( .* ?) P l u s ( .* ?) $ / ;
107+ const matches = property . match ( regex ) ;
108+ if ( matches ) {
109+ const [ , class1 , class2 ] = matches ;
110+ return `:has(.${ class1 . replace ( / _ / g, '-' ) . toLowerCase ( ) } + ${ pascalCaseHtmlTags . includes ( class2 ) ? class2 . toLowerCase ( ) : '.' + class2 . replace ( / _ / g, '-' ) . toLowerCase ( ) } )` ;
111+ }
112+ }
113+
114+ if ( prop === 'hasClassChild' ) {
115+ return `:has(> .${ afterNotKebab } )` ;
116+ }
117+
118+ if ( prop === 'hasClassPlus' ) {
119+ return `:has(+ .${ afterNotKebab } )` ;
120+ }
121+
122+ if ( prop === 'hasClass' ) {
123+ return `:has(.${ afterNotKebab } )` ;
124+ }
125+
126+ if ( isHasECEType ( property ) ) {
127+ const regex = / ^ h a s ( .* ?) C h i l d ( .* ?) $ / ;
128+ const matches = property . match ( regex ) ;
129+ if ( matches ) {
130+ const [ , tag1 , tag2 ] = matches ;
131+ return `:has(${ tag1 . toLowerCase ( ) } > ${ pascalCaseHtmlTags . includes ( tag2 ) ? tag2 . toLowerCase ( ) : '.' + tag2 . replace ( / _ / g, '-' ) . toLowerCase ( ) } )` ;
132+ }
133+ }
134+
135+ if ( isHasEPEType ( property ) ) {
136+ const regex = / ^ h a s ( .* ?) P l u s ( .* ?) $ / ;
137+ const matches = property . match ( regex ) ;
138+ if ( matches ) {
139+ const [ , tag1 , tag2 ] = matches ;
140+ return `:has(${ tag1 . toLowerCase ( ) } + ${ pascalCaseHtmlTags . includes ( tag2 ) ? tag2 . toLowerCase ( ) : '.' + tag2 . replace ( / _ / g, '-' ) . toLowerCase ( ) } )` ;
141+ }
142+ }
143+
144+ if ( prop === 'hasChild' ) {
145+ return `:has(> ${ afterProp . toLowerCase ( ) } )` ;
146+ }
147+
148+ if ( prop === 'hasPlus' ) {
149+ return `:has(+ ${ afterProp . toLowerCase ( ) } )` ;
150+ }
151+
152+ if ( prop === 'has' ) {
153+ if ( property . includes ( 'has(' ) ) {
154+ return `:has${ afterProp . toLowerCase ( ) } ` ;
155+ } else {
156+ return `:has(${ afterProp . toLowerCase ( ) } )` ;
45157 }
46158 }
47159
0 commit comments