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 +20 -0
- package/README.md +1 -1
- package/lib/cli-engine/config-array-factory.js +1 -33
- package/lib/rules/array-callback-return.js +21 -10
- package/lib/rules/arrow-body-style.js +2 -2
- package/lib/rules/arrow-parens.js +91 -108
- package/lib/rules/camelcase.js +47 -0
- package/lib/rules/{id-blacklist.js → id-denylist.js} +12 -12
- package/lib/rules/index.js +4 -1
- package/lib/rules/no-extra-parens.js +21 -3
- package/lib/rules/prefer-regex-literals.js +66 -8
- package/package.json +1 -1
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://
|
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
|
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: "
|
157
|
-
expectedInside: "
|
158
|
-
expectedReturnValue: "{{
|
159
|
-
expectedNoReturnValue: "{{
|
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
|
-
|
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:
|
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
|
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
|
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
|
-
*
|
19
|
-
* @param {ASTNode} node
|
20
|
-
* @returns {
|
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
|
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
|
-
*
|
79
|
-
*
|
80
|
-
* @
|
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
|
83
|
-
const
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
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
|
-
|
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
|
-
|
117
|
-
|
118
|
-
|
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
|
-
|
123
|
-
|
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
|
-
|
127
|
-
|
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
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
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
|
-
|
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: "
|
172
|
-
loc:
|
173
|
-
fix
|
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
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
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: "
|
187
|
-
loc:
|
188
|
-
fix(fixer) {
|
189
|
-
|
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
|
};
|
package/lib/rules/camelcase.js
CHANGED
@@ -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
|
-
*
|
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-
|
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
|
-
|
131
|
+
restricted: "Identifier '{{name}}' is restricted."
|
132
132
|
}
|
133
133
|
},
|
134
134
|
|
135
135
|
create(context) {
|
136
136
|
|
137
|
-
const
|
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
|
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
|
145
|
+
* @returns {boolean} `true` if the name is restricted.
|
146
146
|
* @private
|
147
147
|
*/
|
148
|
-
function
|
149
|
-
return
|
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
|
176
|
-
* Write access isn't allowed, because it potentially creates a new property with a
|
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: "
|
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 (
|
224
|
+
if (isRestricted(node.name) && shouldCheck(node)) {
|
225
225
|
report(node);
|
226
226
|
}
|
227
227
|
}
|
package/lib/rules/index.js
CHANGED
@@ -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
|
-
|
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
|
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
|
-
|
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
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
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
|
}
|