eslint 7.3.1 → 7.4.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,23 @@
1
+ v7.4.0 - July 3, 2020
2
+
3
+ * [`f21bad2`](https://github.com/eslint/eslint/commit/f21bad2680406a2671b877f8dba47f4475d0cc64) Docs: fix description for `never` in multiline-ternary (fixes #13368) (#13452) (Milos Djermanovic)
4
+ * [`ada2c89`](https://github.com/eslint/eslint/commit/ada2c891298382f82dfabf37cacd59a1057b2bb7) Fix: support typescript generics in arrow-parens (fixes #12570) (#13451) (Milos Djermanovic)
5
+ * [`89ee01e`](https://github.com/eslint/eslint/commit/89ee01e083f1e02293bf8d1447f9b0fdb3cb9384) Fix: Revert config cloning (fixes #13447) (#13449) (薛定谔的猫)
6
+ * [`0a463db`](https://github.com/eslint/eslint/commit/0a463dbf7cc5a77d442879c9117204d4d38db972) Docs: fix no-multiple-empty-lines examples (fixes #13432) (#13433) (Milos Djermanovic)
7
+ * [`ff5317e`](https://github.com/eslint/eslint/commit/ff5317e93425f93cfdf808609551ee67b2032543) Update: Improve array-callback-return report message (#13395) (Philip (flip) Kromer)
8
+ * [`3f51930`](https://github.com/eslint/eslint/commit/3f51930eea7cddc921a9ee3cb0328c7b649c0f83) Fix: false positive new with member in no-extra-parens (fixes #12740) (#13375) (YeonJuan)
9
+ * [`825a5b9`](https://github.com/eslint/eslint/commit/825a5b98d3d84f6eb72b75f7d8519de763cc8898) Fix: Clarify documentation on implicit ignore behavior (fixes #12348) (#12600) (Scott Hardin)
10
+ * [`c139156`](https://github.com/eslint/eslint/commit/c1391566a5f765f25716527de7b5cdee16c0ce36) Sponsors: Sync README with website (ESLint Jenkins)
11
+ * [`0c17e9d`](https://github.com/eslint/eslint/commit/0c17e9d2ac307cc288eea6ed7971bd5a7d33321a) Sponsors: Sync README with website (ESLint Jenkins)
12
+ * [`c680387`](https://github.com/eslint/eslint/commit/c680387ba61f6dccf0390d24a85d871fa83e9fea) Sponsors: Sync README with website (ESLint Jenkins)
13
+ * [`bf3939b`](https://github.com/eslint/eslint/commit/bf3939bbd9a33d0eb96cebe6a53bf61c855f9ba6) Sponsors: Sync README with website (ESLint Jenkins)
14
+ * [`7baf02e`](https://github.com/eslint/eslint/commit/7baf02e983af909800261263f125cca901a5bd0f) Sponsors: Sync README with website (ESLint Jenkins)
15
+ * [`5c4c3fd`](https://github.com/eslint/eslint/commit/5c4c3fdfbda18a13223ad36f44283adbfee8c496) Sponsors: Sync README with website (ESLint Jenkins)
16
+ * [`53912aa`](https://github.com/eslint/eslint/commit/53912aab1856327b399cca26cbb2ba81fd01bfa2) Sponsors: Sync README with website (ESLint Jenkins)
17
+ * [`51e42ec`](https://github.com/eslint/eslint/commit/51e42eca3e87d8259815d736ffe81e604f184057) Update: Add option "ignoreGlobals" to camelcase rule (fixes #11716) (#12782) (David Gasperoni)
18
+ * [`0655f66`](https://github.com/eslint/eslint/commit/0655f66525d167ca1288167b79a77087cfc8fcf6) Update: improve report location in arrow-body-style (refs #12334) (#13424) (YeonJuan)
19
+ * [`d53d69a`](https://github.com/eslint/eslint/commit/d53d69af08cfe55f42e0a0ca725b1014dabccc21) Update: prefer-regex-literal detect regex literals (fixes #12840) (#12842) (Mathias Schreck)
20
+ * [`004adae`](https://github.com/eslint/eslint/commit/004adae3f959414f56e44e5884f6221e9dcda142) Update: rename id-blacklist to id-denylist (fixes #13407) (#13408) (Kai Cataldo)
1
21
  v7.3.1 - June 22, 2020
2
22
 
3
23
  * [`de77c11`](https://github.com/eslint/eslint/commit/de77c11e7515f2097ff355ddc0d7b6db9c83c892) Fix: Replace Infinity with Number.MAX_SAFE_INTEGER (fixes #13427) (#13435) (Nicholas C. Zakas)
package/README.md CHANGED
@@ -256,7 +256,7 @@ The following companies, organizations, and individuals support ESLint's ongoing
256
256
  <h3>Gold Sponsors</h3>
257
257
  <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></p><h3>Silver Sponsors</h3>
258
258
  <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>
259
- <p><a href="https://www.norgekasino.com"><img src="https://images.opencollective.com/norgekasino/ecfd57a/logo.png" alt="Norgekasino" height="32"></a> <a href="https://www.japanesecasino.com/"><img src="https://images.opencollective.com/japanesecasino/b0ffe3c/logo.png" alt="Japanesecasino" height="32"></a> <a href="https://bruce.agency"><img src="https://images.opencollective.com/brucemade/0c70c59/logo.png" alt="Bruce" height="32"></a> <a href="https://edubirdie.com/"><img src="https://images.opencollective.com/edubirdie2/b1d51ab/logo.png" alt="EduBirdie" height="32"></a> <a href="https://www.casinotop.com/"><img src="https://images.opencollective.com/casinotop-com/10fd95b/logo.png" alt="CasinoTop.com" height="32"></a> <a href="https://www.casinotopp.net/"><img src="https://images.opencollective.com/casino-topp/1dd399a/logo.png" alt="Casino Topp" height="32"></a> <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.crosswordsolver.org/anagram-solver/"><img src="https://images.opencollective.com/anagram-solver/2666271/logo.png" alt="Anagram Solver" height="32"></a> <a href="https://cooltechzone.com/netflix-vpn"><img src="https://images.opencollective.com/vpn-netflix/4850160/logo.png" alt="vpn netflix" height="32"></a> <a href="https://www.kasinot.fi"><img src="https://images.opencollective.com/kasinot-fi/e09aa2e/logo.png" alt="Kasinot.fi" height="32"></a> <a href="https://www.pelisivut.com"><img src="https://images.opencollective.com/pelisivut/04f08f2/logo.png" alt="Pelisivut" height="32"></a> <a href="https://www.nettikasinot.org"><img src="https://images.opencollective.com/nettikasinot-org/53a4b44/logo.png" alt="Nettikasinot.org" height="32"></a> <a href="https://www.bonus.com.de/freispiele"><img src="https://images.opencollective.com/bonusfinder-deutschland/646169e/logo.png" alt="BonusFinder Deutschland" height="32"></a> <a href="https://www.bugsnag.com/platforms?utm_source=Open Collective&utm_medium=Website&utm_content=open-source&utm_campaign=2019-community&utm_term="><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/0b37d14/logo.png" alt="Free Icons by Icons8" 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://tekhattan.com"><img src="https://images.opencollective.com/tekhattan/bc73c28/logo.png" alt="TekHattan" 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="http://www.firesticktricks.com"><img src="https://images.opencollective.com/fire-stick-tricks/b8fbe2c/logo.png" alt="Fire Stick Tricks" height="32"></a></p>
259
+ <p><a href="https://mytruemedia.com/"><img src="https://images.opencollective.com/my-true-media/03e2168/logo.png" alt="My True Media" height="32"></a> <a href="https://www.norgekasino.com"><img src="https://images.opencollective.com/norgekasino/ecfd57a/logo.png" alt="Norgekasino" height="32"></a> <a href="https://www.japanesecasino.com/"><img src="https://images.opencollective.com/japanesecasino/b0ffe3c/logo.png" alt="Japanesecasino" height="32"></a> <a href="https://bruce.agency"><img src="https://images.opencollective.com/brucemade/0c70c59/logo.png" alt="Bruce" height="32"></a> <a href="https://edubirdie.com/"><img src="https://images.opencollective.com/edubirdie2/b1d51ab/logo.png" alt="EduBirdie" height="32"></a> <a href="https://www.casinotop.com/"><img src="https://images.opencollective.com/casinotop-com/10fd95b/logo.png" alt="CasinoTop.com" height="32"></a> <a href="https://www.casinotopp.net/"><img src="https://images.opencollective.com/casino-topp/1dd399a/logo.png" alt="Casino Topp" height="32"></a> <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.crosswordsolver.org/anagram-solver/"><img src="https://images.opencollective.com/anagram-solver/2666271/logo.png" alt="Anagram Solver" height="32"></a> <a href="https://www.kasinot.fi"><img src="https://images.opencollective.com/kasinot-fi/e09aa2e/logo.png" alt="Kasinot.fi" height="32"></a> <a href="https://www.pelisivut.com"><img src="https://images.opencollective.com/pelisivut/04f08f2/logo.png" alt="Pelisivut" height="32"></a> <a href="https://www.nettikasinot.org"><img src="https://images.opencollective.com/nettikasinot-org/53a4b44/logo.png" alt="Nettikasinot.org" height="32"></a> <a href="https://www.bonus.com.de/freispiele"><img src="https://images.opencollective.com/bonusfinder-deutschland/646169e/logo.png" alt="BonusFinder Deutschland" height="32"></a> <a href="https://www.bugsnag.com/platforms?utm_source=Open Collective&utm_medium=Website&utm_content=open-source&utm_campaign=2019-community&utm_term="><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/6e889f6/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://tekhattan.com"><img src="https://images.opencollective.com/tekhattan/bc73c28/logo.png" alt="TekHattan" 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="http://www.firesticktricks.com"><img src="https://images.opencollective.com/fire-stick-tricks/b8fbe2c/logo.png" alt="Fire Stick Tricks" height="32"></a></p>
260
260
  <!--sponsorsend-->
261
261
 
262
262
  ## <a name="technology-sponsors"></a>Technology Sponsors
@@ -697,38 +697,6 @@ class ConfigArrayFactory {
697
697
  ctx.matchBasePath
698
698
  );
699
699
 
700
- /**
701
- * Cloning the rule's config as we are setting `useDefaults` to true`
702
- * which mutates the config with its default value if present. And when we
703
- * refer to a same variable for config for different rules, that referred variable will
704
- * be mutated and it will be used for both.
705
- *
706
- * Example:
707
- *
708
- * const commonRuleConfig = ['error', {}];
709
- *
710
- * Now if we use this variable as a config for rules like this
711
- *
712
- * {
713
- * rules: {
714
- * "a" : commonRuleConfig,
715
- * "b" : commonRuleConfig,
716
- * }
717
- * }
718
- *
719
- * And if these rules have default values in their schema, their
720
- * config will be mutated with default values, the mutated `commonRuleConfig` will be used for `b` as well and it probably
721
- * throw schema voilation errors.
722
- *
723
- * Refer https://github.com/eslint/eslint/issues/12592
724
- */
725
- const clonedRulesConfig = rules && JSON.parse(
726
- JSON.stringify(
727
- rules,
728
- (key, value) => (value === Infinity ? Number.MAX_SAFE_INTEGER : value)
729
- )
730
- );
731
-
732
700
  // Flatten `extends`.
733
701
  for (const extendName of extendList.filter(Boolean)) {
734
702
  yield* this._loadExtends(extendName, ctx);
@@ -763,7 +731,7 @@ class ConfigArrayFactory {
763
731
  processor,
764
732
  reportUnusedDisableDirectives,
765
733
  root,
766
- rules: clonedRulesConfig,
734
+ rules,
767
735
  settings
768
736
  };
769
737
 
@@ -9,8 +9,6 @@
9
9
  // Requirements
10
10
  //------------------------------------------------------------------------------
11
11
 
12
- const lodash = require("lodash");
13
-
14
12
  const astUtils = require("./utils/ast-utils");
15
13
 
16
14
  //------------------------------------------------------------------------------
@@ -43,6 +41,19 @@ function isTargetMethod(node) {
43
41
  );
44
42
  }
45
43
 
44
+ /**
45
+ * Returns a human-legible description of an array method
46
+ * @param {string} arrayMethodName A method name to fully qualify
47
+ * @returns {string} the method name prefixed with `Array.` if it is a class method,
48
+ * or else `Array.prototype.` if it is an instance method.
49
+ */
50
+ function fullMethodName(arrayMethodName) {
51
+ if (["from", "of", "isArray"].includes(arrayMethodName)) {
52
+ return "Array.".concat(arrayMethodName);
53
+ }
54
+ return "Array.prototype.".concat(arrayMethodName);
55
+ }
56
+
46
57
  /**
47
58
  * Checks whether or not a given node is a function expression which is the
48
59
  * callback of an array method, returning the method name.
@@ -153,10 +164,10 @@ module.exports = {
153
164
  ],
154
165
 
155
166
  messages: {
156
- expectedAtEnd: "Expected to return a value at the end of {{name}}.",
157
- expectedInside: "Expected to return a value in {{name}}.",
158
- expectedReturnValue: "{{name}} expected a return value.",
159
- expectedNoReturnValue: "{{name}} did not expect a return value."
167
+ expectedAtEnd: "{{arrayMethodName}}() expects a value to be returned at the end of {{name}}.",
168
+ expectedInside: "{{arrayMethodName}}() expects a return value from {{name}}.",
169
+ expectedReturnValue: "{{arrayMethodName}}() expects a return value from {{name}}.",
170
+ expectedNoReturnValue: "{{arrayMethodName}}() expects no useless return value from {{name}}."
160
171
  }
161
172
  },
162
173
 
@@ -202,14 +213,13 @@ module.exports = {
202
213
  }
203
214
 
204
215
  if (messageId) {
205
- let name = astUtils.getFunctionNameWithKind(node);
216
+ const name = astUtils.getFunctionNameWithKind(node);
206
217
 
207
- name = messageId === "expectedNoReturnValue" ? lodash.upperFirst(name) : name;
208
218
  context.report({
209
219
  node,
210
220
  loc: astUtils.getFunctionHeadLoc(node, sourceCode),
211
221
  messageId,
212
- data: { name }
222
+ data: { name, arrayMethodName: fullMethodName(funcInfo.arrayMethodName) }
213
223
  });
214
224
  }
215
225
  }
@@ -273,7 +283,8 @@ module.exports = {
273
283
  node,
274
284
  messageId,
275
285
  data: {
276
- name: lodash.upperFirst(astUtils.getFunctionNameWithKind(funcInfo.node))
286
+ name: astUtils.getFunctionNameWithKind(funcInfo.node),
287
+ arrayMethodName: fullMethodName(funcInfo.arrayMethodName)
277
288
  }
278
289
  });
279
290
  }
@@ -136,7 +136,7 @@ module.exports = {
136
136
 
137
137
  context.report({
138
138
  node,
139
- loc: arrowBody.loc.start,
139
+ loc: arrowBody.loc,
140
140
  messageId,
141
141
  fix(fixer) {
142
142
  const fixes = [];
@@ -201,7 +201,7 @@ module.exports = {
201
201
  if (always || (asNeeded && requireReturnForObjectLiteral && arrowBody.type === "ObjectExpression")) {
202
202
  context.report({
203
203
  node,
204
- loc: arrowBody.loc.start,
204
+ loc: arrowBody.loc,
205
205
  messageId: "expectedBlock",
206
206
  fix(fixer) {
207
207
  const fixes = [];
@@ -15,15 +15,12 @@ const astUtils = require("./utils/ast-utils");
15
15
  //------------------------------------------------------------------------------
16
16
 
17
17
  /**
18
- * Get location should be reported by AST node.
19
- * @param {ASTNode} node AST Node.
20
- * @returns {Location} Location information.
18
+ * Determines if the given arrow function has block body.
19
+ * @param {ASTNode} node `ArrowFunctionExpression` node.
20
+ * @returns {boolean} `true` if the function has block body.
21
21
  */
22
- function getLocation(node) {
23
- return {
24
- start: node.params[0].loc.start,
25
- end: node.params[node.params.length - 1].loc.end
26
- };
22
+ function hasBlockBody(node) {
23
+ return node.body.type === "BlockStatement";
27
24
  }
28
25
 
29
26
  //------------------------------------------------------------------------------
@@ -75,126 +72,112 @@ module.exports = {
75
72
  const sourceCode = context.getSourceCode();
76
73
 
77
74
  /**
78
- * Determines whether a arrow function argument end with `)`
79
- * @param {ASTNode} node The arrow function node.
80
- * @returns {void}
75
+ * Finds opening paren of parameters for the given arrow function, if it exists.
76
+ * It is assumed that the given arrow function has exactly one parameter.
77
+ * @param {ASTNode} node `ArrowFunctionExpression` node.
78
+ * @returns {Token|null} the opening paren, or `null` if the given arrow function doesn't have parens of parameters.
81
79
  */
82
- function parens(node) {
83
- const isAsync = node.async;
84
- const firstTokenOfParam = sourceCode.getFirstToken(node, isAsync ? 1 : 0);
85
-
86
- /**
87
- * Remove the parenthesis around a parameter
88
- * @param {Fixer} fixer Fixer
89
- * @returns {string} fixed parameter
90
- */
91
- function fixParamsWithParenthesis(fixer) {
92
- const paramToken = sourceCode.getTokenAfter(firstTokenOfParam);
93
-
94
- /*
95
- * ES8 allows Trailing commas in function parameter lists and calls
96
- * https://github.com/eslint/eslint/issues/8834
97
- */
98
- const closingParenToken = sourceCode.getTokenAfter(paramToken, astUtils.isClosingParenToken);
99
- const asyncToken = isAsync ? sourceCode.getTokenBefore(firstTokenOfParam) : null;
100
- const shouldAddSpaceForAsync = asyncToken && (asyncToken.range[1] === firstTokenOfParam.range[0]);
101
-
102
- return fixer.replaceTextRange([
103
- firstTokenOfParam.range[0],
104
- closingParenToken.range[1]
105
- ], `${shouldAddSpaceForAsync ? " " : ""}${paramToken.value}`);
80
+ function findOpeningParenOfParams(node) {
81
+ const tokenBeforeParams = sourceCode.getTokenBefore(node.params[0]);
82
+
83
+ if (
84
+ tokenBeforeParams &&
85
+ astUtils.isOpeningParenToken(tokenBeforeParams) &&
86
+ node.range[0] <= tokenBeforeParams.range[0]
87
+ ) {
88
+ return tokenBeforeParams;
106
89
  }
107
90
 
108
- /**
109
- * Checks whether there are comments inside the params or not.
110
- * @returns {boolean} `true` if there are comments inside of parens, else `false`
111
- */
112
- function hasCommentsInParens() {
113
- if (astUtils.isOpeningParenToken(firstTokenOfParam)) {
114
- const closingParenToken = sourceCode.getTokenAfter(node.params[0], astUtils.isClosingParenToken);
91
+ return null;
92
+ }
115
93
 
116
- return closingParenToken && sourceCode.commentsExistBetween(firstTokenOfParam, closingParenToken);
117
- }
118
- return false;
94
+ /**
95
+ * Finds closing paren of parameters for the given arrow function.
96
+ * It is assumed that the given arrow function has parens of parameters and that it has exactly one parameter.
97
+ * @param {ASTNode} node `ArrowFunctionExpression` node.
98
+ * @returns {Token} the closing paren of parameters.
99
+ */
100
+ function getClosingParenOfParams(node) {
101
+ return sourceCode.getTokenAfter(node.params[0], astUtils.isClosingParenToken);
102
+ }
119
103
 
120
- }
104
+ /**
105
+ * Determines whether the given arrow function has comments inside parens of parameters.
106
+ * It is assumed that the given arrow function has parens of parameters.
107
+ * @param {ASTNode} node `ArrowFunctionExpression` node.
108
+ * @param {Token} openingParen Opening paren of parameters.
109
+ * @returns {boolean} `true` if the function has at least one comment inside of parens of parameters.
110
+ */
111
+ function hasCommentsInParensOfParams(node, openingParen) {
112
+ return sourceCode.commentsExistBetween(openingParen, getClosingParenOfParams(node));
113
+ }
121
114
 
122
- if (hasCommentsInParens()) {
123
- return;
124
- }
115
+ /**
116
+ * Determines whether the given arrow function has unexpected tokens before opening paren of parameters,
117
+ * in which case it will be assumed that the existing parens of parameters are necessary.
118
+ * Only tokens within the range of the arrow function (tokens that are part of the arrow function) are taken into account.
119
+ * Example: <T>(a) => b
120
+ * @param {ASTNode} node `ArrowFunctionExpression` node.
121
+ * @param {Token} openingParen Opening paren of parameters.
122
+ * @returns {boolean} `true` if the function has at least one unexpected token.
123
+ */
124
+ function hasUnexpectedTokensBeforeOpeningParen(node, openingParen) {
125
+ const expectedCount = node.async ? 1 : 0;
125
126
 
126
- // "as-needed", { "requireForBlockBody": true }: x => x
127
- if (
128
- requireForBlockBody &&
129
- node.params[0].type === "Identifier" &&
130
- !node.params[0].typeAnnotation &&
131
- node.body.type !== "BlockStatement" &&
132
- !node.returnType
133
- ) {
134
- if (astUtils.isOpeningParenToken(firstTokenOfParam)) {
135
- context.report({
136
- node,
137
- messageId: "unexpectedParensInline",
138
- loc: getLocation(node),
139
- fix: fixParamsWithParenthesis
140
- });
141
- }
142
- return;
143
- }
127
+ return sourceCode.getFirstToken(node, { skip: expectedCount }) !== openingParen;
128
+ }
144
129
 
145
- if (
146
- requireForBlockBody &&
147
- node.body.type === "BlockStatement"
148
- ) {
149
- if (!astUtils.isOpeningParenToken(firstTokenOfParam)) {
150
- context.report({
151
- node,
152
- messageId: "expectedParensBlock",
153
- loc: getLocation(node),
154
- fix(fixer) {
155
- return fixer.replaceText(firstTokenOfParam, `(${firstTokenOfParam.value})`);
156
- }
157
- });
158
- }
159
- return;
160
- }
130
+ return {
131
+ "ArrowFunctionExpression[params.length=1]"(node) {
132
+ const shouldHaveParens = !asNeeded || requireForBlockBody && hasBlockBody(node);
133
+ const openingParen = findOpeningParenOfParams(node);
134
+ const hasParens = openingParen !== null;
135
+ const [param] = node.params;
161
136
 
162
- // "as-needed": x => x
163
- if (asNeeded &&
164
- node.params[0].type === "Identifier" &&
165
- !node.params[0].typeAnnotation &&
166
- !node.returnType
167
- ) {
168
- if (astUtils.isOpeningParenToken(firstTokenOfParam)) {
137
+ if (shouldHaveParens && !hasParens) {
169
138
  context.report({
170
139
  node,
171
- messageId: "unexpectedParens",
172
- loc: getLocation(node),
173
- fix: fixParamsWithParenthesis
140
+ messageId: requireForBlockBody ? "expectedParensBlock" : "expectedParens",
141
+ loc: param.loc,
142
+ *fix(fixer) {
143
+ yield fixer.insertTextBefore(param, "(");
144
+ yield fixer.insertTextAfter(param, ")");
145
+ }
174
146
  });
175
147
  }
176
- return;
177
- }
178
148
 
179
- if (firstTokenOfParam.type === "Identifier") {
180
- const after = sourceCode.getTokenAfter(firstTokenOfParam);
181
-
182
- // (x) => x
183
- if (after.value !== ")") {
149
+ if (
150
+ !shouldHaveParens &&
151
+ hasParens &&
152
+ param.type === "Identifier" &&
153
+ !param.typeAnnotation &&
154
+ !node.returnType &&
155
+ !hasCommentsInParensOfParams(node, openingParen) &&
156
+ !hasUnexpectedTokensBeforeOpeningParen(node, openingParen)
157
+ ) {
184
158
  context.report({
185
159
  node,
186
- messageId: "expectedParens",
187
- loc: getLocation(node),
188
- fix(fixer) {
189
- return fixer.replaceText(firstTokenOfParam, `(${firstTokenOfParam.value})`);
160
+ messageId: requireForBlockBody ? "unexpectedParensInline" : "unexpectedParens",
161
+ loc: param.loc,
162
+ *fix(fixer) {
163
+ const tokenBeforeOpeningParen = sourceCode.getTokenBefore(openingParen);
164
+ const closingParen = getClosingParenOfParams(node);
165
+
166
+ if (
167
+ tokenBeforeOpeningParen &&
168
+ tokenBeforeOpeningParen.range[1] === openingParen.range[0] &&
169
+ !astUtils.canTokensBeAdjacent(tokenBeforeOpeningParen, sourceCode.getFirstToken(param))
170
+ ) {
171
+ yield fixer.insertTextBefore(openingParen, " ");
172
+ }
173
+
174
+ // remove parens, whitespace inside parens, and possible trailing comma
175
+ yield fixer.removeRange([openingParen.range[0], param.range[0]]);
176
+ yield fixer.removeRange([param.range[1], closingParen.range[1]]);
190
177
  }
191
178
  });
192
179
  }
193
180
  }
194
- }
195
-
196
- return {
197
- "ArrowFunctionExpression[params.length=1]": parens
198
181
  };
199
182
  }
200
183
  };
@@ -32,6 +32,10 @@ module.exports = {
32
32
  type: "boolean",
33
33
  default: false
34
34
  },
35
+ ignoreGlobals: {
36
+ type: "boolean",
37
+ default: false
38
+ },
35
39
  properties: {
36
40
  enum: ["always", "never"]
37
41
  },
@@ -61,8 +65,11 @@ module.exports = {
61
65
  let properties = options.properties || "";
62
66
  const ignoreDestructuring = options.ignoreDestructuring;
63
67
  const ignoreImports = options.ignoreImports;
68
+ const ignoreGlobals = options.ignoreGlobals;
64
69
  const allow = options.allow || [];
65
70
 
71
+ let globalScope;
72
+
66
73
  if (properties !== "always" && properties !== "never") {
67
74
  properties = "always";
68
75
  }
@@ -159,6 +166,37 @@ module.exports = {
159
166
  return false;
160
167
  }
161
168
 
169
+ /**
170
+ * Checks whether the given node represents a reference to a global variable that is not declared in the source code.
171
+ * These identifiers will be allowed, as it is assumed that user has no control over the names of external global variables.
172
+ * @param {ASTNode} node `Identifier` node to check.
173
+ * @returns {boolean} `true` if the node is a reference to a global variable.
174
+ */
175
+ function isReferenceToGlobalVariable(node) {
176
+ const variable = globalScope.set.get(node.name);
177
+
178
+ return variable && variable.defs.length === 0 &&
179
+ variable.references.some(ref => ref.identifier === node);
180
+ }
181
+
182
+ /**
183
+ * Checks whether the given node represents a reference to a property of an object in an object literal expression.
184
+ * This allows to differentiate between a global variable that is allowed to be used as a reference, and the key
185
+ * of the expressed object (which shouldn't be allowed).
186
+ * @param {ASTNode} node `Identifier` node to check.
187
+ * @returns {boolean} `true` if the node is a property name of an object literal expression
188
+ */
189
+ function isPropertyNameInObjectLiteral(node) {
190
+ const parent = node.parent;
191
+
192
+ return (
193
+ parent.type === "Property" &&
194
+ parent.parent.type === "ObjectExpression" &&
195
+ !parent.computed &&
196
+ parent.key === node
197
+ );
198
+ }
199
+
162
200
  /**
163
201
  * Reports an AST node as a rule violation.
164
202
  * @param {ASTNode} node The node to report.
@@ -174,6 +212,10 @@ module.exports = {
174
212
 
175
213
  return {
176
214
 
215
+ Program() {
216
+ globalScope = context.getScope();
217
+ },
218
+
177
219
  Identifier(node) {
178
220
 
179
221
  /*
@@ -189,6 +231,11 @@ module.exports = {
189
231
  return;
190
232
  }
191
233
 
234
+ // Check if it's a global variable
235
+ if (ignoreGlobals && isReferenceToGlobalVariable(node) && !isPropertyNameInObjectLiteral(node)) {
236
+ return;
237
+ }
238
+
192
239
  // MemberExpressions get special rules
193
240
  if (node.parent.type === "MemberExpression") {
194
241
 
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @fileoverview Rule that warns when identifier names that are
3
- * blacklisted in the configuration are used.
3
+ * specified in the configuration are used.
4
4
  * @author Keith Cirkel (http://keithcirkel.co.uk)
5
5
  */
6
6
 
@@ -117,7 +117,7 @@ module.exports = {
117
117
  description: "disallow specified identifiers",
118
118
  category: "Stylistic Issues",
119
119
  recommended: false,
120
- url: "https://eslint.org/docs/rules/id-blacklist"
120
+ url: "https://eslint.org/docs/rules/id-denylist"
121
121
  },
122
122
 
123
123
  schema: {
@@ -128,25 +128,25 @@ module.exports = {
128
128
  uniqueItems: true
129
129
  },
130
130
  messages: {
131
- blacklisted: "Identifier '{{name}}' is blacklisted."
131
+ restricted: "Identifier '{{name}}' is restricted."
132
132
  }
133
133
  },
134
134
 
135
135
  create(context) {
136
136
 
137
- const blacklist = new Set(context.options);
137
+ const denyList = new Set(context.options);
138
138
  const reportedNodes = new Set();
139
139
 
140
140
  let globalScope;
141
141
 
142
142
  /**
143
- * Checks whether the given name is blacklisted.
143
+ * Checks whether the given name is restricted.
144
144
  * @param {string} name The name to check.
145
- * @returns {boolean} `true` if the name is blacklisted.
145
+ * @returns {boolean} `true` if the name is restricted.
146
146
  * @private
147
147
  */
148
- function isBlacklisted(name) {
149
- return blacklist.has(name);
148
+ function isRestricted(name) {
149
+ return denyList.has(name);
150
150
  }
151
151
 
152
152
  /**
@@ -172,8 +172,8 @@ module.exports = {
172
172
 
173
173
  /*
174
174
  * Member access has special rules for checking property names.
175
- * Read access to a property with a blacklisted name is allowed, because it can be on an object that user has no control over.
176
- * Write access isn't allowed, because it potentially creates a new property with a blacklisted name.
175
+ * Read access to a property with a restricted name is allowed, because it can be on an object that user has no control over.
176
+ * Write access isn't allowed, because it potentially creates a new property with a restricted name.
177
177
  */
178
178
  if (
179
179
  parent.type === "MemberExpression" &&
@@ -205,7 +205,7 @@ module.exports = {
205
205
  if (!reportedNodes.has(node)) {
206
206
  context.report({
207
207
  node,
208
- messageId: "blacklisted",
208
+ messageId: "restricted",
209
209
  data: {
210
210
  name: node.name
211
211
  }
@@ -221,7 +221,7 @@ module.exports = {
221
221
  },
222
222
 
223
223
  Identifier(node) {
224
- if (isBlacklisted(node.name) && shouldCheck(node)) {
224
+ if (isRestricted(node.name) && shouldCheck(node)) {
225
225
  report(node);
226
226
  }
227
227
  }
@@ -56,7 +56,10 @@ module.exports = new LazyLoadingRuleMap(Object.entries({
56
56
  "grouped-accessor-pairs": () => require("./grouped-accessor-pairs"),
57
57
  "guard-for-in": () => require("./guard-for-in"),
58
58
  "handle-callback-err": () => require("./handle-callback-err"),
59
- "id-blacklist": () => require("./id-blacklist"),
59
+
60
+ // Renamed to id-denylist.
61
+ "id-blacklist": () => require("./id-denylist"),
62
+ "id-denylist": () => require("./id-denylist"),
60
63
  "id-length": () => require("./id-length"),
61
64
  "id-match": () => require("./id-match"),
62
65
  "implicit-arrow-linebreak": () => require("./implicit-arrow-linebreak"),
@@ -710,6 +710,20 @@ module.exports = {
710
710
  reportsBuffer.reports = reportsBuffer.reports.filter(r => r.node !== node);
711
711
  }
712
712
 
713
+ /**
714
+ * Checks whether a node is a MemberExpression at NewExpression's callee.
715
+ * @param {ASTNode} node node to check.
716
+ * @returns {boolean} True if the node is a MemberExpression at NewExpression's callee. false otherwise.
717
+ */
718
+ function isMemberExpInNewCallee(node) {
719
+ if (node.type === "MemberExpression") {
720
+ return node.parent.type === "NewExpression" && node.parent.callee === node
721
+ ? true
722
+ : node.parent.object === node && isMemberExpInNewCallee(node.parent);
723
+ }
724
+ return false;
725
+ }
726
+
713
727
  return {
714
728
  ArrayExpression(node) {
715
729
  node.elements
@@ -950,7 +964,11 @@ module.exports = {
950
964
  LogicalExpression: checkBinaryLogical,
951
965
 
952
966
  MemberExpression(node) {
953
- const nodeObjHasExcessParens = hasExcessParens(node.object) &&
967
+ const shouldAllowWrapOnce = isMemberExpInNewCallee(node) &&
968
+ doesMemberExpressionContainCallExpression(node);
969
+ const nodeObjHasExcessParens = shouldAllowWrapOnce
970
+ ? hasDoubleExcessParens(node.object)
971
+ : hasExcessParens(node.object) &&
954
972
  !(
955
973
  isImmediateFunctionPrototypeMethodCall(node.parent) &&
956
974
  node.parent.callee === node &&
@@ -974,8 +992,8 @@ module.exports = {
974
992
  }
975
993
 
976
994
  if (nodeObjHasExcessParens &&
977
- node.object.type === "CallExpression" &&
978
- node.parent.type !== "NewExpression") {
995
+ node.object.type === "CallExpression"
996
+ ) {
979
997
  report(node.object);
980
998
  }
981
999
 
@@ -25,6 +25,15 @@ function isStringLiteral(node) {
25
25
  return node.type === "Literal" && typeof node.value === "string";
26
26
  }
27
27
 
28
+ /**
29
+ * Determines whether the given node is a regex literal.
30
+ * @param {ASTNode} node Node to check.
31
+ * @returns {boolean} True if the node is a regex literal.
32
+ */
33
+ function isRegexLiteral(node) {
34
+ return node.type === "Literal" && Object.prototype.hasOwnProperty.call(node, "regex");
35
+ }
36
+
28
37
  /**
29
38
  * Determines whether the given node is a template literal without expressions.
30
39
  * @param {ASTNode} node Node to check.
@@ -50,14 +59,28 @@ module.exports = {
50
59
  url: "https://eslint.org/docs/rules/prefer-regex-literals"
51
60
  },
52
61
 
53
- schema: [],
62
+ schema: [
63
+ {
64
+ type: "object",
65
+ properties: {
66
+ disallowRedundantWrapping: {
67
+ type: "boolean",
68
+ default: false
69
+ }
70
+ },
71
+ additionalProperties: false
72
+ }
73
+ ],
54
74
 
55
75
  messages: {
56
- unexpectedRegExp: "Use a regular expression literal instead of the 'RegExp' constructor."
76
+ unexpectedRegExp: "Use a regular expression literal instead of the 'RegExp' constructor.",
77
+ unexpectedRedundantRegExp: "Regular expression literal is unnecessarily wrapped within a 'RegExp' constructor.",
78
+ unexpectedRedundantRegExpWithFlags: "Use regular expression literal with flags instead of the 'RegExp' constructor."
57
79
  }
58
80
  },
59
81
 
60
82
  create(context) {
83
+ const [{ disallowRedundantWrapping = false } = {}] = context.options;
61
84
 
62
85
  /**
63
86
  * Determines whether the given identifier node is a reference to a global variable.
@@ -98,6 +121,40 @@ module.exports = {
98
121
  isStringRawTaggedStaticTemplateLiteral(node);
99
122
  }
100
123
 
124
+ /**
125
+ * Determines whether the relevant arguments of the given are all static string literals.
126
+ * @param {ASTNode} node Node to check.
127
+ * @returns {boolean} True if all arguments are static strings.
128
+ */
129
+ function hasOnlyStaticStringArguments(node) {
130
+ const args = node.arguments;
131
+
132
+ if ((args.length === 1 || args.length === 2) && args.every(isStaticString)) {
133
+ return true;
134
+ }
135
+
136
+ return false;
137
+ }
138
+
139
+ /**
140
+ * Determines whether the arguments of the given node indicate that a regex literal is unnecessarily wrapped.
141
+ * @param {ASTNode} node Node to check.
142
+ * @returns {boolean} True if the node already contains a regex literal argument.
143
+ */
144
+ function isUnnecessarilyWrappedRegexLiteral(node) {
145
+ const args = node.arguments;
146
+
147
+ if (args.length === 1 && isRegexLiteral(args[0])) {
148
+ return true;
149
+ }
150
+
151
+ if (args.length === 2 && isRegexLiteral(args[0]) && isStaticString(args[1])) {
152
+ return true;
153
+ }
154
+
155
+ return false;
156
+ }
157
+
101
158
  return {
102
159
  Program() {
103
160
  const scope = context.getScope();
@@ -110,12 +167,13 @@ module.exports = {
110
167
  };
111
168
 
112
169
  for (const { node } of tracker.iterateGlobalReferences(traceMap)) {
113
- const args = node.arguments;
114
-
115
- if (
116
- (args.length === 1 || args.length === 2) &&
117
- args.every(isStaticString)
118
- ) {
170
+ if (disallowRedundantWrapping && isUnnecessarilyWrappedRegexLiteral(node)) {
171
+ if (node.arguments.length === 2) {
172
+ context.report({ node, messageId: "unexpectedRedundantRegExpWithFlags" });
173
+ } else {
174
+ context.report({ node, messageId: "unexpectedRedundantRegExp" });
175
+ }
176
+ } else if (hasOnlyStaticStringArguments(node)) {
119
177
  context.report({ node, messageId: "unexpectedRegExp" });
120
178
  }
121
179
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint",
3
- "version": "7.3.1",
3
+ "version": "7.4.0",
4
4
  "author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>",
5
5
  "description": "An AST-based pattern checker for JavaScript.",
6
6
  "bin": {