1
1
import * as Lint from 'tslint' ;
2
- import { SelectorValidator } from './util/selectorValidator' ;
2
+ import { SelectorValidator } from './util/selectorValidator' ;
3
3
import * as ts from 'typescript' ;
4
4
import { sprintf } from 'sprintf-js' ;
5
5
import * as compiler from '@angular/compiler' ;
6
6
import { IOptions } from 'tslint' ;
7
7
import SyntaxKind = require( './util/syntaxKind' ) ;
8
8
9
- export abstract class SelectorRule extends Lint . Rules . AbstractRule {
10
-
11
- public isMultiPrefix :boolean ;
12
- public prefixArguments :string ;
13
- public cssSelectorProperty :string ;
9
+ export type SelectorType = 'element' | 'attribute' ;
10
+ export type SelectorTypeInternal = 'element' | 'attrs' ;
11
+ export type SelectorStyle = 'kebab-case' | 'camelCase' ;
14
12
15
- public handleType : string ;
16
-
17
- private typeValidator :Function ;
18
- private prefixValidator :Function ;
19
- private nameValidator :Function ;
20
- private FAILURE_PREFIX ;
21
- private isMultiSelectors :boolean ;
13
+ export abstract class SelectorRule extends Lint . Rules . AbstractRule {
14
+ handleType : string ;
15
+ prefixes : string [ ] ;
16
+ types : SelectorTypeInternal [ ] ;
17
+ style : SelectorStyle [ ] ;
22
18
23
19
constructor ( options : IOptions ) {
24
- const args = options . ruleArguments ;
25
- let type = args [ 1 ] ;
26
- let prefix = args [ 2 ] || [ ] ;
27
- let name = args [ 3 ] ;
28
20
super ( options ) ;
29
- this . setMultiPrefix ( prefix ) ;
30
- this . setPrefixArguments ( prefix ) ;
31
- this . setPrefixValidator ( prefix , name ) ;
32
- this . setPrefixFailure ( ) ;
33
- this . setTypeValidator ( type ) ;
34
- this . setNameValidator ( name ) ;
35
- }
36
-
37
- public getPrefixFailure ( ) :string {
38
- return this . FAILURE_PREFIX ;
39
- }
40
-
41
- public validateType ( selector :string ) :boolean {
42
- return this . typeValidator ( selector ) ;
43
- }
21
+ const args = options . ruleArguments ;
44
22
45
- public validateName ( selector :any ) :boolean {
46
- if ( this . isMultiSelectors ) {
47
- return selector . some ( ( a ) => this . nameValidator ( a ) ) ;
48
- } else {
49
- return this . nameValidator ( selector ) ;
23
+ let type : SelectorType [ ] = args [ 1 ] || [ 'element' , 'attribute' ] ;
24
+ if ( ! ( type instanceof Array ) ) {
25
+ type = [ type ] ;
50
26
}
51
- }
52
-
53
- public validatePrefix ( selector :any ) :boolean {
54
- if ( this . isMultiSelectors ) {
55
- return selector . some ( ( a ) => this . prefixValidator ( a ) ) ;
56
- } else {
57
- return this . prefixValidator ( selector ) ;
27
+ let internal : SelectorTypeInternal [ ] = [ ] ;
28
+ if ( type . indexOf ( 'element' ) >= 0 ) {
29
+ internal . push ( 'element' ) ;
58
30
}
59
- }
60
-
61
- public apply ( sourceFile : ts . SourceFile ) : Lint . RuleFailure [ ] {
62
- return this . applyWithWalker (
63
- new SelectorValidatorWalker (
64
- sourceFile ,
65
- this ) ) ;
66
- }
31
+ if ( type . indexOf ( 'attribute' ) >= 0 ) {
32
+ internal . push ( 'attrs' ) ;
33
+ }
34
+ this . types = internal ;
67
35
68
- public abstract getTypeFailure ( ) : any ;
69
- public abstract getNameFailure ( ) : any ;
70
- protected abstract getSinglePrefixFailure ( ) : any ;
71
- protected abstract getManyPrefixFailure ( ) : any ;
36
+ let prefix = args [ 2 ] || [ ] ;
37
+ if ( ! ( prefix instanceof Array ) ) {
38
+ prefix = [ prefix ] ;
39
+ }
40
+ this . prefixes = prefix ;
72
41
73
- private setNameValidator ( name :string ) {
74
- if ( name === 'camelCase' ) {
75
- this . nameValidator = SelectorValidator . camelCase ;
76
- } else if ( name === 'kebab-case' ) {
77
- this . nameValidator = SelectorValidator . kebabCase ;
42
+ let style = args [ 3 ] ;
43
+ if ( ! ( style instanceof Array ) ) {
44
+ style = [ style ] ;
78
45
}
46
+ this . style = style ;
79
47
}
80
48
81
- private setMultiPrefix ( prefix : string ) {
82
- this . isMultiPrefix = typeof prefix === 'string' ;
49
+ public validateType ( selectors : compiler . CssSelector [ ] ) : boolean {
50
+ return this . getValidSelectors ( selectors ) . length > 0 ;
83
51
}
84
52
85
- private setPrefixArguments ( prefix :any ) {
86
- this . prefixArguments = this . isMultiPrefix ?prefix :prefix . join ( ',' ) ;
53
+ public validateStyle ( selectors : compiler . CssSelector [ ] ) : boolean {
54
+ return this . getValidSelectors ( selectors ) . some ( selector => {
55
+ return this . style . some ( style => {
56
+ let validator = SelectorValidator . camelCase ;
57
+ if ( style === 'kebab-case' ) {
58
+ validator = SelectorValidator . kebabCase ;
59
+ }
60
+ return validator ( selector ) ;
61
+ } ) ;
62
+ } ) ;
87
63
}
88
64
89
- private setPrefixValidator ( prefix : any , name : string ) {
90
- if ( ! this . isMultiPrefix ) {
91
- prefix = ( prefix || [ ] ) . sort ( ( a : string , b : string ) => {
92
- return a . length < b . length ? 1 : - 1 ;
93
- } ) ;
94
- }
95
- let prefixExpression : string = this . isMultiPrefix ?prefix :( prefix || [ ] ) . join ( '|' ) ;
96
- this . prefixValidator = SelectorValidator . prefix ( prefixExpression , name ) ;
65
+ public validatePrefix ( selectors : compiler . CssSelector [ ] ) : boolean {
66
+ return this . getValidSelectors ( selectors )
67
+ . some ( selector => ! this . prefixes . length || this . prefixes . some ( p =>
68
+ this . style . some ( s => SelectorValidator . prefix ( p , s ) ( selector ) ) ) ) ;
97
69
}
98
70
99
- private setPrefixFailure ( ) {
100
- this . FAILURE_PREFIX = this . isMultiPrefix ? this . getSinglePrefixFailure ( ) : this . getManyPrefixFailure ( ) ;
71
+ public apply ( sourceFile : ts . SourceFile ) : Lint . RuleFailure [ ] {
72
+ return this . applyWithWalker ( new SelectorValidatorWalker ( sourceFile , this ) ) ;
101
73
}
102
74
103
- private setTypeValidator ( type :string ) {
104
- if ( type === 'element' ) {
105
- this . typeValidator = SelectorValidator . element ;
106
- this . isMultiSelectors = false ;
107
- this . cssSelectorProperty = 'element' ;
108
- } else if ( type === 'attribute' ) {
109
- this . typeValidator = SelectorValidator . attribute ;
110
- this . isMultiSelectors = true ;
111
- this . cssSelectorProperty = 'attrs' ;
112
- }
75
+ abstract getTypeFailure ( ) : string ;
76
+ abstract getStyleFailure ( ) : string ;
77
+ abstract getPrefixFailure ( prefixes : string [ ] ) : string ;
78
+
79
+ private getValidSelectors ( selectors : compiler . CssSelector [ ] ) {
80
+ return [ ] . concat . apply ( [ ] , selectors . map ( selector => {
81
+ return [ ] . concat . apply ( [ ] , this . types . map ( t => {
82
+ let prop = selector [ t ] ;
83
+ if ( prop && ! ( prop instanceof Array ) ) {
84
+ prop = [ prop ] ;
85
+ }
86
+ return prop ;
87
+ } ) . filter ( s => ! ! s ) ) ;
88
+ } ) ) ;
113
89
}
114
90
}
115
91
@@ -140,30 +116,21 @@ export class SelectorValidatorWalker extends Lint.RuleWalker {
140
116
private validateSelector ( className : string , arg : ts . Node ) {
141
117
if ( arg . kind === SyntaxKind . current ( ) . ObjectLiteralExpression ) {
142
118
( < ts . ObjectLiteralExpression > arg ) . properties . filter ( prop => this . validateProperty ( prop ) )
143
- . map ( prop => ( < any > prop ) . initializer )
119
+ . map ( prop => ( < any > prop ) . initializer )
144
120
. forEach ( i => {
145
- const selectors : any = this . extractMainSelector ( i ) ;
146
- const validateSelectors = ( cb : any ) => {
147
- // If all selectors fail, this will return true which means that the selector is invalid
148
- // according to the validation callback.
149
- // Since the method is called validateSelector, it's suppose to return true if the
150
- // selector is valid, so we're taking the result with "!".
151
- return ! selectors . every ( ( selector : any ) => {
152
- return ! cb ( selector [ this . rule . cssSelectorProperty ] ) ;
153
- } ) ;
154
- } ;
155
- if ( ! validateSelectors ( this . rule . validateType . bind ( this . rule ) ) ) {
121
+ const selectors : compiler . CssSelector [ ] = this . extractMainSelector ( i ) ;
122
+ if ( ! this . rule . validateType ( selectors ) ) {
156
123
let error = sprintf ( this . rule . getTypeFailure ( ) , className , this . rule . getOptions ( ) . ruleArguments [ 1 ] ) ;
157
- this . addFailure ( this . createFailure ( i . getStart ( ) , i . getWidth ( ) , error ) ) ;
158
- } else if ( ! validateSelectors ( this . rule . validateName . bind ( this . rule ) ) ) {
124
+ this . addFailure ( this . createFailure ( i . getStart ( ) , i . getWidth ( ) , error ) ) ;
125
+ } else if ( ! this . rule . validateStyle ( selectors ) ) {
159
126
let name = this . rule . getOptions ( ) . ruleArguments [ 3 ] ;
160
127
if ( name === 'kebab-case' ) {
161
128
name += ' and include dash' ;
162
129
}
163
- let error = sprintf ( this . rule . getNameFailure ( ) , className , name ) ;
130
+ let error = sprintf ( this . rule . getStyleFailure ( ) , className , name ) ;
164
131
this . addFailure ( this . createFailure ( i . getStart ( ) , i . getWidth ( ) , error ) ) ;
165
- } else if ( ! validateSelectors ( this . rule . validatePrefix . bind ( this . rule ) ) ) {
166
- let error = sprintf ( this . rule . getPrefixFailure ( ) , className , this . rule . prefixArguments ) ;
132
+ } else if ( ! this . rule . validatePrefix ( selectors ) ) {
133
+ let error = sprintf ( this . rule . getPrefixFailure ( this . rule . prefixes ) , className , this . rule . prefixes . join ( ', ' ) ) ;
167
134
this . addFailure ( this . createFailure ( i . getStart ( ) , i . getWidth ( ) , error ) ) ;
168
135
}
169
136
} ) ;
@@ -179,7 +146,7 @@ export class SelectorValidatorWalker extends Lint.RuleWalker {
179
146
return [ current . StringLiteral , current . NoSubstitutionTemplateLiteral ] . some ( kindType => kindType === kind ) ;
180
147
}
181
148
182
- private extractMainSelector ( i :any ) {
149
+ private extractMainSelector ( i : any ) {
183
150
return compiler . CssSelector . parse ( i . text ) ;
184
151
}
185
152
}
0 commit comments