eslint 4.7.1 → 4.10.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/CHANGELOG.md +113 -0
- package/README.md +34 -19
- package/conf/default-cli-options.js +2 -1
- package/conf/eslint-recommended.js +2 -0
- package/lib/ast-utils.js +2 -1
- package/lib/cli-engine.js +26 -5
- package/lib/cli.js +17 -9
- package/lib/code-path-analysis/code-path-segment.js +39 -39
- package/lib/code-path-analysis/code-path-state.js +3 -0
- package/lib/formatters/html-template-message.html +1 -1
- package/lib/formatters/html-template-page.html +3 -1
- package/lib/formatters/html.js +2 -1
- package/lib/ignored-paths.js +1 -1
- package/lib/linter.js +43 -71
- package/lib/logging.js +2 -2
- package/lib/options.js +12 -0
- package/lib/rules/array-bracket-newline.js +19 -5
- package/lib/rules/block-spacing.js +1 -1
- package/lib/rules/callback-return.js +2 -1
- package/lib/rules/capitalized-comments.js +2 -1
- package/lib/rules/comma-style.js +3 -1
- package/lib/rules/dot-notation.js +56 -35
- package/lib/rules/generator-star-spacing.js +3 -3
- package/lib/rules/indent-legacy.js +5 -2
- package/lib/rules/indent.js +25 -19
- package/lib/rules/lines-around-comment.js +33 -4
- package/lib/rules/lines-between-class-members.js +91 -0
- package/lib/rules/max-len.js +2 -3
- package/lib/rules/multiline-comment-style.js +294 -0
- package/lib/rules/new-cap.js +2 -1
- package/lib/rules/newline-before-return.js +4 -2
- package/lib/rules/no-alert.js +7 -15
- package/lib/rules/no-catch-shadow.js +1 -1
- package/lib/rules/no-constant-condition.js +2 -2
- package/lib/rules/no-control-regex.js +2 -1
- package/lib/rules/no-else-return.js +43 -8
- package/lib/rules/no-extra-parens.js +6 -3
- package/lib/rules/no-lonely-if.js +2 -1
- package/lib/rules/no-loop-func.js +2 -3
- package/lib/rules/no-mixed-requires.js +8 -4
- package/lib/rules/no-restricted-imports.js +86 -17
- package/lib/rules/no-restricted-modules.js +84 -15
- package/lib/rules/no-trailing-spaces.js +1 -1
- package/lib/rules/no-unneeded-ternary.js +3 -1
- package/lib/rules/no-unused-labels.js +2 -1
- package/lib/rules/no-useless-computed-key.js +2 -1
- package/lib/rules/no-useless-escape.js +8 -1
- package/lib/rules/no-var.js +11 -0
- package/lib/rules/object-shorthand.js +6 -2
- package/lib/rules/operator-linebreak.js +3 -1
- package/lib/rules/padding-line-between-statements.js +2 -2
- package/lib/rules/require-jsdoc.js +11 -18
- package/lib/rules/sort-imports.js +6 -3
- package/lib/rules/space-unary-ops.js +6 -8
- package/lib/rules/valid-jsdoc.js +39 -33
- package/lib/testers/rule-tester.js +20 -6
- package/lib/util/apply-disable-directives.js +56 -27
- package/lib/util/node-event-generator.js +6 -20
- package/lib/util/safe-emitter.js +54 -0
- package/lib/util/source-code.js +73 -67
- package/messages/no-config-found.txt +1 -1
- package/package.json +3 -4
- package/lib/internal-rules/.eslintrc.yml +0 -3
- package/lib/internal-rules/internal-consistent-docs-description.js +0 -130
- package/lib/internal-rules/internal-no-invalid-meta.js +0 -188
package/lib/rules/no-alert.js
CHANGED
@@ -53,11 +53,10 @@ function findReference(scope, node) {
|
|
53
53
|
/**
|
54
54
|
* Checks if the given identifier node is shadowed in the given scope.
|
55
55
|
* @param {Object} scope The current scope.
|
56
|
-
* @param {Object} globalScope The global scope.
|
57
56
|
* @param {string} node The identifier node to check
|
58
57
|
* @returns {boolean} Whether or not the name is shadowed.
|
59
58
|
*/
|
60
|
-
function isShadowed(scope,
|
59
|
+
function isShadowed(scope, node) {
|
61
60
|
const reference = findReference(scope, node);
|
62
61
|
|
63
62
|
return reference && reference.resolved && reference.resolved.defs.length > 0;
|
@@ -66,15 +65,15 @@ function isShadowed(scope, globalScope, node) {
|
|
66
65
|
/**
|
67
66
|
* Checks if the given identifier node is a ThisExpression in the global scope or the global window property.
|
68
67
|
* @param {Object} scope The current scope.
|
69
|
-
* @param {Object} globalScope The global scope.
|
70
68
|
* @param {string} node The identifier node to check
|
71
69
|
* @returns {boolean} Whether or not the node is a reference to the global object.
|
72
70
|
*/
|
73
|
-
function isGlobalThisReferenceOrGlobalWindow(scope,
|
71
|
+
function isGlobalThisReferenceOrGlobalWindow(scope, node) {
|
74
72
|
if (scope.type === "global" && node.type === "ThisExpression") {
|
75
73
|
return true;
|
76
|
-
}
|
77
|
-
|
74
|
+
}
|
75
|
+
if (node.name === "window") {
|
76
|
+
return !isShadowed(scope, node);
|
78
77
|
}
|
79
78
|
|
80
79
|
return false;
|
@@ -96,14 +95,7 @@ module.exports = {
|
|
96
95
|
},
|
97
96
|
|
98
97
|
create(context) {
|
99
|
-
let globalScope;
|
100
|
-
|
101
98
|
return {
|
102
|
-
|
103
|
-
Program() {
|
104
|
-
globalScope = context.getScope();
|
105
|
-
},
|
106
|
-
|
107
99
|
CallExpression(node) {
|
108
100
|
const callee = node.callee,
|
109
101
|
currentScope = context.getScope();
|
@@ -112,11 +104,11 @@ module.exports = {
|
|
112
104
|
if (callee.type === "Identifier") {
|
113
105
|
const identifierName = callee.name;
|
114
106
|
|
115
|
-
if (!isShadowed(currentScope,
|
107
|
+
if (!isShadowed(currentScope, callee) && isProhibitedIdentifier(callee.name)) {
|
116
108
|
report(context, node, identifierName);
|
117
109
|
}
|
118
110
|
|
119
|
-
} else if (callee.type === "MemberExpression" && isGlobalThisReferenceOrGlobalWindow(currentScope,
|
111
|
+
} else if (callee.type === "MemberExpression" && isGlobalThisReferenceOrGlobalWindow(currentScope, callee.object)) {
|
120
112
|
const identifierName = getPropertyName(callee);
|
121
113
|
|
122
114
|
if (isProhibitedIdentifier(identifierName)) {
|
@@ -51,7 +51,7 @@ module.exports = {
|
|
51
51
|
CatchClause(node) {
|
52
52
|
let scope = context.getScope();
|
53
53
|
|
54
|
-
// When
|
54
|
+
// When ecmaVersion >= 6, CatchClause creates its own scope
|
55
55
|
// so start from one upper scope to exclude the current node
|
56
56
|
if (scope.block === node) {
|
57
57
|
scope = scope.upper;
|
@@ -138,7 +138,7 @@ module.exports = {
|
|
138
138
|
function checkConstantConditionLoopInSet(node) {
|
139
139
|
if (loopsInCurrentScope.has(node)) {
|
140
140
|
loopsInCurrentScope.delete(node);
|
141
|
-
context.report({ node, message: "Unexpected constant condition." });
|
141
|
+
context.report({ node: node.test, message: "Unexpected constant condition." });
|
142
142
|
}
|
143
143
|
}
|
144
144
|
|
@@ -150,7 +150,7 @@ module.exports = {
|
|
150
150
|
*/
|
151
151
|
function reportIfConstant(node) {
|
152
152
|
if (node.test && isConstant(node.test, true)) {
|
153
|
-
context.report({ node, message: "Unexpected constant condition." });
|
153
|
+
context.report({ node: node.test, message: "Unexpected constant condition." });
|
154
154
|
}
|
155
155
|
}
|
156
156
|
|
@@ -24,8 +24,15 @@ module.exports = {
|
|
24
24
|
recommended: false
|
25
25
|
},
|
26
26
|
|
27
|
-
schema: [
|
28
|
-
|
27
|
+
schema: [{
|
28
|
+
type: "object",
|
29
|
+
properties: {
|
30
|
+
allowElseIf: {
|
31
|
+
type: "boolean"
|
32
|
+
}
|
33
|
+
},
|
34
|
+
additionalProperties: false
|
35
|
+
}],
|
29
36
|
fixable: "code"
|
30
37
|
},
|
31
38
|
|
@@ -134,13 +141,13 @@ module.exports = {
|
|
134
141
|
|
135
142
|
/**
|
136
143
|
* Check to see if the node is valid for evaluation,
|
137
|
-
* meaning it has an else
|
144
|
+
* meaning it has an else.
|
138
145
|
*
|
139
146
|
* @param {Node} node The node being evaluated
|
140
147
|
* @returns {boolean} True if the node is valid
|
141
148
|
*/
|
142
149
|
function hasElse(node) {
|
143
|
-
return node.alternate && node.consequent
|
150
|
+
return node.alternate && node.consequent;
|
144
151
|
}
|
145
152
|
|
146
153
|
/**
|
@@ -189,14 +196,15 @@ module.exports = {
|
|
189
196
|
return checkForReturnOrIf(node);
|
190
197
|
}
|
191
198
|
|
199
|
+
|
192
200
|
/**
|
193
|
-
* Check the if statement
|
201
|
+
* Check the if statement, but don't catch else-if blocks.
|
194
202
|
* @returns {void}
|
195
203
|
* @param {Node} node The node for the if statement to check
|
196
204
|
* @private
|
197
205
|
*/
|
198
|
-
function
|
199
|
-
const parent =
|
206
|
+
function checkIfWithoutElse(node) {
|
207
|
+
const parent = node.parent;
|
200
208
|
let consequents,
|
201
209
|
alternate;
|
202
210
|
|
@@ -221,13 +229,40 @@ module.exports = {
|
|
221
229
|
}
|
222
230
|
}
|
223
231
|
|
232
|
+
/**
|
233
|
+
* Check the if statement
|
234
|
+
* @returns {void}
|
235
|
+
* @param {Node} node The node for the if statement to check
|
236
|
+
* @private
|
237
|
+
*/
|
238
|
+
function checkIfWithElse(node) {
|
239
|
+
const parent = node.parent;
|
240
|
+
|
241
|
+
|
242
|
+
/*
|
243
|
+
* Fixing this would require splitting one statement into two, so no error should
|
244
|
+
* be reported if this node is in a position where only one statement is allowed.
|
245
|
+
*/
|
246
|
+
if (!astUtils.STATEMENT_LIST_PARENTS.has(parent.type)) {
|
247
|
+
return;
|
248
|
+
}
|
249
|
+
|
250
|
+
const alternate = node.alternate;
|
251
|
+
|
252
|
+
if (alternate && alwaysReturns(node.consequent)) {
|
253
|
+
displayReport(alternate);
|
254
|
+
}
|
255
|
+
}
|
256
|
+
|
257
|
+
const allowElseIf = !(context.options[0] && context.options[0].allowElseIf === false);
|
258
|
+
|
224
259
|
//--------------------------------------------------------------------------
|
225
260
|
// Public API
|
226
261
|
//--------------------------------------------------------------------------
|
227
262
|
|
228
263
|
return {
|
229
264
|
|
230
|
-
"IfStatement:exit":
|
265
|
+
"IfStatement:exit": allowElseIf ? checkIfWithoutElse : checkIfWithElse
|
231
266
|
|
232
267
|
};
|
233
268
|
|
@@ -195,10 +195,12 @@ module.exports = {
|
|
195
195
|
function containsAssignment(node) {
|
196
196
|
if (node.type === "AssignmentExpression") {
|
197
197
|
return true;
|
198
|
-
}
|
198
|
+
}
|
199
|
+
if (node.type === "ConditionalExpression" &&
|
199
200
|
(node.consequent.type === "AssignmentExpression" || node.alternate.type === "AssignmentExpression")) {
|
200
201
|
return true;
|
201
|
-
}
|
202
|
+
}
|
203
|
+
if ((node.left && node.left.type === "AssignmentExpression") ||
|
202
204
|
(node.right && node.right.type === "AssignmentExpression")) {
|
203
205
|
return true;
|
204
206
|
}
|
@@ -219,7 +221,8 @@ module.exports = {
|
|
219
221
|
|
220
222
|
if (node.type === "ReturnStatement") {
|
221
223
|
return node.argument && containsAssignment(node.argument);
|
222
|
-
}
|
224
|
+
}
|
225
|
+
if (node.type === "ArrowFunctionExpression" && node.body.type !== "BlockStatement") {
|
223
226
|
return containsAssignment(node.body);
|
224
227
|
}
|
225
228
|
return containsAssignment(node);
|
@@ -45,7 +45,8 @@ module.exports = {
|
|
45
45
|
const lastIfToken = sourceCode.getLastToken(node.consequent);
|
46
46
|
const sourceText = sourceCode.getText();
|
47
47
|
|
48
|
-
if (sourceText.slice(openingElseCurly.range[1],
|
48
|
+
if (sourceText.slice(openingElseCurly.range[1],
|
49
|
+
node.range[0]).trim() || sourceText.slice(node.range[1], closingElseCurly.range[0]).trim()) {
|
49
50
|
|
50
51
|
// Don't fix if there are any non-whitespace characters interfering (e.g. comments)
|
51
52
|
return null;
|
@@ -88,12 +88,11 @@ function getTopLoopNode(node, excludedNode) {
|
|
88
88
|
* Checks whether a given reference which refers to an upper scope's variable is
|
89
89
|
* safe or not.
|
90
90
|
*
|
91
|
-
* @param {ASTNode} funcNode - A target function node.
|
92
91
|
* @param {ASTNode} loopNode - A containing loop node.
|
93
92
|
* @param {eslint-scope.Reference} reference - A reference to check.
|
94
93
|
* @returns {boolean} `true` if the reference is safe or not.
|
95
94
|
*/
|
96
|
-
function isSafe(
|
95
|
+
function isSafe(loopNode, reference) {
|
97
96
|
const variable = reference.resolved;
|
98
97
|
const definition = variable && variable.defs[0];
|
99
98
|
const declaration = definition && definition.parent;
|
@@ -183,7 +182,7 @@ module.exports = {
|
|
183
182
|
const references = context.getScope().through;
|
184
183
|
|
185
184
|
if (references.length > 0 &&
|
186
|
-
!references.every(isSafe.bind(null,
|
185
|
+
!references.every(isSafe.bind(null, loopNode))
|
187
186
|
) {
|
188
187
|
context.report({ node, message: "Don't make functions within a loop." });
|
189
188
|
}
|
@@ -104,14 +104,16 @@ module.exports = {
|
|
104
104
|
|
105
105
|
// "var x = require('util');"
|
106
106
|
return DECL_REQUIRE;
|
107
|
-
}
|
107
|
+
}
|
108
|
+
if (allowCall &&
|
108
109
|
initExpression.type === "CallExpression" &&
|
109
110
|
initExpression.callee.type === "CallExpression"
|
110
111
|
) {
|
111
112
|
|
112
113
|
// "var x = require('diagnose')('sub-module');"
|
113
114
|
return getDeclarationType(initExpression.callee);
|
114
|
-
}
|
115
|
+
}
|
116
|
+
if (initExpression.type === "MemberExpression") {
|
115
117
|
|
116
118
|
// "var x = require('glob').Glob;"
|
117
119
|
return getDeclarationType(initExpression.object);
|
@@ -131,7 +133,8 @@ module.exports = {
|
|
131
133
|
|
132
134
|
// "var x = require('glob').Glob;"
|
133
135
|
return inferModuleType(initExpression.object);
|
134
|
-
}
|
136
|
+
}
|
137
|
+
if (initExpression.arguments.length === 0) {
|
135
138
|
|
136
139
|
// "var x = require();"
|
137
140
|
return REQ_COMPUTED;
|
@@ -149,7 +152,8 @@ module.exports = {
|
|
149
152
|
|
150
153
|
// "var fs = require('fs');"
|
151
154
|
return REQ_CORE;
|
152
|
-
}
|
155
|
+
}
|
156
|
+
if (/^\.{0,2}\//.test(arg.value)) {
|
153
157
|
|
154
158
|
// "var utils = require('./utils');"
|
155
159
|
return REQ_FILE;
|
@@ -4,6 +4,13 @@
|
|
4
4
|
*/
|
5
5
|
"use strict";
|
6
6
|
|
7
|
+
//------------------------------------------------------------------------------
|
8
|
+
// Helpers
|
9
|
+
//------------------------------------------------------------------------------
|
10
|
+
|
11
|
+
const DEFAULT_MESSAGE_TEMPLATE = "'{{importName}}' import is restricted from being used.";
|
12
|
+
const CUSTOM_MESSAGE_TEMPLATE = "'{{importName}}' import is restricted from being used. {{customMessage}}";
|
13
|
+
|
7
14
|
//------------------------------------------------------------------------------
|
8
15
|
// Rule Definition
|
9
16
|
//------------------------------------------------------------------------------
|
@@ -11,9 +18,29 @@
|
|
11
18
|
const ignore = require("ignore");
|
12
19
|
|
13
20
|
const arrayOfStrings = {
|
21
|
+
type: "array",
|
22
|
+
items: { type: "string" },
|
23
|
+
uniqueItems: true
|
24
|
+
};
|
25
|
+
|
26
|
+
const arrayOfStringsOrObjects = {
|
14
27
|
type: "array",
|
15
28
|
items: {
|
16
|
-
|
29
|
+
anyOf: [
|
30
|
+
{ type: "string" },
|
31
|
+
{
|
32
|
+
type: "object",
|
33
|
+
properties: {
|
34
|
+
name: { type: "string" },
|
35
|
+
message: {
|
36
|
+
type: "string",
|
37
|
+
minLength: 1
|
38
|
+
}
|
39
|
+
},
|
40
|
+
additionalProperties: false,
|
41
|
+
required: ["name"]
|
42
|
+
}
|
43
|
+
]
|
17
44
|
},
|
18
45
|
uniqueItems: true
|
19
46
|
};
|
@@ -28,17 +55,17 @@ module.exports = {
|
|
28
55
|
|
29
56
|
schema: {
|
30
57
|
anyOf: [
|
31
|
-
|
58
|
+
arrayOfStringsOrObjects,
|
32
59
|
{
|
33
60
|
type: "array",
|
34
|
-
items:
|
61
|
+
items: {
|
35
62
|
type: "object",
|
36
63
|
properties: {
|
37
|
-
paths:
|
64
|
+
paths: arrayOfStringsOrObjects,
|
38
65
|
patterns: arrayOfStrings
|
39
66
|
},
|
40
67
|
additionalProperties: false
|
41
|
-
}
|
68
|
+
},
|
42
69
|
additionalItems: false
|
43
70
|
}
|
44
71
|
]
|
@@ -47,35 +74,77 @@ module.exports = {
|
|
47
74
|
|
48
75
|
create(context) {
|
49
76
|
const options = Array.isArray(context.options) ? context.options : [];
|
50
|
-
const
|
51
|
-
|
52
|
-
|
77
|
+
const isPathAndPatternsObject =
|
78
|
+
typeof options[0] === "object" &&
|
79
|
+
(options[0].hasOwnProperty("paths") || options[0].hasOwnProperty("patterns"));
|
80
|
+
|
81
|
+
const restrictedPaths = (isPathAndPatternsObject ? options[0].paths : context.options) || [];
|
82
|
+
const restrictedPatterns = (isPathAndPatternsObject ? options[0].patterns : []) || [];
|
83
|
+
|
84
|
+
const restrictedPathMessages = restrictedPaths.reduce((memo, importName) => {
|
85
|
+
if (typeof importName === "string") {
|
86
|
+
memo[importName] = null;
|
87
|
+
} else {
|
88
|
+
memo[importName.name] = importName.message;
|
89
|
+
}
|
90
|
+
return memo;
|
91
|
+
}, {});
|
53
92
|
|
54
93
|
// if no imports are restricted we don"t need to check
|
55
|
-
if (restrictedPaths.
|
94
|
+
if (Object.keys(restrictedPaths).length === 0 && restrictedPatterns.length === 0) {
|
56
95
|
return {};
|
57
96
|
}
|
58
97
|
|
59
98
|
const ig = ignore().add(restrictedPatterns);
|
60
99
|
|
100
|
+
/**
|
101
|
+
* Report a restricted path.
|
102
|
+
* @param {node} node representing the restricted path reference
|
103
|
+
* @returns {void}
|
104
|
+
* @private
|
105
|
+
*/
|
106
|
+
function reportPath(node) {
|
107
|
+
const importName = node.source.value.trim();
|
108
|
+
const customMessage = restrictedPathMessages[importName];
|
109
|
+
const message = customMessage
|
110
|
+
? CUSTOM_MESSAGE_TEMPLATE
|
111
|
+
: DEFAULT_MESSAGE_TEMPLATE;
|
112
|
+
|
113
|
+
context.report({
|
114
|
+
node,
|
115
|
+
message,
|
116
|
+
data: {
|
117
|
+
importName,
|
118
|
+
customMessage
|
119
|
+
}
|
120
|
+
});
|
121
|
+
}
|
122
|
+
|
123
|
+
/**
|
124
|
+
* Check if the given name is a restricted path name.
|
125
|
+
* @param {string} name name of a variable
|
126
|
+
* @returns {boolean} whether the variable is a restricted path or not
|
127
|
+
* @private
|
128
|
+
*/
|
129
|
+
function isRestrictedPath(name) {
|
130
|
+
return Object.prototype.hasOwnProperty.call(restrictedPathMessages, name);
|
131
|
+
}
|
132
|
+
|
61
133
|
return {
|
62
134
|
ImportDeclaration(node) {
|
63
135
|
if (node && node.source && node.source.value) {
|
64
|
-
|
65
136
|
const importName = node.source.value.trim();
|
66
137
|
|
67
|
-
if (
|
68
|
-
|
69
|
-
node,
|
70
|
-
message: "'{{importName}}' import is restricted from being used.",
|
71
|
-
data: { importName }
|
72
|
-
});
|
138
|
+
if (isRestrictedPath(importName)) {
|
139
|
+
reportPath(node);
|
73
140
|
}
|
74
141
|
if (restrictedPatterns.length > 0 && ig.ignores(importName)) {
|
75
142
|
context.report({
|
76
143
|
node,
|
77
144
|
message: "'{{importName}}' import is restricted from being used by a pattern.",
|
78
|
-
data: {
|
145
|
+
data: {
|
146
|
+
importName
|
147
|
+
}
|
79
148
|
});
|
80
149
|
}
|
81
150
|
}
|
@@ -4,6 +4,13 @@
|
|
4
4
|
*/
|
5
5
|
"use strict";
|
6
6
|
|
7
|
+
//------------------------------------------------------------------------------
|
8
|
+
// Helpers
|
9
|
+
//------------------------------------------------------------------------------
|
10
|
+
|
11
|
+
const DEFAULT_MESSAGE_TEMPLATE = "'{{moduleName}}' module is restricted from being used.";
|
12
|
+
const CUSTOM_MESSAGE_TEMPLATE = "'{{moduleName}}' module is restricted from being used. {{customMessage}}";
|
13
|
+
|
7
14
|
//------------------------------------------------------------------------------
|
8
15
|
// Rule Definition
|
9
16
|
//------------------------------------------------------------------------------
|
@@ -11,9 +18,29 @@
|
|
11
18
|
const ignore = require("ignore");
|
12
19
|
|
13
20
|
const arrayOfStrings = {
|
21
|
+
type: "array",
|
22
|
+
items: { type: "string" },
|
23
|
+
uniqueItems: true
|
24
|
+
};
|
25
|
+
|
26
|
+
const arrayOfStringsOrObjects = {
|
14
27
|
type: "array",
|
15
28
|
items: {
|
16
|
-
|
29
|
+
anyOf: [
|
30
|
+
{ type: "string" },
|
31
|
+
{
|
32
|
+
type: "object",
|
33
|
+
properties: {
|
34
|
+
name: { type: "string" },
|
35
|
+
message: {
|
36
|
+
type: "string",
|
37
|
+
minLength: 1
|
38
|
+
}
|
39
|
+
},
|
40
|
+
additionalProperties: false,
|
41
|
+
required: ["name"]
|
42
|
+
}
|
43
|
+
]
|
17
44
|
},
|
18
45
|
uniqueItems: true
|
19
46
|
};
|
@@ -28,17 +55,17 @@ module.exports = {
|
|
28
55
|
|
29
56
|
schema: {
|
30
57
|
anyOf: [
|
31
|
-
|
58
|
+
arrayOfStringsOrObjects,
|
32
59
|
{
|
33
60
|
type: "array",
|
34
|
-
items:
|
61
|
+
items: {
|
35
62
|
type: "object",
|
36
63
|
properties: {
|
37
|
-
paths:
|
64
|
+
paths: arrayOfStringsOrObjects,
|
38
65
|
patterns: arrayOfStrings
|
39
66
|
},
|
40
67
|
additionalProperties: false
|
41
|
-
}
|
68
|
+
},
|
42
69
|
additionalItems: false
|
43
70
|
}
|
44
71
|
]
|
@@ -47,17 +74,30 @@ module.exports = {
|
|
47
74
|
|
48
75
|
create(context) {
|
49
76
|
const options = Array.isArray(context.options) ? context.options : [];
|
50
|
-
const
|
51
|
-
|
52
|
-
|
77
|
+
const isPathAndPatternsObject =
|
78
|
+
typeof options[0] === "object" &&
|
79
|
+
(options[0].hasOwnProperty("paths") || options[0].hasOwnProperty("patterns"));
|
80
|
+
|
81
|
+
const restrictedPaths = (isPathAndPatternsObject ? options[0].paths : context.options) || [];
|
82
|
+
const restrictedPatterns = (isPathAndPatternsObject ? options[0].patterns : []) || [];
|
83
|
+
|
84
|
+
const restrictedPathMessages = restrictedPaths.reduce((memo, importName) => {
|
85
|
+
if (typeof importName === "string") {
|
86
|
+
memo[importName] = null;
|
87
|
+
} else {
|
88
|
+
memo[importName.name] = importName.message;
|
89
|
+
}
|
90
|
+
return memo;
|
91
|
+
}, {});
|
53
92
|
|
54
93
|
// if no imports are restricted we don"t need to check
|
55
|
-
if (restrictedPaths.
|
94
|
+
if (Object.keys(restrictedPaths).length === 0 && restrictedPatterns.length === 0) {
|
56
95
|
return {};
|
57
96
|
}
|
58
97
|
|
59
98
|
const ig = ignore().add(restrictedPatterns);
|
60
99
|
|
100
|
+
|
61
101
|
/**
|
62
102
|
* Function to check if a node is a string literal.
|
63
103
|
* @param {ASTNode} node The node to check.
|
@@ -76,6 +116,39 @@ module.exports = {
|
|
76
116
|
return node.callee.type === "Identifier" && node.callee.name === "require";
|
77
117
|
}
|
78
118
|
|
119
|
+
/**
|
120
|
+
* Report a restricted path.
|
121
|
+
* @param {node} node representing the restricted path reference
|
122
|
+
* @returns {void}
|
123
|
+
* @private
|
124
|
+
*/
|
125
|
+
function reportPath(node) {
|
126
|
+
const moduleName = node.arguments[0].value.trim();
|
127
|
+
const customMessage = restrictedPathMessages[moduleName];
|
128
|
+
const message = customMessage
|
129
|
+
? CUSTOM_MESSAGE_TEMPLATE
|
130
|
+
: DEFAULT_MESSAGE_TEMPLATE;
|
131
|
+
|
132
|
+
context.report({
|
133
|
+
node,
|
134
|
+
message,
|
135
|
+
data: {
|
136
|
+
moduleName,
|
137
|
+
customMessage
|
138
|
+
}
|
139
|
+
});
|
140
|
+
}
|
141
|
+
|
142
|
+
/**
|
143
|
+
* Check if the given name is a restricted path name
|
144
|
+
* @param {string} name name of a variable
|
145
|
+
* @returns {boolean} whether the variable is a restricted path or not
|
146
|
+
* @private
|
147
|
+
*/
|
148
|
+
function isRestrictedPath(name) {
|
149
|
+
return Object.prototype.hasOwnProperty.call(restrictedPathMessages, name);
|
150
|
+
}
|
151
|
+
|
79
152
|
return {
|
80
153
|
CallExpression(node) {
|
81
154
|
if (isRequireCall(node)) {
|
@@ -85,12 +158,8 @@ module.exports = {
|
|
85
158
|
const moduleName = node.arguments[0].value.trim();
|
86
159
|
|
87
160
|
// check if argument value is in restricted modules array
|
88
|
-
if (
|
89
|
-
|
90
|
-
node,
|
91
|
-
message: "'{{moduleName}}' module is restricted from being used.",
|
92
|
-
data: { moduleName }
|
93
|
-
});
|
161
|
+
if (isRestrictedPath(moduleName)) {
|
162
|
+
reportPath(node);
|
94
163
|
}
|
95
164
|
|
96
165
|
if (restrictedPatterns.length > 0 && ig.ignores(moduleName)) {
|
@@ -49,7 +49,7 @@ module.exports = {
|
|
49
49
|
|
50
50
|
const options = context.options[0] || {},
|
51
51
|
skipBlankLines = options.skipBlankLines || false,
|
52
|
-
ignoreComments = typeof options.ignoreComments === "
|
52
|
+
ignoreComments = typeof options.ignoreComments === "boolean" && options.ignoreComments;
|
53
53
|
|
54
54
|
/**
|
55
55
|
* Report the error message
|
@@ -72,8 +72,10 @@ module.exports = {
|
|
72
72
|
node.right,
|
73
73
|
token => token.value === node.operator
|
74
74
|
);
|
75
|
+
const text = sourceCode.getText();
|
75
76
|
|
76
|
-
return
|
77
|
+
return text.slice(node.range[0],
|
78
|
+
operatorToken.range[0]) + OPERATOR_INVERSES[node.operator] + text.slice(operatorToken.range[1], node.range[1]);
|
77
79
|
}
|
78
80
|
|
79
81
|
if (astUtils.getPrecedence(node) < astUtils.getPrecedence({ type: "UnaryExpression" })) {
|
@@ -59,7 +59,8 @@ module.exports = {
|
|
59
59
|
* Only perform a fix if there are no comments between the label and the body. This will be the case
|
60
60
|
* when there is exactly one token/comment (the ":") between the label and the body.
|
61
61
|
*/
|
62
|
-
if (sourceCode.getTokenAfter(node.label, { includeComments: true }) ===
|
62
|
+
if (sourceCode.getTokenAfter(node.label, { includeComments: true }) ===
|
63
|
+
sourceCode.getTokenBefore(node.body, { includeComments: true })) {
|
63
64
|
return fixer.removeRange([node.range[0], node.body.range[0]]);
|
64
65
|
}
|
65
66
|
|
@@ -50,7 +50,8 @@ module.exports = {
|
|
50
50
|
const rightSquareBracket = sourceCode.getFirstTokenBetween(node.key, node.value, astUtils.isClosingBracketToken);
|
51
51
|
const tokensBetween = sourceCode.getTokensBetween(leftSquareBracket, rightSquareBracket, 1);
|
52
52
|
|
53
|
-
if (tokensBetween.slice(0, -1).some((token, index) =>
|
53
|
+
if (tokensBetween.slice(0, -1).some((token, index) =>
|
54
|
+
sourceCode.getText().slice(token.range[1], tokensBetween[index + 1].range[0]).trim())) {
|
54
55
|
|
55
56
|
// If there are comments between the brackets and the property name, don't do a fix.
|
56
57
|
return null;
|
@@ -63,7 +63,14 @@ function parseRegExp(regExpText) {
|
|
63
63
|
return Object.assign(state, { inCharClass: false, startingCharClass: false });
|
64
64
|
}
|
65
65
|
}
|
66
|
-
charList.push({
|
66
|
+
charList.push({
|
67
|
+
text: char,
|
68
|
+
index,
|
69
|
+
escaped: state.escapeNextChar,
|
70
|
+
inCharClass: state.inCharClass,
|
71
|
+
startsCharClass: state.startingCharClass,
|
72
|
+
endsCharClass: false
|
73
|
+
});
|
67
74
|
return Object.assign(state, { escapeNextChar: false, startingCharClass: false });
|
68
75
|
}, { escapeNextChar: false, inCharClass: false, startingCharClass: false });
|
69
76
|
|