@spinnaker/eslint-plugin 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintignore +3 -0
- package/CHANGELOG.md +77 -0
- package/LICENSE.txt +203 -0
- package/README.md +259 -0
- package/babel.config.js +3 -0
- package/base.config.js +86 -0
- package/create-rule.js +67 -0
- package/eslint-plugin.ts +47 -0
- package/index.js +6 -0
- package/newrule.sh +88 -0
- package/none.config.js +18 -0
- package/package.json +51 -0
- package/rules/api-deprecation.spec.ts +79 -0
- package/rules/api-deprecation.ts +254 -0
- package/rules/api-no-slashes.spec.ts +84 -0
- package/rules/api-no-slashes.ts +148 -0
- package/rules/api-no-unused-chaining.spec.ts +26 -0
- package/rules/api-no-unused-chaining.ts +47 -0
- package/rules/import-from-alias-not-npm.spec.ts +22 -0
- package/rules/import-from-alias-not-npm.ts +53 -0
- package/rules/import-from-npm-not-alias.spec.ts +23 -0
- package/rules/import-from-npm-not-alias.ts +56 -0
- package/rules/import-from-npm-not-relative.spec.ts +22 -0
- package/rules/import-from-npm-not-relative.ts +57 -0
- package/rules/import-from-presentation-not-core.spec.ts +44 -0
- package/rules/import-from-presentation-not-core.ts +106 -0
- package/rules/import-relative-within-subpackage.spec.ts +50 -0
- package/rules/import-relative-within-subpackage.ts +71 -0
- package/rules/import-sort.spec.ts +85 -0
- package/rules/import-sort.ts +280 -0
- package/rules/migrate-to-mock-http-client.spec.ts +78 -0
- package/rules/migrate-to-mock-http-client.ts +122 -0
- package/rules/ng-no-component-class.spec.ts +45 -0
- package/rules/ng-no-component-class.ts +68 -0
- package/rules/ng-no-module-export.spec.ts +26 -0
- package/rules/ng-no-module-export.ts +117 -0
- package/rules/ng-no-require-angularjs.spec.ts +27 -0
- package/rules/ng-no-require-angularjs.ts +94 -0
- package/rules/ng-no-require-module-deps.spec.ts +33 -0
- package/rules/ng-no-require-module-deps.ts +211 -0
- package/rules/ng-strictdi.spec.ts +100 -0
- package/rules/ng-strictdi.ts +304 -0
- package/rules/prefer-promise-like.spec.ts +75 -0
- package/rules/prefer-promise-like.ts +108 -0
- package/rules/react2angular-with-error-boundary.spec.ts +29 -0
- package/rules/react2angular-with-error-boundary.ts +118 -0
- package/rules/rest-prefer-static-strings-in-initializer.spec.ts +34 -0
- package/rules/rest-prefer-static-strings-in-initializer.ts +89 -0
- package/template/template-rule.spec.ts +17 -0
- package/template/template-rule.ts +21 -0
- package/test.eslintrc +20 -0
- package/test_rule_against_deck_source.sh +18 -0
- package/tsconfig.json +17 -0
- package/utils/angular-rule/angular-rule.js +302 -0
- package/utils/angular-rule/false-values.js +6 -0
- package/utils/angular-rule/utils.js +624 -0
- package/utils/import-aliases.mock.ts +17 -0
- package/utils/import-aliases.ts +91 -0
- package/utils/mockModule.js +15 -0
- package/utils/ruleTester.js +8 -0
- package/utils/utils.ts +90 -0
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
module.exports = angularRule;
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Method names from an AngularJS module which can be chained.
|
|
7
|
+
*/
|
|
8
|
+
var angularChainableNames = [
|
|
9
|
+
'animation',
|
|
10
|
+
'component',
|
|
11
|
+
'config',
|
|
12
|
+
'constant',
|
|
13
|
+
'controller',
|
|
14
|
+
'decorator',
|
|
15
|
+
'directive',
|
|
16
|
+
'factory',
|
|
17
|
+
'filter',
|
|
18
|
+
'provider',
|
|
19
|
+
'run',
|
|
20
|
+
'service',
|
|
21
|
+
'value',
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* An angularRule defines a simplified interface for AngularJS component based rules.
|
|
26
|
+
*
|
|
27
|
+
* A full rule definition containing rules for all supported rules looks like this:
|
|
28
|
+
* ```js
|
|
29
|
+
* module.exports = angularRule(function(context) {
|
|
30
|
+
* return {
|
|
31
|
+
* 'angular?animation': function(configCallee, configFn) {},
|
|
32
|
+
* 'angular?component': function(componentCallee, componentObj) {},
|
|
33
|
+
* 'angular?config': function(configCallee, configFn) {},
|
|
34
|
+
* 'angular?controller': function(controllerCallee, controllerFn) {},
|
|
35
|
+
* 'angular?decorator': function(decoratorCallee, decoratorFn) {},
|
|
36
|
+
* 'angular?directive': function(directiveCallee, directiveFn) {},
|
|
37
|
+
* 'angular?factory': function(factoryCallee, factoryFn) {},
|
|
38
|
+
* 'angular?filter': function(filterCallee, filterFn) {},
|
|
39
|
+
* 'angular?inject': function(injectCallee, injectFn) {}, // inject() calls from angular-mocks
|
|
40
|
+
* 'angular?run': function(runCallee, runFn) {},
|
|
41
|
+
* 'angular?service': function(serviceCallee, serviceFn) {},
|
|
42
|
+
* 'angular?provider': function(providerCallee, providerFn, provider$getFn) {}
|
|
43
|
+
* };
|
|
44
|
+
* })
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
function angularRule(ruleDefinition) {
|
|
48
|
+
var angularComponents;
|
|
49
|
+
var angularModuleCalls;
|
|
50
|
+
var angularModuleIdentifiers;
|
|
51
|
+
var angularChainables;
|
|
52
|
+
var injectCalls;
|
|
53
|
+
|
|
54
|
+
return wrapper;
|
|
55
|
+
|
|
56
|
+
function reset() {
|
|
57
|
+
angularComponents = [];
|
|
58
|
+
angularModuleCalls = [];
|
|
59
|
+
angularModuleIdentifiers = [];
|
|
60
|
+
angularChainables = [];
|
|
61
|
+
injectCalls = [];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* A wrapper around the rule definition.
|
|
66
|
+
*/
|
|
67
|
+
function wrapper(context) {
|
|
68
|
+
reset();
|
|
69
|
+
var ruleObject = ruleDefinition(context);
|
|
70
|
+
injectCall(ruleObject, context, 'CallExpression:exit', checkCallee);
|
|
71
|
+
injectCall(ruleObject, context, 'Program:exit', callAngularRules);
|
|
72
|
+
return ruleObject;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Makes sure an extra function gets called after custom defined rule has run.
|
|
77
|
+
*/
|
|
78
|
+
function injectCall(ruleObject, context, propName, toCallAlso) {
|
|
79
|
+
var original = ruleObject[propName];
|
|
80
|
+
ruleObject[propName] = callBoth;
|
|
81
|
+
|
|
82
|
+
function callBoth(node) {
|
|
83
|
+
if (original) {
|
|
84
|
+
original.call(ruleObject, node);
|
|
85
|
+
}
|
|
86
|
+
toCallAlso(ruleObject, context, node);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Collect expressions from an entire Angular module call chain expression statement and inject calls.
|
|
92
|
+
*
|
|
93
|
+
* This collects the following nodes:
|
|
94
|
+
* ```js
|
|
95
|
+
* angular.module()
|
|
96
|
+
* ^^^^^^
|
|
97
|
+
* .animation('', function() {})
|
|
98
|
+
* ^^^^^^^^^ ^^^^^^^^^^
|
|
99
|
+
* .component('', {})
|
|
100
|
+
* ^^^^^^^^^
|
|
101
|
+
* .config(function() {})
|
|
102
|
+
* ^^^^^^ ^^^^^^^^^^
|
|
103
|
+
* .constant()
|
|
104
|
+
* ^^^^^^^^
|
|
105
|
+
* .controller('', function() {})
|
|
106
|
+
* ^^^^^^^^^^ ^^^^^^^^^^
|
|
107
|
+
* .directive('', function() {})
|
|
108
|
+
* ^^^^^^^^^ ^^^^^^^^^^
|
|
109
|
+
* .factory('', function() {})
|
|
110
|
+
* ^^^^^^^ ^^^^^^^^^^
|
|
111
|
+
* .filter('', function() {})
|
|
112
|
+
* ^^^^^^ ^^^^^^^^^^
|
|
113
|
+
* .provider('', function() {})
|
|
114
|
+
* ^^^^^^^^ ^^^^^^^^^^
|
|
115
|
+
* .run('', function() {})
|
|
116
|
+
* ^^^ ^^^^^^^^^^
|
|
117
|
+
* .service('', function() {})
|
|
118
|
+
* ^^^^^^^ ^^^^^^^^^^
|
|
119
|
+
* .value();
|
|
120
|
+
* ^^^^^
|
|
121
|
+
*
|
|
122
|
+
* inject(function() {})
|
|
123
|
+
* ^^^^^^ ^^^^^^^^^^
|
|
124
|
+
* ```
|
|
125
|
+
*/
|
|
126
|
+
function checkCallee(ruleObject, context, callExpressionNode) {
|
|
127
|
+
// const text = context.getSourceCode().getText(callExpressionNode);
|
|
128
|
+
// console.log(text);
|
|
129
|
+
|
|
130
|
+
function getThisGuyRightHere() {
|
|
131
|
+
return {
|
|
132
|
+
callExpression: callExpressionNode,
|
|
133
|
+
node: findInjectedArgument(callExpressionNode),
|
|
134
|
+
scope: context.getScope(),
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
var callee = callExpressionNode.callee;
|
|
139
|
+
if (callee.type === 'Identifier') {
|
|
140
|
+
const args = callExpressionNode.arguments;
|
|
141
|
+
if ((callee.name === 'module' && args.every((x) => !!x) && args.length === 1) || args.length === 2) {
|
|
142
|
+
const [moduleName, deps] = args;
|
|
143
|
+
const isString = moduleName.type === 'Literal' && typeof moduleName.value === 'string';
|
|
144
|
+
const isIdentifier = moduleName.type === 'Identifier';
|
|
145
|
+
const isDepsArray = deps && deps.type === 'ArrayExpression';
|
|
146
|
+
if ((isString || isIdentifier) && (!deps || isDepsArray)) {
|
|
147
|
+
// module('stringliteral', [dep1, dep2])
|
|
148
|
+
// ^^^^^^^^
|
|
149
|
+
angularModuleCalls.push(callExpressionNode);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const { parent } = callExpressionNode;
|
|
154
|
+
if (callee.name === 'inject' && !(parent && parent.type === 'Decorator')) {
|
|
155
|
+
// inject()
|
|
156
|
+
// ^^^^^^
|
|
157
|
+
injectCalls.push(getThisGuyRightHere());
|
|
158
|
+
}
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (callee.type === 'MemberExpression') {
|
|
163
|
+
const objName = callee.object.name;
|
|
164
|
+
const propName = callee.property.name;
|
|
165
|
+
|
|
166
|
+
if (objName === 'angular' && propName === 'module') {
|
|
167
|
+
// angular.module()
|
|
168
|
+
// ^^^^^^
|
|
169
|
+
angularModuleCalls.push(callExpressionNode);
|
|
170
|
+
} else if (
|
|
171
|
+
angularChainableNames.includes(propName) &&
|
|
172
|
+
((!!objName && objName.match(/[Mm]od(?:ule)$/)) ||
|
|
173
|
+
angularModuleCalls.includes(callee.object) ||
|
|
174
|
+
angularChainables.includes(callee.object))
|
|
175
|
+
) {
|
|
176
|
+
// someVariableEndingInModule.controller()
|
|
177
|
+
// ^^^^^^^^^^
|
|
178
|
+
// angular.module().factory().controller()
|
|
179
|
+
// ^^^^^^^ ^^^^^^^^^^
|
|
180
|
+
angularChainables.push(callExpressionNode);
|
|
181
|
+
angularComponents.push(getThisGuyRightHere());
|
|
182
|
+
} else if (callee.object.type === 'Identifier') {
|
|
183
|
+
// var app = angular.module(); app.factory()
|
|
184
|
+
// ^^^^^^^
|
|
185
|
+
var scope = context.getScope();
|
|
186
|
+
var isAngularModule = scope.variables.some(function (variable) {
|
|
187
|
+
if (callee.object.name !== variable.name) {
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
return variable.identifiers.some(function (id) {
|
|
191
|
+
return angularModuleIdentifiers.indexOf(id) !== -1;
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
if (isAngularModule) {
|
|
195
|
+
angularChainables.push(callExpressionNode);
|
|
196
|
+
angularComponents.push(getThisGuyRightHere());
|
|
197
|
+
} else {
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
} else {
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (callExpressionNode.parent.type === 'VariableDeclarator') {
|
|
205
|
+
// var app = angular.module()
|
|
206
|
+
// ^^^
|
|
207
|
+
angularModuleIdentifiers.push(callExpressionNode.parent.id);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Find the argument by an Angular component callee.
|
|
214
|
+
*/
|
|
215
|
+
function findInjectedArgument(callExpressionNode) {
|
|
216
|
+
const callee = callExpressionNode.callee;
|
|
217
|
+
if (callee.type === 'Identifier') {
|
|
218
|
+
return callExpressionNode.arguments[0];
|
|
219
|
+
} else if (['run', 'config'].includes(callee.property.name)) {
|
|
220
|
+
return callExpressionNode.arguments[0];
|
|
221
|
+
} else {
|
|
222
|
+
return callExpressionNode.arguments[1];
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Call the Angular specific rules defined by the rule definition.
|
|
228
|
+
*/
|
|
229
|
+
function callAngularRules(ruleObject, context) {
|
|
230
|
+
angularComponents.forEach(function (component) {
|
|
231
|
+
var name = component.callExpression.callee.property.name;
|
|
232
|
+
var fn = ruleObject['angular?' + name];
|
|
233
|
+
if (!fn) {
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
fn.apply(ruleObject, assembleArguments(component, context));
|
|
237
|
+
});
|
|
238
|
+
var injectRule = ruleObject['angular?inject'];
|
|
239
|
+
if (injectRule) {
|
|
240
|
+
injectCalls.forEach(function (thisGuy) {
|
|
241
|
+
injectRule.call(ruleObject, thisGuy.callExpression.callee, thisGuy);
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Assemble the arguments for an Angular callee check.
|
|
248
|
+
*/
|
|
249
|
+
function assembleArguments(thisGuy) {
|
|
250
|
+
switch (thisGuy.callExpression.callee.property.name) {
|
|
251
|
+
case 'animation':
|
|
252
|
+
case 'component':
|
|
253
|
+
case 'config':
|
|
254
|
+
case 'controller':
|
|
255
|
+
case 'decorator':
|
|
256
|
+
case 'directive':
|
|
257
|
+
case 'factory':
|
|
258
|
+
case 'filter':
|
|
259
|
+
case 'run':
|
|
260
|
+
case 'service':
|
|
261
|
+
return [thisGuy.callExpression.callee, thisGuy];
|
|
262
|
+
case 'provider':
|
|
263
|
+
return assembleProviderArguments(thisGuy);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Assemble arguments for a provider rule.
|
|
269
|
+
*
|
|
270
|
+
* On top of a regular Angular component rule, the provider rule gets called with the $get function as its 3rd argument.
|
|
271
|
+
*/
|
|
272
|
+
function assembleProviderArguments(thisGuy) {
|
|
273
|
+
return [thisGuy.callExpression, thisGuy, Object.assign({}, thisGuy, { fn: findProviderGet(thisGuy) })];
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Find the $get function of a provider based on the provider function body.
|
|
278
|
+
*/
|
|
279
|
+
function findProviderGet(thisGuy) {
|
|
280
|
+
let providerFn = thisGuy.node;
|
|
281
|
+
if (providerFn && providerFn.type === 'Identifier') {
|
|
282
|
+
providerFn = thisGuy.scope.variables.find((v) => v.name === providerFn.name).defs[0].node;
|
|
283
|
+
}
|
|
284
|
+
if (!providerFn) {
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
const class$get = providerFn.body.body.find((node) => node.type === 'MethodDefinition' && node.key.name === '$get');
|
|
289
|
+
const obj$get = providerFn.body.body.find((node) => {
|
|
290
|
+
const expr = node.expression;
|
|
291
|
+
return (
|
|
292
|
+
expr &&
|
|
293
|
+
expr.type === 'AssignmentExpression' &&
|
|
294
|
+
expr.left.type === 'MemberExpression' &&
|
|
295
|
+
expr.left.property.name === '$get'
|
|
296
|
+
);
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
const getFn = (class$get && class$get.value) || (obj$get && obj$get.expression.right);
|
|
300
|
+
return getFn && getFn.type === 'ArrayExpression' ? getFn.elements[getFn.elements.length - 1] : getFn;
|
|
301
|
+
}
|
|
302
|
+
}
|