eslint 7.0.0-alpha.2 → 7.1.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 +332 -0
- package/README.md +9 -10
- package/bin/eslint.js +115 -77
- package/conf/category-list.json +0 -1
- package/conf/environments.js +2 -1
- package/lib/api.js +2 -0
- package/lib/cli-engine/cascading-config-array-factory.js +16 -2
- package/lib/cli-engine/cli-engine.js +53 -47
- package/lib/cli-engine/config-array/config-array.js +30 -1
- package/lib/cli-engine/config-array/ignore-pattern.js +7 -1
- package/lib/cli-engine/config-array-factory.js +244 -235
- package/lib/cli.js +181 -95
- package/lib/eslint/eslint.js +656 -0
- package/lib/eslint/index.js +7 -0
- package/lib/init/autoconfig.js +4 -4
- package/lib/init/config-file.js +2 -2
- package/lib/init/config-initializer.js +3 -4
- package/lib/init/source-code-utils.js +2 -2
- package/lib/linter/linter.js +2 -1
- package/lib/linter/node-event-generator.js +2 -2
- package/lib/options.js +0 -1
- package/lib/rule-tester/rule-tester.js +132 -22
- package/lib/rules/accessor-pairs.js +1 -1
- package/lib/rules/array-callback-return.js +3 -18
- package/lib/rules/arrow-parens.js +19 -3
- package/lib/rules/block-spacing.js +19 -2
- package/lib/rules/callback-return.js +4 -0
- package/lib/rules/camelcase.js +38 -1
- package/lib/rules/comma-style.js +3 -8
- package/lib/rules/func-call-spacing.js +4 -3
- package/lib/rules/getter-return.js +2 -12
- package/lib/rules/global-require.js +4 -0
- package/lib/rules/handle-callback-err.js +4 -0
- package/lib/rules/id-blacklist.js +138 -102
- package/lib/rules/index.js +1 -0
- package/lib/rules/key-spacing.js +1 -1
- package/lib/rules/linebreak-style.js +8 -2
- package/lib/rules/max-lines-per-function.js +1 -1
- package/lib/rules/new-cap.js +1 -1
- package/lib/rules/newline-per-chained-call.js +6 -3
- package/lib/rules/no-alert.js +5 -3
- package/lib/rules/no-buffer-constructor.js +4 -0
- package/lib/rules/no-empty-function.js +4 -2
- package/lib/rules/no-eval.js +2 -1
- package/lib/rules/no-extra-bind.js +1 -1
- package/lib/rules/no-extra-boolean-cast.js +102 -23
- package/lib/rules/no-extra-parens.js +9 -5
- package/lib/rules/no-implied-eval.js +83 -101
- package/lib/rules/no-inner-declarations.js +31 -39
- package/lib/rules/no-lone-blocks.js +1 -1
- package/lib/rules/no-loss-of-precision.js +198 -0
- package/lib/rules/no-magic-numbers.js +72 -37
- package/lib/rules/no-mixed-requires.js +4 -0
- package/lib/rules/no-new-func.js +22 -19
- package/lib/rules/no-new-object.js +15 -3
- package/lib/rules/no-new-require.js +4 -0
- package/lib/rules/no-new-symbol.js +2 -1
- package/lib/rules/no-new-wrappers.js +1 -1
- package/lib/rules/no-obj-calls.js +24 -5
- package/lib/rules/no-path-concat.js +4 -0
- package/lib/rules/no-plusplus.js +39 -3
- package/lib/rules/no-process-env.js +4 -0
- package/lib/rules/no-process-exit.js +4 -0
- package/lib/rules/no-prototype-builtins.js +1 -1
- package/lib/rules/no-restricted-modules.js +4 -0
- package/lib/rules/no-sync.js +4 -0
- package/lib/rules/no-unexpected-multiline.js +22 -12
- package/lib/rules/no-useless-concat.js +1 -1
- package/lib/rules/one-var-declaration-per-line.js +1 -1
- package/lib/rules/operator-assignment.js +3 -3
- package/lib/rules/operator-linebreak.js +4 -16
- package/lib/rules/padded-blocks.js +17 -4
- package/lib/rules/prefer-numeric-literals.js +3 -3
- package/lib/rules/prefer-object-spread.js +2 -2
- package/lib/rules/require-await.js +1 -1
- package/lib/rules/rest-spread-spacing.js +3 -6
- package/lib/rules/semi-spacing.js +32 -8
- package/lib/rules/space-before-function-paren.js +5 -2
- package/lib/rules/template-curly-spacing.js +59 -42
- package/lib/rules/utils/ast-utils.js +116 -10
- package/lib/rules/yoda.js +101 -51
- package/lib/shared/relative-module-resolver.js +1 -0
- package/lib/shared/types.js +9 -2
- package/lib/source-code/source-code.js +1 -0
- package/messages/extend-config-missing.txt +1 -1
- package/messages/no-config-found.txt +1 -1
- package/messages/plugin-conflict.txt +7 -0
- package/messages/plugin-missing.txt +1 -1
- package/messages/whitespace-found.txt +1 -1
- package/package.json +27 -26
@@ -6,6 +6,105 @@
|
|
6
6
|
|
7
7
|
"use strict";
|
8
8
|
|
9
|
+
//------------------------------------------------------------------------------
|
10
|
+
// Helpers
|
11
|
+
//------------------------------------------------------------------------------
|
12
|
+
|
13
|
+
/**
|
14
|
+
* Checks whether the given node represents assignment target in a normal assignment or destructuring.
|
15
|
+
* @param {ASTNode} node The node to check.
|
16
|
+
* @returns {boolean} `true` if the node is assignment target.
|
17
|
+
*/
|
18
|
+
function isAssignmentTarget(node) {
|
19
|
+
const parent = node.parent;
|
20
|
+
|
21
|
+
return (
|
22
|
+
|
23
|
+
// normal assignment
|
24
|
+
(
|
25
|
+
parent.type === "AssignmentExpression" &&
|
26
|
+
parent.left === node
|
27
|
+
) ||
|
28
|
+
|
29
|
+
// destructuring
|
30
|
+
parent.type === "ArrayPattern" ||
|
31
|
+
parent.type === "RestElement" ||
|
32
|
+
(
|
33
|
+
parent.type === "Property" &&
|
34
|
+
parent.value === node &&
|
35
|
+
parent.parent.type === "ObjectPattern"
|
36
|
+
) ||
|
37
|
+
(
|
38
|
+
parent.type === "AssignmentPattern" &&
|
39
|
+
parent.left === node
|
40
|
+
)
|
41
|
+
);
|
42
|
+
}
|
43
|
+
|
44
|
+
/**
|
45
|
+
* Checks whether the given node represents an imported name that is renamed in the same import/export specifier.
|
46
|
+
*
|
47
|
+
* Examples:
|
48
|
+
* import { a as b } from 'mod'; // node `a` is renamed import
|
49
|
+
* export { a as b } from 'mod'; // node `a` is renamed import
|
50
|
+
* @param {ASTNode} node `Identifier` node to check.
|
51
|
+
* @returns {boolean} `true` if the node is a renamed import.
|
52
|
+
*/
|
53
|
+
function isRenamedImport(node) {
|
54
|
+
const parent = node.parent;
|
55
|
+
|
56
|
+
return (
|
57
|
+
(
|
58
|
+
parent.type === "ImportSpecifier" &&
|
59
|
+
parent.imported !== parent.local &&
|
60
|
+
parent.imported === node
|
61
|
+
) ||
|
62
|
+
(
|
63
|
+
parent.type === "ExportSpecifier" &&
|
64
|
+
parent.parent.source && // re-export
|
65
|
+
parent.local !== parent.exported &&
|
66
|
+
parent.local === node
|
67
|
+
)
|
68
|
+
);
|
69
|
+
}
|
70
|
+
|
71
|
+
/**
|
72
|
+
* Checks whether the given node is a renamed identifier node in an ObjectPattern destructuring.
|
73
|
+
*
|
74
|
+
* Examples:
|
75
|
+
* const { a : b } = foo; // node `a` is renamed node.
|
76
|
+
* @param {ASTNode} node `Identifier` node to check.
|
77
|
+
* @returns {boolean} `true` if the node is a renamed node in an ObjectPattern destructuring.
|
78
|
+
*/
|
79
|
+
function isRenamedInDestructuring(node) {
|
80
|
+
const parent = node.parent;
|
81
|
+
|
82
|
+
return (
|
83
|
+
(
|
84
|
+
!parent.computed &&
|
85
|
+
parent.type === "Property" &&
|
86
|
+
parent.parent.type === "ObjectPattern" &&
|
87
|
+
parent.value !== node &&
|
88
|
+
parent.key === node
|
89
|
+
)
|
90
|
+
);
|
91
|
+
}
|
92
|
+
|
93
|
+
/**
|
94
|
+
* Checks whether the given node represents shorthand definition of a property in an object literal.
|
95
|
+
* @param {ASTNode} node `Identifier` node to check.
|
96
|
+
* @returns {boolean} `true` if the node is a shorthand property definition.
|
97
|
+
*/
|
98
|
+
function isShorthandPropertyDefinition(node) {
|
99
|
+
const parent = node.parent;
|
100
|
+
|
101
|
+
return (
|
102
|
+
parent.type === "Property" &&
|
103
|
+
parent.parent.type === "ObjectExpression" &&
|
104
|
+
parent.shorthand
|
105
|
+
);
|
106
|
+
}
|
107
|
+
|
9
108
|
//------------------------------------------------------------------------------
|
10
109
|
// Rule Definition
|
11
110
|
//------------------------------------------------------------------------------
|
@@ -35,88 +134,64 @@ module.exports = {
|
|
35
134
|
|
36
135
|
create(context) {
|
37
136
|
|
38
|
-
|
39
|
-
//--------------------------------------------------------------------------
|
40
|
-
// Helpers
|
41
|
-
//--------------------------------------------------------------------------
|
42
|
-
|
43
|
-
const blacklist = context.options;
|
137
|
+
const blacklist = new Set(context.options);
|
44
138
|
const reportedNodes = new Set();
|
45
139
|
|
140
|
+
let globalScope;
|
46
141
|
|
47
142
|
/**
|
48
|
-
* Checks
|
49
|
-
* @param {string} name The
|
50
|
-
* @returns {boolean} if the
|
143
|
+
* Checks whether the given name is blacklisted.
|
144
|
+
* @param {string} name The name to check.
|
145
|
+
* @returns {boolean} `true` if the name is blacklisted.
|
51
146
|
* @private
|
52
147
|
*/
|
53
|
-
function
|
54
|
-
return blacklist.
|
148
|
+
function isBlacklisted(name) {
|
149
|
+
return blacklist.has(name);
|
55
150
|
}
|
56
151
|
|
57
152
|
/**
|
58
|
-
* Checks whether the given node represents
|
59
|
-
*
|
60
|
-
* Examples:
|
61
|
-
* import { a as b } from 'mod'; // node `a` is renamed import
|
62
|
-
* export { a as b } from 'mod'; // node `a` is renamed import
|
153
|
+
* Checks whether the given node represents a reference to a global variable that is not declared in the source code.
|
154
|
+
* These identifiers will be allowed, as it is assumed that user has no control over the names of external global variables.
|
63
155
|
* @param {ASTNode} node `Identifier` node to check.
|
64
|
-
* @returns {boolean} `true` if the node is a
|
156
|
+
* @returns {boolean} `true` if the node is a reference to a global variable.
|
65
157
|
*/
|
66
|
-
function
|
67
|
-
const
|
158
|
+
function isReferenceToGlobalVariable(node) {
|
159
|
+
const variable = globalScope.set.get(node.name);
|
68
160
|
|
69
|
-
return
|
70
|
-
(
|
71
|
-
parent.type === "ImportSpecifier" &&
|
72
|
-
parent.imported !== parent.local &&
|
73
|
-
parent.imported === node
|
74
|
-
) ||
|
75
|
-
(
|
76
|
-
parent.type === "ExportSpecifier" &&
|
77
|
-
parent.parent.source && // re-export
|
78
|
-
parent.local !== parent.exported &&
|
79
|
-
parent.local === node
|
80
|
-
)
|
81
|
-
);
|
161
|
+
return variable && variable.defs.length === 0 &&
|
162
|
+
variable.references.some(ref => ref.identifier === node);
|
82
163
|
}
|
83
164
|
|
84
165
|
/**
|
85
|
-
*
|
86
|
-
*
|
87
|
-
*
|
88
|
-
* const { a : b } = foo; // node `a` is renamed node.
|
89
|
-
* @param {ASTNode} node `Identifier` node to check.
|
90
|
-
* @returns {boolean} `true` if the node is a renamed node in an ObjectPattern destructuring.
|
166
|
+
* Determines whether the given node should be checked.
|
167
|
+
* @param {ASTNode} node `Identifier` node.
|
168
|
+
* @returns {boolean} `true` if the node should be checked.
|
91
169
|
*/
|
92
|
-
function
|
170
|
+
function shouldCheck(node) {
|
93
171
|
const parent = node.parent;
|
94
172
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
* Verifies if we should report an error or not.
|
108
|
-
* @param {ASTNode} node The node to check
|
109
|
-
* @returns {boolean} whether an error should be reported or not
|
110
|
-
*/
|
111
|
-
function shouldReport(node) {
|
112
|
-
const parent = node.parent;
|
173
|
+
/*
|
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.
|
177
|
+
*/
|
178
|
+
if (
|
179
|
+
parent.type === "MemberExpression" &&
|
180
|
+
parent.property === node &&
|
181
|
+
!parent.computed
|
182
|
+
) {
|
183
|
+
return isAssignmentTarget(parent);
|
184
|
+
}
|
113
185
|
|
114
186
|
return (
|
115
187
|
parent.type !== "CallExpression" &&
|
116
188
|
parent.type !== "NewExpression" &&
|
117
189
|
!isRenamedImport(node) &&
|
118
190
|
!isRenamedInDestructuring(node) &&
|
119
|
-
|
191
|
+
!(
|
192
|
+
isReferenceToGlobalVariable(node) &&
|
193
|
+
!isShorthandPropertyDefinition(node)
|
194
|
+
)
|
120
195
|
);
|
121
196
|
}
|
122
197
|
|
@@ -141,54 +216,15 @@ module.exports = {
|
|
141
216
|
|
142
217
|
return {
|
143
218
|
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
if (node.parent.type === "MemberExpression") {
|
148
|
-
const name = node.name,
|
149
|
-
effectiveParent = node.parent.parent;
|
150
|
-
|
151
|
-
// Always check object names
|
152
|
-
if (node.parent.object.type === "Identifier" &&
|
153
|
-
node.parent.object.name === name) {
|
154
|
-
if (isInvalid(name)) {
|
155
|
-
report(node);
|
156
|
-
}
|
157
|
-
|
158
|
-
// Report AssignmentExpressions only if they are the left side of the assignment
|
159
|
-
} else if (effectiveParent.type === "AssignmentExpression" &&
|
160
|
-
(effectiveParent.right.type !== "MemberExpression" ||
|
161
|
-
effectiveParent.left.type === "MemberExpression" &&
|
162
|
-
effectiveParent.left.property.name === name)) {
|
163
|
-
if (isInvalid(name)) {
|
164
|
-
report(node);
|
165
|
-
}
|
166
|
-
|
167
|
-
// Report the last identifier in an ObjectPattern destructuring.
|
168
|
-
} else if (
|
169
|
-
(
|
170
|
-
effectiveParent.type === "Property" &&
|
171
|
-
effectiveParent.value === node.parent &&
|
172
|
-
effectiveParent.parent.type === "ObjectPattern"
|
173
|
-
) ||
|
174
|
-
effectiveParent.type === "RestElement" ||
|
175
|
-
effectiveParent.type === "ArrayPattern" ||
|
176
|
-
(
|
177
|
-
effectiveParent.type === "AssignmentPattern" &&
|
178
|
-
effectiveParent.left === node.parent
|
179
|
-
)
|
180
|
-
) {
|
181
|
-
if (isInvalid(name)) {
|
182
|
-
report(node);
|
183
|
-
}
|
184
|
-
}
|
219
|
+
Program() {
|
220
|
+
globalScope = context.getScope();
|
221
|
+
},
|
185
222
|
|
186
|
-
|
223
|
+
Identifier(node) {
|
224
|
+
if (isBlacklisted(node.name) && shouldCheck(node)) {
|
187
225
|
report(node);
|
188
226
|
}
|
189
227
|
}
|
190
|
-
|
191
228
|
};
|
192
|
-
|
193
229
|
}
|
194
230
|
};
|
package/lib/rules/index.js
CHANGED
@@ -148,6 +148,7 @@ module.exports = new LazyLoadingRuleMap(Object.entries({
|
|
148
148
|
"no-lone-blocks": () => require("./no-lone-blocks"),
|
149
149
|
"no-lonely-if": () => require("./no-lonely-if"),
|
150
150
|
"no-loop-func": () => require("./no-loop-func"),
|
151
|
+
"no-loss-of-precision": () => require("./no-loss-of-precision"),
|
151
152
|
"no-magic-numbers": () => require("./no-magic-numbers"),
|
152
153
|
"no-misleading-character-class": () => require("./no-misleading-character-class"),
|
153
154
|
"no-mixed-operators": () => require("./no-mixed-operators"),
|
package/lib/rules/key-spacing.js
CHANGED
@@ -45,7 +45,7 @@ function isSingleLine(node) {
|
|
45
45
|
/**
|
46
46
|
* Checks whether the properties on a single line.
|
47
47
|
* @param {ASTNode[]} properties List of Property AST nodes.
|
48
|
-
* @returns {boolean} True if all
|
48
|
+
* @returns {boolean} True if all properties is on a single line.
|
49
49
|
*/
|
50
50
|
function isSingleLineProperties(properties) {
|
51
51
|
const [firstProp] = properties,
|
@@ -86,8 +86,14 @@ module.exports = {
|
|
86
86
|
context.report({
|
87
87
|
node,
|
88
88
|
loc: {
|
89
|
-
|
90
|
-
|
89
|
+
start: {
|
90
|
+
line: i,
|
91
|
+
column: sourceCode.lines[i - 1].length
|
92
|
+
},
|
93
|
+
end: {
|
94
|
+
line: i + 1,
|
95
|
+
column: 0
|
96
|
+
}
|
91
97
|
},
|
92
98
|
messageId: expectedLF ? "expectedLF" : "expectedCRLF",
|
93
99
|
fix: createFix(range, expectedLFChars)
|
@@ -134,7 +134,7 @@ module.exports = {
|
|
134
134
|
* @returns {boolean} True if it's an IIFE
|
135
135
|
*/
|
136
136
|
function isIIFE(node) {
|
137
|
-
return node.type === "FunctionExpression" && node.parent && node.parent.type === "CallExpression" && node.parent.callee === node;
|
137
|
+
return (node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression") && node.parent && node.parent.type === "CallExpression" && node.parent.callee === node;
|
138
138
|
}
|
139
139
|
|
140
140
|
/**
|
package/lib/rules/new-cap.js
CHANGED
@@ -235,7 +235,7 @@ module.exports = {
|
|
235
235
|
callee = callee.property;
|
236
236
|
}
|
237
237
|
|
238
|
-
context.report({ node, loc: callee.loc
|
238
|
+
context.report({ node, loc: callee.loc, messageId });
|
239
239
|
}
|
240
240
|
|
241
241
|
//--------------------------------------------------------------------------
|
@@ -90,16 +90,19 @@ module.exports = {
|
|
90
90
|
}
|
91
91
|
|
92
92
|
if (depth > ignoreChainWithDepth && astUtils.isTokenOnSameLine(callee.object, callee.property)) {
|
93
|
+
const firstTokenAfterObject = sourceCode.getTokenAfter(callee.object, astUtils.isNotClosingParenToken);
|
94
|
+
|
93
95
|
context.report({
|
94
96
|
node: callee.property,
|
95
|
-
loc:
|
97
|
+
loc: {
|
98
|
+
start: firstTokenAfterObject.loc.start,
|
99
|
+
end: callee.loc.end
|
100
|
+
},
|
96
101
|
messageId: "expected",
|
97
102
|
data: {
|
98
103
|
callee: getPropertyText(callee)
|
99
104
|
},
|
100
105
|
fix(fixer) {
|
101
|
-
const firstTokenAfterObject = sourceCode.getTokenAfter(callee.object, astUtils.isNotClosingParenToken);
|
102
|
-
|
103
106
|
return fixer.insertTextBefore(firstTokenAfterObject, "\n");
|
104
107
|
}
|
105
108
|
});
|
package/lib/rules/no-alert.js
CHANGED
@@ -8,7 +8,10 @@
|
|
8
8
|
// Requirements
|
9
9
|
//------------------------------------------------------------------------------
|
10
10
|
|
11
|
-
const
|
11
|
+
const {
|
12
|
+
getStaticPropertyName: getPropertyName,
|
13
|
+
getVariableByName
|
14
|
+
} = require("./utils/ast-utils");
|
12
15
|
|
13
16
|
//------------------------------------------------------------------------------
|
14
17
|
// Helpers
|
@@ -61,7 +64,7 @@ function isGlobalThisReferenceOrGlobalWindow(scope, node) {
|
|
61
64
|
if (scope.type === "global" && node.type === "ThisExpression") {
|
62
65
|
return true;
|
63
66
|
}
|
64
|
-
if (node.name === "window") {
|
67
|
+
if (node.name === "window" || (node.name === "globalThis" && getVariableByName(scope, "globalThis"))) {
|
65
68
|
return !isShadowed(scope, node);
|
66
69
|
}
|
67
70
|
|
@@ -119,7 +122,6 @@ module.exports = {
|
|
119
122
|
});
|
120
123
|
}
|
121
124
|
}
|
122
|
-
|
123
125
|
}
|
124
126
|
};
|
125
127
|
|
@@ -23,7 +23,9 @@ const ALLOW_OPTIONS = Object.freeze([
|
|
23
23
|
"generatorMethods",
|
24
24
|
"getters",
|
25
25
|
"setters",
|
26
|
-
"constructors"
|
26
|
+
"constructors",
|
27
|
+
"asyncFunctions",
|
28
|
+
"asyncMethods"
|
27
29
|
]);
|
28
30
|
|
29
31
|
/**
|
@@ -149,7 +151,7 @@ module.exports = {
|
|
149
151
|
) {
|
150
152
|
context.report({
|
151
153
|
node,
|
152
|
-
loc: node.body.loc
|
154
|
+
loc: node.body.loc,
|
153
155
|
messageId: "unexpected",
|
154
156
|
data: { name }
|
155
157
|
});
|
package/lib/rules/no-eval.js
CHANGED
@@ -64,7 +64,7 @@ module.exports = {
|
|
64
64
|
context.report({
|
65
65
|
node: node.parent.parent,
|
66
66
|
messageId: "unexpected",
|
67
|
-
loc: node.parent.property.loc
|
67
|
+
loc: node.parent.property.loc,
|
68
68
|
fix(fixer) {
|
69
69
|
if (node.parent.parent.arguments.length && !isSideEffectFree(node.parent.parent.arguments[0])) {
|
70
70
|
return null;
|
@@ -10,6 +10,9 @@
|
|
10
10
|
//------------------------------------------------------------------------------
|
11
11
|
|
12
12
|
const astUtils = require("./utils/ast-utils");
|
13
|
+
const eslintUtils = require("eslint-utils");
|
14
|
+
|
15
|
+
const precedence = astUtils.getPrecedence;
|
13
16
|
|
14
17
|
//------------------------------------------------------------------------------
|
15
18
|
// Rule Definition
|
@@ -126,6 +129,60 @@ module.exports = {
|
|
126
129
|
return Boolean(sourceCode.getCommentsInside(node).length);
|
127
130
|
}
|
128
131
|
|
132
|
+
/**
|
133
|
+
* Checks if the given node is wrapped in grouping parentheses. Parentheses for constructs such as if() don't count.
|
134
|
+
* @param {ASTNode} node The node to check.
|
135
|
+
* @returns {boolean} `true` if the node is parenthesized.
|
136
|
+
* @private
|
137
|
+
*/
|
138
|
+
function isParenthesized(node) {
|
139
|
+
return eslintUtils.isParenthesized(1, node, sourceCode);
|
140
|
+
}
|
141
|
+
|
142
|
+
/**
|
143
|
+
* Determines whether the given node needs to be parenthesized when replacing the previous node.
|
144
|
+
* It assumes that `previousNode` is the node to be reported by this rule, so it has a limited list
|
145
|
+
* of possible parent node types. By the same assumption, the node's role in a particular parent is already known.
|
146
|
+
* For example, if the parent is `ConditionalExpression`, `previousNode` must be its `test` child.
|
147
|
+
* @param {ASTNode} previousNode Previous node.
|
148
|
+
* @param {ASTNode} node The node to check.
|
149
|
+
* @returns {boolean} `true` if the node needs to be parenthesized.
|
150
|
+
*/
|
151
|
+
function needsParens(previousNode, node) {
|
152
|
+
if (isParenthesized(previousNode)) {
|
153
|
+
|
154
|
+
// parentheses around the previous node will stay, so there is no need for an additional pair
|
155
|
+
return false;
|
156
|
+
}
|
157
|
+
|
158
|
+
// parent of the previous node will become parent of the replacement node
|
159
|
+
const parent = previousNode.parent;
|
160
|
+
|
161
|
+
switch (parent.type) {
|
162
|
+
case "CallExpression":
|
163
|
+
case "NewExpression":
|
164
|
+
return node.type === "SequenceExpression";
|
165
|
+
case "IfStatement":
|
166
|
+
case "DoWhileStatement":
|
167
|
+
case "WhileStatement":
|
168
|
+
case "ForStatement":
|
169
|
+
return false;
|
170
|
+
case "ConditionalExpression":
|
171
|
+
return precedence(node) <= precedence(parent);
|
172
|
+
case "UnaryExpression":
|
173
|
+
return precedence(node) < precedence(parent);
|
174
|
+
case "LogicalExpression":
|
175
|
+
if (previousNode === parent.left) {
|
176
|
+
return precedence(node) < precedence(parent);
|
177
|
+
}
|
178
|
+
return precedence(node) <= precedence(parent);
|
179
|
+
|
180
|
+
/* istanbul ignore next */
|
181
|
+
default:
|
182
|
+
throw new Error(`Unexpected parent type: ${parent.type}`);
|
183
|
+
}
|
184
|
+
}
|
185
|
+
|
129
186
|
return {
|
130
187
|
UnaryExpression(node) {
|
131
188
|
const parent = node.parent;
|
@@ -143,32 +200,34 @@ module.exports = {
|
|
143
200
|
context.report({
|
144
201
|
node: parent,
|
145
202
|
messageId: "unexpectedNegation",
|
146
|
-
fix
|
203
|
+
fix(fixer) {
|
147
204
|
if (hasCommentsInside(parent)) {
|
148
205
|
return null;
|
149
206
|
}
|
150
207
|
|
208
|
+
if (needsParens(parent, node.argument)) {
|
209
|
+
return fixer.replaceText(parent, `(${sourceCode.getText(node.argument)})`);
|
210
|
+
}
|
211
|
+
|
151
212
|
let prefix = "";
|
152
213
|
const tokenBefore = sourceCode.getTokenBefore(parent);
|
153
214
|
const firstReplacementToken = sourceCode.getFirstToken(node.argument);
|
154
215
|
|
155
|
-
if (
|
156
|
-
|
216
|
+
if (
|
217
|
+
tokenBefore &&
|
218
|
+
tokenBefore.range[1] === parent.range[0] &&
|
219
|
+
!astUtils.canTokensBeAdjacent(tokenBefore, firstReplacementToken)
|
220
|
+
) {
|
157
221
|
prefix = " ";
|
158
222
|
}
|
159
223
|
|
160
|
-
if (astUtils.getPrecedence(node.argument) < astUtils.getPrecedence(parent.parent)) {
|
161
|
-
return fixer.replaceText(parent, `(${sourceCode.getText(node.argument)})`);
|
162
|
-
}
|
163
|
-
|
164
224
|
return fixer.replaceText(parent, prefix + sourceCode.getText(node.argument));
|
165
225
|
}
|
166
226
|
});
|
167
227
|
}
|
168
228
|
},
|
169
|
-
CallExpression(node) {
|
170
|
-
const parent = node.parent;
|
171
229
|
|
230
|
+
CallExpression(node) {
|
172
231
|
if (node.callee.type !== "Identifier" || node.callee.name !== "Boolean") {
|
173
232
|
return;
|
174
233
|
}
|
@@ -177,11 +236,15 @@ module.exports = {
|
|
177
236
|
context.report({
|
178
237
|
node,
|
179
238
|
messageId: "unexpectedCall",
|
180
|
-
fix
|
181
|
-
|
239
|
+
fix(fixer) {
|
240
|
+
const parent = node.parent;
|
241
|
+
|
242
|
+
if (node.arguments.length === 0) {
|
182
243
|
if (parent.type === "UnaryExpression" && parent.operator === "!") {
|
183
244
|
|
184
|
-
|
245
|
+
/*
|
246
|
+
* !Boolean() -> true
|
247
|
+
*/
|
185
248
|
|
186
249
|
if (hasCommentsInside(parent)) {
|
187
250
|
return null;
|
@@ -191,32 +254,48 @@ module.exports = {
|
|
191
254
|
let prefix = "";
|
192
255
|
const tokenBefore = sourceCode.getTokenBefore(parent);
|
193
256
|
|
194
|
-
if (
|
195
|
-
|
257
|
+
if (
|
258
|
+
tokenBefore &&
|
259
|
+
tokenBefore.range[1] === parent.range[0] &&
|
260
|
+
!astUtils.canTokensBeAdjacent(tokenBefore, replacement)
|
261
|
+
) {
|
196
262
|
prefix = " ";
|
197
263
|
}
|
198
264
|
|
199
265
|
return fixer.replaceText(parent, prefix + replacement);
|
200
266
|
}
|
201
267
|
|
202
|
-
|
268
|
+
/*
|
269
|
+
* Boolean() -> false
|
270
|
+
*/
|
271
|
+
|
203
272
|
if (hasCommentsInside(node)) {
|
204
273
|
return null;
|
205
274
|
}
|
275
|
+
|
206
276
|
return fixer.replaceText(node, "false");
|
207
277
|
}
|
208
278
|
|
209
|
-
if (node.arguments.length
|
210
|
-
|
211
|
-
|
212
|
-
|
279
|
+
if (node.arguments.length === 1) {
|
280
|
+
const argument = node.arguments[0];
|
281
|
+
|
282
|
+
if (argument.type === "SpreadElement" || hasCommentsInside(node)) {
|
283
|
+
return null;
|
284
|
+
}
|
285
|
+
|
286
|
+
/*
|
287
|
+
* Boolean(expression) -> expression
|
288
|
+
*/
|
213
289
|
|
214
|
-
|
290
|
+
if (needsParens(node, argument)) {
|
291
|
+
return fixer.replaceText(node, `(${sourceCode.getText(argument)})`);
|
292
|
+
}
|
215
293
|
|
216
|
-
|
217
|
-
return fixer.replaceText(node, `(${sourceCode.getText(argument)})`);
|
294
|
+
return fixer.replaceText(node, sourceCode.getText(argument));
|
218
295
|
}
|
219
|
-
|
296
|
+
|
297
|
+
// two or more arguments
|
298
|
+
return null;
|
220
299
|
}
|
221
300
|
});
|
222
301
|
}
|