eslint 7.12.0 → 7.15.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 CHANGED
@@ -1,3 +1,38 @@
1
+ v7.15.0 - December 5, 2020
2
+
3
+ * [`5c11aab`](https://github.com/eslint/eslint/commit/5c11aabbe8249aeb8cad29bc6a33fc20c8c683ef) Upgrade: @eslint/esintrc and espree for bug fixes (refs #13878) (#13908) (Brandon Mills)
4
+ * [`0eb7957`](https://github.com/eslint/eslint/commit/0eb7957e27fd521317bd5c8479ce7abc1399169c) Upgrade: file-entry-cache@6.0.0 (#13877) (Rouven Weßling)
5
+ * [`683ad00`](https://github.com/eslint/eslint/commit/683ad00c41e1ae4d889deff82b2a94318e8c2129) New: no-unsafe-optional-chaining rule (fixes #13431) (#13859) (YeonJuan)
6
+ * [`cbc57fb`](https://github.com/eslint/eslint/commit/cbc57fb7d07c00663ed5781f5e6bc8f534cc2d76) Fix: one-var autofixing for export (fixes #13834) (#13891) (Anix)
7
+ * [`110cf96`](https://github.com/eslint/eslint/commit/110cf962d05625a8a1bf7b5f4ec2194db150eb32) Docs: Fix a broken link in working-with-rules.md (#13875) (Anton Niklasson)
8
+
9
+ v7.14.0 - November 20, 2020
10
+
11
+ * [`5f09073`](https://github.com/eslint/eslint/commit/5f0907399a9666dec78c74384c8969c01483c30e) Update: fix 'skip' options in no-irregular-whitespace (fixes #13852) (#13853) (Milos Djermanovic)
12
+ * [`1861b40`](https://github.com/eslint/eslint/commit/1861b4086f1018f43ab19744d866d5da986c500d) Docs: correct the function-call-argument-newline 'default' descriptions (#13866) (Trevin Hofmann)
13
+ * [`98c00c4`](https://github.com/eslint/eslint/commit/98c00c41d2aecb3a990393d430694f4ce6b47de5) New: Add no-nonoctal-decimal-escape rule (fixes #13765) (#13845) (Milos Djermanovic)
14
+ * [`95d2fe6`](https://github.com/eslint/eslint/commit/95d2fe6057498fc1cc2193d28c8c2d1593224b33) Chore: remove eslint comment from no-octal-escape tests (#13846) (Milos Djermanovic)
15
+ * [`2004b7e`](https://github.com/eslint/eslint/commit/2004b7ecd3db0d4e7376cc3344246f7b9ada5801) Fix: enable debug logs for @eslint/eslintrc (fixes #13850) (#13861) (Milos Djermanovic)
16
+ * [`d2239a1`](https://github.com/eslint/eslint/commit/d2239a1fdec452e24ede04e990d16d42516fa538) Fix: no-useless-constructor crash on bodyless constructor (fixes #13830) (#13842) (Ari Perkkiö)
17
+ * [`eda0aa1`](https://github.com/eslint/eslint/commit/eda0aa18498dd85eb618873e8e0f4ac97032cfca) Docs: no-restricted-imports is only for static imports (#13863) (Robat Williams)
18
+ * [`042ae44`](https://github.com/eslint/eslint/commit/042ae44682a8a6c5037d920689124e2304056dd8) Docs: Fix JS syntax and doc URL in working-with-custom-formatters.md (#13828) (Raphael LANG)
19
+ * [`038dc73`](https://github.com/eslint/eslint/commit/038dc73c99ae68eae2035ef303f3a947053c8f05) Chore: Test on Node.js 15 (#13844) (Brandon Mills)
20
+ * [`37a06d6`](https://github.com/eslint/eslint/commit/37a06d633d3669f0f43236141dc43465b8bc7ec5) Sponsors: Sync README with website (ESLint Jenkins)
21
+
22
+ v7.13.0 - November 6, 2020
23
+
24
+ * [`254e00f`](https://github.com/eslint/eslint/commit/254e00fea8745ff5a8bcc8cb874fcfd02996d81b) New: Configurable List Size For Per-Rule Performance Metrics (#13812) (Bryan Mishkin)
25
+ * [`6c3c710`](https://github.com/eslint/eslint/commit/6c3c710ade7cd8654990f1adb55b58f038eab92d) Docs: fix broken url in docs (#13815) (SaintMalik)
26
+ * [`4a09149`](https://github.com/eslint/eslint/commit/4a091495a236d231a5065ece972719a0c4dd1b77) Sponsors: Sync README with website (ESLint Jenkins)
27
+ * [`fb6fcbf`](https://github.com/eslint/eslint/commit/fb6fcbfe0a8c41b92f0a33ab90f159037bd195e2) Docs: Fix reference to Code of Conduct (#13797) (Tobias Nießen)
28
+ * [`1b89ebe`](https://github.com/eslint/eslint/commit/1b89ebe1bdbef7de6001100945b8f71429df302c) Sponsors: Sync README with website (ESLint Jenkins)
29
+
30
+ v7.12.1 - October 26, 2020
31
+
32
+ * [`08f33e8`](https://github.com/eslint/eslint/commit/08f33e8b9a353c3183be6f937785db7a30fb90eb) Upgrade: @eslint/eslintrc to fix rule schema validation (fixes #13793) (#13794) (Brandon Mills)
33
+ * [`aeef485`](https://github.com/eslint/eslint/commit/aeef485dc790571b1a82ac09904329e0226b66a9) Fix: Pass internal config paths in FileEnumerator default (fixes #13789) (#13792) (Brandon Mills)
34
+ * [`631ae8b`](https://github.com/eslint/eslint/commit/631ae8b50e5f7975f10860e9e763b70b4f25182e) Sponsors: Sync README with website (ESLint Jenkins)
35
+
1
36
  v7.12.0 - October 23, 2020
2
37
 
3
38
  * [`cbf3585`](https://github.com/eslint/eslint/commit/cbf3585f1d6c60414c07380367a8b4505ee3538d) Update: skip keyword check for fns in space-before-blocks (fixes #13553) (#13712) (Milos Djermanovic)
package/README.md CHANGED
@@ -14,7 +14,7 @@
14
14
  [Rules](https://eslint.org/docs/rules/) |
15
15
  [Contributing](https://eslint.org/docs/developer-guide/contributing) |
16
16
  [Reporting Bugs](https://eslint.org/docs/developer-guide/contributing/reporting-bugs) |
17
- [Code of Conduct](https://js.foundation/community/code-of-conduct) |
17
+ [Code of Conduct](https://eslint.org/conduct) |
18
18
  [Twitter](https://twitter.com/geteslint) |
19
19
  [Mailing List](https://groups.google.com/group/eslint) |
20
20
  [Chat Room](https://eslint.org/chat)
@@ -85,7 +85,7 @@ The three error levels allow you fine-grained control over how ESLint applies ru
85
85
 
86
86
  ## <a name="code-of-conduct"></a>Code of Conduct
87
87
 
88
- ESLint adheres to the [JS Foundation Code of Conduct](https://js.foundation/community/code-of-conduct).
88
+ ESLint adheres to the [JS Foundation Code of Conduct](https://eslint.org/conduct).
89
89
 
90
90
  ## <a name="filing-issues"></a>Filing Issues
91
91
 
@@ -265,9 +265,9 @@ The following companies, organizations, and individuals support ESLint's ongoing
265
265
  <!--sponsorsstart-->
266
266
  <h3>Platinum Sponsors</h3>
267
267
  <p><a href="https://automattic.com"><img src="https://images.opencollective.com/photomatt/ff91f0b/logo.png" alt="Automattic" height="undefined"></a></p><h3>Gold Sponsors</h3>
268
- <p><a href="https://www.shopify.com"><img src="https://images.opencollective.com/shopify/e780cd4/logo.png" alt="Shopify" 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://aka.ms/microsoftfossfund"><img src="https://avatars1.githubusercontent.com/u/67931232?u=7fddc652a464d7151b97e8f108392af7d54fa3e8&v=4" alt="Microsoft FOSS Fund Sponsorships" height="96"></a></p><h3>Silver Sponsors</h3>
268
+ <p><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.shopify.com"><img src="https://images.opencollective.com/shopify/e780cd4/logo.png" alt="Shopify" 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://aka.ms/microsoftfossfund"><img src="https://avatars1.githubusercontent.com/u/67931232?u=7fddc652a464d7151b97e8f108392af7d54fa3e8&v=4" alt="Microsoft FOSS Fund Sponsorships" height="96"></a></p><h3>Silver Sponsors</h3>
269
269
  <p><a href="https://liftoff.io/"><img src="https://images.opencollective.com/liftoff/5c4fa84/logo.png" alt="Liftoff" height="64"></a> <a href="https://www.ampproject.org/"><img src="https://images.opencollective.com/amp/c8a3b25/logo.png" alt="AMP Project" height="64"></a></p><h3>Bronze Sponsors</h3>
270
- <p><a href="https://writersperhour.com/write-my-essay"><img src="https://images.opencollective.com/writersperhour/5787d4b/logo.png" alt="Writers Per Hour" height="32"></a> <a href="https://www.betacalendars.com/printable-calendar"><img src="https://images.opencollective.com/betacalendars/9334b33/logo.png" alt="2021 calendar" height="32"></a> <a href="https://buy.fineproxy.org/eng/"><img src="https://images.opencollective.com/buy-fineproxy-org/2002c40/logo.png" alt="Buy.Fineproxy.Org" height="32"></a> <a href="https://www.veikkaajat.com"><img src="https://images.opencollective.com/veikkaajat/b92b427/logo.png" alt="Veikkaajat.com" 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://discordapp.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.marfeel.com/"><img src="https://images.opencollective.com/marfeel/4b88e30/logo.png" alt="Marfeel" 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>
270
+ <p><a href="https://writersperhour.com"><img src="https://images.opencollective.com/writersperhour/5787d4b/logo.png" alt="Writers Per Hour" height="32"></a> <a href="https://www.betacalendars.com/printable-calendar"><img src="https://images.opencollective.com/betacalendars/9334b33/logo.png" alt="2021 calendar" height="32"></a> <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.veikkaajat.com"><img src="https://images.opencollective.com/veikkaajat/b92b427/logo.png" alt="Veikkaajat.com" 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://discordapp.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>
271
271
  <!--sponsorsend-->
272
272
 
273
273
  ## <a name="technology-sponsors"></a>Technology Sponsors
package/bin/eslint.js CHANGED
@@ -14,7 +14,7 @@ require("v8-compile-cache");
14
14
 
15
15
  // must do this initialization *before* other requires in order to work
16
16
  if (process.argv.includes("--debug")) {
17
- require("debug").enable("eslint:*,-eslint:code-path");
17
+ require("debug").enable("eslint:*,-eslint:code-path,eslintrc:*");
18
18
  }
19
19
 
20
20
  //------------------------------------------------------------------------------
@@ -213,7 +213,11 @@ class FileEnumerator {
213
213
  */
214
214
  constructor({
215
215
  cwd = process.cwd(),
216
- configArrayFactory = new CascadingConfigArrayFactory({ cwd }),
216
+ configArrayFactory = new CascadingConfigArrayFactory({
217
+ cwd,
218
+ eslintRecommendedPath: path.resolve(__dirname, "../../conf/eslint-recommended.js"),
219
+ eslintAllPath: path.resolve(__dirname, "../../conf/eslint-all.js")
220
+ }),
217
221
  extensions = null,
218
222
  globInputPaths = true,
219
223
  errorOnUnmatchedPattern = true,
@@ -44,6 +44,26 @@ const enabled = !!process.env.TIMING;
44
44
  const HEADERS = ["Rule", "Time (ms)", "Relative"];
45
45
  const ALIGN = [alignLeft, alignRight, alignRight];
46
46
 
47
+ /**
48
+ * Decide how many rules to show in the output list.
49
+ * @returns {number} the number of rules to show
50
+ */
51
+ function getListSize() {
52
+ const MINIMUM_SIZE = 10;
53
+
54
+ if (typeof process.env.TIMING !== "string") {
55
+ return MINIMUM_SIZE;
56
+ }
57
+
58
+ if (process.env.TIMING.toLowerCase() === "all") {
59
+ return Number.POSITIVE_INFINITY;
60
+ }
61
+
62
+ const TIMING_ENV_VAR_AS_INTEGER = Number.parseInt(process.env.TIMING, 10);
63
+
64
+ return TIMING_ENV_VAR_AS_INTEGER > 10 ? TIMING_ENV_VAR_AS_INTEGER : MINIMUM_SIZE;
65
+ }
66
+
47
67
  /* istanbul ignore next */
48
68
  /**
49
69
  * display the data
@@ -61,7 +81,7 @@ function display(data) {
61
81
  return [key, time];
62
82
  })
63
83
  .sort((a, b) => b[1] - a[1])
64
- .slice(0, 10);
84
+ .slice(0, getListSize());
65
85
 
66
86
  rows.forEach(row => {
67
87
  row.push(`${(row[1] * 100 / total).toFixed(1)}%`);
@@ -133,7 +153,8 @@ module.exports = (function() {
133
153
 
134
154
  return {
135
155
  time,
136
- enabled
156
+ enabled,
157
+ getListSize
137
158
  };
138
159
 
139
160
  }());
@@ -169,6 +169,7 @@ module.exports = new LazyLoadingRuleMap(Object.entries({
169
169
  "no-new-require": () => require("./no-new-require"),
170
170
  "no-new-symbol": () => require("./no-new-symbol"),
171
171
  "no-new-wrappers": () => require("./no-new-wrappers"),
172
+ "no-nonoctal-decimal-escape": () => require("./no-nonoctal-decimal-escape"),
172
173
  "no-obj-calls": () => require("./no-obj-calls"),
173
174
  "no-octal": () => require("./no-octal"),
174
175
  "no-octal-escape": () => require("./no-octal-escape"),
@@ -217,6 +218,7 @@ module.exports = new LazyLoadingRuleMap(Object.entries({
217
218
  "no-unreachable-loop": () => require("./no-unreachable-loop"),
218
219
  "no-unsafe-finally": () => require("./no-unsafe-finally"),
219
220
  "no-unsafe-negation": () => require("./no-unsafe-negation"),
221
+ "no-unsafe-optional-chaining": () => require("./no-unsafe-optional-chaining"),
220
222
  "no-unused-expressions": () => require("./no-unused-expressions"),
221
223
  "no-unused-labels": () => require("./no-unused-labels"),
222
224
  "no-unused-vars": () => require("./no-unused-vars"),
@@ -82,7 +82,7 @@ module.exports = {
82
82
  const commentNodes = sourceCode.getAllComments();
83
83
 
84
84
  /**
85
- * Removes errors that occur inside a string node
85
+ * Removes errors that occur inside the given node
86
86
  * @param {ASTNode} node to check for matching errors.
87
87
  * @returns {void}
88
88
  * @private
@@ -91,14 +91,12 @@ module.exports = {
91
91
  const locStart = node.loc.start;
92
92
  const locEnd = node.loc.end;
93
93
 
94
- errors = errors.filter(({ loc: { start: errorLoc } }) => {
95
- if (errorLoc.line >= locStart.line && errorLoc.line <= locEnd.line) {
96
- if (errorLoc.column >= locStart.column && (errorLoc.column <= locEnd.column || errorLoc.line < locEnd.line)) {
97
- return false;
98
- }
99
- }
100
- return true;
101
- });
94
+ errors = errors.filter(({ loc: { start: errorLocStart } }) => (
95
+ errorLocStart.line < locStart.line ||
96
+ errorLocStart.line === locStart.line && errorLocStart.column < locStart.column ||
97
+ errorLocStart.line === locEnd.line && errorLocStart.column >= locEnd.column ||
98
+ errorLocStart.line > locEnd.line
99
+ ));
102
100
  }
103
101
 
104
102
  /**
@@ -0,0 +1,147 @@
1
+ /**
2
+ * @fileoverview Rule to disallow `\8` and `\9` escape sequences in string literals.
3
+ * @author Milos Djermanovic
4
+ */
5
+
6
+ "use strict";
7
+
8
+ //------------------------------------------------------------------------------
9
+ // Helpers
10
+ //------------------------------------------------------------------------------
11
+
12
+ const QUICK_TEST_REGEX = /\\[89]/u;
13
+
14
+ /**
15
+ * Returns unicode escape sequence that represents the given character.
16
+ * @param {string} character A single code unit.
17
+ * @returns {string} "\uXXXX" sequence.
18
+ */
19
+ function getUnicodeEscape(character) {
20
+ return `\\u${character.charCodeAt(0).toString(16).padStart(4, "0")}`;
21
+ }
22
+
23
+ //------------------------------------------------------------------------------
24
+ // Rule Definition
25
+ //------------------------------------------------------------------------------
26
+
27
+ module.exports = {
28
+ meta: {
29
+ type: "suggestion",
30
+
31
+ docs: {
32
+ description: "disallow `\\8` and `\\9` escape sequences in string literals",
33
+ category: "Best Practices",
34
+ recommended: false,
35
+ url: "https://eslint.org/docs/rules/no-nonoctal-decimal-escape",
36
+ suggestion: true
37
+ },
38
+
39
+ schema: [],
40
+
41
+ messages: {
42
+ decimalEscape: "Don't use '{{decimalEscape}}' escape sequence.",
43
+
44
+ // suggestions
45
+ refactor: "Replace '{{original}}' with '{{replacement}}'. This maintains the current functionality.",
46
+ escapeBackslash: "Replace '{{original}}' with '{{replacement}}' to include the actual backslash character."
47
+ }
48
+ },
49
+
50
+ create(context) {
51
+ const sourceCode = context.getSourceCode();
52
+
53
+ /**
54
+ * Creates a new Suggestion object.
55
+ * @param {string} messageId "refactor" or "escapeBackslash".
56
+ * @param {int[]} range The range to replace.
57
+ * @param {string} replacement New text for the range.
58
+ * @returns {Object} Suggestion
59
+ */
60
+ function createSuggestion(messageId, range, replacement) {
61
+ return {
62
+ messageId,
63
+ data: {
64
+ original: sourceCode.getText().slice(...range),
65
+ replacement
66
+ },
67
+ fix(fixer) {
68
+ return fixer.replaceTextRange(range, replacement);
69
+ }
70
+ };
71
+ }
72
+
73
+ return {
74
+ Literal(node) {
75
+ if (typeof node.value !== "string") {
76
+ return;
77
+ }
78
+
79
+ if (!QUICK_TEST_REGEX.test(node.raw)) {
80
+ return;
81
+ }
82
+
83
+ const regex = /(?:[^\\]|(?<previousEscape>\\.))*?(?<decimalEscape>\\[89])/suy;
84
+ let match;
85
+
86
+ while ((match = regex.exec(node.raw))) {
87
+ const { previousEscape, decimalEscape } = match.groups;
88
+ const decimalEscapeRangeEnd = node.range[0] + match.index + match[0].length;
89
+ const decimalEscapeRangeStart = decimalEscapeRangeEnd - decimalEscape.length;
90
+ const decimalEscapeRange = [decimalEscapeRangeStart, decimalEscapeRangeEnd];
91
+ const suggest = [];
92
+
93
+ // When `regex` is matched, `previousEscape` can only capture characters adjacent to `decimalEscape`
94
+ if (previousEscape === "\\0") {
95
+
96
+ /*
97
+ * Now we have a NULL escape "\0" immediately followed by a decimal escape, e.g.: "\0\8".
98
+ * Fixing this to "\08" would turn "\0" into a legacy octal escape. To avoid producing
99
+ * an octal escape while fixing a decimal escape, we provide different suggestions.
100
+ */
101
+ suggest.push(
102
+ createSuggestion( // "\0\8" -> "\u00008"
103
+ "refactor",
104
+ [decimalEscapeRangeStart - previousEscape.length, decimalEscapeRangeEnd],
105
+ `${getUnicodeEscape("\0")}${decimalEscape[1]}`
106
+ ),
107
+ createSuggestion( // "\8" -> "\u0038"
108
+ "refactor",
109
+ decimalEscapeRange,
110
+ getUnicodeEscape(decimalEscape[1])
111
+ )
112
+ );
113
+ } else {
114
+ suggest.push(
115
+ createSuggestion( // "\8" -> "8"
116
+ "refactor",
117
+ decimalEscapeRange,
118
+ decimalEscape[1]
119
+ )
120
+ );
121
+ }
122
+
123
+ suggest.push(
124
+ createSuggestion( // "\8" -> "\\8"
125
+ "escapeBackslash",
126
+ decimalEscapeRange,
127
+ `\\${decimalEscape}`
128
+ )
129
+ );
130
+
131
+ context.report({
132
+ node,
133
+ loc: {
134
+ start: sourceCode.getLocFromIndex(decimalEscapeRangeStart),
135
+ end: sourceCode.getLocFromIndex(decimalEscapeRangeEnd)
136
+ },
137
+ messageId: "decimalEscape",
138
+ data: {
139
+ decimalEscape
140
+ },
141
+ suggest
142
+ });
143
+ }
144
+ }
145
+ };
146
+ }
147
+ };
@@ -0,0 +1,205 @@
1
+ /**
2
+ * @fileoverview Rule to disallow unsafe optional chaining
3
+ * @author Yeon JuAn
4
+ */
5
+
6
+ "use strict";
7
+
8
+ const UNSAFE_ARITHMETIC_OPERATORS = new Set(["+", "-", "/", "*", "%", "**"]);
9
+ const UNSAFE_ASSIGNMENT_OPERATORS = new Set(["+=", "-=", "/=", "*=", "%=", "**="]);
10
+ const UNSAFE_RELATIONAL_OPERATORS = new Set(["in", "instanceof"]);
11
+
12
+ /**
13
+ * Checks whether a node is a destructuring pattern or not
14
+ * @param {ASTNode} node node to check
15
+ * @returns {boolean} `true` if a node is a destructuring pattern, otherwise `false`
16
+ */
17
+ function isDestructuringPattern(node) {
18
+ return node.type === "ObjectPattern" || node.type === "ArrayPattern";
19
+ }
20
+
21
+ module.exports = {
22
+ meta: {
23
+ type: "problem",
24
+
25
+ docs: {
26
+ description: "disallow use of optional chaining in contexts where the `undefined` value is not allowed",
27
+ category: "Possible Errors",
28
+ recommended: false,
29
+ url: "https://eslint.org/docs/rules/no-unsafe-optional-chaining"
30
+ },
31
+ schema: [{
32
+ type: "object",
33
+ properties: {
34
+ disallowArithmeticOperators: {
35
+ type: "boolean",
36
+ default: false
37
+ }
38
+ },
39
+ additionalProperties: false
40
+ }],
41
+ fixable: null,
42
+ messages: {
43
+ unsafeOptionalChain: "Unsafe usage of optional chaining. If it short-circuits with 'undefined' the evaluation will throw TypeError.",
44
+ unsafeArithmetic: "Unsafe arithmetic operation on optional chaining. It can result in NaN."
45
+ }
46
+ },
47
+
48
+ create(context) {
49
+ const options = context.options[0] || {};
50
+ const disallowArithmeticOperators = (options.disallowArithmeticOperators) || false;
51
+
52
+ /**
53
+ * Reports unsafe usage of optional chaining
54
+ * @param {ASTNode} node node to report
55
+ * @returns {void}
56
+ */
57
+ function reportUnsafeUsage(node) {
58
+ context.report({
59
+ messageId: "unsafeOptionalChain",
60
+ node
61
+ });
62
+ }
63
+
64
+ /**
65
+ * Reports unsafe arithmetic operation on optional chaining
66
+ * @param {ASTNode} node node to report
67
+ * @returns {void}
68
+ */
69
+ function reportUnsafeArithmetic(node) {
70
+ context.report({
71
+ messageId: "unsafeArithmetic",
72
+ node
73
+ });
74
+ }
75
+
76
+ /**
77
+ * Checks and reports if a node can short-circuit with `undefined` by optional chaining.
78
+ * @param {ASTNode} [node] node to check
79
+ * @param {Function} reportFunc report function
80
+ * @returns {void}
81
+ */
82
+ function checkUndefinedShortCircuit(node, reportFunc) {
83
+ if (!node) {
84
+ return;
85
+ }
86
+ switch (node.type) {
87
+ case "LogicalExpression":
88
+ if (node.operator === "||" || node.operator === "??") {
89
+ checkUndefinedShortCircuit(node.right, reportFunc);
90
+ } else if (node.operator === "&&") {
91
+ checkUndefinedShortCircuit(node.left, reportFunc);
92
+ checkUndefinedShortCircuit(node.right, reportFunc);
93
+ }
94
+ break;
95
+ case "SequenceExpression":
96
+ checkUndefinedShortCircuit(
97
+ node.expressions[node.expressions.length - 1],
98
+ reportFunc
99
+ );
100
+ break;
101
+ case "ConditionalExpression":
102
+ checkUndefinedShortCircuit(node.consequent, reportFunc);
103
+ checkUndefinedShortCircuit(node.alternate, reportFunc);
104
+ break;
105
+ case "AwaitExpression":
106
+ checkUndefinedShortCircuit(node.argument, reportFunc);
107
+ break;
108
+ case "ChainExpression":
109
+ reportFunc(node);
110
+ break;
111
+ default:
112
+ break;
113
+ }
114
+ }
115
+
116
+ /**
117
+ * Checks unsafe usage of optional chaining
118
+ * @param {ASTNode} node node to check
119
+ * @returns {void}
120
+ */
121
+ function checkUnsafeUsage(node) {
122
+ checkUndefinedShortCircuit(node, reportUnsafeUsage);
123
+ }
124
+
125
+ /**
126
+ * Checks unsafe arithmetic operations on optional chaining
127
+ * @param {ASTNode} node node to check
128
+ * @returns {void}
129
+ */
130
+ function checkUnsafeArithmetic(node) {
131
+ checkUndefinedShortCircuit(node, reportUnsafeArithmetic);
132
+ }
133
+
134
+ return {
135
+ "AssignmentExpression, AssignmentPattern"(node) {
136
+ if (isDestructuringPattern(node.left)) {
137
+ checkUnsafeUsage(node.right);
138
+ }
139
+ },
140
+ "ClassDeclaration, ClassExpression"(node) {
141
+ checkUnsafeUsage(node.superClass);
142
+ },
143
+ CallExpression(node) {
144
+ if (!node.optional) {
145
+ checkUnsafeUsage(node.callee);
146
+ }
147
+ },
148
+ NewExpression(node) {
149
+ checkUnsafeUsage(node.callee);
150
+ },
151
+ VariableDeclarator(node) {
152
+ if (isDestructuringPattern(node.id)) {
153
+ checkUnsafeUsage(node.init);
154
+ }
155
+ },
156
+ MemberExpression(node) {
157
+ if (!node.optional) {
158
+ checkUnsafeUsage(node.object);
159
+ }
160
+ },
161
+ TaggedTemplateExpression(node) {
162
+ checkUnsafeUsage(node.tag);
163
+ },
164
+ ForOfStatement(node) {
165
+ checkUnsafeUsage(node.right);
166
+ },
167
+ SpreadElement(node) {
168
+ if (node.parent && node.parent.type !== "ObjectExpression") {
169
+ checkUnsafeUsage(node.argument);
170
+ }
171
+ },
172
+ BinaryExpression(node) {
173
+ if (UNSAFE_RELATIONAL_OPERATORS.has(node.operator)) {
174
+ checkUnsafeUsage(node.right);
175
+ }
176
+ if (
177
+ disallowArithmeticOperators &&
178
+ UNSAFE_ARITHMETIC_OPERATORS.has(node.operator)
179
+ ) {
180
+ checkUnsafeArithmetic(node.right);
181
+ checkUnsafeArithmetic(node.left);
182
+ }
183
+ },
184
+ WithStatement(node) {
185
+ checkUnsafeUsage(node.object);
186
+ },
187
+ UnaryExpression(node) {
188
+ if (
189
+ disallowArithmeticOperators &&
190
+ UNSAFE_ARITHMETIC_OPERATORS.has(node.operator)
191
+ ) {
192
+ checkUnsafeArithmetic(node.argument);
193
+ }
194
+ },
195
+ AssignmentExpression(node) {
196
+ if (
197
+ disallowArithmeticOperators &&
198
+ UNSAFE_ASSIGNMENT_OPERATORS.has(node.operator)
199
+ ) {
200
+ checkUnsafeArithmetic(node.right);
201
+ }
202
+ }
203
+ };
204
+ }
205
+ };
@@ -162,6 +162,14 @@ module.exports = {
162
162
  return;
163
163
  }
164
164
 
165
+ /*
166
+ * Prevent crashing on parsers which do not require class constructor
167
+ * to have a body, e.g. typescript and flow
168
+ */
169
+ if (!node.value.body) {
170
+ return;
171
+ }
172
+
165
173
  const body = node.value.body.body;
166
174
  const ctorParams = node.value.params;
167
175
  const superClass = node.parent.parent.superClass;
@@ -314,12 +314,14 @@ module.exports = {
314
314
  return null;
315
315
  }
316
316
 
317
+ const exportPlacement = declaration.parent.type === "ExportNamedDeclaration" ? "export " : "";
318
+
317
319
  /*
318
320
  * `var x,y`
319
321
  * tokenAfterDeclarator ^^ afterComma
320
322
  */
321
323
  if (afterComma.range[0] === tokenAfterDeclarator.range[1]) {
322
- return fixer.replaceText(tokenAfterDeclarator, `; ${declaration.kind} `);
324
+ return fixer.replaceText(tokenAfterDeclarator, `; ${exportPlacement}${declaration.kind} `);
323
325
  }
324
326
 
325
327
  /*
@@ -341,11 +343,11 @@ module.exports = {
341
343
 
342
344
  return fixer.replaceTextRange(
343
345
  [tokenAfterDeclarator.range[0], lastComment.range[0]],
344
- `;${sourceCode.text.slice(tokenAfterDeclarator.range[1], lastComment.range[0])}${declaration.kind} `
346
+ `;${sourceCode.text.slice(tokenAfterDeclarator.range[1], lastComment.range[0])}${exportPlacement}${declaration.kind} `
345
347
  );
346
348
  }
347
349
 
348
- return fixer.replaceText(tokenAfterDeclarator, `; ${declaration.kind}`);
350
+ return fixer.replaceText(tokenAfterDeclarator, `; ${exportPlacement}${declaration.kind}`);
349
351
  }).filter(x => x);
350
352
  }
351
353
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint",
3
- "version": "7.12.0",
3
+ "version": "7.15.0",
4
4
  "author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>",
5
5
  "description": "An AST-based pattern checker for JavaScript.",
6
6
  "bin": {
@@ -47,7 +47,7 @@
47
47
  "bugs": "https://github.com/eslint/eslint/issues/",
48
48
  "dependencies": {
49
49
  "@babel/code-frame": "^7.0.0",
50
- "@eslint/eslintrc": "^0.2.0",
50
+ "@eslint/eslintrc": "^0.2.2",
51
51
  "ajv": "^6.10.0",
52
52
  "chalk": "^4.0.0",
53
53
  "cross-spawn": "^7.0.2",
@@ -57,10 +57,10 @@
57
57
  "eslint-scope": "^5.1.1",
58
58
  "eslint-utils": "^2.1.0",
59
59
  "eslint-visitor-keys": "^2.0.0",
60
- "espree": "^7.3.0",
60
+ "espree": "^7.3.1",
61
61
  "esquery": "^1.2.0",
62
62
  "esutils": "^2.0.2",
63
- "file-entry-cache": "^5.0.1",
63
+ "file-entry-cache": "^6.0.0",
64
64
  "functional-red-black-tree": "^1.0.1",
65
65
  "glob-parent": "^5.0.0",
66
66
  "globals": "^12.1.0",