eslint 7.3.0 → 7.6.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 +63 -0
- package/README.md +5 -3
- package/lib/cli-engine/config-array-factory.js +1 -28
- package/lib/cli-engine/formatters/checkstyle.js +2 -2
- package/lib/linter/code-path-analysis/code-path-analyzer.js +38 -0
- package/lib/linter/code-path-analysis/code-path-segment.js +0 -1
- package/lib/linter/code-path-analysis/code-path-state.js +59 -0
- package/lib/linter/code-path-analysis/debug-helpers.js +26 -19
- package/lib/rule-tester/rule-tester.js +10 -0
- package/lib/rules/accessor-pairs.js +1 -14
- package/lib/rules/array-callback-return.js +26 -17
- package/lib/rules/arrow-body-style.js +43 -8
- package/lib/rules/arrow-parens.js +91 -108
- package/lib/rules/camelcase.js +47 -0
- package/lib/rules/consistent-return.js +1 -12
- package/lib/rules/constructor-super.js +1 -0
- package/lib/rules/dot-location.js +20 -14
- package/lib/rules/dot-notation.js +36 -33
- package/lib/rules/func-call-spacing.js +42 -6
- package/lib/rules/func-name-matching.js +1 -4
- package/lib/rules/global-require.js +2 -1
- package/lib/rules/id-blacklist.js +14 -11
- package/lib/rules/id-denylist.js +230 -0
- package/lib/rules/indent.js +19 -0
- package/lib/rules/index.js +1 -0
- package/lib/rules/keyword-spacing.js +2 -2
- package/lib/rules/max-len.js +13 -2
- package/lib/rules/new-cap.js +10 -14
- package/lib/rules/newline-per-chained-call.js +15 -5
- package/lib/rules/no-alert.js +10 -3
- package/lib/rules/no-duplicate-case.js +23 -4
- package/lib/rules/no-eval.js +8 -38
- package/lib/rules/no-extend-native.js +37 -40
- package/lib/rules/no-extra-bind.js +57 -17
- package/lib/rules/no-extra-boolean-cast.js +7 -0
- package/lib/rules/no-extra-parens.js +48 -10
- package/lib/rules/no-implicit-coercion.js +11 -6
- package/lib/rules/no-implied-eval.js +7 -28
- package/lib/rules/no-import-assign.js +33 -32
- package/lib/rules/no-irregular-whitespace.js +22 -12
- package/lib/rules/no-magic-numbers.js +4 -8
- package/lib/rules/no-obj-calls.js +7 -4
- package/lib/rules/no-prototype-builtins.js +13 -3
- package/lib/rules/no-self-assign.js +3 -53
- package/lib/rules/no-setter-return.js +5 -8
- package/lib/rules/no-unexpected-multiline.js +2 -2
- package/lib/rules/no-unneeded-ternary.js +0 -2
- package/lib/rules/no-unused-expressions.js +55 -23
- package/lib/rules/no-useless-call.js +10 -7
- package/lib/rules/no-whitespace-before-property.js +16 -4
- package/lib/rules/object-curly-newline.js +4 -4
- package/lib/rules/operator-assignment.js +3 -42
- package/lib/rules/padding-line-between-statements.js +2 -2
- package/lib/rules/prefer-arrow-callback.js +90 -25
- package/lib/rules/prefer-exponentiation-operator.js +1 -1
- package/lib/rules/prefer-numeric-literals.js +4 -13
- package/lib/rules/prefer-promise-reject-errors.js +1 -3
- package/lib/rules/prefer-regex-literals.js +68 -13
- package/lib/rules/prefer-spread.js +2 -6
- package/lib/rules/radix.js +5 -2
- package/lib/rules/sort-imports.js +28 -0
- package/lib/rules/use-isnan.js +1 -1
- package/lib/rules/utils/ast-utils.js +317 -153
- package/lib/rules/wrap-iife.js +9 -2
- package/lib/rules/yoda.js +2 -55
- package/messages/extend-config-missing.txt +1 -1
- package/messages/no-config-found.txt +1 -1
- package/messages/plugin-conflict.txt +1 -1
- package/messages/plugin-missing.txt +1 -1
- package/messages/whitespace-found.txt +1 -1
- package/package.json +6 -6
@@ -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
|
|
@@ -9,23 +9,12 @@
|
|
9
9
|
//------------------------------------------------------------------------------
|
10
10
|
|
11
11
|
const lodash = require("lodash");
|
12
|
-
|
13
12
|
const astUtils = require("./utils/ast-utils");
|
14
13
|
|
15
14
|
//------------------------------------------------------------------------------
|
16
15
|
// Helpers
|
17
16
|
//------------------------------------------------------------------------------
|
18
17
|
|
19
|
-
/**
|
20
|
-
* Checks whether or not a given node is an `Identifier` node which was named a given name.
|
21
|
-
* @param {ASTNode} node A node to check.
|
22
|
-
* @param {string} name An expected name of the node.
|
23
|
-
* @returns {boolean} `true` if the node is an `Identifier` node which was named as expected.
|
24
|
-
*/
|
25
|
-
function isIdentifier(node, name) {
|
26
|
-
return node.type === "Identifier" && node.name === name;
|
27
|
-
}
|
28
|
-
|
29
18
|
/**
|
30
19
|
* Checks whether or not a given code path segment is unreachable.
|
31
20
|
* @param {CodePathSegment} segment A CodePathSegment to check.
|
@@ -165,7 +154,7 @@ module.exports = {
|
|
165
154
|
let hasReturnValue = Boolean(argument);
|
166
155
|
|
167
156
|
if (treatUndefinedAsUnspecified && hasReturnValue) {
|
168
|
-
hasReturnValue = !
|
157
|
+
hasReturnValue = !astUtils.isSpecificId(argument, "undefined") && argument.operator !== "void";
|
169
158
|
}
|
170
159
|
|
171
160
|
if (!funcInfo.hasReturn) {
|
@@ -52,31 +52,37 @@ module.exports = {
|
|
52
52
|
*/
|
53
53
|
function checkDotLocation(node) {
|
54
54
|
const property = node.property;
|
55
|
-
const
|
56
|
-
|
57
|
-
// `obj` expression can be parenthesized, but those paren tokens are not a part of the `obj` node.
|
58
|
-
const tokenBeforeDot = sourceCode.getTokenBefore(dot);
|
59
|
-
|
60
|
-
const textBeforeDot = sourceCode.getText().slice(tokenBeforeDot.range[1], dot.range[0]);
|
61
|
-
const textAfterDot = sourceCode.getText().slice(dot.range[1], property.range[0]);
|
55
|
+
const dotToken = sourceCode.getTokenBefore(property);
|
62
56
|
|
63
57
|
if (onObject) {
|
64
|
-
if (!astUtils.isTokenOnSameLine(tokenBeforeDot, dot)) {
|
65
|
-
const neededTextAfterToken = astUtils.isDecimalIntegerNumericToken(tokenBeforeDot) ? " " : "";
|
66
58
|
|
59
|
+
// `obj` expression can be parenthesized, but those paren tokens are not a part of the `obj` node.
|
60
|
+
const tokenBeforeDot = sourceCode.getTokenBefore(dotToken);
|
61
|
+
|
62
|
+
if (!astUtils.isTokenOnSameLine(tokenBeforeDot, dotToken)) {
|
67
63
|
context.report({
|
68
64
|
node,
|
69
|
-
loc:
|
65
|
+
loc: dotToken.loc,
|
70
66
|
messageId: "expectedDotAfterObject",
|
71
|
-
fix
|
67
|
+
*fix(fixer) {
|
68
|
+
if (dotToken.value.startsWith(".") && astUtils.isDecimalIntegerNumericToken(tokenBeforeDot)) {
|
69
|
+
yield fixer.insertTextAfter(tokenBeforeDot, ` ${dotToken.value}`);
|
70
|
+
} else {
|
71
|
+
yield fixer.insertTextAfter(tokenBeforeDot, dotToken.value);
|
72
|
+
}
|
73
|
+
yield fixer.remove(dotToken);
|
74
|
+
}
|
72
75
|
});
|
73
76
|
}
|
74
|
-
} else if (!astUtils.isTokenOnSameLine(
|
77
|
+
} else if (!astUtils.isTokenOnSameLine(dotToken, property)) {
|
75
78
|
context.report({
|
76
79
|
node,
|
77
|
-
loc:
|
80
|
+
loc: dotToken.loc,
|
78
81
|
messageId: "expectedDotBeforeProperty",
|
79
|
-
fix
|
82
|
+
*fix(fixer) {
|
83
|
+
yield fixer.remove(dotToken);
|
84
|
+
yield fixer.insertTextBefore(property, dotToken.value);
|
85
|
+
}
|
80
86
|
});
|
81
87
|
}
|
82
88
|
}
|
@@ -87,28 +87,36 @@ module.exports = {
|
|
87
87
|
data: {
|
88
88
|
key: formattedValue
|
89
89
|
},
|
90
|
-
fix(fixer) {
|
90
|
+
*fix(fixer) {
|
91
91
|
const leftBracket = sourceCode.getTokenAfter(node.object, astUtils.isOpeningBracketToken);
|
92
92
|
const rightBracket = sourceCode.getLastToken(node);
|
93
|
+
const nextToken = sourceCode.getTokenAfter(node);
|
93
94
|
|
94
|
-
|
95
|
-
|
96
|
-
//
|
97
|
-
return null;
|
95
|
+
// Don't perform any fixes if there are comments inside the brackets.
|
96
|
+
if (sourceCode.commentsExistBetween(leftBracket, rightBracket)) {
|
97
|
+
return; // eslint-disable-line eslint-plugin/fixer-return -- false positive
|
98
98
|
}
|
99
99
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
return fixer.replaceTextRange(
|
100
|
+
// Replace the brackets by an identifier.
|
101
|
+
if (!node.optional) {
|
102
|
+
yield fixer.insertTextBefore(
|
103
|
+
leftBracket,
|
104
|
+
astUtils.isDecimalInteger(node.object) ? " ." : "."
|
105
|
+
);
|
106
|
+
}
|
107
|
+
yield fixer.replaceTextRange(
|
109
108
|
[leftBracket.range[0], rightBracket.range[1]],
|
110
|
-
|
109
|
+
value
|
111
110
|
);
|
111
|
+
|
112
|
+
// Insert a space after the property if it will be connected to the next token.
|
113
|
+
if (
|
114
|
+
nextToken &&
|
115
|
+
rightBracket.range[1] === nextToken.range[0] &&
|
116
|
+
!astUtils.canTokensBeAdjacent(String(value), nextToken)
|
117
|
+
) {
|
118
|
+
yield fixer.insertTextAfter(node, " ");
|
119
|
+
}
|
112
120
|
}
|
113
121
|
});
|
114
122
|
}
|
@@ -141,29 +149,24 @@ module.exports = {
|
|
141
149
|
data: {
|
142
150
|
key: node.property.name
|
143
151
|
},
|
144
|
-
fix(fixer) {
|
145
|
-
const
|
146
|
-
const textAfterDot = sourceCode.text.slice(dot.range[1], node.property.range[0]);
|
147
|
-
|
148
|
-
if (textAfterDot.trim()) {
|
152
|
+
*fix(fixer) {
|
153
|
+
const dotToken = sourceCode.getTokenBefore(node.property);
|
149
154
|
|
150
|
-
|
151
|
-
|
155
|
+
// A statement that starts with `let[` is parsed as a destructuring variable declaration, not a MemberExpression.
|
156
|
+
if (node.object.type === "Identifier" && node.object.name === "let" && !node.optional) {
|
157
|
+
return; // eslint-disable-line eslint-plugin/fixer-return -- false positive
|
152
158
|
}
|
153
159
|
|
154
|
-
if
|
155
|
-
|
156
|
-
|
157
|
-
* A statement that starts with `let[` is parsed as a destructuring variable declaration, not
|
158
|
-
* a MemberExpression.
|
159
|
-
*/
|
160
|
-
return null;
|
160
|
+
// Don't perform any fixes if there are comments between the dot and the property name.
|
161
|
+
if (sourceCode.commentsExistBetween(dotToken, node.property)) {
|
162
|
+
return; // eslint-disable-line eslint-plugin/fixer-return -- false positive
|
161
163
|
}
|
162
164
|
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
165
|
+
// Replace the identifier to brackets.
|
166
|
+
if (!node.optional) {
|
167
|
+
yield fixer.remove(dotToken);
|
168
|
+
}
|
169
|
+
yield fixer.replaceText(node.property, `["${node.property.name}"]`);
|
167
170
|
}
|
168
171
|
});
|
169
172
|
}
|
@@ -126,15 +126,24 @@ module.exports = {
|
|
126
126
|
messageId: "unexpectedWhitespace",
|
127
127
|
fix(fixer) {
|
128
128
|
|
129
|
+
// Don't remove comments.
|
130
|
+
if (sourceCode.commentsExistBetween(leftToken, rightToken)) {
|
131
|
+
return null;
|
132
|
+
}
|
133
|
+
|
134
|
+
// If `?.` exsits, it doesn't hide no-undexpected-multiline errors
|
135
|
+
if (node.optional) {
|
136
|
+
return fixer.replaceTextRange([leftToken.range[1], rightToken.range[0]], "?.");
|
137
|
+
}
|
138
|
+
|
129
139
|
/*
|
130
140
|
* Only autofix if there is no newline
|
131
141
|
* https://github.com/eslint/eslint/issues/7787
|
132
142
|
*/
|
133
|
-
if (
|
134
|
-
return
|
143
|
+
if (hasNewline) {
|
144
|
+
return null;
|
135
145
|
}
|
136
|
-
|
137
|
-
return null;
|
146
|
+
return fixer.removeRange([leftToken.range[1], rightToken.range[0]]);
|
138
147
|
}
|
139
148
|
});
|
140
149
|
} else if (!never && !hasWhitespace) {
|
@@ -149,6 +158,9 @@ module.exports = {
|
|
149
158
|
},
|
150
159
|
messageId: "missing",
|
151
160
|
fix(fixer) {
|
161
|
+
if (node.optional) {
|
162
|
+
return null; // Not sure if inserting a space to either before/after `?.` token.
|
163
|
+
}
|
152
164
|
return fixer.insertTextBefore(rightToken, " ");
|
153
165
|
}
|
154
166
|
});
|
@@ -161,7 +173,31 @@ module.exports = {
|
|
161
173
|
},
|
162
174
|
messageId: "unexpectedNewline",
|
163
175
|
fix(fixer) {
|
164
|
-
|
176
|
+
|
177
|
+
/*
|
178
|
+
* Only autofix if there is no newline
|
179
|
+
* https://github.com/eslint/eslint/issues/7787
|
180
|
+
* But if `?.` exsits, it doesn't hide no-undexpected-multiline errors
|
181
|
+
*/
|
182
|
+
if (!node.optional) {
|
183
|
+
return null;
|
184
|
+
}
|
185
|
+
|
186
|
+
// Don't remove comments.
|
187
|
+
if (sourceCode.commentsExistBetween(leftToken, rightToken)) {
|
188
|
+
return null;
|
189
|
+
}
|
190
|
+
|
191
|
+
const range = [leftToken.range[1], rightToken.range[0]];
|
192
|
+
const qdToken = sourceCode.getTokenAfter(leftToken);
|
193
|
+
|
194
|
+
if (qdToken.range[0] === leftToken.range[1]) {
|
195
|
+
return fixer.replaceTextRange(range, "?. ");
|
196
|
+
}
|
197
|
+
if (qdToken.range[1] === rightToken.range[0]) {
|
198
|
+
return fixer.replaceTextRange(range, " ?.");
|
199
|
+
}
|
200
|
+
return fixer.replaceTextRange(range, " ?. ");
|
165
201
|
}
|
166
202
|
});
|
167
203
|
}
|
@@ -172,7 +208,7 @@ module.exports = {
|
|
172
208
|
const lastToken = sourceCode.getLastToken(node);
|
173
209
|
const lastCalleeToken = sourceCode.getLastToken(node.callee);
|
174
210
|
const parenToken = sourceCode.getFirstTokenBetween(lastCalleeToken, lastToken, astUtils.isOpeningParenToken);
|
175
|
-
const prevToken = parenToken && sourceCode.getTokenBefore(parenToken);
|
211
|
+
const prevToken = parenToken && sourceCode.getTokenBefore(parenToken, astUtils.isNotQuestionDotToken);
|
176
212
|
|
177
213
|
// Parens in NewExpression are optional
|
178
214
|
if (!(parenToken && parenToken.range[1] < node.range[1])) {
|
@@ -117,10 +117,7 @@ module.exports = {
|
|
117
117
|
if (!node) {
|
118
118
|
return false;
|
119
119
|
}
|
120
|
-
return node.type === "CallExpression" &&
|
121
|
-
node.callee.type === "MemberExpression" &&
|
122
|
-
node.callee.object.name === objName &&
|
123
|
-
node.callee.property.name === funcName;
|
120
|
+
return node.type === "CallExpression" && astUtils.isSpecificMemberAccess(node.callee, objName, funcName);
|
124
121
|
}
|
125
122
|
|
126
123
|
/**
|