eslint 3.16.1 → 3.19.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 (86) hide show
  1. package/CHANGELOG.md +103 -0
  2. package/README.md +1 -0
  3. package/conf/eslint-recommended.js +2 -0
  4. package/lib/ast-utils.js +3 -67
  5. package/lib/code-path-analysis/code-path-analyzer.js +2 -7
  6. package/lib/code-path-analysis/debug-helpers.js +17 -16
  7. package/lib/config/config-file.js +68 -38
  8. package/lib/config/config-rule.js +14 -10
  9. package/lib/config/plugins.js +19 -8
  10. package/lib/eslint.js +11 -10
  11. package/lib/formatters/codeframe.js +4 -9
  12. package/lib/formatters/stylish.js +5 -4
  13. package/lib/ignored-paths.js +6 -0
  14. package/lib/internal-rules/internal-no-invalid-meta.js +2 -40
  15. package/lib/rules/array-callback-return.js +15 -5
  16. package/lib/rules/arrow-body-style.js +2 -2
  17. package/lib/rules/arrow-parens.js +9 -3
  18. package/lib/rules/capitalized-comments.js +2 -1
  19. package/lib/rules/comma-dangle.js +3 -2
  20. package/lib/rules/comma-spacing.js +4 -14
  21. package/lib/rules/comma-style.js +8 -14
  22. package/lib/rules/complexity.js +14 -8
  23. package/lib/rules/consistent-return.js +17 -10
  24. package/lib/rules/curly.js +2 -2
  25. package/lib/rules/dot-notation.js +12 -6
  26. package/lib/rules/func-name-matching.js +18 -7
  27. package/lib/rules/func-names.js +20 -5
  28. package/lib/rules/keyword-spacing.js +19 -4
  29. package/lib/rules/line-comment-position.js +15 -5
  30. package/lib/rules/lines-around-comment.js +19 -0
  31. package/lib/rules/lines-around-directive.js +1 -1
  32. package/lib/rules/max-params.js +17 -4
  33. package/lib/rules/max-statements.js +11 -10
  34. package/lib/rules/new-parens.js +7 -21
  35. package/lib/rules/no-compare-neg-zero.js +53 -0
  36. package/lib/rules/no-cond-assign.js +4 -17
  37. package/lib/rules/no-else-return.js +19 -4
  38. package/lib/rules/no-empty-function.js +9 -16
  39. package/lib/rules/no-extra-parens.js +110 -121
  40. package/lib/rules/no-extra-semi.js +16 -3
  41. package/lib/rules/no-global-assign.js +1 -1
  42. package/lib/rules/no-implicit-coercion.js +21 -8
  43. package/lib/rules/no-invalid-regexp.js +2 -1
  44. package/lib/rules/no-multiple-empty-lines.js +2 -4
  45. package/lib/rules/no-native-reassign.js +1 -1
  46. package/lib/rules/no-negated-in-lhs.js +1 -1
  47. package/lib/rules/no-new-func.js +6 -8
  48. package/lib/rules/no-new.js +2 -6
  49. package/lib/rules/no-param-reassign.js +37 -7
  50. package/lib/rules/no-process-exit.js +2 -10
  51. package/lib/rules/no-restricted-properties.js +2 -0
  52. package/lib/rules/no-restricted-syntax.js +32 -21
  53. package/lib/rules/no-return-await.js +1 -1
  54. package/lib/rules/no-sequences.js +2 -2
  55. package/lib/rules/no-sync.js +8 -13
  56. package/lib/rules/no-unsafe-negation.js +1 -1
  57. package/lib/rules/no-unused-expressions.js +10 -1
  58. package/lib/rules/no-unused-vars.js +12 -12
  59. package/lib/rules/no-use-before-define.js +1 -1
  60. package/lib/rules/no-useless-computed-key.js +12 -1
  61. package/lib/rules/no-useless-escape.js +8 -2
  62. package/lib/rules/no-useless-return.js +13 -2
  63. package/lib/rules/nonblock-statement-body-position.js +114 -0
  64. package/lib/rules/object-curly-spacing.js +2 -2
  65. package/lib/rules/object-shorthand.js +10 -3
  66. package/lib/rules/operator-assignment.js +20 -3
  67. package/lib/rules/padded-blocks.js +37 -31
  68. package/lib/rules/prefer-const.js +1 -1
  69. package/lib/rules/prefer-destructuring.js +1 -1
  70. package/lib/rules/quotes.js +1 -0
  71. package/lib/rules/semi-spacing.js +2 -15
  72. package/lib/rules/semi.js +17 -13
  73. package/lib/rules/sort-vars.js +3 -5
  74. package/lib/rules/space-before-function-paren.js +53 -77
  75. package/lib/rules/space-in-parens.js +4 -8
  76. package/lib/rules/space-unary-ops.js +19 -1
  77. package/lib/rules/strict.js +8 -2
  78. package/lib/rules/yoda.js +2 -2
  79. package/lib/testers/rule-tester.js +44 -13
  80. package/lib/util/fix-tracker.js +121 -0
  81. package/lib/util/glob-util.js +1 -1
  82. package/lib/util/node-event-generator.js +274 -4
  83. package/lib/util/source-code-fixer.js +3 -9
  84. package/lib/util/source-code.js +99 -2
  85. package/lib/util/traverser.js +16 -25
  86. package/package.json +34 -34
