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.
Files changed (90) hide show
  1. package/CHANGELOG.md +332 -0
  2. package/README.md +9 -10
  3. package/bin/eslint.js +115 -77
  4. package/conf/category-list.json +0 -1
  5. package/conf/environments.js +2 -1
  6. package/lib/api.js +2 -0
  7. package/lib/cli-engine/cascading-config-array-factory.js +16 -2
  8. package/lib/cli-engine/cli-engine.js +53 -47
  9. package/lib/cli-engine/config-array/config-array.js +30 -1
  10. package/lib/cli-engine/config-array/ignore-pattern.js +7 -1
  11. package/lib/cli-engine/config-array-factory.js +244 -235
  12. package/lib/cli.js +181 -95
  13. package/lib/eslint/eslint.js +656 -0
  14. package/lib/eslint/index.js +7 -0
  15. package/lib/init/autoconfig.js +4 -4
  16. package/lib/init/config-file.js +2 -2
  17. package/lib/init/config-initializer.js +3 -4
  18. package/lib/init/source-code-utils.js +2 -2
  19. package/lib/linter/linter.js +2 -1
  20. package/lib/linter/node-event-generator.js +2 -2
  21. package/lib/options.js +0 -1
  22. package/lib/rule-tester/rule-tester.js +132 -22
  23. package/lib/rules/accessor-pairs.js +1 -1
  24. package/lib/rules/array-callback-return.js +3 -18
  25. package/lib/rules/arrow-parens.js +19 -3
  26. package/lib/rules/block-spacing.js +19 -2
  27. package/lib/rules/callback-return.js +4 -0
  28. package/lib/rules/camelcase.js +38 -1
  29. package/lib/rules/comma-style.js +3 -8
  30. package/lib/rules/func-call-spacing.js +4 -3
  31. package/lib/rules/getter-return.js +2 -12
  32. package/lib/rules/global-require.js +4 -0
  33. package/lib/rules/handle-callback-err.js +4 -0
  34. package/lib/rules/id-blacklist.js +138 -102
  35. package/lib/rules/index.js +1 -0
  36. package/lib/rules/key-spacing.js +1 -1
  37. package/lib/rules/linebreak-style.js +8 -2
  38. package/lib/rules/max-lines-per-function.js +1 -1
  39. package/lib/rules/new-cap.js +1 -1
  40. package/lib/rules/newline-per-chained-call.js +6 -3
  41. package/lib/rules/no-alert.js +5 -3
  42. package/lib/rules/no-buffer-constructor.js +4 -0
  43. package/lib/rules/no-empty-function.js +4 -2
  44. package/lib/rules/no-eval.js +2 -1
  45. package/lib/rules/no-extra-bind.js +1 -1
  46. package/lib/rules/no-extra-boolean-cast.js +102 -23
  47. package/lib/rules/no-extra-parens.js +9 -5
  48. package/lib/rules/no-implied-eval.js +83 -101
  49. package/lib/rules/no-inner-declarations.js +31 -39
  50. package/lib/rules/no-lone-blocks.js +1 -1
  51. package/lib/rules/no-loss-of-precision.js +198 -0
  52. package/lib/rules/no-magic-numbers.js +72 -37
  53. package/lib/rules/no-mixed-requires.js +4 -0
  54. package/lib/rules/no-new-func.js +22 -19
  55. package/lib/rules/no-new-object.js +15 -3
  56. package/lib/rules/no-new-require.js +4 -0
  57. package/lib/rules/no-new-symbol.js +2 -1
  58. package/lib/rules/no-new-wrappers.js +1 -1
  59. package/lib/rules/no-obj-calls.js +24 -5
  60. package/lib/rules/no-path-concat.js +4 -0
  61. package/lib/rules/no-plusplus.js +39 -3
  62. package/lib/rules/no-process-env.js +4 -0
  63. package/lib/rules/no-process-exit.js +4 -0
  64. package/lib/rules/no-prototype-builtins.js +1 -1
  65. package/lib/rules/no-restricted-modules.js +4 -0
  66. package/lib/rules/no-sync.js +4 -0
  67. package/lib/rules/no-unexpected-multiline.js +22 -12
  68. package/lib/rules/no-useless-concat.js +1 -1
  69. package/lib/rules/one-var-declaration-per-line.js +1 -1
  70. package/lib/rules/operator-assignment.js +3 -3
  71. package/lib/rules/operator-linebreak.js +4 -16
  72. package/lib/rules/padded-blocks.js +17 -4
  73. package/lib/rules/prefer-numeric-literals.js +3 -3
  74. package/lib/rules/prefer-object-spread.js +2 -2
  75. package/lib/rules/require-await.js +1 -1
  76. package/lib/rules/rest-spread-spacing.js +3 -6
  77. package/lib/rules/semi-spacing.js +32 -8
  78. package/lib/rules/space-before-function-paren.js +5 -2
  79. package/lib/rules/template-curly-spacing.js +59 -42
  80. package/lib/rules/utils/ast-utils.js +116 -10
  81. package/lib/rules/yoda.js +101 -51
  82. package/lib/shared/relative-module-resolver.js +1 -0
  83. package/lib/shared/types.js +9 -2
  84. package/lib/source-code/source-code.js +1 -0
  85. package/messages/extend-config-missing.txt +1 -1
  86. package/messages/no-config-found.txt +1 -1
  87. package/messages/plugin-conflict.txt +7 -0
  88. package/messages/plugin-missing.txt +1 -1
  89. package/messages/whitespace-found.txt +1 -1
  90. package/package.json +27 -26
