eslint 7.26.0 → 7.27.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 (54) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/README.md +7 -2
  3. package/bin/eslint.js +2 -12
  4. package/lib/cli-engine/file-enumerator.js +1 -1
  5. package/lib/cli-engine/formatters/html.js +193 -9
  6. package/lib/init/autoconfig.js +2 -2
  7. package/lib/linter/apply-disable-directives.js +15 -3
  8. package/lib/linter/linter.js +6 -4
  9. package/lib/linter/node-event-generator.js +43 -6
  10. package/lib/rule-tester/rule-tester.js +14 -10
  11. package/lib/rules/comma-dangle.js +16 -7
  12. package/lib/rules/comma-spacing.js +1 -1
  13. package/lib/rules/complexity.js +2 -3
  14. package/lib/rules/consistent-return.js +2 -2
  15. package/lib/rules/eol-last.js +2 -7
  16. package/lib/rules/indent.js +8 -9
  17. package/lib/rules/max-lines-per-function.js +2 -3
  18. package/lib/rules/max-lines.js +32 -7
  19. package/lib/rules/max-params.js +2 -3
  20. package/lib/rules/max-statements.js +2 -3
  21. package/lib/rules/no-fallthrough.js +2 -8
  22. package/lib/rules/no-restricted-imports.js +61 -24
  23. package/lib/rules/no-unused-vars.js +27 -3
  24. package/lib/rules/no-useless-backreference.js +1 -2
  25. package/lib/rules/no-useless-computed-key.js +8 -2
  26. package/lib/rules/no-warning-comments.js +1 -1
  27. package/lib/rules/object-curly-newline.js +19 -4
  28. package/lib/rules/spaced-comment.js +2 -2
  29. package/lib/rules/utils/ast-utils.js +2 -2
  30. package/lib/shared/deprecation-warnings.js +12 -3
  31. package/lib/shared/string-utils.js +22 -0
  32. package/lib/source-code/source-code.js +6 -5
  33. package/lib/source-code/token-store/utils.js +4 -12
  34. package/messages/{all-files-ignored.txt → all-files-ignored.js} +10 -2
  35. package/messages/extend-config-missing.js +13 -0
  36. package/messages/failed-to-read-json.js +11 -0
  37. package/messages/file-not-found.js +10 -0
  38. package/messages/{no-config-found.txt → no-config-found.js} +9 -1
  39. package/messages/plugin-conflict.js +22 -0
  40. package/messages/plugin-invalid.js +16 -0
  41. package/messages/plugin-missing.js +19 -0
  42. package/messages/{print-config-with-directory-path.txt → print-config-with-directory-path.js} +6 -0
  43. package/messages/whitespace-found.js +11 -0
  44. package/package.json +5 -4
  45. package/lib/cli-engine/formatters/html-template-message.html +0 -8
  46. package/lib/cli-engine/formatters/html-template-page.html +0 -115
  47. package/lib/cli-engine/formatters/html-template-result.html +0 -6
  48. package/messages/extend-config-missing.txt +0 -5
  49. package/messages/failed-to-read-json.txt +0 -3
  50. package/messages/file-not-found.txt +0 -2
  51. package/messages/plugin-conflict.txt +0 -7
  52. package/messages/plugin-invalid.txt +0 -8
  53. package/messages/plugin-missing.txt +0 -11
  54. package/messages/whitespace-found.txt +0 -3