@@ -90,23 +90,32 @@ module.exports = {
90
90
  return node.type === "Line" || node.type === "Block";
91
91
  }
92
92
 
93
+ /**
94
+ * Checks if there is padding between two tokens
95
+ * @param {Token} first The first token
96
+ * @param {Token} second The second token
97
+ * @returns {boolean} True if there is at least a line between the tokens
98
+ */
99
+ function isPaddingBetweenTokens(first, second) {
100
+ return second.loc.start.line - first.loc.end.line >= 2;
101
+ }
102
+
103
+
93
104
  /**
94
105
  * Checks if the given token has a blank line after it.
95
106
  * @param {Token} token The token to check.
96
107
  * @returns {boolean} Whether or not the token is followed by a blank line.
97
108
  */
98
- function isTokenTopPadded(token) {
99
- const tokenStartLine = token.loc.start.line,
100
- expectedFirstLine = tokenStartLine + 2;
101
- let first = token;
109
+ function getFirstBlockToken(token) {
110
+ let prev = token,
111
+ first = token;
102
112
 
103
113
  do {
114
+ prev = first;
104
115
  first = sourceCode.getTokenAfter(first, { includeComments: true });
105
- } while (isComment(first) && first.loc.start.line === tokenStartLine);
106
-
107
- const firstLine = first.loc.start.line;
116
+ } while (isComment(first) && first.loc.start.line === prev.loc.end.line);
108
117
 
109
- return expectedFirstLine <= firstLine;
118
+ return first;
110
119
  }
111
120
 
112
121
  /**
@@ -114,18 +123,16 @@ module.exports = {
114
123
  * @param {Token} token The token to check
115
124
  * @returns {boolean} Whether or not the token is preceeded by a blank line
116
125
  */
117
- function isTokenBottomPadded(token) {
118
- const blockEnd = token.loc.end.line,
119
- expectedLastLine = blockEnd - 2;
120
- let last = token;
126
+ function getLastBlockToken(token) {
127
+ let last = token,
128
+ next = token;
121
129
 
122
130
  do {
131
+ next = last;
123
132
  last = sourceCode.getTokenBefore(last, { includeComments: true });
124
- } while (isComment(last) && last.loc.end.line === blockEnd);
133
+ } while (isComment(last) && last.loc.end.line === next.loc.start.line);
125
134
 
126
- const lastLine = last.loc.end.line;
127
-
128
- return lastLine <= expectedLastLine;
135
+ return last;
129
136
  }
