@@ -9,18 +9,6 @@ import type {
99import { findClassesInAttribute } from '../utils/ast-utils.js' ;
1010import { getSourceCode } from '../utils/compat.js' ;
1111import { createRule } from '../utils/index.js' ;
12- import type { RuleContext , SourceCode } from '../types.js' ;
13-
14- interface RuleGlobals {
15- checkGlobal : boolean ;
16- style : string [ ] ;
17- classSelections : Map < string , AST . SvelteHTMLElement [ ] > ;
18- idSelections : Map < string , AST . SvelteHTMLElement [ ] > ;
19- typeSelections : Map < string , AST . SvelteHTMLElement [ ] > ;
20- context : RuleContext ;
21- getStyleSelectorAST : NonNullable < SourceCode [ 'parserServices' ] [ 'getStyleSelectorAST' ] > ;
22- styleSelectorNodeLoc : NonNullable < SourceCode [ 'parserServices' ] [ 'styleSelectorNodeLoc' ] > ;
23- }
2412
2513export default createRule ( 'consistent-selector-style' , {
2614 meta : {
@@ -61,9 +49,15 @@ export default createRule('consistent-selector-style', {
6149 } ,
6250 create ( context ) {
6351 const sourceCode = getSourceCode ( context ) ;
64- if ( ! sourceCode . parserServices . isSvelte ) {
52+ if (
53+ ! sourceCode . parserServices . isSvelte ||
54+ sourceCode . parserServices . getStyleSelectorAST === undefined ||
55+ sourceCode . parserServices . styleSelectorNodeLoc === undefined
56+ ) {
6557 return { } ;
6658 }
59+ const getStyleSelectorAST = sourceCode . parserServices . getStyleSelectorAST ;
60+ const styleSelectorNodeLoc = sourceCode . parserServices . styleSelectorNodeLoc ;
6761
6862 const checkGlobal = context . options [ 0 ] ?. checkGlobal ?? false ;
6963 const style = context . options [ 0 ] ?. style ?? [ 'type' , 'id' , 'class' ] ;
@@ -72,6 +66,123 @@ export default createRule('consistent-selector-style', {
7266 const idSelections : Map < string , AST . SvelteHTMLElement [ ] > = new Map ( ) ;
7367 const typeSelections : Map < string , AST . SvelteHTMLElement [ ] > = new Map ( ) ;
7468
69+ /**
70+ * Checks selectors in a given PostCSS node
71+ */
72+ function checkSelectorsInPostCSSNode ( node : AnyNode ) : void {
73+ if ( node . type === 'rule' ) {
74+ checkSelector ( getStyleSelectorAST ( node ) ) ;
75+ }
76+ if (
77+ ( node . type === 'root' ||
78+ ( node . type === 'rule' && ( node . selector !== ':global' || checkGlobal ) ) ||
79+ node . type === 'atrule' ) &&
80+ node . nodes !== undefined
81+ ) {
82+ node . nodes . flatMap ( ( node ) => checkSelectorsInPostCSSNode ( node ) ) ;
83+ }
84+ }
85+
86+ /**
87+ * Checks an individual selector
88+ */
89+ function checkSelector ( node : SelectorNode ) : void {
90+ if ( node . type === 'class' ) {
91+ checkClassSelector ( node ) ;
92+ }
93+ if ( node . type === 'id' ) {
94+ checkIdSelector ( node ) ;
95+ }
96+ if ( node . type === 'tag' ) {
97+ checkTypeSelector ( node ) ;
98+ }
99+ if (
100+ ( node . type === 'pseudo' && ( node . value !== ':global' || checkGlobal ) ) ||
101+ node . type === 'root' ||
102+ node . type === 'selector'
103+ ) {
104+ node . nodes . flatMap ( ( node ) => checkSelector ( node ) ) ;
105+ }
106+ }
107+
108+ /**
109+ * Checks a class selector
110+ */
111+ function checkClassSelector ( node : SelectorClass ) : void {
112+ const selection = classSelections . get ( node . value ) ?? [ ] ;
113+ for ( const styleValue of style ) {
114+ if ( styleValue === 'class' ) {
115+ return ;
116+ }
117+ if ( styleValue === 'id' && canUseIdSelector ( selection ) ) {
118+ context . report ( {
119+ messageId : 'classShouldBeId' ,
120+ loc : styleSelectorNodeLoc ( node ) as AST . SourceLocation
121+ } ) ;
122+ return ;
123+ }
124+ if ( styleValue === 'type' && canUseTypeSelector ( selection , typeSelections ) ) {
125+ context . report ( {
126+ messageId : 'classShouldBeType' ,
127+ loc : styleSelectorNodeLoc ( node ) as AST . SourceLocation
128+ } ) ;
129+ return ;
130+ }
131+ }
132+ }
133+
134+ /**
135+ * Checks an ID selector
136+ */
137+ function checkIdSelector ( node : SelectorIdentifier ) : void {
138+ const selection = idSelections . get ( node . value ) ?? [ ] ;
139+ for ( const styleValue of style ) {
140+ if ( styleValue === 'class' ) {
141+ context . report ( {
142+ messageId : 'idShouldBeClass' ,
143+ loc : styleSelectorNodeLoc ( node ) as AST . SourceLocation
144+ } ) ;
145+ return ;
146+ }
147+ if ( styleValue === 'id' ) {
148+ return ;
149+ }
150+ if ( styleValue === 'type' && canUseTypeSelector ( selection , typeSelections ) ) {
151+ context . report ( {
152+ messageId : 'idShouldBeType' ,
153+ loc : styleSelectorNodeLoc ( node ) as AST . SourceLocation
154+ } ) ;
155+ return ;
156+ }
157+ }
158+ }
159+
160+ /**
161+ * Checks a type selector
162+ */
163+ function checkTypeSelector ( node : SelectorTag ) : void {
164+ const selection = typeSelections . get ( node . value ) ?? [ ] ;
165+ for ( const styleValue of style ) {
166+ if ( styleValue === 'class' ) {
167+ context . report ( {
168+ messageId : 'typeShouldBeClass' ,
169+ loc : styleSelectorNodeLoc ( node ) as AST . SourceLocation
170+ } ) ;
171+ return ;
172+ }
173+ if ( styleValue === 'id' && canUseIdSelector ( selection ) ) {
174+ context . report ( {
175+ messageId : 'typeShouldBeId' ,
176+ loc : styleSelectorNodeLoc ( node ) as AST . SourceLocation
177+ } ) ;
178+ return ;
179+ }
180+ if ( styleValue === 'type' ) {
181+ return ;
182+ }
183+ }
184+ }
185+
75186 return {
76187 SvelteElement ( node ) {
77188 if ( node . kind !== 'html' ) {
@@ -97,21 +208,11 @@ export default createRule('consistent-selector-style', {
97208 const styleContext = sourceCode . parserServices . getStyleContext ! ( ) ;
98209 if (
99210 styleContext . status !== 'success' ||
100- sourceCode . parserServices . getStyleSelectorAST === undefined ||
101- sourceCode . parserServices . styleSelectorNodeLoc === undefined
211+ sourceCode . parserServices . getStyleSelectorAST === undefined
102212 ) {
103213 return ;
104214 }
105- checkSelectorsInPostCSSNode ( styleContext . sourceAst , {
106- checkGlobal,
107- style,
108- classSelections,
109- idSelections,
110- typeSelections,
111- context,
112- getStyleSelectorAST : sourceCode . parserServices . getStyleSelectorAST ,
113- styleSelectorNodeLoc : sourceCode . parserServices . styleSelectorNodeLoc
114- } ) ;
215+ checkSelectorsInPostCSSNode ( styleContext . sourceAst ) ;
115216 }
116217 } ;
117218 }
@@ -128,134 +229,17 @@ function addToArrayMap(
128229 map . set ( key , ( map . get ( key ) ?? [ ] ) . concat ( value ) ) ;
129230}
130231
131- /**
132- * Checks selectors in a given PostCSS node
133- */
134- function checkSelectorsInPostCSSNode ( node : AnyNode , ruleGlobals : RuleGlobals ) : void {
135- if ( node . type === 'rule' ) {
136- checkSelector ( ruleGlobals . getStyleSelectorAST ( node ) , ruleGlobals ) ;
137- }
138- if (
139- ( node . type === 'root' ||
140- ( node . type === 'rule' && ( node . selector !== ':global' || ruleGlobals . checkGlobal ) ) ||
141- node . type === 'atrule' ) &&
142- node . nodes !== undefined
143- ) {
144- node . nodes . flatMap ( ( node ) => checkSelectorsInPostCSSNode ( node , ruleGlobals ) ) ;
145- }
146- }
147-
148- /**
149- * Checks an individual selector
150- */
151- function checkSelector ( node : SelectorNode , ruleGlobals : RuleGlobals ) : void {
152- if ( node . type === 'class' ) {
153- checkClassSelector ( node , ruleGlobals ) ;
154- }
155- if ( node . type === 'id' ) {
156- checkIdSelector ( node , ruleGlobals ) ;
157- }
158- if ( node . type === 'tag' ) {
159- checkTypeSelector ( node , ruleGlobals ) ;
160- }
161- if (
162- ( node . type === 'pseudo' && ( node . value !== ':global' || ruleGlobals . checkGlobal ) ) ||
163- node . type === 'root' ||
164- node . type === 'selector'
165- ) {
166- node . nodes . flatMap ( ( node ) => checkSelector ( node , ruleGlobals ) ) ;
167- }
168- }
169-
170- /**
171- * Checks a class selector
172- */
173- function checkClassSelector ( node : SelectorClass , ruleGlobals : RuleGlobals ) : void {
174- const selection = ruleGlobals . classSelections . get ( node . value ) ?? [ ] ;
175- for ( const styleValue of ruleGlobals . style ) {
176- if ( styleValue === 'class' ) {
177- return ;
178- }
179- if ( styleValue === 'id' && couldBeId ( selection ) ) {
180- ruleGlobals . context . report ( {
181- messageId : 'classShouldBeId' ,
182- loc : ruleGlobals . styleSelectorNodeLoc ( node ) as AST . SourceLocation
183- } ) ;
184- return ;
185- }
186- if ( styleValue === 'type' && couldBeType ( selection , ruleGlobals . typeSelections ) ) {
187- ruleGlobals . context . report ( {
188- messageId : 'classShouldBeType' ,
189- loc : ruleGlobals . styleSelectorNodeLoc ( node ) as AST . SourceLocation
190- } ) ;
191- return ;
192- }
193- }
194- }
195-
196- /**
197- * Checks an ID selector
198- */
199- function checkIdSelector ( node : SelectorIdentifier , ruleGlobals : RuleGlobals ) : void {
200- const selection = ruleGlobals . idSelections . get ( node . value ) ?? [ ] ;
201- for ( const styleValue of ruleGlobals . style ) {
202- if ( styleValue === 'class' ) {
203- ruleGlobals . context . report ( {
204- messageId : 'idShouldBeClass' ,
205- loc : ruleGlobals . styleSelectorNodeLoc ( node ) as AST . SourceLocation
206- } ) ;
207- return ;
208- }
209- if ( styleValue === 'id' ) {
210- return ;
211- }
212- if ( styleValue === 'type' && couldBeType ( selection , ruleGlobals . typeSelections ) ) {
213- ruleGlobals . context . report ( {
214- messageId : 'idShouldBeType' ,
215- loc : ruleGlobals . styleSelectorNodeLoc ( node ) as AST . SourceLocation
216- } ) ;
217- return ;
218- }
219- }
220- }
221-
222- /**
223- * Checks a type selector
224- */
225- function checkTypeSelector ( node : SelectorTag , ruleGlobals : RuleGlobals ) : void {
226- const selection = ruleGlobals . typeSelections . get ( node . value ) ?? [ ] ;
227- for ( const styleValue of ruleGlobals . style ) {
228- if ( styleValue === 'class' ) {
229- ruleGlobals . context . report ( {
230- messageId : 'typeShouldBeClass' ,
231- loc : ruleGlobals . styleSelectorNodeLoc ( node ) as AST . SourceLocation
232- } ) ;
233- return ;
234- }
235- if ( styleValue === 'id' && couldBeId ( selection ) ) {
236- ruleGlobals . context . report ( {
237- messageId : 'typeShouldBeId' ,
238- loc : ruleGlobals . styleSelectorNodeLoc ( node ) as AST . SourceLocation
239- } ) ;
240- return ;
241- }
242- if ( styleValue === 'type' ) {
243- return ;
244- }
245- }
246- }
247-
248232/**
249233 * Checks whether a given selection could be obtained using an ID selector
250234 */
251- function couldBeId ( selection : AST . SvelteHTMLElement [ ] ) : boolean {
235+ function canUseIdSelector ( selection : AST . SvelteHTMLElement [ ] ) : boolean {
252236 return selection . length <= 1 ;
253237}
254238
255239/**
256240 * Checks whether a given selection could be obtained using a type selector
257241 */
258- function couldBeType (
242+ function canUseTypeSelector (
259243 selection : AST . SvelteHTMLElement [ ] ,
260244 typeSelections : Map < string , AST . SvelteHTMLElement [ ] >
261245) : boolean {
0 commit comments