eslint 7.26.0 → 7.30.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 +58 -0
- package/README.md +12 -7
- package/bin/eslint.js +2 -12
- package/lib/cli-engine/file-enumerator.js +1 -1
- package/lib/cli-engine/formatters/html.js +193 -9
- package/lib/config/default-config.js +52 -0
- package/lib/config/flat-config-array.js +125 -0
- package/lib/config/flat-config-schema.js +452 -0
- package/lib/config/rule-validator.js +169 -0
- package/lib/eslint/eslint.js +38 -2
- package/lib/init/autoconfig.js +2 -2
- package/lib/init/npm-utils.js +1 -2
- package/lib/linter/apply-disable-directives.js +15 -3
- package/lib/linter/linter.js +31 -21
- package/lib/linter/node-event-generator.js +43 -6
- package/lib/rule-tester/rule-tester.js +89 -23
- package/lib/rules/arrow-body-style.js +21 -11
- package/lib/rules/comma-dangle.js +16 -7
- package/lib/rules/comma-spacing.js +1 -1
- package/lib/rules/comma-style.js +1 -2
- package/lib/rules/complexity.js +2 -3
- package/lib/rules/consistent-return.js +2 -2
- package/lib/rules/dot-notation.js +3 -3
- package/lib/rules/eol-last.js +2 -7
- package/lib/rules/indent.js +10 -13
- package/lib/rules/max-lines-per-function.js +2 -3
- package/lib/rules/max-lines.js +32 -7
- package/lib/rules/max-params.js +2 -3
- package/lib/rules/max-statements.js +2 -3
- package/lib/rules/no-duplicate-imports.js +214 -66
- package/lib/rules/no-fallthrough.js +18 -13
- package/lib/rules/no-implicit-coercion.js +21 -2
- package/lib/rules/no-restricted-imports.js +61 -24
- package/lib/rules/no-unused-vars.js +40 -10
- package/lib/rules/no-useless-backreference.js +1 -2
- package/lib/rules/no-useless-computed-key.js +8 -2
- package/lib/rules/no-warning-comments.js +1 -1
- package/lib/rules/object-curly-newline.js +19 -4
- package/lib/rules/prefer-arrow-callback.js +4 -4
- package/lib/rules/spaced-comment.js +2 -2
- package/lib/rules/use-isnan.js +4 -1
- package/lib/rules/utils/ast-utils.js +2 -2
- package/lib/shared/deprecation-warnings.js +12 -3
- package/lib/shared/string-utils.js +22 -0
- package/lib/source-code/source-code.js +8 -7
- package/lib/source-code/token-store/utils.js +4 -12
- package/messages/{all-files-ignored.txt → all-files-ignored.js} +10 -2
- package/messages/extend-config-missing.js +13 -0
- package/messages/failed-to-read-json.js +11 -0
- package/messages/file-not-found.js +10 -0
- package/messages/{no-config-found.txt → no-config-found.js} +9 -1
- package/messages/plugin-conflict.js +22 -0
- package/messages/plugin-invalid.js +16 -0
- package/messages/plugin-missing.js +19 -0
- package/messages/{print-config-with-directory-path.txt → print-config-with-directory-path.js} +6 -0
- package/messages/whitespace-found.js +11 -0
- package/package.json +9 -7
- package/lib/cli-engine/formatters/html-template-message.html +0 -8
- package/lib/cli-engine/formatters/html-template-page.html +0 -115
- package/lib/cli-engine/formatters/html-template-result.html +0 -6
- package/messages/extend-config-missing.txt +0 -5
- package/messages/failed-to-read-json.txt +0 -3
- package/messages/file-not-found.txt +0 -2
- package/messages/plugin-conflict.txt +0 -7
- package/messages/plugin-invalid.txt +0 -8
- package/messages/plugin-missing.txt +0 -11
- package/messages/whitespace-found.txt +0 -3
@@ -4,12 +4,6 @@
|
|
4
4
|
*/
|
5
5
|
"use strict";
|
6
6
|
|
7
|
-
//------------------------------------------------------------------------------
|
8
|
-
// Requirements
|
9
|
-
//------------------------------------------------------------------------------
|
10
|
-
|
11
|
-
const lodash = require("lodash");
|
12
|
-
|
13
7
|
//------------------------------------------------------------------------------
|
14
8
|
// Helpers
|
15
9
|
//------------------------------------------------------------------------------
|
@@ -17,15 +11,26 @@ const lodash = require("lodash");
|
|
17
11
|
const DEFAULT_FALLTHROUGH_COMMENT = /falls?\s?through/iu;
|
18
12
|
|
19
13
|
/**
|
20
|
-
* Checks whether or not a given
|
21
|
-
* @param {ASTNode}
|
14
|
+
* Checks whether or not a given case has a fallthrough comment.
|
15
|
+
* @param {ASTNode} caseWhichFallsThrough SwitchCase node which falls through.
|
16
|
+
* @param {ASTNode} subsequentCase The case after caseWhichFallsThrough.
|
22
17
|
* @param {RuleContext} context A rule context which stores comments.
|
23
18
|
* @param {RegExp} fallthroughCommentPattern A pattern to match comment to.
|
24
|
-
* @returns {boolean} `true` if the
|
19
|
+
* @returns {boolean} `true` if the case has a valid fallthrough comment.
|
25
20
|
*/
|
26
|
-
function hasFallthroughComment(
|
21
|
+
function hasFallthroughComment(caseWhichFallsThrough, subsequentCase, context, fallthroughCommentPattern) {
|
27
22
|
const sourceCode = context.getSourceCode();
|
28
|
-
|
23
|
+
|
24
|
+
if (caseWhichFallsThrough.consequent.length === 1 && caseWhichFallsThrough.consequent[0].type === "BlockStatement") {
|
25
|
+
const trailingCloseBrace = sourceCode.getLastToken(caseWhichFallsThrough.consequent[0]);
|
26
|
+
const commentInBlock = sourceCode.getCommentsBefore(trailingCloseBrace).pop();
|
27
|
+
|
28
|
+
if (commentInBlock && fallthroughCommentPattern.test(commentInBlock.value)) {
|
29
|
+
return true;
|
30
|
+
}
|
31
|
+
}
|
32
|
+
|
33
|
+
const comment = sourceCode.getCommentsBefore(subsequentCase).pop();
|
29
34
|
|
30
35
|
return Boolean(comment && fallthroughCommentPattern.test(comment.value));
|
31
36
|
}
|
@@ -114,7 +119,7 @@ module.exports = {
|
|
114
119
|
* Checks whether or not there is a fallthrough comment.
|
115
120
|
* And reports the previous fallthrough node if that does not exist.
|
116
121
|
*/
|
117
|
-
if (fallthroughCase && !hasFallthroughComment(node, context, fallthroughCommentPattern)) {
|
122
|
+
if (fallthroughCase && !hasFallthroughComment(fallthroughCase, node, context, fallthroughCommentPattern)) {
|
118
123
|
context.report({
|
119
124
|
messageId: node.test ? "case" : "default",
|
120
125
|
node
|
@@ -133,7 +138,7 @@ module.exports = {
|
|
133
138
|
*/
|
134
139
|
if (currentCodePath.currentSegments.some(isReachable) &&
|
135
140
|
(node.consequent.length > 0 || hasBlankLinesBetween(node, nextToken)) &&
|
136
|
-
|
141
|
+
node.parent.cases[node.parent.cases.length - 1] !== node) {
|
137
142
|
fallthroughCase = node;
|
138
143
|
}
|
139
144
|
}
|
@@ -109,6 +109,20 @@ function getNonNumericOperand(node) {
|
|
109
109
|
return null;
|
110
110
|
}
|
111
111
|
|
112
|
+
/**
|
113
|
+
* Checks whether an expression evaluates to a string.
|
114
|
+
* @param {ASTNode} node node that represents the expression to check.
|
115
|
+
* @returns {boolean} Whether or not the expression evaluates to a string.
|
116
|
+
*/
|
117
|
+
function isStringType(node) {
|
118
|
+
return astUtils.isStringLiteral(node) ||
|
119
|
+
(
|
120
|
+
node.type === "CallExpression" &&
|
121
|
+
node.callee.type === "Identifier" &&
|
122
|
+
node.callee.name === "String"
|
123
|
+
);
|
124
|
+
}
|
125
|
+
|
112
126
|
/**
|
113
127
|
* Checks whether a node is an empty string literal or not.
|
114
128
|
* @param {ASTNode} node The node to check.
|
@@ -126,8 +140,8 @@ function isEmptyString(node) {
|
|
126
140
|
*/
|
127
141
|
function isConcatWithEmptyString(node) {
|
128
142
|
return node.operator === "+" && (
|
129
|
-
(isEmptyString(node.left) && !
|
130
|
-
(isEmptyString(node.right) && !
|
143
|
+
(isEmptyString(node.left) && !isStringType(node.right)) ||
|
144
|
+
(isEmptyString(node.right) && !isStringType(node.left))
|
131
145
|
);
|
132
146
|
}
|
133
147
|
|
@@ -332,6 +346,11 @@ module.exports = {
|
|
332
346
|
return;
|
333
347
|
}
|
334
348
|
|
349
|
+
// if the expression is already a string, then this isn't a coercion
|
350
|
+
if (isStringType(node.expressions[0])) {
|
351
|
+
return;
|
352
|
+
}
|
353
|
+
|
335
354
|
const code = sourceCode.getText(node.expressions[0]);
|
336
355
|
const recommendation = `String(${code})`;
|
337
356
|
|
@@ -10,12 +10,6 @@
|
|
10
10
|
|
11
11
|
const ignore = require("ignore");
|
12
12
|
|
13
|
-
const arrayOfStrings = {
|
14
|
-
type: "array",
|
15
|
-
items: { type: "string" },
|
16
|
-
uniqueItems: true
|
17
|
-
};
|
18
|
-
|
19
13
|
const arrayOfStringsOrObjects = {
|
20
14
|
type: "array",
|
21
15
|
items: {
|
@@ -44,6 +38,41 @@ const arrayOfStringsOrObjects = {
|
|
44
38
|
uniqueItems: true
|
45
39
|
};
|
46
40
|
|
41
|
+
const arrayOfStringsOrObjectPatterns = {
|
42
|
+
anyOf: [
|
43
|
+
{
|
44
|
+
type: "array",
|
45
|
+
items: {
|
46
|
+
type: "string"
|
47
|
+
},
|
48
|
+
uniqueItems: true
|
49
|
+
},
|
50
|
+
{
|
51
|
+
type: "array",
|
52
|
+
items: {
|
53
|
+
type: "object",
|
54
|
+
properties: {
|
55
|
+
group: {
|
56
|
+
type: "array",
|
57
|
+
items: {
|
58
|
+
type: "string"
|
59
|
+
},
|
60
|
+
minItems: 1,
|
61
|
+
uniqueItems: true
|
62
|
+
},
|
63
|
+
message: {
|
64
|
+
type: "string",
|
65
|
+
minLength: 1
|
66
|
+
}
|
67
|
+
},
|
68
|
+
additionalProperties: false,
|
69
|
+
required: ["group"]
|
70
|
+
},
|
71
|
+
uniqueItems: true
|
72
|
+
}
|
73
|
+
]
|
74
|
+
};
|
75
|
+
|
47
76
|
module.exports = {
|
48
77
|
meta: {
|
49
78
|
type: "suggestion",
|
@@ -61,6 +90,8 @@ module.exports = {
|
|
61
90
|
pathWithCustomMessage: "'{{importSource}}' import is restricted from being used. {{customMessage}}",
|
62
91
|
|
63
92
|
patterns: "'{{importSource}}' import is restricted from being used by a pattern.",
|
93
|
+
// eslint-disable-next-line eslint-plugin/report-message-format
|
94
|
+
patternWithCustomMessage: "'{{importSource}}' import is restricted from being used by a pattern. {{customMessage}}",
|
64
95
|
|
65
96
|
everything: "* import is invalid because '{{importNames}}' from '{{importSource}}' is restricted.",
|
66
97
|
// eslint-disable-next-line eslint-plugin/report-message-format
|
@@ -80,7 +111,7 @@ module.exports = {
|
|
80
111
|
type: "object",
|
81
112
|
properties: {
|
82
113
|
paths: arrayOfStringsOrObjects,
|
83
|
-
patterns:
|
114
|
+
patterns: arrayOfStringsOrObjectPatterns
|
84
115
|
},
|
85
116
|
additionalProperties: false
|
86
117
|
}],
|
@@ -98,13 +129,6 @@ module.exports = {
|
|
98
129
|
(Object.prototype.hasOwnProperty.call(options[0], "paths") || Object.prototype.hasOwnProperty.call(options[0], "patterns"));
|
99
130
|
|
100
131
|
const restrictedPaths = (isPathAndPatternsObject ? options[0].paths : context.options) || [];
|
101
|
-
const restrictedPatterns = (isPathAndPatternsObject ? options[0].patterns : []) || [];
|
102
|
-
|
103
|
-
// if no imports are restricted we don"t need to check
|
104
|
-
if (Object.keys(restrictedPaths).length === 0 && restrictedPatterns.length === 0) {
|
105
|
-
return {};
|
106
|
-
}
|
107
|
-
|
108
132
|
const restrictedPathMessages = restrictedPaths.reduce((memo, importSource) => {
|
109
133
|
if (typeof importSource === "string") {
|
110
134
|
memo[importSource] = { message: null };
|
@@ -117,7 +141,16 @@ module.exports = {
|
|
117
141
|
return memo;
|
118
142
|
}, {});
|
119
143
|
|
120
|
-
|
144
|
+
// Handle patterns too, either as strings or groups
|
145
|
+
const restrictedPatterns = (isPathAndPatternsObject ? options[0].patterns : []) || [];
|
146
|
+
const restrictedPatternGroups = restrictedPatterns.length > 0 && typeof restrictedPatterns[0] === "string"
|
147
|
+
? [{ matcher: ignore().add(restrictedPatterns) }]
|
148
|
+
: restrictedPatterns.map(({ group, message }) => ({ matcher: ignore().add(group), customMessage: message }));
|
149
|
+
|
150
|
+
// if no imports are restricted we don"t need to check
|
151
|
+
if (Object.keys(restrictedPaths).length === 0 && restrictedPatternGroups.length === 0) {
|
152
|
+
return {};
|
153
|
+
}
|
121
154
|
|
122
155
|
/**
|
123
156
|
* Report a restricted path.
|
@@ -184,17 +217,19 @@ module.exports = {
|
|
184
217
|
/**
|
185
218
|
* Report a restricted path specifically for patterns.
|
186
219
|
* @param {node} node representing the restricted path reference
|
220
|
+
* @param {Object} group contains a Ignore instance for paths, and the customMessage to show if it fails
|
187
221
|
* @returns {void}
|
188
222
|
* @private
|
189
223
|
*/
|
190
|
-
function reportPathForPatterns(node) {
|
224
|
+
function reportPathForPatterns(node, group) {
|
191
225
|
const importSource = node.source.value.trim();
|
192
226
|
|
193
227
|
context.report({
|
194
228
|
node,
|
195
|
-
messageId: "patterns",
|
229
|
+
messageId: group.customMessage ? "patternWithCustomMessage" : "patterns",
|
196
230
|
data: {
|
197
|
-
importSource
|
231
|
+
importSource,
|
232
|
+
customMessage: group.customMessage
|
198
233
|
}
|
199
234
|
});
|
200
235
|
}
|
@@ -202,11 +237,12 @@ module.exports = {
|
|
202
237
|
/**
|
203
238
|
* Check if the given importSource is restricted by a pattern.
|
204
239
|
* @param {string} importSource path of the import
|
240
|
+
* @param {Object} group contains a Ignore instance for paths, and the customMessage to show if it fails
|
205
241
|
* @returns {boolean} whether the variable is a restricted pattern or not
|
206
242
|
* @private
|
207
243
|
*/
|
208
|
-
function isRestrictedPattern(importSource) {
|
209
|
-
return
|
244
|
+
function isRestrictedPattern(importSource, group) {
|
245
|
+
return group.matcher.ignores(importSource);
|
210
246
|
}
|
211
247
|
|
212
248
|
/**
|
@@ -249,10 +285,11 @@ module.exports = {
|
|
249
285
|
}
|
250
286
|
|
251
287
|
checkRestrictedPathAndReport(importSource, importNames, node);
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
288
|
+
restrictedPatternGroups.forEach(group => {
|
289
|
+
if (isRestrictedPattern(importSource, group)) {
|
290
|
+
reportPathForPatterns(node, group);
|
291
|
+
}
|
292
|
+
});
|
256
293
|
}
|
257
294
|
|
258
295
|
return {
|
@@ -410,6 +410,31 @@ module.exports = {
|
|
410
410
|
);
|
411
411
|
}
|
412
412
|
|
413
|
+
/**
|
414
|
+
* Checks whether a given node is unused expression or not.
|
415
|
+
* @param {ASTNode} node The node itself
|
416
|
+
* @returns {boolean} The node is an unused expression.
|
417
|
+
* @private
|
418
|
+
*/
|
419
|
+
function isUnusedExpression(node) {
|
420
|
+
const parent = node.parent;
|
421
|
+
|
422
|
+
if (parent.type === "ExpressionStatement") {
|
423
|
+
return true;
|
424
|
+
}
|
425
|
+
|
426
|
+
if (parent.type === "SequenceExpression") {
|
427
|
+
const isLastExpression = parent.expressions[parent.expressions.length - 1] === node;
|
428
|
+
|
429
|
+
if (!isLastExpression) {
|
430
|
+
return true;
|
431
|
+
}
|
432
|
+
return isUnusedExpression(parent);
|
433
|
+
}
|
434
|
+
|
435
|
+
return false;
|
436
|
+
}
|
437
|
+
|
413
438
|
/**
|
414
439
|
* Checks whether a given reference is a read to update itself or not.
|
415
440
|
* @param {eslint-scope.Reference} ref A reference to check.
|
@@ -420,23 +445,28 @@ module.exports = {
|
|
420
445
|
function isReadForItself(ref, rhsNode) {
|
421
446
|
const id = ref.identifier;
|
422
447
|
const parent = id.parent;
|
423
|
-
const grandparent = parent.parent;
|
424
448
|
|
425
449
|
return ref.isRead() && (
|
426
450
|
|
427
451
|
// self update. e.g. `a += 1`, `a++`
|
428
|
-
(
|
429
|
-
(
|
452
|
+
(
|
453
|
+
(
|
430
454
|
parent.type === "AssignmentExpression" &&
|
431
|
-
|
432
|
-
|
455
|
+
parent.left === id &&
|
456
|
+
isUnusedExpression(parent)
|
433
457
|
) ||
|
458
|
+
(
|
459
|
+
parent.type === "UpdateExpression" &&
|
460
|
+
isUnusedExpression(parent)
|
461
|
+
)
|
462
|
+
) ||
|
463
|
+
|
464
|
+
// in RHS of an assignment for itself. e.g. `a = a + 1`
|
434
465
|
(
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
!isInsideOfStorableFunction(id, rhsNode)))
|
466
|
+
rhsNode &&
|
467
|
+
isInside(id, rhsNode) &&
|
468
|
+
!isInsideOfStorableFunction(id, rhsNode)
|
469
|
+
)
|
440
470
|
);
|
441
471
|
}
|
442
472
|
|
@@ -11,7 +11,6 @@
|
|
11
11
|
|
12
12
|
const { CALL, CONSTRUCT, ReferenceTracker, getStringIfConstant } = require("eslint-utils");
|
13
13
|
const { RegExpParser, visitRegExpAST } = require("regexpp");
|
14
|
-
const lodash = require("lodash");
|
15
14
|
|
16
15
|
//------------------------------------------------------------------------------
|
17
16
|
// Helpers
|
@@ -137,7 +136,7 @@ module.exports = {
|
|
137
136
|
|
138
137
|
// the opposite of the previous when the regex is matching backward in a lookbehind context.
|
139
138
|
messageId = "backward";
|
140
|
-
} else if (
|
139
|
+
} else if (groupCut[groupCut.length - 1].type === "Alternative") {
|
141
140
|
|
142
141
|
// group's and bref's ancestor nodes below the lowest common ancestor are sibling alternatives => they're disjunctive.
|
143
142
|
messageId = "disjunctive";
|
@@ -8,7 +8,6 @@
|
|
8
8
|
// Requirements
|
9
9
|
//------------------------------------------------------------------------------
|
10
10
|
|
11
|
-
const lodash = require("lodash");
|
12
11
|
const astUtils = require("./utils/ast-utils");
|
13
12
|
|
14
13
|
//------------------------------------------------------------------------------
|
@@ -95,9 +94,16 @@ module.exports = {
|
|
95
94
|
}
|
96
95
|
}
|
97
96
|
|
97
|
+
/**
|
98
|
+
* A no-op function to act as placeholder for checking a node when the `enforceForClassMembers` option is `false`.
|
99
|
+
* @returns {void}
|
100
|
+
* @private
|
101
|
+
*/
|
102
|
+
function noop() {}
|
103
|
+
|
98
104
|
return {
|
99
105
|
Property: check,
|
100
|
-
MethodDefinition: enforceForClassMembers ? check :
|
106
|
+
MethodDefinition: enforceForClassMembers ? check : noop
|
101
107
|
};
|
102
108
|
}
|
103
109
|
};
|
@@ -10,7 +10,6 @@
|
|
10
10
|
//------------------------------------------------------------------------------
|
11
11
|
|
12
12
|
const astUtils = require("./utils/ast-utils");
|
13
|
-
const lodash = require("lodash");
|
14
13
|
|
15
14
|
//------------------------------------------------------------------------------
|
16
15
|
// Helpers
|
@@ -69,6 +68,24 @@ function normalizeOptionValue(value) {
|
|
69
68
|
return { multiline, minProperties, consistent };
|
70
69
|
}
|
71
70
|
|
71
|
+
/**
|
72
|
+
* Checks if a value is an object.
|
73
|
+
* @param {any} value The value to check
|
74
|
+
* @returns {boolean} `true` if the value is an object, otherwise `false`
|
75
|
+
*/
|
76
|
+
function isObject(value) {
|
77
|
+
return typeof value === "object" && value !== null;
|
78
|
+
}
|
79
|
+
|
80
|
+
/**
|
81
|
+
* Checks if an option is a node-specific option
|
82
|
+
* @param {any} option The option to check
|
83
|
+
* @returns {boolean} `true` if the option is node-specific, otherwise `false`
|
84
|
+
*/
|
85
|
+
function isNodeSpecificOption(option) {
|
86
|
+
return isObject(option) || typeof option === "string";
|
87
|
+
}
|
88
|
+
|
72
89
|
/**
|
73
90
|
* Normalizes a given option value.
|
74
91
|
* @param {string|Object|undefined} options An option value to parse.
|
@@ -80,9 +97,7 @@ function normalizeOptionValue(value) {
|
|
80
97
|
* }} Normalized option object.
|
81
98
|
*/
|
82
99
|
function normalizeOptions(options) {
|
83
|
-
|
84
|
-
|
85
|
-
if (lodash.isPlainObject(options) && Object.values(options).some(isNodeSpecificOption)) {
|
100
|
+
if (isObject(options) && Object.values(options).some(isNodeSpecificOption)) {
|
86
101
|
return {
|
87
102
|
ObjectExpression: normalizeOptionValue(options.ObjectExpression),
|
88
103
|
ObjectPattern: normalizeOptionValue(options.ObjectPattern),
|
@@ -295,7 +295,7 @@ module.exports = {
|
|
295
295
|
* If the callback function has duplicates in its list of parameters (possible in sloppy mode),
|
296
296
|
* don't replace it with an arrow function, because this is a SyntaxError with arrow functions.
|
297
297
|
*/
|
298
|
-
return;
|
298
|
+
return;
|
299
299
|
}
|
300
300
|
|
301
301
|
// Remove `.bind(this)` if exists.
|
@@ -307,7 +307,7 @@ module.exports = {
|
|
307
307
|
* E.g. `(foo || function(){}).bind(this)`
|
308
308
|
*/
|
309
309
|
if (memberNode.type !== "MemberExpression") {
|
310
|
-
return;
|
310
|
+
return;
|
311
311
|
}
|
312
312
|
|
313
313
|
const callNode = memberNode.parent;
|
@@ -320,12 +320,12 @@ module.exports = {
|
|
320
320
|
* ^^^^^^^^^^^^
|
321
321
|
*/
|
322
322
|
if (astUtils.isParenthesised(sourceCode, memberNode)) {
|
323
|
-
return;
|
323
|
+
return;
|
324
324
|
}
|
325
325
|
|
326
326
|
// If comments exist in the `.bind(this)`, don't remove those.
|
327
327
|
if (sourceCode.commentsExistBetween(firstTokenToRemove, lastTokenToRemove)) {
|
328
|
-
return;
|
328
|
+
return;
|
329
329
|
}
|
330
330
|
|
331
331
|
yield fixer.removeRange([firstTokenToRemove.range[0], lastTokenToRemove.range[1]]);
|
@@ -4,7 +4,7 @@
|
|
4
4
|
*/
|
5
5
|
"use strict";
|
6
6
|
|
7
|
-
const
|
7
|
+
const escapeRegExp = require("escape-string-regexp");
|
8
8
|
const astUtils = require("./utils/ast-utils");
|
9
9
|
|
10
10
|
//------------------------------------------------------------------------------
|
@@ -17,7 +17,7 @@ const astUtils = require("./utils/ast-utils");
|
|
17
17
|
* @returns {string} An escaped string.
|
18
18
|
*/
|
19
19
|
function escape(s) {
|
20
|
-
return `(?:${
|
20
|
+
return `(?:${escapeRegExp(s)})`;
|
21
21
|
}
|
22
22
|
|
23
23
|
/**
|
package/lib/rules/use-isnan.js
CHANGED
@@ -21,7 +21,10 @@ const astUtils = require("./utils/ast-utils");
|
|
21
21
|
* @returns {boolean} `true` if the node is 'NaN' identifier.
|
22
22
|
*/
|
23
23
|
function isNaNIdentifier(node) {
|
24
|
-
return Boolean(node) &&
|
24
|
+
return Boolean(node) && (
|
25
|
+
astUtils.isSpecificId(node, "NaN") ||
|
26
|
+
astUtils.isSpecificMemberAccess(node, "Number", "NaN")
|
27
|
+
);
|
25
28
|
}
|
26
29
|
|
27
30
|
//------------------------------------------------------------------------------
|
@@ -11,7 +11,7 @@
|
|
11
11
|
|
12
12
|
const esutils = require("esutils");
|
13
13
|
const espree = require("espree");
|
14
|
-
const
|
14
|
+
const escapeRegExp = require("escape-string-regexp");
|
15
15
|
const {
|
16
16
|
breakableTypePattern,
|
17
17
|
createGlobalLinebreakMatcher,
|
@@ -1756,7 +1756,7 @@ module.exports = {
|
|
1756
1756
|
* @returns {SourceLocation} The `loc` object.
|
1757
1757
|
*/
|
1758
1758
|
getNameLocationInGlobalDirectiveComment(sourceCode, comment, name) {
|
1759
|
-
const namePattern = new RegExp(`[\\s,]${
|
1759
|
+
const namePattern = new RegExp(`[\\s,]${escapeRegExp(name)}(?:$|[\\s,:])`, "gu");
|
1760
1760
|
|
1761
1761
|
// To ignore the first text "global".
|
1762
1762
|
namePattern.lastIndex = comment.value.indexOf("global") + 6;
|
@@ -9,7 +9,6 @@
|
|
9
9
|
//------------------------------------------------------------------------------
|
10
10
|
|
11
11
|
const path = require("path");
|
12
|
-
const lodash = require("lodash");
|
13
12
|
|
14
13
|
//------------------------------------------------------------------------------
|
15
14
|
// Private
|
@@ -28,6 +27,8 @@ const deprecationWarningMessages = {
|
|
28
27
|
"projects in order to avoid loading '~/.eslintrc.*' accidentally."
|
29
28
|
};
|
30
29
|
|
30
|
+
const sourceFileErrorCache = new Set();
|
31
|
+
|
31
32
|
/**
|
32
33
|
* Emits a deprecation warning containing a given filepath. A new deprecation warning is emitted
|
33
34
|
* for each unique file path, but repeated invocations with the same file path have no effect.
|
@@ -36,7 +37,15 @@ const deprecationWarningMessages = {
|
|
36
37
|
* @param {string} errorCode The warning message to show.
|
37
38
|
* @returns {void}
|
38
39
|
*/
|
39
|
-
|
40
|
+
function emitDeprecationWarning(source, errorCode) {
|
41
|
+
const cacheKey = JSON.stringify({ source, errorCode });
|
42
|
+
|
43
|
+
if (sourceFileErrorCache.has(cacheKey)) {
|
44
|
+
return;
|
45
|
+
}
|
46
|
+
|
47
|
+
sourceFileErrorCache.add(cacheKey);
|
48
|
+
|
40
49
|
const rel = path.relative(process.cwd(), source);
|
41
50
|
const message = deprecationWarningMessages[errorCode];
|
42
51
|
|
@@ -45,7 +54,7 @@ const emitDeprecationWarning = lodash.memoize((source, errorCode) => {
|
|
45
54
|
"DeprecationWarning",
|
46
55
|
errorCode
|
47
56
|
);
|
48
|
-
}
|
57
|
+
}
|
49
58
|
|
50
59
|
//------------------------------------------------------------------------------
|
51
60
|
// Public Interface
|
@@ -0,0 +1,22 @@
|
|
1
|
+
/**
|
2
|
+
* @fileoverview Utilities to operate on strings.
|
3
|
+
* @author Stephen Wade
|
4
|
+
*/
|
5
|
+
|
6
|
+
"use strict";
|
7
|
+
|
8
|
+
/**
|
9
|
+
* Converts the first letter of a string to uppercase.
|
10
|
+
* @param {string} string The string to operate on
|
11
|
+
* @returns {string} The converted string
|
12
|
+
*/
|
13
|
+
function upperCaseFirst(string) {
|
14
|
+
if (string.length <= 1) {
|
15
|
+
return string.toUpperCase();
|
16
|
+
}
|
17
|
+
return string[0].toUpperCase() + string.slice(1);
|
18
|
+
}
|
19
|
+
|
20
|
+
module.exports = {
|
21
|
+
upperCaseFirst
|
22
|
+
};
|
@@ -12,8 +12,7 @@ const
|
|
12
12
|
{ isCommentToken } = require("eslint-utils"),
|
13
13
|
TokenStore = require("./token-store"),
|
14
14
|
astUtils = require("../shared/ast-utils"),
|
15
|
-
Traverser = require("../shared/traverser")
|
16
|
-
lodash = require("lodash");
|
15
|
+
Traverser = require("../shared/traverser");
|
17
16
|
|
18
17
|
//------------------------------------------------------------------------------
|
19
18
|
// Private
|
@@ -350,7 +349,7 @@ class SourceCode extends TokenStore {
|
|
350
349
|
let currentToken = this.getTokenBefore(node, { includeComments: true });
|
351
350
|
|
352
351
|
while (currentToken && isCommentToken(currentToken)) {
|
353
|
-
if (node.parent && (currentToken.start < node.parent.start)) {
|
352
|
+
if (node.parent && node.parent.type !== "Program" && (currentToken.start < node.parent.start)) {
|
354
353
|
break;
|
355
354
|
}
|
356
355
|
comments.leading.push(currentToken);
|
@@ -362,7 +361,7 @@ class SourceCode extends TokenStore {
|
|
362
361
|
currentToken = this.getTokenAfter(node, { includeComments: true });
|
363
362
|
|
364
363
|
while (currentToken && isCommentToken(currentToken)) {
|
365
|
-
if (node.parent && (currentToken.end > node.parent.end)) {
|
364
|
+
if (node.parent && node.parent.type !== "Program" && (currentToken.end > node.parent.end)) {
|
366
365
|
break;
|
367
366
|
}
|
368
367
|
comments.trailing.push(currentToken);
|
@@ -531,10 +530,12 @@ class SourceCode extends TokenStore {
|
|
531
530
|
}
|
532
531
|
|
533
532
|
/*
|
534
|
-
* To figure out which line
|
535
|
-
* be inserted into
|
533
|
+
* To figure out which line index is on, determine the last place at which index could
|
534
|
+
* be inserted into lineStartIndices to keep the list sorted.
|
536
535
|
*/
|
537
|
-
const lineNumber =
|
536
|
+
const lineNumber = index >= this.lineStartIndices[this.lineStartIndices.length - 1]
|
537
|
+
? this.lineStartIndices.length
|
538
|
+
: this.lineStartIndices.findIndex(el => index < el);
|
538
539
|
|
539
540
|
return { line: lineNumber, column: index - this.lineStartIndices[lineNumber - 1] };
|
540
541
|
}
|
@@ -4,12 +4,6 @@
|
|
4
4
|
*/
|
5
5
|
"use strict";
|
6
6
|
|
7
|
-
//------------------------------------------------------------------------------
|
8
|
-
// Requirements
|
9
|
-
//------------------------------------------------------------------------------
|
10
|
-
|
11
|
-
const lodash = require("lodash");
|
12
|
-
|
13
7
|
//------------------------------------------------------------------------------
|
14
8
|
// Helpers
|
15
9
|
//------------------------------------------------------------------------------
|
@@ -29,18 +23,16 @@ function getStartLocation(token) {
|
|
29
23
|
//------------------------------------------------------------------------------
|
30
24
|
|
31
25
|
/**
|
32
|
-
*
|
26
|
+
* Finds the index of the first token which is after the given location.
|
33
27
|
* If it was not found, this returns `tokens.length`.
|
34
28
|
* @param {(Token|Comment)[]} tokens It searches the token in this list.
|
35
29
|
* @param {number} location The location to search.
|
36
30
|
* @returns {number} The found index or `tokens.length`.
|
37
31
|
*/
|
38
32
|
exports.search = function search(tokens, location) {
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
getStartLocation
|
43
|
-
);
|
33
|
+
const index = tokens.findIndex(el => location <= getStartLocation(el));
|
34
|
+
|
35
|
+
return index === -1 ? tokens.length : index;
|
44
36
|
};
|
45
37
|
|
46
38
|
/**
|