130
137
 
131
138
  /**
@@ -155,17 +162,21 @@ module.exports = {
155
162
  */
156
163
  function checkPadding(node) {
157
164
  const openBrace = getOpenBrace(node),
165
+ firstBlockToken = getFirstBlockToken(openBrace),
166
+ tokenBeforeFirst = sourceCode.getTokenBefore(firstBlockToken, { includeComments: true }),
158
167
  closeBrace = sourceCode.getLastToken(node),
159
- blockHasTopPadding = isTokenTopPadded(openBrace),
160
- blockHasBottomPadding = isTokenBottomPadded(closeBrace);
168
+ lastBlockToken = getLastBlockToken(closeBrace),
169
+ tokenAfterLast = sourceCode.getTokenAfter(lastBlockToken, { includeComments: true }),
170
+ blockHasTopPadding = isPaddingBetweenTokens(tokenBeforeFirst, firstBlockToken),
171
+ blockHasBottomPadding = isPaddingBetweenTokens(lastBlockToken, tokenAfterLast);
161
172
 
162
173
  if (requirePaddingFor(node)) {
163
174
  if (!blockHasTopPadding) {
164
175
  context.report({
165
176
  node,
166
- loc: { line: openBrace.loc.start.line, column: openBrace.loc.start.column },
177
+ loc: { line: tokenBeforeFirst.loc.start.line, column: tokenBeforeFirst.loc.start.column },
167
178
  fix(fixer) {
168
- return fixer.insertTextAfter(openBrace, "\n");
179
+ return fixer.insertTextAfter(tokenBeforeFirst, "\n");
169
180
  },
170
181
  message: ALWAYS_MESSAGE
171
182
  });
@@ -173,39 +184,34 @@ module.exports = {
173
184
  if (!blockHasBottomPadding) {
174
185
  context.report({
175
186
  node,
176
- loc: { line: closeBrace.loc.end.line, column: closeBrace.loc.end.column - 1 },
187
+ loc: { line: tokenAfterLast.loc.end.line, column: tokenAfterLast.loc.end.column - 1 },
177
188
  fix(fixer) {
178
- return fixer.insertTextBefore(closeBrace, "\n");
189
+ return fixer.insertTextBefore(tokenAfterLast, "\n");
179
190
  },
180
191
  message: ALWAYS_MESSAGE
181
192
  });
182
193
  }
183
194
  } else {
184
195
  if (blockHasTopPadding) {
185
- const nextToken = sourceCode.getTokenAfter(openBrace, { includeComments: true });
186
196
 
187
197
  context.report({
188
198
  node,
189
- loc: { line: openBrace.loc.start.line, column: openBrace.loc.start.column },
199
+ loc: { line: tokenBeforeFirst.loc.start.line, column: tokenBeforeFirst.loc.start.column },
190
200
  fix(fixer) {
191
-
192
- // FIXME: The start of this range is sometimes larger than the end.
193
- // https://github.com/eslint/eslint/issues/8116
194
- return fixer.replaceTextRange([openBrace.end, nextToken.start - nextToken.loc.start.column], "\n");
201
+ return fixer.replaceTextRange([tokenBeforeFirst.end, firstBlockToken.start - firstBlockToken.loc.start.column], "\n");
195
202
  },
196
203
  message: NEVER_MESSAGE
197
204
  });
198
205
  }
199
206
 
200
207
  if (blockHasBottomPadding) {
201
- const previousToken = sourceCode.getTokenBefore(closeBrace, { includeComments: true });
202
208
 
203
209
  context.report({
204
210
  node,
205
- loc: { line: closeBrace.loc.end.line, column: closeBrace.loc.end.column - 1 },
211
+ loc: { line: tokenAfterLast.loc.end.line, column: tokenAfterLast.loc.end.column - 1 },
206
212
  message: NEVER_MESSAGE,
207
213
  fix(fixer) {
208
- return fixer.replaceTextRange([previousToken.end, closeBrace.start - closeBrace.loc.start.column], "\n");
214
+ return fixer.replaceTextRange([lastBlockToken.end, tokenAfterLast.start - tokenAfterLast.loc.start.column], "\n");
209
215
  }
210
216
  });
211
217
  }
@@ -9,7 +9,7 @@
9
9
  // Helpers
10
10
  //------------------------------------------------------------------------------
11
11
 
12
- const PATTERN_TYPE = /^(?:.+?Pattern|RestElement|Property)$/;
12
+ const PATTERN_TYPE = /^(?:.+?Pattern|RestElement|SpreadProperty|ExperimentalRestProperty|Property)$/;
13
13
  const DECLARATION_HOST_TYPE = /^(?:Program|BlockStatement|SwitchCase)$/;
14
14
  const DESTRUCTURING_HOST_TYPE = /^(?:VariableDeclarator|AssignmentExpression)$/;
15
15
 
@@ -89,7 +89,7 @@ module.exports = {
89
89
  * @returns {void}
90
90
  */
91
91
  function report(reportNode, type) {
92
- context.report({ node: reportNode, message: `Use ${type} destructuring` });
92
+ context.report({ node: reportNode, message: "Use {{type}} destructuring.", data: { type } });
93
93
  }
94
94
 
95
95
  /**
@@ -209,6 +209,7 @@ module.exports = {
209
209
 
210
210
  // LiteralPropertyName.
211
211
  case "Property":
212
+ case "MethodDefinition":
212
213
  return parent.key === node && !parent.computed;
213
214
 
214
215
  // ModuleSpecifier.
@@ -105,20 +105,7 @@ module.exports = {
105
105
  function isBeforeClosingParen(token) {
106
106
  const nextToken = sourceCode.getTokenAfter(token);
107
107
 
108
- return (
109
- nextToken &&
110
- nextToken.type === "Punctuator" &&
111
- (nextToken.value === "}" || nextToken.value === ")")
112
- );
113
- }
114
-
115
- /**
116
- * Checks if the given token is a semicolon.
117
- * @param {Token} token The token to check.
118
- * @returns {boolean} Whether or not the given token is a semicolon.
119
- */
120
- function isSemicolon(token) {
121
- return token.type === "Punctuator" && token.value === ";";
108
+ return (nextToken && astUtils.isClosingBraceToken(nextToken) || astUtils.isClosingParenToken(nextToken));
122
109
  }
123
110
 
124
111
  /**
@@ -128,7 +115,7 @@ module.exports = {
128
115
  * @returns {void}
129
116
  */
130
117
  function checkSemicolonSpacing(token, node) {
131
- if (isSemicolon(token)) {
118
+ if (astUtils.isSemicolonToken(token)) {
132
119
  const location = token.loc.start;
133
120
 
134
121
  if (hasLeadingSpace(token)) {
package/lib/rules/semi.js CHANGED
@@ -4,6 +4,13 @@
4
4
  */
5
5
  "use strict";
6
6
 
7
+ //------------------------------------------------------------------------------
8
+ // Requirements
9
+ //------------------------------------------------------------------------------
10
+
11
+ const FixTracker = require("../util/fix-tracker");
12
+ const astUtils = require("../ast-utils");
13
+
7
14
  //------------------------------------------------------------------------------
8
15
  // Rule Definition
9
16
  //------------------------------------------------------------------------------
@@ -85,7 +92,13 @@ module.exports = {
85
92
  message = "Extra semicolon.";
86
93
  loc = loc.start;
87
94
  fix = function(fixer) {
88
- return fixer.remove(lastToken);
95
+
96
+ // Expand the replacement range to include the surrounding
97
+ // tokens to avoid conflicting with no-extra-semi.
98
+ // https://github.com/eslint/eslint/issues/7928
99
+ return new FixTracker(fixer, sourceCode)
100
+ .retainSurroundingTokens(lastToken)
101
+ .remove(lastToken);
89
102
  };
90
103
  }
91
104
 
@@ -98,15 +111,6 @@ module.exports = {
98
111
 
99
112
  }
100
113
 
101
- /**
102
- * Checks whether a token is a semicolon punctuator.
103
- * @param {Token} token The token.
104
- * @returns {boolean} True if token is a semicolon punctuator.
105
- */
106
- function isSemicolon(token) {
107
- return (token.type === "Punctuator" && token.value === ";");
108
- }
109
-
110
114
  /**
111
115
  * Check if a semicolon is unnecessary, only true if:
112
116
  * - next token is on a new line and is not one of the opt-out tokens
@@ -115,7 +119,7 @@ module.exports = {
115
119
  * @returns {boolean} whether the semicolon is unnecessary.
116
120
  */
117
121
  function isUnnecessarySemicolon(lastToken) {
118
- if (!isSemicolon(lastToken)) {
122
+ if (!astUtils.isSemicolonToken(lastToken)) {
119
123
  return false;
120
124
  }
121
125
 
@@ -128,7 +132,7 @@ module.exports = {
128
132
  const lastTokenLine = lastToken.loc.end.line;
129
133
  const nextTokenLine = nextToken.loc.start.line;
130
134
  const isOptOutToken = OPT_OUT_PATTERN.test(nextToken.value) && nextToken.value !== "++" && nextToken.value !== "--";
131
- const isDivider = (nextToken.value === "}" || nextToken.value === ";");
135
+ const isDivider = (astUtils.isClosingBraceToken(nextToken) || astUtils.isSemicolonToken(nextToken));
132
136
 
133
137
  return (lastTokenLine !== nextTokenLine && !isOptOutToken) || isDivider;
134
138
  }
@@ -164,7 +168,7 @@ module.exports = {
164
168
  report(node, true);
165
169
  }
166
170
  } else {
167
- if (!isSemicolon(lastToken)) {
171
+ if (!astUtils.isSemicolonToken(lastToken)) {
168
172
  if (!exceptOneLine || !isOneLinerBlock(node)) {
169
173
  report(node);
170
174
  }
@@ -37,11 +37,9 @@ module.exports = {
37
37
 
38
38
  return {
39
39
  VariableDeclaration(node) {
40
- node.declarations.reduce((memo, decl) => {
41
- if (decl.id.type === "ObjectPattern" || decl.id.type === "ArrayPattern") {
42
- return memo;
43
- }
40
+ const idDeclarations = node.declarations.filter(decl => decl.id.type === "Identifier");
44
41
 
42
+ idDeclarations.slice(1).reduce((memo, decl) => {
45
43
  let lastVariableName = memo.id.name,
46
44
  currenVariableName = decl.id.name;
47
45
 
@@ -56,7 +54,7 @@ module.exports = {
56
54
  }
57
55
  return decl;
58
56
 
59
- }, node.declarations[0]);
57
+ }, idDeclarations[0]);
60
58
  }
61
59
  };
62
60
  }
@@ -51,31 +51,9 @@ module.exports = {
51
51
  },
52
52
 
53
53
  create(context) {
54
-
55
- const configuration = context.options[0],
56
- sourceCode = context.getSourceCode();
57
- let requireAnonymousFunctionSpacing = true,
58
- forbidAnonymousFunctionSpacing = false,
59
- requireNamedFunctionSpacing = true,
60
- forbidNamedFunctionSpacing = false,
61
- requireArrowFunctionSpacing = false,
62
- forbidArrowFunctionSpacing = false;
63
-
64
- if (typeof configuration === "object") {
65
- requireAnonymousFunctionSpacing = (
66
- !configuration.anonymous || configuration.anonymous === "always");
67
- forbidAnonymousFunctionSpacing = configuration.anonymous === "never";
68
- requireNamedFunctionSpacing = (
69
- !configuration.named || configuration.named === "always");
70
- forbidNamedFunctionSpacing = configuration.named === "never";
71
- requireArrowFunctionSpacing = configuration.asyncArrow === "always";
72
- forbidArrowFunctionSpacing = configuration.asyncArrow === "never";
73
- } else if (configuration === "never") {
74
- requireAnonymousFunctionSpacing = false;
75
- forbidAnonymousFunctionSpacing = true;
76
- requireNamedFunctionSpacing = false;
77
- forbidNamedFunctionSpacing = true;
78
- }
54
+ const sourceCode = context.getSourceCode();
55
+ const baseConfig = typeof context.options[0] === "string" ? context.options[0] : "always";
56
+ const overrideConfig = typeof context.options[0] === "object" ? context.options[0] : {};
79
57
 
80
58
  /**
81
59
  * Determines whether a function has a name.
@@ -100,69 +78,67 @@ module.exports = {
100
78
  }
101
79
 
102
80
  /**
103
- * Validates the spacing before function parentheses.
104
- * @param {ASTNode} node The node to be validated.
105
- * @returns {void}
81
+ * Gets the config for a given function
82
+ * @param {ASTNode} node The function node
83
+ * @returns {string} "always", "never", or "ignore"
106
84
  */
107
- function validateSpacingBeforeParentheses(node) {
108
- const isArrow = node.type === "ArrowFunctionExpression";
109
- const isNamed = !isArrow && isNamedFunction(node);
110
- const isAnonymousGenerator = node.generator && !isNamed;
111
- const isNormalArrow = isArrow && !node.async;
112
- const isArrowWithoutParens = isArrow && sourceCode.getFirstToken(node, 1).value !== "(";
113
- let forbidSpacing, requireSpacing;
114
-
115
- // isAnonymousGenerator → `generator-star-spacing` should warn it. E.g. `function* () {}`
116
- // isNormalArrow ignore always.
117
- // isArrowWithoutParens → ignore always. E.g. `async a => a`
118
- if (isAnonymousGenerator || isNormalArrow || isArrowWithoutParens) {
119
- return;
85
+ function getConfigForFunction(node) {
86
+ if (node.type === "ArrowFunctionExpression") {
87
+
88
+ // Always ignore non-async functions and arrow functions without parens, e.g. async foo => bar
89
+ if (node.async && astUtils.isOpeningParenToken(sourceCode.getFirstToken(node, { skip: 1 }))) {
90
+
91
+ // For backwards compatibility, the base config does not apply to async arrow functions.
92
+ return overrideConfig.asyncArrow || "ignore";
93
+ }
94
+ } else if (isNamedFunction(node)) {
95
+ return overrideConfig.named || baseConfig;
96
+
97
+ // `generator-star-spacing` should warn anonymous generators. E.g. `function* () {}`
98
+ } else if (!node.generator) {
99
+ return overrideConfig.anonymous || baseConfig;
120
100
  }
121
101
 
122
- if (isArrow) {
123
- forbidSpacing = forbidArrowFunctionSpacing;
124
- requireSpacing = requireArrowFunctionSpacing;
125
- } else if (isNamed) {
126
- forbidSpacing = forbidNamedFunctionSpacing;
127
- requireSpacing = requireNamedFunctionSpacing;
128
- } else {
129
- forbidSpacing = forbidAnonymousFunctionSpacing;
130
- requireSpacing = requireAnonymousFunctionSpacing;
102
+ return "ignore";
103
+ }
104
+
105
+ /**
106
+ * Checks the parens of a function node
107
+ * @param {ASTNode} node A function node
108
+ * @returns {void}
109
+ */
110
+ function checkFunction(node) {
111
+ const functionConfig = getConfigForFunction(node);
112
+
113
+ if (functionConfig === "ignore") {
114
+ return;
131
115
  }
132
116
 
133
117
  const rightToken = sourceCode.getFirstToken(node, astUtils.isOpeningParenToken);
134
118
  const leftToken = sourceCode.getTokenBefore(rightToken);
135
- const location = leftToken.loc.end;
136
-
137
- if (sourceCode.isSpaceBetweenTokens(leftToken, rightToken)) {
138
- if (forbidSpacing) {
139
- context.report({
140
- node,
141
- loc: location,
142
- message: "Unexpected space before function parentheses.",
143
- fix(fixer) {
144
- return fixer.removeRange([leftToken.range[1], rightToken.range[0]]);
145
- }
146
- });
147
- }
148
- } else {
149
- if (requireSpacing) {
150
- context.report({
151
- node,
152
- loc: location,
153
- message: "Missing space before function parentheses.",
154
- fix(fixer) {
155
- return fixer.insertTextAfter(leftToken, " ");
156
- }
157
- });
158
- }
119
+ const hasSpacing = sourceCode.isSpaceBetweenTokens(leftToken, rightToken);
120
+
121
+ if (hasSpacing && functionConfig === "never") {
122
+ context.report({
123
+ node,
124
+ loc: leftToken.loc.end,
125
+ message: "Unexpected space before function parentheses.",
126
+ fix: fixer => fixer.removeRange([leftToken.range[1], rightToken.range[0]])
127
+ });
128
+ } else if (!hasSpacing && functionConfig === "always") {
129
+ context.report({
130
+ node,
131
+ loc: leftToken.loc.end,
132
+ message: "Missing space before function parentheses.",
133
+ fix: fixer => fixer.insertTextAfter(leftToken, " ")
134
+ });
159
135
  }
160
136
  }
161
137
 
162
138
  return {
163
- FunctionDeclaration: validateSpacingBeforeParentheses,
164
- FunctionExpression: validateSpacingBeforeParentheses,
165
- ArrowFunctionExpression: validateSpacingBeforeParentheses
139
+ ArrowFunctionExpression: checkFunction,
140
+ FunctionDeclaration: checkFunction,
141
+ FunctionExpression: checkFunction
166
142
  };
167
143
  }
168
144
  };
@@ -128,7 +128,7 @@ module.exports = {
128
128
  }
129
129
 
130
130
  if (ALWAYS) {
131
- if (right.type === "Punctuator" && right.value === ")") {
131
+ if (astUtils.isClosingParenToken(right)) {
132
132
  return false;
133
133
  }
134
134
  return !isOpenerException(right);
@@ -144,7 +144,7 @@ module.exports = {
144
144
  * @returns {boolean} True if the paren should have a space
145
145
  */
146
146
  function shouldCloserHaveSpace(left, right) {
147
- if (left.type === "Punctuator" && left.value === "(") {
147
+ if (astUtils.isOpeningParenToken(left)) {
148
148
  return false;
149
149
  }
150
150
 
@@ -192,7 +192,7 @@ module.exports = {
192
192
  * @returns {boolean} True if the paren should reject the space
193
193
  */
194
194
  function shouldCloserRejectSpace(left, right) {
195
- if (left.type === "Punctuator" && left.value === "(") {
195
+ if (astUtils.isOpeningParenToken(left)) {
196
196
  return false;
197
197
  }
198
198
 
@@ -224,11 +224,7 @@ module.exports = {
224
224
  const prevToken = tokens[i - 1];
225
225
  const nextToken = tokens[i + 1];
226
226
 
227
- if (token.type !== "Punctuator") {
228
- return;
229
- }
230
-
231
- if (token.value !== "(" && token.value !== ")") {
227
+ if (!astUtils.isOpeningParenToken(token) && !astUtils.isClosingParenToken(token)) {
232
228
  return;
233
229
  }
234
230
 
@@ -68,6 +68,21 @@ module.exports = {
68
68
  return node.argument && node.argument.type && node.argument.type === "ObjectExpression";
69
69
  }
70
70
 
71
+ /**
72
+ * Check if it is safe to remove the spaces between the two tokens in
73
+ * the context of a non-word prefix unary operator. For example, `+ +1`
74
+ * cannot safely be changed to `++1`.
75
+ * @param {Token} firstToken The operator for a non-word prefix unary operator
76
+ * @param {Token} secondToken The first token of its operand
77
+ * @returns {boolean} Whether or not the spacing between the tokens can be removed
78
+ */
79
+ function canRemoveSpacesBetween(firstToken, secondToken) {
80
+ return !(
81
+ (firstToken.value === "+" && secondToken.value[0] === "+") ||
82
+ (firstToken.value === "-" && secondToken.value[0] === "-")
83
+ );
84
+ }
85
+
71
86
  /**
72
87
  * Checks if an override exists for a given operator.
73
88
  * @param {ASTnode} node AST node
@@ -244,7 +259,10 @@ module.exports = {
244
259
  operator: firstToken.value
245
260
  },
246
261
  fix(fixer) {
247
- return fixer.removeRange([firstToken.range[1], secondToken.range[0]]);
262
+ if (canRemoveSpacesBetween(firstToken, secondToken)) {
263
+ return fixer.removeRange([firstToken.range[1], secondToken.range[0]]);
264
+ }
265
+ return null;
248
266
  }
249
267
  });
250
268
  }
@@ -9,6 +9,8 @@
9
9
  // Requirements
10
10
  //------------------------------------------------------------------------------
11
11
 
12
+ const astUtils = require("../ast-utils");
13
+
12
14
  //------------------------------------------------------------------------------
13
15
  // Helpers
14
16
  //------------------------------------------------------------------------------
@@ -23,7 +25,7 @@ const messages = {
23
25
  implied: "'use strict' is unnecessary when implied strict mode is enabled.",
24
26
  unnecessaryInClasses: "'use strict' is unnecessary inside of classes.",
25
27
  nonSimpleParameterList: "'use strict' directive inside a function with non-simple parameter list throws a syntax error since ES2016.",
26
- wrap: "Wrap this function in a function with 'use strict' directive."
28
+ wrap: "Wrap {{name}} in a function with 'use strict' directive."
27
29
  };
28
30
 
29
31
  /**
@@ -188,7 +190,11 @@ module.exports = {
188
190
  if (isSimpleParameterList(node.params)) {
189
191
  context.report({ node, message: messages.function });
190
192
  } else {
191
- context.report({ node, message: messages.wrap });
193
+ context.report({
194
+ node,
195
+ message: messages.wrap,
196
+ data: { name: astUtils.getFunctionNameWithKind(node) }
197
+ });
192
198
  }
193
199
  }
194
200
 
package/lib/rules/yoda.js CHANGED
@@ -267,8 +267,8 @@ module.exports = {
267
267
  const operatorToken = sourceCode.getFirstTokenBetween(node.left, node.right, token => token.value === node.operator);
268
268
  const textBeforeOperator = sourceCode.getText().slice(sourceCode.getTokenBefore(operatorToken).range[1], operatorToken.range[0]);
269
269
  const textAfterOperator = sourceCode.getText().slice(operatorToken.range[1], sourceCode.getTokenAfter(operatorToken).range[0]);
270
- const leftText = sourceCode.getText().slice(sourceCode.getFirstToken(node).range[0], sourceCode.getTokenBefore(operatorToken).range[1]);
271
- const rightText = sourceCode.getText().slice(sourceCode.getTokenAfter(operatorToken).range[0], sourceCode.getLastToken(node).range[1]);
270
+ const leftText = sourceCode.getText().slice(node.range[0], sourceCode.getTokenBefore(operatorToken).range[1]);
271
+ const rightText = sourceCode.getText().slice(sourceCode.getTokenAfter(operatorToken).range[0], node.range[1]);
272
272
 
273
273
  return rightText + textBeforeOperator + OPERATOR_FLIP_MAP[operatorToken.value] + textAfterOperator + leftText;
274
274
  }