eslint 7.25.0 → 7.29.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.
Files changed (65) hide show
  1. package/CHANGELOG.md +55 -0
  2. package/README.md +7 -7
  3. package/bin/eslint.js +2 -12
  4. package/lib/cli-engine/cli-engine.js +2 -7
  5. package/lib/cli-engine/file-enumerator.js +1 -1
  6. package/lib/cli-engine/formatters/html.js +193 -9
  7. package/lib/eslint/eslint.js +38 -2
  8. package/lib/init/autoconfig.js +2 -2
  9. package/lib/init/config-file.js +1 -0
  10. package/lib/init/config-initializer.js +14 -1
  11. package/lib/init/npm-utils.js +2 -2
  12. package/lib/linter/apply-disable-directives.js +15 -3
  13. package/lib/linter/linter.js +15 -9
  14. package/lib/linter/node-event-generator.js +43 -6
  15. package/lib/rule-tester/rule-tester.js +83 -23
  16. package/lib/rules/arrow-body-style.js +21 -11
  17. package/lib/rules/comma-dangle.js +16 -7
  18. package/lib/rules/comma-spacing.js +1 -1
  19. package/lib/rules/comma-style.js +1 -2
  20. package/lib/rules/complexity.js +2 -3
  21. package/lib/rules/consistent-return.js +2 -2
  22. package/lib/rules/eol-last.js +2 -7
  23. package/lib/rules/indent.js +10 -13
  24. package/lib/rules/max-lines-per-function.js +2 -3
  25. package/lib/rules/max-lines.js +32 -7
  26. package/lib/rules/max-params.js +2 -3
  27. package/lib/rules/max-statements.js +2 -3
  28. package/lib/rules/no-duplicate-imports.js +214 -66
  29. package/lib/rules/no-fallthrough.js +18 -13
  30. package/lib/rules/no-implicit-coercion.js +21 -2
  31. package/lib/rules/no-restricted-imports.js +61 -24
  32. package/lib/rules/no-unused-vars.js +40 -10
  33. package/lib/rules/no-useless-backreference.js +1 -2
  34. package/lib/rules/no-useless-computed-key.js +8 -2
  35. package/lib/rules/no-warning-comments.js +1 -1
  36. package/lib/rules/object-curly-newline.js +19 -4
  37. package/lib/rules/radix.js +19 -3
  38. package/lib/rules/require-atomic-updates.js +23 -20
  39. package/lib/rules/spaced-comment.js +2 -2
  40. package/lib/rules/utils/ast-utils.js +2 -2
  41. package/lib/shared/deprecation-warnings.js +12 -3
  42. package/lib/shared/string-utils.js +22 -0
  43. package/lib/source-code/source-code.js +6 -5
  44. package/lib/source-code/token-store/utils.js +4 -12
  45. package/messages/{all-files-ignored.txt → all-files-ignored.js} +10 -2
  46. package/messages/extend-config-missing.js +13 -0
  47. package/messages/failed-to-read-json.js +11 -0
  48. package/messages/file-not-found.js +10 -0
  49. package/messages/{no-config-found.txt → no-config-found.js} +9 -1
  50. package/messages/plugin-conflict.js +22 -0
  51. package/messages/plugin-invalid.js +16 -0
  52. package/messages/plugin-missing.js +19 -0
  53. package/messages/{print-config-with-directory-path.txt → print-config-with-directory-path.js} +6 -0
  54. package/messages/whitespace-found.js +11 -0
  55. package/package.json +8 -8
  56. package/lib/cli-engine/formatters/html-template-message.html +0 -8
  57. package/lib/cli-engine/formatters/html-template-page.html +0 -115
  58. package/lib/cli-engine/formatters/html-template-result.html +0 -6
  59. package/messages/extend-config-missing.txt +0 -5
  60. package/messages/failed-to-read-json.txt +0 -3
  61. package/messages/file-not-found.txt +0 -2
  62. package/messages/plugin-conflict.txt +0 -7
  63. package/messages/plugin-invalid.txt +0 -8
  64. package/messages/plugin-missing.txt +0 -11
  65. package/messages/whitespace-found.txt +0 -3
