@@ -28,7 +28,19 @@ import * as ts from "typescript";
28
28
29
29
import * as Lint from ".." ;
30
30
31
- const OPTION_NEVER = "never" ;
31
+ const OPTION_VALUE_NEVER = "never" ;
32
+ const OPTION_KEY_PROPERTY = "property" ;
33
+ const OPTION_KEY_METHOD = "method" ;
34
+
35
+ interface RawOptions {
36
+ [ OPTION_KEY_PROPERTY ] ?: "never" | "always" ;
37
+ [ OPTION_KEY_METHOD ] ?: "never" | "always" ;
38
+ }
39
+
40
+ interface Options {
41
+ enforceShorthandMethods : boolean ;
42
+ enforceShorthandProperties : boolean ;
43
+ }
32
44
33
45
export class Rule extends Lint . Rules . AbstractRule {
34
46
/* tslint:disable:object-literal-sort-keys */
@@ -37,12 +49,43 @@ export class Rule extends Lint.Rules.AbstractRule {
37
49
description : "Enforces/disallows use of ES6 object literal shorthand." ,
38
50
hasFix : true ,
39
51
optionsDescription : Lint . Utils . dedent `
40
- If the \'never\' option is provided, any shorthand object literal syntax will cause a failure.` ,
52
+ \`"always"\` assumed to be default option, thus with no options provided
53
+ the rule enforces object literal methods and properties shorthands.
54
+ With \`"never"\` option provided, any shorthand object literal syntax causes an error.
55
+
56
+ The rule can be configured in a more granular way.
57
+ With \`{"property": "never"}\` provided (which is equivalent to \`{"property": "never", "method": "always"}\`),
58
+ the rule only flags property shorthand assignments,
59
+ and respectively with \`{"method": "never"}\` (equivalent to \`{"property": "always", "method": "never"}\`),
60
+ the rule fails only on method shorthands.` ,
41
61
options : {
42
- type : "string" ,
43
- enum : [ OPTION_NEVER ] ,
62
+ oneOf : [
63
+ {
64
+ type : "string" ,
65
+ enum : [ OPTION_VALUE_NEVER ] ,
66
+ } ,
67
+ {
68
+ type : "object" ,
69
+ properties : {
70
+ [ OPTION_KEY_PROPERTY ] : {
71
+ type : "string" ,
72
+ enum : [ OPTION_VALUE_NEVER ] ,
73
+ } ,
74
+ [ OPTION_KEY_METHOD ] : {
75
+ type : "string" ,
76
+ enum : [ OPTION_VALUE_NEVER ] ,
77
+ } ,
78
+ } ,
79
+ minProperties : 1 ,
80
+ maxProperties : 2 ,
81
+ } ,
82
+ ] ,
44
83
} ,
45
- optionExamples : [ true , [ true , OPTION_NEVER ] ] ,
84
+ optionExamples : [
85
+ true ,
86
+ [ true , OPTION_VALUE_NEVER ] ,
87
+ [ true , { [ OPTION_KEY_PROPERTY ] : OPTION_VALUE_NEVER } ] ,
88
+ ] ,
46
89
type : "style" ,
47
90
typescriptOnly : false ,
48
91
} ;
@@ -52,74 +95,104 @@ export class Rule extends Lint.Rules.AbstractRule {
52
95
public static LONGHAND_METHOD = "Expected method shorthand in object literal " ;
53
96
public static SHORTHAND_ASSIGNMENT = "Shorthand property assignments have been disallowed." ;
54
97
98
+ public static getLonghandPropertyErrorMessage ( nodeText : string ) {
99
+ return `Expected property shorthand in object literal ('${ nodeText } ').` ;
100
+ }
101
+ public static getLonghandMethodErrorMessage ( nodeText : string ) {
102
+ return `Expected method shorthand in object literal ('${ nodeText } ').` ;
103
+ }
104
+ public static getDisallowedShorthandErrorMessage ( options : Options ) {
105
+ if ( options . enforceShorthandMethods && ! options . enforceShorthandProperties ) {
106
+ return "Shorthand property assignments have been disallowed." ;
107
+ } else if ( ! options . enforceShorthandMethods && options . enforceShorthandProperties ) {
108
+ return "Shorthand method assignments have been disallowed." ;
109
+ }
110
+ return "Shorthand property and method assignments have been disallowed." ;
111
+ }
112
+
55
113
public apply ( sourceFile : ts . SourceFile ) : Lint . RuleFailure [ ] {
56
- return this . applyWithFunction (
57
- sourceFile ,
58
- this . ruleArguments . indexOf ( OPTION_NEVER ) === - 1
59
- ? enforceShorthandWalker
60
- : disallowShorthandWalker ,
114
+ return this . applyWithFunction ( sourceFile , walk , this . parseOptions ( this . ruleArguments ) ) ;
115
+ }
116
+
117
+ private parseOptions ( options : Array < string | RawOptions > ) : Options {
118
+ if ( options . indexOf ( OPTION_VALUE_NEVER ) !== - 1 ) {
119
+ return {
120
+ enforceShorthandMethods : false ,
121
+ enforceShorthandProperties : false ,
122
+ } ;
123
+ }
124
+ const optionsObject : RawOptions | undefined = options . find (
125
+ ( el : string | RawOptions ) : el is RawOptions =>
126
+ typeof el === "object" &&
127
+ ( el [ OPTION_KEY_PROPERTY ] === OPTION_VALUE_NEVER ||
128
+ el [ OPTION_KEY_METHOD ] === OPTION_VALUE_NEVER ) ,
61
129
) ;
130
+ if ( optionsObject !== undefined ) {
131
+ return {
132
+ enforceShorthandMethods : ! ( optionsObject [ OPTION_KEY_METHOD ] === OPTION_VALUE_NEVER ) ,
133
+ enforceShorthandProperties : ! (
134
+ optionsObject [ OPTION_KEY_PROPERTY ] === OPTION_VALUE_NEVER
135
+ ) ,
136
+ } ;
137
+ } else {
138
+ return {
139
+ enforceShorthandMethods : true ,
140
+ enforceShorthandProperties : true ,
141
+ } ;
142
+ }
62
143
}
63
144
}
64
145
65
- function disallowShorthandWalker ( ctx : Lint . WalkContext ) {
146
+ function walk ( ctx : Lint . WalkContext < Options > ) {
147
+ const { enforceShorthandMethods, enforceShorthandProperties } = ctx . options ;
66
148
return ts . forEachChild ( ctx . sourceFile , function cb ( node ) : void {
67
- if ( isShorthandPropertyAssignment ( node ) ) {
149
+ if (
150
+ enforceShorthandProperties &&
151
+ isPropertyAssignment ( node ) &&
152
+ node . name . kind === ts . SyntaxKind . Identifier &&
153
+ isIdentifier ( node . initializer ) &&
154
+ node . name . text === node . initializer . text
155
+ ) {
156
+ ctx . addFailureAtNode (
157
+ node ,
158
+ Rule . getLonghandPropertyErrorMessage ( `{${ node . name . text } }` ) ,
159
+ Lint . Replacement . deleteFromTo ( node . name . end , node . end ) ,
160
+ ) ;
161
+ } else if (
162
+ enforceShorthandMethods &&
163
+ isPropertyAssignment ( node ) &&
164
+ isFunctionExpression ( node . initializer ) &&
165
+ // allow named function expressions
166
+ node . initializer . name === undefined
167
+ ) {
168
+ const [ name , fix ] = handleLonghandMethod ( node . name , node . initializer , ctx . sourceFile ) ;
169
+ ctx . addFailure (
170
+ node . getStart ( ctx . sourceFile ) ,
171
+ getChildOfKind ( node . initializer , ts . SyntaxKind . OpenParenToken , ctx . sourceFile ) ! . pos ,
172
+ Rule . getLonghandMethodErrorMessage ( `{${ name } () {...}}` ) ,
173
+ fix ,
174
+ ) ;
175
+ } else if ( ! enforceShorthandProperties && isShorthandPropertyAssignment ( node ) ) {
68
176
ctx . addFailureAtNode (
69
177
node . name ,
70
- Rule . SHORTHAND_ASSIGNMENT ,
178
+ Rule . getDisallowedShorthandErrorMessage ( ctx . options ) ,
71
179
Lint . Replacement . appendText ( node . getStart ( ctx . sourceFile ) , `${ node . name . text } : ` ) ,
72
180
) ;
73
181
} else if (
182
+ ! enforceShorthandMethods &&
74
183
isMethodDeclaration ( node ) &&
75
184
node . parent . kind === ts . SyntaxKind . ObjectLiteralExpression
76
185
) {
77
186
ctx . addFailureAtNode (
78
187
node . name ,
79
- Rule . SHORTHAND_ASSIGNMENT ,
188
+ Rule . getDisallowedShorthandErrorMessage ( ctx . options ) ,
80
189
fixShorthandMethodDeclaration ( node , ctx . sourceFile ) ,
81
190
) ;
82
191
}
83
192
return ts . forEachChild ( node , cb ) ;
84
193
} ) ;
85
194
}
86
195
87
- function enforceShorthandWalker ( ctx : Lint . WalkContext ) {
88
- return ts . forEachChild ( ctx . sourceFile , function cb ( node ) : void {
89
- if ( isPropertyAssignment ( node ) ) {
90
- if (
91
- node . name . kind === ts . SyntaxKind . Identifier &&
92
- isIdentifier ( node . initializer ) &&
93
- node . name . text === node . initializer . text
94
- ) {
95
- ctx . addFailureAtNode (
96
- node ,
97
- `${ Rule . LONGHAND_PROPERTY } ('{${ node . name . text } }').` ,
98
- Lint . Replacement . deleteFromTo ( node . name . end , node . end ) ,
99
- ) ;
100
- } else if (
101
- isFunctionExpression ( node . initializer ) &&
102
- // allow named function expressions
103
- node . initializer . name === undefined
104
- ) {
105
- const [ name , fix ] = handleLonghandMethod (
106
- node . name ,
107
- node . initializer ,
108
- ctx . sourceFile ,
109
- ) ;
110
- ctx . addFailure (
111
- node . getStart ( ctx . sourceFile ) ,
112
- getChildOfKind ( node . initializer , ts . SyntaxKind . OpenParenToken , ctx . sourceFile ) !
113
- . pos ,
114
- `${ Rule . LONGHAND_METHOD } ('{${ name } () {...}}').` ,
115
- fix ,
116
- ) ;
117
- }
118
- }
119
- return ts . forEachChild ( node , cb ) ;
120
- } ) ;
121
- }
122
-
123
196
function fixShorthandMethodDeclaration ( node : ts . MethodDeclaration , sourceFile : ts . SourceFile ) {
124
197
const isGenerator = node . asteriskToken !== undefined ;
125
198
const isAsync = hasModifier ( node . modifiers , ts . SyntaxKind . AsyncKeyword ) ;
0 commit comments