package/CHANGELOG.md CHANGED
@@ -1,3 +1,16 @@
1
+ v7.27.0 - May 21, 2021
2
+
3
+ * [`2c0868c`](https://github.com/eslint/eslint/commit/2c0868cbeadc9f42716fa1178ebdc6b4cee6d31e) Chore: merge all html formatter files into `html.js` (#14612) (Milos Djermanovic)
4
+ * [`9e9b5e0`](https://github.com/eslint/eslint/commit/9e9b5e07475564813b62cd1d7562a93c5fb4bc74) Update: no-unused-vars false negative with comma operator (fixes #14325) (#14354) (Nitin Kumar)
5
+ * [`afe9569`](https://github.com/eslint/eslint/commit/afe95693e1e4316a1c6f01d39345061d4c5921c7) Chore: use includes instead of indexOf (#14607) (Mikhail Bodrov)
6
+ * [`c0f418e`](https://github.com/eslint/eslint/commit/c0f418e2476df98519bc156b81d20431984e8704) Chore: Remove lodash (#14287) (Stephen Wade)
7
+ * [`52655dd`](https://github.com/eslint/eslint/commit/52655dd54925ee02af2ba3a0ebc09de959ae3101) Update: no-restricted-imports custom message for patterns (fixes #11843) (#14580) (Alex Holden)
8
+ * [`967b1c4`](https://github.com/eslint/eslint/commit/967b1c4ceca8f5248378477da94ff118dafaa647) Chore: Fix typo in large.js (#14589) (Ikko Ashimine)
9
+ * [`2466a05`](https://github.com/eslint/eslint/commit/2466a05160de60958457d984b79fd445c12ebc98) Sponsors: Sync README with website (ESLint Jenkins)
10
+ * [`fe29f18`](https://github.com/eslint/eslint/commit/fe29f18227fd02fd7c3da033417d621275b00d0a) Sponsors: Sync README with website (ESLint Jenkins)
11
+ * [`086c1d6`](https://github.com/eslint/eslint/commit/086c1d6e8593cf8e7851daa8f2a890c213cf6999) Chore: add more test cases for `no-sequences` (#14579) (Nitin Kumar)
12
+ * [`6a2ced8`](https://github.com/eslint/eslint/commit/6a2ced892c0dc43fa4942293b9f1c4b9151c3741) Docs: Update README team and sponsors (ESLint Jenkins)
13
+
1
14
  v7.26.0 - May 7, 2021
2
15
 
3
16
  * [`aaf65e6`](https://github.com/eslint/eslint/commit/aaf65e629adb74401092c3ccc9cb4e4bd1c8609b) Upgrade: eslintrc for ModuleResolver fix (#14577) (Brandon Mills)
package/README.md CHANGED
@@ -240,6 +240,11 @@ Milos Djermanovic
240
240
  The people who review and implement new features.
241
241
 
242
242
  <table><tbody><tr><td align="center" valign="top" width="11%">
243
+ <a href="https://github.com/mysticatea">
244
+ <img src="https://github.com/mysticatea.png?s=75" width="75" height="75"><br />
245
+ Toru Nagashima
246
+ </a>
247
+ </td><td align="center" valign="top" width="11%">
243
248
  <a href="https://github.com/aladdin-add">
244
249
  <img src="https://github.com/aladdin-add.png?s=75" width="75" height="75"><br />
245
250
  薛定谔的猫
@@ -281,9 +286,9 @@ The following companies, organizations, and individuals support ESLint's ongoing
281
286
  <!--sponsorsstart-->
282
287
  <h3>Platinum Sponsors</h3>
283
288
  <p><a href="https://automattic.com"><img src="https://images.opencollective.com/photomatt/d0ef3e1/logo.png" alt="Automattic" height="undefined"></a></p><h3>Gold Sponsors</h3>
284
- <p><a href="https://nx.dev"><img src="https://images.opencollective.com/nx/0efbe42/logo.png" alt="Nx (by Nrwl)" height="96"></a> <a href="https://google.com/chrome"><img src="https://images.opencollective.com/chrome/dc55bd4/logo.png" alt="Chrome's Web Framework & Tools Performance Fund" height="96"></a> <a href="https://www.salesforce.com"><img src="https://images.opencollective.com/salesforce/ca8f997/logo.png" alt="Salesforce" height="96"></a> <a href="https://www.airbnb.com/"><img src="https://images.opencollective.com/airbnb/d327d66/logo.png" alt="Airbnb" height="96"></a> <a href="https://opensource.microsoft.com"><img src="https://avatars.githubusercontent.com/u/6154722?v=4" alt="Microsoft" height="96"></a> <a href="https://substack.com/"><img src="https://avatars.githubusercontent.com/u/53023767?v=4" alt="Substack" height="96"></a></p><h3>Silver Sponsors</h3>
289
+ <p><a href="https://nx.dev"><img src="https://images.opencollective.com/nx/0efbe42/logo.png" alt="Nx (by Nrwl)" height="96"></a> <a href="https://google.com/chrome"><img src="https://images.opencollective.com/chrome/dc55bd4/logo.png" alt="Chrome's Web Framework & Tools Performance Fund" height="96"></a> <a href="https://www.salesforce.com"><img src="https://images.opencollective.com/salesforce/ca8f997/logo.png" alt="Salesforce" height="96"></a> <a href="https://www.airbnb.com/"><img src="https://images.opencollective.com/airbnb/d327d66/logo.png" alt="Airbnb" height="96"></a> <a href="https://substack.com/"><img src="https://avatars.githubusercontent.com/u/53023767?v=4" alt="Substack" height="96"></a></p><h3>Silver Sponsors</h3>
285
290
  <p><a href="https://retool.com/"><img src="https://images.opencollective.com/retool/98ea68e/logo.png" alt="Retool" height="64"></a> <a href="https://liftoff.io/"><img src="https://images.opencollective.com/liftoff/5c4fa84/logo.png" alt="Liftoff" height="64"></a></p><h3>Bronze Sponsors</h3>
286
- <p><a href="https://buy.fineproxy.org/eng/"><img src="https://images.opencollective.com/buy-fineproxy-org/b282e39/logo.png" alt="Buy.Fineproxy.Org" height="32"></a> <a href="https://www.crosswordsolver.org/anagram-solver/"><img src="https://images.opencollective.com/anagram-solver/2666271/logo.png" alt="Anagram Solver" height="32"></a> <a href="null"><img src="https://images.opencollective.com/bugsnag-stability-monitoring/c2cef36/logo.png" alt="Bugsnag Stability Monitoring" height="32"></a> <a href="https://mixpanel.com"><img src="https://images.opencollective.com/mixpanel/cd682f7/logo.png" alt="Mixpanel" height="32"></a> <a href="https://www.vpsserver.com"><img src="https://images.opencollective.com/vpsservercom/logo.png" alt="VPS Server" height="32"></a> <a href="https://icons8.com"><img src="https://images.opencollective.com/icons8/7fa1641/logo.png" alt="Icons8: free icons, photos, illustrations, and music" height="32"></a> <a href="https://discord.com"><img src="https://images.opencollective.com/discordapp/7e3d9a9/logo.png" alt="Discord" height="32"></a> <a href="https://themeisle.com"><img src="https://images.opencollective.com/themeisle/d5592fe/logo.png" alt="ThemeIsle" height="32"></a> <a href="https://www.firesticktricks.com"><img src="https://images.opencollective.com/fire-stick-tricks/b8fbe2c/logo.png" alt="Fire Stick Tricks" height="32"></a></p>
291
+ <p><a href="https://www.crosswordsolver.org/anagram-solver/"><img src="https://images.opencollective.com/anagram-solver/2666271/logo.png" alt="Anagram Solver" height="32"></a> <a href="null"><img src="https://images.opencollective.com/bugsnag-stability-monitoring/c2cef36/logo.png" alt="Bugsnag Stability Monitoring" height="32"></a> <a href="https://mixpanel.com"><img src="https://images.opencollective.com/mixpanel/cd682f7/logo.png" alt="Mixpanel" height="32"></a> <a href="https://www.vpsserver.com"><img src="https://images.opencollective.com/vpsservercom/logo.png" alt="VPS Server" height="32"></a> <a href="https://icons8.com"><img src="https://images.opencollective.com/icons8/7fa1641/logo.png" alt="Icons8: free icons, photos, illustrations, and music" height="32"></a> <a href="https://discord.com"><img src="https://images.opencollective.com/discordapp/7e3d9a9/logo.png" alt="Discord" height="32"></a> <a href="https://themeisle.com"><img src="https://images.opencollective.com/themeisle/d5592fe/logo.png" alt="ThemeIsle" height="32"></a> <a href="https://www.firesticktricks.com"><img src="https://images.opencollective.com/fire-stick-tricks/b8fbe2c/logo.png" alt="Fire Stick Tricks" height="32"></a></p>
287
292
  <!--sponsorsend-->
288
293
 
289
294
  ## <a name="technology-sponsors"></a>Technology Sponsors
package/bin/eslint.js CHANGED
@@ -66,11 +66,8 @@ function readStdin() {
66
66
  */
67
67
  function getErrorMessage(error) {
68
68
 
69
- // Lazy loading because those are used only if error happened.
70
- const fs = require("fs");
71
- const path = require("path");
69
+ // Lazy loading because this is used only if an error happened.
72
70
  const util = require("util");
73
- const lodash = require("lodash");
74
71
 
75
72
  // Foolproof -- thirdparty module might throw non-object.
76
73
  if (typeof error !== "object" || error === null) {
@@ -80,14 +77,7 @@ function getErrorMessage(error) {
80
77
  // Use templates if `error.messageTemplate` is present.
81
78
  if (typeof error.messageTemplate === "string") {
82
79
  try {
83
- const templateFilePath = path.resolve(
84
- __dirname,
85
- `../messages/${error.messageTemplate}.txt`
86
- );
87
-
88
- // Use sync API because Node.js should exit at this tick.
89
- const templateText = fs.readFileSync(templateFilePath, "utf-8");
90
- const template = lodash.template(templateText);
80
+ const template = require(`../messages/${error.messageTemplate}.js`);
91
81
 
92
82
  return template(error.messageData || {});
93
83
  } catch {
@@ -38,7 +38,7 @@ const fs = require("fs");
38
38
  const path = require("path");
39
39
  const getGlobParent = require("glob-parent");
40
40
  const isGlob = require("is-glob");
41
- const { escapeRegExp } = require("lodash");
41
+ const escapeRegExp = require("escape-string-regexp");
42
42
  const { Minimatch } = require("minimatch");
43
43
 
44
44
  const {
@@ -4,17 +4,153 @@
4
4
  */
5
5
  "use strict";
6
6
 
7
- const lodash = require("lodash");
8
- const fs = require("fs");
9
- const path = require("path");
10
-
11
7
  //------------------------------------------------------------------------------
12
8
  // Helpers
13
9
  //------------------------------------------------------------------------------
14
10
 
15
- const pageTemplate = lodash.template(fs.readFileSync(path.join(__dirname, "html-template-page.html"), "utf-8"));
16
- const messageTemplate = lodash.template(fs.readFileSync(path.join(__dirname, "html-template-message.html"), "utf-8"));
17
- const resultTemplate = lodash.template(fs.readFileSync(path.join(__dirname, "html-template-result.html"), "utf-8"));
11
+ const encodeHTML = (function() {
12
+ const encodeHTMLRules = {
13
+ "&": "&#38;",
14
+ "<": "&#60;",
15
+ ">": "&#62;",
16
+ '"': "&#34;",
17
+ "'": "&#39;"
18
+ };
19
+ const matchHTML = /[&<>"']/ug;
20
+
21
+ return function(code) {
22
+ return code
23
+ ? code.toString().replace(matchHTML, m => encodeHTMLRules[m] || m)
24
+ : "";
25
+ };
26
+ }());
27
+
28
+ /**
29
+ * Get the final HTML document.
30
+ * @param {Object} it data for the document.
31
+ * @returns {string} HTML document.
32
+ */
33
+ function pageTemplate(it) {
34
+ const { reportColor, reportSummary, date, results } = it;
35
+
36
+ return `
37
+ <!DOCTYPE html>
38
+ <html>
39
+ <head>
40
+ <meta charset="UTF-8">
41
+ <title>ESLint Report</title>
42
+ <style>
43
+ body {
44
+ font-family:Arial, "Helvetica Neue", Helvetica, sans-serif;
45
+ font-size:16px;
46
+ font-weight:normal;
47
+ margin:0;
48
+ padding:0;
49
+ color:#333
50
+ }
51
+ #overview {
52
+ padding:20px 30px
53
+ }
54
+ td, th {
55
+ padding:5px 10px
56
+ }
57
+ h1 {
58
+ margin:0
59
+ }
60
+ table {
61
+ margin:30px;
62
+ width:calc(100% - 60px);
63
+ max-width:1000px;
64
+ border-radius:5px;
65
+ border:1px solid #ddd;
66
+ border-spacing:0px;
67
+ }
68
+ th {
69
+ font-weight:400;
70
+ font-size:medium;
71
+ text-align:left;
72
+ cursor:pointer
73
+ }
74
+ td.clr-1, td.clr-2, th span {
75
+ font-weight:700
76
+ }
77
+ th span {
78
+ float:right;
79
+ margin-left:20px
80
+ }
81
+ th span:after {
82
+ content:"";
83
+ clear:both;
84
+ display:block
85
+ }
86
+ tr:last-child td {
87
+ border-bottom:none
88
+ }
89
+ tr td:first-child, tr td:last-child {
90
+ color:#9da0a4
91
+ }
92
+ #overview.bg-0, tr.bg-0 th {
93
+ color:#468847;
94
+ background:#dff0d8;
95
+ border-bottom:1px solid #d6e9c6
96
+ }
97
+ #overview.bg-1, tr.bg-1 th {
98
+ color:#f0ad4e;
99
+ background:#fcf8e3;
100
+ border-bottom:1px solid #fbeed5
101
+ }
102
+ #overview.bg-2, tr.bg-2 th {
103
+ color:#b94a48;
104
+ background:#f2dede;
105
+ border-bottom:1px solid #eed3d7
106
+ }
107
+ td {
108
+ border-bottom:1px solid #ddd
109
+ }
110
+ td.clr-1 {
111
+ color:#f0ad4e
112
+ }
113
+ td.clr-2 {
114
+ color:#b94a48
115
+ }
116
+ td a {
117
+ color:#3a33d1;
118
+ text-decoration:none
119
+ }
120
+ td a:hover {
121
+ color:#272296;
122
+ text-decoration:underline
123
+ }
124
+ </style>
125
+ </head>
126
+ <body>
127
+ <div id="overview" class="bg-${reportColor}">
128
+ <h1>ESLint Report</h1>
129
+ <div>
130
+ <span>${reportSummary}</span> - Generated on ${date}
131
+ </div>
132
+ </div>
133
+ <table>
134
+ <tbody>
135
+ ${results}
136
+ </tbody>
137
+ </table>
138
+ <script type="text/javascript">
139
+ var groups = document.querySelectorAll("tr[data-group]");
140
+ for (i = 0; i < groups.length; i++) {
141
+ groups[i].addEventListener("click", function() {
142
+ var inGroup = document.getElementsByClassName(this.getAttribute("data-group"));
143
+ this.innerHTML = (this.innerHTML.indexOf("+") > -1) ? this.innerHTML.replace("+", "-") : this.innerHTML.replace("-", "+");
144
+ for (var j = 0; j < inGroup.length; j++) {
145
+ inGroup[j].style.display = (inGroup[j].style.display !== "none") ? "none" : "table-row";
146
+ }
147
+ });
148
+ }
149
+ </script>
150
+ </body>
151
+ </html>
152
+ `.trimLeft();
153
+ }
18
154
 
19
155
  /**
20
156
  * Given a word and a count, append an s if count is not one.
@@ -58,6 +194,35 @@ function renderColor(totalErrors, totalWarnings) {
58
194
  return 0;
59
195
  }
60
196
 
197
+ /**
198
+ * Get HTML (table row) describing a single message.
199
+ * @param {Object} it data for the message.
200
+ * @returns {string} HTML (table row) describing the message.
201
+ */
202
+ function messageTemplate(it) {
203
+ const {
204
+ parentIndex,
205
+ lineNumber,
206
+ columnNumber,
207
+ severityNumber,
208
+ severityName,
209
+ message,
210
+ ruleUrl,
211
+ ruleId
212
+ } = it;
213
+
214
+ return `
215
+ <tr style="display:none" class="f-${parentIndex}">
216
+ <td>${lineNumber}:${columnNumber}</td>
217
+ <td class="clr-${severityNumber}">${severityName}</td>
218
+ <td>${encodeHTML(message)}</td>
219
+ <td>
220
+ <a href="${ruleUrl ? ruleUrl : ""}" target="_blank" rel="noopener noreferrer">${ruleId ? ruleId : ""}</a>
221
+ </td>
222
+ </tr>
223
+ `.trimLeft();
224
+ }
225
+
61
226
  /**
62
227
  * Get HTML (table rows) describing the messages.
63
228
  * @param {Array} messages Messages.
@@ -80,7 +245,9 @@ function renderMessages(messages, parentIndex, rulesMeta) {
80
245
  if (rulesMeta) {
81
246
  const meta = rulesMeta[message.ruleId];
82
247
 
83
- ruleUrl = lodash.get(meta, "docs.url", null);
248
+ if (meta && meta.docs && meta.docs.url) {
249
+ ruleUrl = meta.docs.url;
250
+ }
84
251
  }
85
252
 
86
253
  return messageTemplate({
@@ -96,6 +263,24 @@ function renderMessages(messages, parentIndex, rulesMeta) {
96
263
  }).join("\n");
97
264
  }
98
265
 
266
+ /**
267
+ * Get HTML (table row) describing the result for a single file.
268
+ * @param {Object} it data for the file.
269
+ * @returns {string} HTML (table row) describing the result for the file.
270
+ */
271
+ function resultTemplate(it) {
272
+ const { color, index, filePath, summary } = it;
273
+
274
+ return `
275
+ <tr class="bg-${color}" data-group="f-${index}">
276
+ <th colspan="4">
277
+ [+] ${encodeHTML(filePath)}
278
+ <span>${encodeHTML(summary)}</span>
279
+ </th>
280
+ </tr>
281
+ `.trimLeft();
282
+ }
283
+
99
284
  // eslint-disable-next-line jsdoc/require-description
100
285
  /**
101
286
  * @param {Array} results Test results.
@@ -108,7 +293,6 @@ function renderResults(results, rulesMeta) {
108
293
  color: renderColor(result.errorCount, result.warningCount),
109
294
  filePath: result.filePath,
110
295
  summary: renderSummary(result.errorCount, result.warningCount)
111
-
112
296
  }) + renderMessages(result.messages, index, rulesMeta)).join("\n");
113
297
  }
114
298
 
@@ -9,7 +9,7 @@
9
9
  // Requirements
10
10
  //------------------------------------------------------------------------------
11
11
 
12
- const lodash = require("lodash"),
12
+ const equal = require("fast-deep-equal"),
13
13
  recConfig = require("../../conf/eslint-recommended"),
14
14
  ConfigOps = require("@eslint/eslintrc/lib/shared/config-ops"),
15
15
  { Linter } = require("../linter"),
@@ -329,7 +329,7 @@ function extendFromRecommended(config) {
329
329
  const recRules = Object.keys(recConfig.rules).filter(ruleId => ConfigOps.isErrorSeverity(recConfig.rules[ruleId]));
330
330
 
331
331
  recRules.forEach(ruleId => {
332
- if (lodash.isEqual(recConfig.rules[ruleId], newConfig.rules[ruleId])) {
332
+ if (equal(recConfig.rules[ruleId], newConfig.rules[ruleId])) {
333
333
  delete newConfig.rules[ruleId];
334
334
  }
335
335
  });
@@ -5,8 +5,6 @@
5
5
 
6
6
  "use strict";
7
7
 
8
- const lodash = require("lodash");
9
-
10
8
  /**
11
9
  * Compares the locations of two objects in a source file
12
10
  * @param {{line: number, column: number}} itemA The first object
@@ -124,7 +122,21 @@ module.exports = ({ directives, problems, reportUnusedDisableDirectives = "off"
124
122
  .map(directive => Object.assign({}, directive, { unprocessedDirective: directive }))
125
123
  .sort(compareLocations);
126
124
 
127
- const lineDirectives = lodash.flatMap(directives, directive => {
125
+ /**
126
+ * Returns a new array formed by applying a given callback function to each element of the array, and then flattening the result by one level.
127
+ * TODO(stephenwade): Replace this with array.flatMap when we drop support for Node v10
128
+ * @param {any[]} array The array to process
129
+ * @param {Function} fn The function to use
130
+ * @returns {any[]} The result array
131
+ */
132
+ function flatMap(array, fn) {
133
+ const mapped = array.map(fn);
134
+ const flattened = [].concat(...mapped);
135
+
136
+ return flattened;
137
+ }
138
+
139
+ const lineDirectives = flatMap(directives, directive => {
128
140
  switch (directive.type) {
129
141
  case "disable":
130
142
  case "enable":
@@ -15,7 +15,7 @@ const
15
15
  eslintScope = require("eslint-scope"),
16
16
  evk = require("eslint-visitor-keys"),
17
17
  espree = require("espree"),
18
- lodash = require("lodash"),
18
+ merge = require("lodash.merge"),
19
19
  BuiltInEnvironments = require("@eslint/eslintrc/conf/environments"),
20
20
  pkg = require("../../package.json"),
21
21
  astUtils = require("../shared/ast-utils"),
@@ -529,8 +529,8 @@ function normalizeVerifyOptions(providedOptions, config) {
529
529
  function resolveParserOptions(parserName, providedOptions, enabledEnvironments) {
530
530
  const parserOptionsFromEnv = enabledEnvironments
531
531
  .filter(env => env.parserOptions)
532
- .reduce((parserOptions, env) => lodash.merge(parserOptions, env.parserOptions), {});
533
- const mergedParserOptions = lodash.merge(parserOptionsFromEnv, providedOptions || {});
532
+ .reduce((parserOptions, env) => merge(parserOptions, env.parserOptions), {});
533
+ const mergedParserOptions = merge(parserOptionsFromEnv, providedOptions || {});
534
534
  const isModule = mergedParserOptions.sourceType === "module";
535
535
 
536
536
  if (isModule) {
@@ -1286,7 +1286,9 @@ class Linter {
1286
1286
  const filenameToExpose = normalizeFilename(filename);
1287
1287
  const text = ensureText(textOrSourceCode);
1288
1288
  const preprocess = options.preprocess || (rawText => [rawText]);
1289
- const postprocess = options.postprocess || lodash.flatten;
1289
+
1290
+ // TODO(stephenwade): Replace this with array.flat() when we drop support for Node v10
1291
+ const postprocess = options.postprocess || (array => [].concat(...array));
1290
1292
  const filterCodeBlock =
1291
1293
  options.filterCodeBlock ||
1292
1294
  (blockFilename => blockFilename.endsWith(".js"));
@@ -10,7 +10,6 @@
10
10
  //------------------------------------------------------------------------------
11
11
 
12
12
  const esquery = require("esquery");
13
- const lodash = require("lodash");
14
13
 
15
14
  //------------------------------------------------------------------------------
16
15
  // Typedefs
@@ -32,6 +31,35 @@ const lodash = require("lodash");
32
31
  // Helpers
33
32
  //------------------------------------------------------------------------------
34
33
 
34
+ /**
35
+ * Computes the union of one or more arrays
36
+ * @param {...any[]} arrays One or more arrays to union
37
+ * @returns {any[]} The union of the input arrays
38
+ */
39
+ function union(...arrays) {
40
+
41
+ // TODO(stephenwade): Replace this with arrays.flat() when we drop support for Node v10
42
+ return [...new Set([].concat(...arrays))];
43
+ }
44
+
45
+ /**
46
+ * Computes the intersection of one or more arrays
47
+ * @param {...any[]} arrays One or more arrays to intersect
48
+ * @returns {any[]} The intersection of the input arrays
49
+ */
50
+ function intersection(...arrays) {
51
+ if (arrays.length === 0) {
52
+ return [];
53
+ }
54
+
55
+ let result = [...new Set(arrays[0])];
56
+
57
+ for (const array of arrays.slice(1)) {
58
+ result = result.filter(x => array.includes(x));
59
+ }
60
+ return result;
61
+ }
62
+
35
63
  /**
36
64
  * Gets the possible types of a selector
37
65
  * @param {Object} parsedSelector An object (from esquery) describing the matching behavior of the selector
@@ -46,7 +74,7 @@ function getPossibleTypes(parsedSelector) {
46
74
  const typesForComponents = parsedSelector.selectors.map(getPossibleTypes);
47
75
 
48
76
  if (typesForComponents.every(Boolean)) {
49
- return lodash.union(...typesForComponents);
77
+ return union(...typesForComponents);
50
78
  }
51
79
  return null;
52
80
  }
@@ -63,7 +91,7 @@ function getPossibleTypes(parsedSelector) {
63
91
  * If at least one of the components could only match a particular type, the compound could only match
64
92
  * the intersection of those types.
65
93
  */
66
- return lodash.intersection(...typesForComponents);
94
+ return intersection(...typesForComponents);
67
95
  }
68
96
 
69
97
  case "child":
@@ -166,15 +194,21 @@ function tryParseSelector(rawSelector) {
166
194
  }
167
195
  }
168
196
 
197
+ const selectorCache = new Map();
198
+
169
199
  /**
170
200
  * Parses a raw selector string, and returns the parsed selector along with specificity and type information.
171
201
  * @param {string} rawSelector A raw AST selector
172
202
  * @returns {ASTSelector} A selector descriptor
173
203
  */
174
- const parseSelector = lodash.memoize(rawSelector => {
204
+ function parseSelector(rawSelector) {
205
+ if (selectorCache.has(rawSelector)) {
206
+ return selectorCache.get(rawSelector);
207
+ }
208
+
175
209
  const parsedSelector = tryParseSelector(rawSelector);
176
210
 
177
- return {
211
+ const result = {
178
212
  rawSelector,
179
213
  isExit: rawSelector.endsWith(":exit"),
180
214
  parsedSelector,
@@ -182,7 +216,10 @@ const parseSelector = lodash.memoize(rawSelector => {
182
216
  attributeCount: countClassAttributes(parsedSelector),
183
217
  identifierCount: countIdentifiers(parsedSelector)
184
218
  };
185
- });
219
+
220
+ selectorCache.set(rawSelector, result);
221
+ return result;
222
+ }
186
223
 
187
224
  //------------------------------------------------------------------------------
188
225
  // Public Interface
@@ -44,7 +44,8 @@ const
44
44
  assert = require("assert"),
45
45
  path = require("path"),
46
46
  util = require("util"),
47
- lodash = require("lodash"),
47
+ merge = require("lodash.merge"),
48
+ equal = require("fast-deep-equal"),
48
49
  Traverser = require("../../lib/shared/traverser"),
49
50
  { getRuleOptionsSchema, validate } = require("../shared/config-validator"),
50
51
  { Linter, SourceCodeFixer, interpolate } = require("../linter");
@@ -324,10 +325,9 @@ class RuleTester {
324
325
  * configuration and the default configuration.
325
326
  * @type {Object}
326
327
  */
327
- this.testerConfig = lodash.merge(
328
-
329
- // we have to clone because merge uses the first argument for recipient
330
- lodash.cloneDeep(defaultConfig),
328
+ this.testerConfig = merge(
329
+ {},
330
+ defaultConfig,
331
331
  testerConfig,
332
332
  { rules: { "rule-tester/validate-ast": "error" } }
333
333
  );
@@ -369,7 +369,7 @@ class RuleTester {
369
369
  * @returns {void}
370
370
  */
371
371
  static resetDefaultConfig() {
372
- defaultConfig = lodash.cloneDeep(testerDefaultConfig);
372
+ defaultConfig = merge({}, testerDefaultConfig);
373
373
  }
374
374
 
375
375
 
@@ -465,7 +465,7 @@ class RuleTester {
465
465
  * @private
466
466
  */
467
467
  function runRuleForItem(item) {
468
- let config = lodash.cloneDeep(testerConfig),
468
+ let config = merge({}, testerConfig),
469
469
  code, filename, output, beforeAST, afterAST;
470
470
 
471
471
  if (typeof item === "string") {
@@ -477,13 +477,17 @@ class RuleTester {
477
477
  * Assumes everything on the item is a config except for the
478
478
  * parameters used by this tester
479
479
  */
480
- const itemConfig = lodash.omit(item, RuleTesterParameters);
480
+ const itemConfig = { ...item };
481
+
482
+ for (const parameter of RuleTesterParameters) {
483
+ delete itemConfig[parameter];
484
+ }
481
485
 
482
486
  /*
483
487
  * Create the config object from the tester config and this item
484
488
  * specific configurations.
485
489
  */
486
- config = lodash.merge(
490
+ config = merge(
487
491
  config,
488
492
  itemConfig
489
493
  );
@@ -589,7 +593,7 @@ class RuleTester {
589
593
  * @private
590
594
  */
591
595
  function assertASTDidntChange(beforeAST, afterAST) {
592
- if (!lodash.isEqual(beforeAST, afterAST)) {
596
+ if (!equal(beforeAST, afterAST)) {
593
597
  assert.fail("Rule should not modify AST.");
594
598
  }
595
599
  }
@@ -9,7 +9,6 @@
9
9
  // Requirements
10
10
  //------------------------------------------------------------------------------
11
11
 
12
- const lodash = require("lodash");
13
12
  const astUtils = require("./utils/ast-utils");
14
13
 
15
14
  //------------------------------------------------------------------------------
@@ -144,23 +143,33 @@ module.exports = {
144
143
  * @returns {ASTNode|null} The last node or null.
145
144
  */
146
145
  function getLastItem(node) {
146
+
147
+ /**
148
+ * Returns the last element of an array
149
+ * @param {any[]} array The input array
150
+ * @returns {any} The last element
151
+ */
152
+ function last(array) {
153
+ return array[array.length - 1];
154
+ }
155
+
147
156
  switch (node.type) {
148
157
  case "ObjectExpression":
149
158
  case "ObjectPattern":
150
- return lodash.last(node.properties);
159
+ return last(node.properties);
151
160
  case "ArrayExpression":
152
161
  case "ArrayPattern":
153
- return lodash.last(node.elements);
162
+ return last(node.elements);
154
163
  case "ImportDeclaration":
155
164
  case "ExportNamedDeclaration":
156
- return lodash.last(node.specifiers);
165
+ return last(node.specifiers);
157
166
  case "FunctionDeclaration":
158
167
  case "FunctionExpression":
159
168
  case "ArrowFunctionExpression":
160
- return lodash.last(node.params);
169
+ return last(node.params);
161
170
  case "CallExpression":
162
171
  case "NewExpression":
163
- return lodash.last(node.arguments);
172
+ return last(node.arguments);
164
173
  default:
165
174
  return null;
166
175
  }
@@ -316,7 +325,7 @@ module.exports = {
316
325
  "always-multiline": forceTrailingCommaIfMultiline,
317
326
  "only-multiline": allowTrailingCommaIfMultiline,
318
327
  never: forbidTrailingComma,
319
- ignore: lodash.noop
328
+ ignore: () => {}
320
329
  };
321
330
 
322
331
  return {
@@ -181,7 +181,7 @@ module.exports = {
181
181
 
182
182
  validateCommaItemSpacing({
183
183
  comma: token,
184
- left: astUtils.isCommaToken(previousToken) || commaTokensToIgnore.indexOf(token) > -1 ? null : previousToken,
184
+ left: astUtils.isCommaToken(previousToken) || commaTokensToIgnore.includes(token) ? null : previousToken,
185
185
  right: astUtils.isCommaToken(nextToken) ? null : nextToken
186
186
  }, token);
187
187
  });
@@ -10,9 +10,8 @@
10
10
  // Requirements
11
11
  //------------------------------------------------------------------------------
12
12
 
13
- const lodash = require("lodash");
14
-
15
13
  const astUtils = require("./utils/ast-utils");
14
+ const { upperCaseFirst } = require("../shared/string-utils");
16
15
 
17
16
  //------------------------------------------------------------------------------
18
17
  // Rule Definition
@@ -95,7 +94,7 @@ module.exports = {
95
94
  * @private
96
95
  */
97
96
  function endFunction(node) {
98
- const name = lodash.upperFirst(astUtils.getFunctionNameWithKind(node));
97
+ const name = upperCaseFirst(astUtils.getFunctionNameWithKind(node));
99
98
  const complexity = fns.pop();
100
99
 
101
100
  if (complexity > THRESHOLD) {