@@ -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: arrayOfStrings
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
- const restrictedPatternsMatcher = ignore().add(restrictedPatterns);
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 restrictedPatterns.length > 0 && restrictedPatternsMatcher.ignores(importSource);
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
- if (isRestrictedPattern(importSource)) {
254
- reportPathForPatterns(node);
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
- (// in RHS of an assignment for itself. e.g. `a = a + 1`
429
- ((
452
+ (
453
+ (
430
454
  parent.type === "AssignmentExpression" &&
431
- grandparent.type === "ExpressionStatement" &&
432
- parent.left === id
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
- parent.type === "UpdateExpression" &&
436
- grandparent.type === "ExpressionStatement"
437
- ) || rhsNode &&
438
- isInside(id, rhsNode) &&
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 (lodash.last(groupCut).type === "Alternative") {
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 : lodash.noop
106
+ MethodDefinition: enforceForClassMembers ? check : noop
101
107
  };
102
108
  }
103
109
  };
@@ -5,7 +5,7 @@
5
5
 
6
6
  "use strict";
7
7
 
8
- const { escapeRegExp } = require("lodash");
8
+ const escapeRegExp = require("escape-string-regexp");
9
9
  const astUtils = require("./utils/ast-utils");
10
10
 
11
11
  const CHAR_LIMIT = 40;
@@ -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
- const isNodeSpecificOption = lodash.overSome([lodash.isPlainObject, lodash.isString]);
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),
@@ -82,7 +82,8 @@ module.exports = {
82
82
  description: "enforce the consistent use of the radix argument when using `parseInt()`",
83
83
  category: "Best Practices",
84
84
  recommended: false,
85
- url: "https://eslint.org/docs/rules/radix"
85
+ url: "https://eslint.org/docs/rules/radix",
86
+ suggestion: true
86
87
  },
87
88
 
88
89
  schema: [
@@ -95,7 +96,8 @@ module.exports = {
95
96
  missingParameters: "Missing parameters.",
96
97
  redundantRadix: "Redundant radix parameter.",
97
98
  missingRadix: "Missing radix parameter.",
98
- invalidRadix: "Invalid radix parameter, must be an integer between 2 and 36."
99
+ invalidRadix: "Invalid radix parameter, must be an integer between 2 and 36.",
100
+ addRadixParameter10: "Add radix parameter `10` for parsing decimal numbers."
99
101
  }
100
102
  },
101
103
 
@@ -123,7 +125,21 @@ module.exports = {
123
125
  if (mode === MODE_ALWAYS) {
124
126
  context.report({
125
127
  node,
126
- messageId: "missingRadix"
128
+ messageId: "missingRadix",
129
+ suggest: [
130
+ {
131
+ messageId: "addRadixParameter10",
132
+ fix(fixer) {
133
+ const sourceCode = context.getSourceCode();
134
+ const tokens = sourceCode.getTokens(node);
135
+ const lastToken = tokens[tokens.length - 1]; // Parenthesis.
136
+ const secondToLastToken = tokens[tokens.length - 2]; // May or may not be a comma.
137
+ const hasTrailingComma = secondToLastToken.type === "Punctuator" && secondToLastToken.value === ",";
138
+
139
+ return fixer.insertTextBefore(lastToken, hasTrailingComma ? " 10," : ", 10");
140
+ }
141
+ }
142
+ ]
127
143
  });
128
144
  }
129
145
  break;
@@ -13,6 +13,10 @@
13
13
  */
14
14
  function createReferenceMap(scope, outReferenceMap = new Map()) {
15
15
  for (const reference of scope.references) {
16
+ if (reference.resolved === null) {
17
+ continue;
18
+ }
19
+
16
20
  outReferenceMap.set(reference.identifier, reference);
17
21
  }
18
22
  for (const childScope of scope.childScopes) {
@@ -86,42 +90,42 @@ class SegmentInfo {
86
90
  * @returns {void}
87
91
  */
88
92
  initialize(segment) {
89
- const outdatedReadVariableNames = new Set();
90
- const freshReadVariableNames = new Set();
93
+ const outdatedReadVariables = new Set();
94
+ const freshReadVariables = new Set();
91
95
 
92
96
  for (const prevSegment of segment.prevSegments) {
93
97
  const info = this.info.get(prevSegment);
94
98
 
95
99
  if (info) {
96
- info.outdatedReadVariableNames.forEach(Set.prototype.add, outdatedReadVariableNames);
97
- info.freshReadVariableNames.forEach(Set.prototype.add, freshReadVariableNames);
100
+ info.outdatedReadVariables.forEach(Set.prototype.add, outdatedReadVariables);
101
+ info.freshReadVariables.forEach(Set.prototype.add, freshReadVariables);
98
102
  }
99
103
  }
100
104
 
101
- this.info.set(segment, { outdatedReadVariableNames, freshReadVariableNames });
105
+ this.info.set(segment, { outdatedReadVariables, freshReadVariables });
102
106
  }
103
107
 
104
108
  /**
105
109
  * Mark a given variable as read on given segments.
106
110
  * @param {PathSegment[]} segments The segments that it read the variable on.
107
- * @param {string} variableName The variable name to be read.
111
+ * @param {Variable} variable The variable to be read.
108
112
  * @returns {void}
109
113
  */
110
- markAsRead(segments, variableName) {
114
+ markAsRead(segments, variable) {
111
115
  for (const segment of segments) {
112
116
  const info = this.info.get(segment);
113
117
 
114
118
  if (info) {
115
- info.freshReadVariableNames.add(variableName);
119
+ info.freshReadVariables.add(variable);
116
120
 
117
121
  // If a variable is freshly read again, then it's no more out-dated.
118
- info.outdatedReadVariableNames.delete(variableName);
122
+ info.outdatedReadVariables.delete(variable);
119
123
  }
120
124
  }
121
125
  }
122
126
 
123
127
  /**
124
- * Move `freshReadVariableNames` to `outdatedReadVariableNames`.
128
+ * Move `freshReadVariables` to `outdatedReadVariables`.
125
129
  * @param {PathSegment[]} segments The segments to process.
126
130
  * @returns {void}
127
131
  */
@@ -130,8 +134,8 @@ class SegmentInfo {
130
134
  const info = this.info.get(segment);
131
135
 
132
136
  if (info) {
133
- info.freshReadVariableNames.forEach(Set.prototype.add, info.outdatedReadVariableNames);
134
- info.freshReadVariableNames.clear();
137
+ info.freshReadVariables.forEach(Set.prototype.add, info.outdatedReadVariables);
138
+ info.freshReadVariables.clear();
135
139
  }
136
140
  }
137
141
  }
@@ -139,14 +143,14 @@ class SegmentInfo {
139
143
  /**
140
144
  * Check if a given variable is outdated on the current segments.
141
145
  * @param {PathSegment[]} segments The current segments.
142
- * @param {string} variableName The variable name to check.
146
+ * @param {Variable} variable The variable to check.
143
147
  * @returns {boolean} `true` if the variable is outdated on the segments.
144
148
  */
145
- isOutdated(segments, variableName) {
149
+ isOutdated(segments, variable) {
146
150
  for (const segment of segments) {
147
151
  const info = this.info.get(segment);
148
152
 
149
- if (info && info.outdatedReadVariableNames.has(variableName)) {
153
+ if (info && info.outdatedReadVariables.has(variable)) {
150
154
  return true;
151
155
  }
152
156
  }
@@ -214,14 +218,13 @@ module.exports = {
214
218
  if (!reference) {
215
219
  return;
216
220
  }
217
- const name = reference.identifier.name;
218
221
  const variable = reference.resolved;
219
222
  const writeExpr = getWriteExpr(reference);
220
223
  const isMemberAccess = reference.identifier.parent.type === "MemberExpression";
221
224
 
222
225
  // Add a fresh read variable.
223
226
  if (reference.isRead() && !(writeExpr && writeExpr.parent.operator === "=")) {
224
- segmentInfo.markAsRead(codePath.currentSegments, name);
227
+ segmentInfo.markAsRead(codePath.currentSegments, variable);
225
228
  }
226
229
 
227
230
  /*
@@ -245,7 +248,7 @@ module.exports = {
245
248
 
246
249
  /*
247
250
  * Verify assignments.
248
- * If the reference exists in `outdatedReadVariableNames` list, report it.
251
+ * If the reference exists in `outdatedReadVariables` list, report it.
249
252
  */
250
253
  ":expression:exit"(node) {
251
254
  const { codePath, referenceMap } = stack;
@@ -267,9 +270,9 @@ module.exports = {
267
270
  assignmentReferences.delete(node);
268
271
 
269
272
  for (const reference of references) {
270
- const name = reference.identifier.name;
273
+ const variable = reference.resolved;
271
274
 
272
- if (segmentInfo.isOutdated(codePath.currentSegments, name)) {
275
+ if (segmentInfo.isOutdated(codePath.currentSegments, variable)) {
273
276
  context.report({
274
277
  node: node.parent,
275
278
  messageId: "nonAtomicUpdate",
@@ -4,7 +4,7 @@
4
4
  */
5
5
  "use strict";
6
6
 
7
- const lodash = require("lodash");
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 `(?:${lodash.escapeRegExp(s)})`;
20
+ return `(?:${escapeRegExp(s)})`;
21
21
  }
22
22
 
23
23
  /**
@@ -11,7 +11,7 @@
11
11
 
12
12
  const esutils = require("esutils");
13
13
  const espree = require("espree");
14
- const lodash = require("lodash");
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,]${lodash.escapeRegExp(name)}(?:$|[\\s,:])`, "gu");
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
- const emitDeprecationWarning = lodash.memoize((source, errorCode) => {
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
- }, (...args) => JSON.stringify(args));
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
@@ -531,10 +530,12 @@ class SourceCode extends TokenStore {
531
530
  }
532
531
 
533
532
  /*
534
- * To figure out which line rangeIndex is on, determine the last index at which rangeIndex could
535
- * be inserted into lineIndices to keep the list sorted.
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 = lodash.sortedLastIndex(this.lineStartIndices, index);
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
- * Binary-searches the index of the first token which is after the given location.
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
- return lodash.sortedIndexBy(
40
- tokens,
41
- { range: [location] },
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
  /**