1
1
import * as util from '../util' ;
2
2
3
- type Options = [ 'never' | 'always' ] ;
3
+ type ParsedOptions =
4
+ | {
5
+ prefixWithI : 'never' ;
6
+ }
7
+ | {
8
+ prefixWithI : 'always' ;
9
+ allowUnderscorePrefix : boolean ;
10
+ } ;
11
+ type Options = [
12
+
13
+ | 'never'
14
+ | 'always'
15
+ | {
16
+ prefixWithI ?: 'never' ;
17
+ }
18
+ | {
19
+ prefixWithI : 'always' ;
20
+ allowUnderscorePrefix ?: boolean ;
21
+ } ,
22
+ ] ;
4
23
type MessageIds = 'noPrefix' | 'alwaysPrefix' ;
5
24
25
+ /**
26
+ * Parses a given value as options.
27
+ */
28
+ export function parseOptions ( [ options ] : Options ) : ParsedOptions {
29
+ if ( options === 'always' ) {
30
+ return { prefixWithI : 'always' , allowUnderscorePrefix : false } ;
31
+ }
32
+ if ( options !== 'never' && options . prefixWithI === 'always' ) {
33
+ return {
34
+ prefixWithI : 'always' ,
35
+ allowUnderscorePrefix : ! ! options . allowUnderscorePrefix ,
36
+ } ;
37
+ }
38
+ return { prefixWithI : 'never' } ;
39
+ }
40
+
6
41
export default util . createRule < Options , MessageIds > ( {
7
42
name : 'interface-name-prefix' ,
8
43
meta : {
@@ -21,13 +56,46 @@ export default util.createRule<Options, MessageIds>({
21
56
} ,
22
57
schema : [
23
58
{
24
- enum : [ 'never' , 'always' ] ,
59
+ oneOf : [
60
+ {
61
+ enum : [
62
+ // Deprecated, equivalent to: { prefixWithI: 'never' }
63
+ 'never' ,
64
+ // Deprecated, equivalent to: { prefixWithI: 'always', allowUnderscorePrefix: false }
65
+ 'always' ,
66
+ ] ,
67
+ } ,
68
+ {
69
+ type : 'object' ,
70
+ properties : {
71
+ prefixWithI : {
72
+ type : 'string' ,
73
+ enum : [ 'never' ] ,
74
+ } ,
75
+ } ,
76
+ additionalProperties : false ,
77
+ } ,
78
+ {
79
+ type : 'object' ,
80
+ properties : {
81
+ prefixWithI : {
82
+ type : 'string' ,
83
+ enum : [ 'always' ] ,
84
+ } ,
85
+ allowUnderscorePrefix : {
86
+ type : 'boolean' ,
87
+ } ,
88
+ } ,
89
+ required : [ 'prefixWithI' ] , // required to select this "oneOf" alternative
90
+ additionalProperties : false ,
91
+ } ,
92
+ ] ,
25
93
} ,
26
94
] ,
27
95
} ,
28
- defaultOptions : [ 'never' ] ,
29
- create ( context , [ option ] ) {
30
- const never = option !== 'always' ;
96
+ defaultOptions : [ { prefixWithI : 'never' } ] ,
97
+ create ( context , [ options ] ) {
98
+ const parsedOptions = parseOptions ( [ options ] ) ;
31
99
32
100
/**
33
101
* Checks if a string is prefixed with "I".
@@ -41,21 +109,42 @@ export default util.createRule<Options, MessageIds>({
41
109
return / ^ I [ A - Z ] / . test ( name ) ;
42
110
}
43
111
112
+ /**
113
+ * Checks if a string is prefixed with "I" or "_I".
114
+ * @param name The string to check
115
+ */
116
+ function isPrefixedWithIOrUnderscoreI ( name : string ) : boolean {
117
+ if ( typeof name !== 'string' ) {
118
+ return false ;
119
+ }
120
+
121
+ return / ^ _ ? I [ A - Z ] / . test ( name ) ;
122
+ }
123
+
44
124
return {
45
125
TSInterfaceDeclaration ( node ) : void {
46
- if ( never ) {
47
- if ( isPrefixedWithI ( node . id . name ) ) {
126
+ if ( parsedOptions . prefixWithI === ' never' ) {
127
+ if ( isPrefixedWithIOrUnderscoreI ( node . id . name ) ) {
48
128
context . report ( {
49
129
node : node . id ,
50
130
messageId : 'noPrefix' ,
51
131
} ) ;
52
132
}
53
133
} else {
54
- if ( ! isPrefixedWithI ( node . id . name ) ) {
55
- context . report ( {
56
- node : node . id ,
57
- messageId : 'alwaysPrefix' ,
58
- } ) ;
134
+ if ( parsedOptions . allowUnderscorePrefix ) {
135
+ if ( ! isPrefixedWithIOrUnderscoreI ( node . id . name ) ) {
136
+ context . report ( {
137
+ node : node . id ,
138
+ messageId : 'alwaysPrefix' ,
139
+ } ) ;
140
+ }
141
+ } else {
142
+ if ( ! isPrefixedWithI ( node . id . name ) ) {
143
+ context . report ( {
144
+ node : node . id ,
145
+ messageId : 'alwaysPrefix' ,
146
+ } ) ;
147
+ }
59
148
}
60
149
}
61
150
} ,
0 commit comments