@@ -11,6 +11,10 @@
11
11
 
12
12
  module.exports = {
13
13
  meta: {
14
+ deprecated: true,
15
+
16
+ replacedBy: [],
17
+
14
18
  type: "suggestion",
15
19
 
16
20
  docs: {
@@ -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 if a string matches the provided pattern
49
- * @param {string} name The string to check.
50
- * @returns {boolean} if the string is a match
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 isInvalid(name) {
54
- return blacklist.indexOf(name) !== -1;
148
+ function isBlacklisted(name) {
149
+ return blacklist.has(name);
55
150
  }
56
151
 
57
152
  /**
58
- * Checks whether the given node represents an imported name that is renamed in the same import/export specifier.
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 renamed import.
156
+ * @returns {boolean} `true` if the node is a reference to a global variable.
65
157
  */
66
- function isRenamedImport(node) {
67
- const parent = node.parent;
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
- * Checks whether the given node is a renamed identifier node in an ObjectPattern destructuring.
86
- *
87
- * Examples:
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 isRenamedInDestructuring(node) {
170
+ function shouldCheck(node) {
93
171
  const parent = node.parent;
94
172
 
95
- return (
96
- (
97
- !parent.computed &&
98
- parent.type === "Property" &&
99
- parent.parent.type === "ObjectPattern" &&
100
- parent.value !== node &&
101
- parent.key === node
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
- isInvalid(node.name)
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
- Identifier(node) {
145
-
146
- // MemberExpressions get special rules
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
- } else if (shouldReport(node)) {
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
  };
@@ -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"),
@@ -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 properies is on a single line.
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
- line: i,
90
- column: sourceCode.lines[i - 1].length
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
  /**
@@ -235,7 +235,7 @@ module.exports = {
235
235
  callee = callee.property;
236
236
  }
237
237
 
238
- context.report({ node, loc: callee.loc.start, messageId });
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: callee.property.loc.start,
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
  });
@@ -8,7 +8,10 @@
8
8
  // Requirements
9
9
  //------------------------------------------------------------------------------
10
10
 
11
- const getPropertyName = require("./utils/ast-utils").getStaticPropertyName;
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
 
@@ -10,6 +10,10 @@
10
10
 
11
11
  module.exports = {
12
12
  meta: {
13
+ deprecated: true,
14
+
15
+ replacedBy: [],
16
+
13
17
  type: "problem",
14
18
 
15
19
  docs: {
@@ -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.start,
154
+ loc: node.body.loc,
153
155
  messageId: "unexpected",
154
156
  data: { name }
155
157
  });
@@ -17,7 +17,8 @@ const astUtils = require("./utils/ast-utils");
17
17
 
18
18
  const candidatesOfGlobalObject = Object.freeze([
19
19
  "global",
20
- "window"
20
+ "window",
21
+ "globalThis"
21
22
  ]);
22
23
 
23
24
  /**
@@ -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.start,
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: fixer => {
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 (tokenBefore && tokenBefore.range[1] === parent.range[0] &&
156
- !astUtils.canTokensBeAdjacent(tokenBefore, firstReplacementToken)) {
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: fixer => {
181
- if (!node.arguments.length) {
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
- // !Boolean() -> true
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 (tokenBefore && tokenBefore.range[1] === parent.range[0] &&
195
- !astUtils.canTokensBeAdjacent(tokenBefore, replacement)) {
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
- // Boolean() -> false
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 > 1 || node.arguments[0].type === "SpreadElement" ||
210
- hasCommentsInside(node)) {
211
- return null;
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
- const argument = node.arguments[0];
290
+ if (needsParens(node, argument)) {
291
+ return fixer.replaceText(node, `(${sourceCode.getText(argument)})`);
292
+ }
215
293
 
216
- if (astUtils.getPrecedence(argument) < astUtils.getPrecedence(node.parent)) {
217
- return fixer.replaceText(node, `(${sourceCode.getText(argument)})`);
294
+ return fixer.replaceText(node, sourceCode.getText(argument));
218
295
  }
219
- return fixer.replaceText(node, sourceCode.getText(argument));
296
+
297
+ // two or more arguments
298
+ return null;
220
299
  }
221
300
  });
222
301
  }