eslint 3.13.1 → 3.16.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 (137) hide show
  1. package/CHANGELOG.md +81 -0
  2. package/README.md +1 -1
  3. package/conf/{eslint.json → eslint-recommended.js} +88 -71
  4. package/lib/ast-utils.js +247 -28
  5. package/lib/cli.js +2 -2
  6. package/lib/code-path-analysis/code-path-state.js +2 -2
  7. package/lib/config/autoconfig.js +24 -20
  8. package/lib/config/config-file.js +31 -24
  9. package/lib/config/config-initializer.js +2 -2
  10. package/lib/config/config-rule.js +15 -16
  11. package/lib/config/config-validator.js +6 -6
  12. package/lib/config.js +3 -2
  13. package/lib/eslint.js +18 -18
  14. package/lib/formatters/checkstyle.js +2 -2
  15. package/lib/formatters/codeframe.js +1 -1
  16. package/lib/formatters/compact.js +2 -2
  17. package/lib/formatters/junit.js +2 -2
  18. package/lib/formatters/tap.js +2 -2
  19. package/lib/formatters/unix.js +2 -2
  20. package/lib/formatters/visualstudio.js +2 -2
  21. package/lib/internal-rules/internal-consistent-docs-description.js +1 -1
  22. package/lib/rule-context.js +2 -2
  23. package/lib/rules/arrow-body-style.js +7 -4
  24. package/lib/rules/arrow-spacing.js +7 -6
  25. package/lib/rules/block-spacing.js +2 -2
  26. package/lib/rules/brace-style.js +93 -202
  27. package/lib/rules/capitalized-comments.js +6 -6
  28. package/lib/rules/comma-dangle.js +6 -6
  29. package/lib/rules/comma-spacing.js +16 -16
  30. package/lib/rules/comma-style.js +1 -1
  31. package/lib/rules/consistent-return.js +1 -1
  32. package/lib/rules/constructor-super.js +3 -3
  33. package/lib/rules/curly.js +11 -7
  34. package/lib/rules/default-case.js +3 -3
  35. package/lib/rules/eqeqeq.js +15 -6
  36. package/lib/rules/func-call-spacing.js +12 -15
  37. package/lib/rules/func-name-matching.js +1 -1
  38. package/lib/rules/generator-star-spacing.js +18 -19
  39. package/lib/rules/global-require.js +2 -2
  40. package/lib/rules/id-blacklist.js +2 -2
  41. package/lib/rules/id-length.js +3 -3
  42. package/lib/rules/id-match.js +2 -2
  43. package/lib/rules/indent.js +21 -20
  44. package/lib/rules/key-spacing.js +20 -23
  45. package/lib/rules/keyword-spacing.js +2 -13
  46. package/lib/rules/line-comment-position.js +1 -1
  47. package/lib/rules/linebreak-style.js +7 -1
  48. package/lib/rules/lines-around-comment.js +4 -4
  49. package/lib/rules/lines-around-directive.js +4 -4
  50. package/lib/rules/max-lines.js +3 -3
  51. package/lib/rules/max-statements-per-line.js +8 -7
  52. package/lib/rules/new-cap.js +2 -2
  53. package/lib/rules/newline-after-var.js +7 -2
  54. package/lib/rules/newline-before-return.js +2 -2
  55. package/lib/rules/newline-per-chained-call.js +3 -1
  56. package/lib/rules/no-await-in-loop.js +5 -5
  57. package/lib/rules/no-cond-assign.js +3 -3
  58. package/lib/rules/no-dupe-keys.js +1 -1
  59. package/lib/rules/no-else-return.js +88 -25
  60. package/lib/rules/no-extend-native.js +3 -3
  61. package/lib/rules/no-extra-bind.js +3 -4
  62. package/lib/rules/no-extra-boolean-cast.js +22 -1
  63. package/lib/rules/no-extra-parens.js +57 -9
  64. package/lib/rules/no-inner-declarations.js +4 -4
  65. package/lib/rules/no-irregular-whitespace.js +7 -1
  66. package/lib/rules/no-lone-blocks.js +10 -10
  67. package/lib/rules/no-mixed-operators.js +1 -7
  68. package/lib/rules/no-mixed-requires.js +4 -4
  69. package/lib/rules/no-multi-assign.js +41 -0
  70. package/lib/rules/no-multi-spaces.js +4 -1
  71. package/lib/rules/no-multi-str.js +7 -3
  72. package/lib/rules/no-redeclare.js +7 -7
  73. package/lib/rules/no-return-assign.js +7 -14
  74. package/lib/rules/no-return-await.js +2 -2
  75. package/lib/rules/no-sequences.js +7 -6
  76. package/lib/rules/no-throw-literal.js +2 -39
  77. package/lib/rules/no-trailing-spaces.js +8 -2
  78. package/lib/rules/no-undefined.js +45 -6
  79. package/lib/rules/no-unexpected-multiline.js +9 -8
  80. package/lib/rules/no-unneeded-ternary.js +5 -1
  81. package/lib/rules/no-unused-labels.js +17 -2
  82. package/lib/rules/no-unused-vars.js +34 -19
  83. package/lib/rules/no-use-before-define.js +33 -29
  84. package/lib/rules/no-useless-computed-key.js +9 -4
  85. package/lib/rules/no-useless-concat.js +10 -7
  86. package/lib/rules/no-useless-escape.js +1 -1
  87. package/lib/rules/no-useless-return.js +5 -11
  88. package/lib/rules/no-var.js +69 -3
  89. package/lib/rules/no-whitespace-before-property.js +5 -16
  90. package/lib/rules/object-curly-newline.js +2 -2
  91. package/lib/rules/object-curly-spacing.js +7 -25
  92. package/lib/rules/object-property-newline.js +3 -3
  93. package/lib/rules/object-shorthand.js +10 -10
  94. package/lib/rules/operator-assignment.js +2 -2
  95. package/lib/rules/operator-linebreak.js +8 -10
  96. package/lib/rules/padded-blocks.js +4 -4
  97. package/lib/rules/prefer-promise-reject-errors.js +124 -0
  98. package/lib/rules/prefer-spread.js +1 -1
  99. package/lib/rules/prefer-template.js +1 -1
  100. package/lib/rules/quotes.js +11 -7
  101. package/lib/rules/require-await.js +1 -1
  102. package/lib/rules/semi-spacing.js +4 -0
  103. package/lib/rules/sort-imports.js +4 -4
  104. package/lib/rules/sort-keys.js +2 -2
  105. package/lib/rules/sort-vars.js +2 -2
  106. package/lib/rules/space-before-function-paren.js +9 -6
  107. package/lib/rules/space-in-parens.js +8 -8
  108. package/lib/rules/spaced-comment.js +10 -10
  109. package/lib/rules/strict.js +2 -2
  110. package/lib/rules/template-tag-spacing.js +77 -0
  111. package/lib/rules/unicode-bom.js +1 -1
  112. package/lib/rules/wrap-iife.js +5 -5
  113. package/lib/rules/yoda.js +2 -7
  114. package/lib/rules.js +2 -2
  115. package/lib/testers/rule-tester.js +28 -21
  116. package/lib/token-store/backward-token-comment-cursor.js +57 -0
  117. package/lib/token-store/backward-token-cursor.js +56 -0
  118. package/lib/token-store/cursor.js +76 -0
  119. package/lib/token-store/cursors.js +92 -0
  120. package/lib/token-store/decorative-cursor.js +39 -0
  121. package/lib/token-store/filter-cursor.js +43 -0
  122. package/lib/token-store/forward-token-comment-cursor.js +57 -0
  123. package/lib/token-store/forward-token-cursor.js +61 -0
  124. package/lib/token-store/index.js +604 -0
  125. package/lib/token-store/limit-cursor.js +40 -0
  126. package/lib/token-store/padded-token-cursor.js +38 -0
  127. package/lib/token-store/skip-cursor.js +42 -0
  128. package/lib/token-store/utils.js +100 -0
  129. package/lib/util/comment-event-generator.js +17 -16
  130. package/lib/util/glob-util.js +1 -1
  131. package/lib/util/glob.js +1 -1
  132. package/lib/util/rule-fixer.js +3 -8
  133. package/lib/util/source-code-fixer.js +41 -45
  134. package/lib/util/source-code.js +35 -19
  135. package/messages/extend-config-missing.txt +3 -0
  136. package/package.json +3 -3
  137. package/lib/token-store.js +0 -203
@@ -5,6 +5,8 @@
5
5
 
6
6
  "use strict";
7
7
 
8
+ const astUtils = require("../ast-utils");
9
+
8
10
  //------------------------------------------------------------------------------
9
11
  // Rule Definition
10
12
  //------------------------------------------------------------------------------
@@ -52,215 +54,86 @@ module.exports = {
52
54
  //--------------------------------------------------------------------------
53
55
 
54
56
  /**
55
- * Determines if a given node is a block statement.
56
- * @param {ASTNode} node The node to check.
57
- * @returns {boolean} True if the node is a block statement, false if not.
58
- * @private
59
- */
60
- function isBlock(node) {
61
- return node && node.type === "BlockStatement";
62
- }
63
-
64
- /**
65
- * Check if the token is an punctuator with a value of curly brace
66
- * @param {Object} token - Token to check
67
- * @returns {boolean} true if its a curly punctuator
68
- * @private
69
- */
70
- function isCurlyPunctuator(token) {
71
- return token.value === "{" || token.value === "}";
72
- }
73
-
74
- /**
75
- * Reports a place where a newline unexpectedly appears
76
- * @param {ASTNode} node The node to report
77
- * @param {string} message The message to report
57
+ * Fixes a place where a newline unexpectedly appears
78
58
  * @param {Token} firstToken The token before the unexpected newline
79
- * @returns {void}
59
+ * @param {Token} secondToken The token after the unexpected newline
60
+ * @returns {Function} A fixer function to remove the newlines between the tokens
80
61
  */
81
- function reportExtraNewline(node, message, firstToken) {
82
- context.report({
83
- node,
84
- message,
85
- fix(fixer) {
86
- const secondToken = sourceCode.getTokenAfter(firstToken);
87
- const textBetween = sourceCode.getText().slice(firstToken.range[1], secondToken.range[0]);
88
- const NEWLINE_REGEX = /\r\n|\r|\n|\u2028|\u2029/g;
62
+ function removeNewlineBetween(firstToken, secondToken) {
63
+ const textRange = [firstToken.range[1], secondToken.range[0]];
64
+ const textBetween = sourceCode.text.slice(textRange[0], textRange[1]);
65
+ const NEWLINE_REGEX = astUtils.createGlobalLinebreakMatcher();
89
66
 
90
- // Don't do a fix if there is a comment between the tokens.
91
- return textBetween.trim() ? null : fixer.replaceTextRange([firstToken.range[1], secondToken.range[0]], textBetween.replace(NEWLINE_REGEX, ""));
92
- }
93
- });
67
+ // Don't do a fix if there is a comment between the tokens
68
+ return fixer => fixer.replaceTextRange(textRange, textBetween.trim() ? null : textBetween.replace(NEWLINE_REGEX, ""));
94
69
  }
95
70
 
96
71
  /**
97
- * Binds a list of properties to a function that verifies that the opening
98
- * curly brace is on the same line as its controlling statement of a given
99
- * node.
100
- * @param {...string} The properties to check on the node.
101
- * @returns {Function} A function that will perform the check on a node
102
- * @private
103
- */
104
- function checkBlock() {
105
- const blockProperties = arguments;
106
-
107
- return function(node) {
108
- Array.prototype.forEach.call(blockProperties, blockProp => {
109
- const block = node[blockProp];
110
-
111
- if (!isBlock(block)) {
112
- return;
113
- }
114
-
115
- const previousToken = sourceCode.getTokenBefore(block);
116
- const curlyToken = sourceCode.getFirstToken(block);
117
- const curlyTokenEnd = sourceCode.getLastToken(block);
118
- const allOnSameLine = previousToken.loc.start.line === curlyTokenEnd.loc.start.line;
119
-
120
- if (allOnSameLine && params.allowSingleLine) {
121
- return;
122
- }
123
-
124
- if (style !== "allman" && previousToken.loc.start.line !== curlyToken.loc.start.line) {
125
- reportExtraNewline(node, OPEN_MESSAGE, previousToken);
126
- } else if (style === "allman" && previousToken.loc.start.line === curlyToken.loc.start.line) {
127
- context.report({
128
- node,
129
- message: OPEN_MESSAGE_ALLMAN,
130
- fix: fixer => fixer.insertTextBefore(curlyToken, "\n")
131
- });
132
- }
133
-
134
- if (!block.body.length) {
135
- return;
136
- }
137
-
138
- if (curlyToken.loc.start.line === block.body[0].loc.start.line) {
139
- context.report({
140
- node: block.body[0],
141
- message: BODY_MESSAGE,
142
- fix: fixer => fixer.insertTextAfter(curlyToken, "\n")
143
- });
144
- }
72
+ * Validates a pair of curly brackets based on the user's config
73
+ * @param {Token} openingCurly The opening curly bracket
74
+ * @param {Token} closingCurly The closing curly bracket
75
+ * @returns {void}
76
+ */
77
+ function validateCurlyPair(openingCurly, closingCurly) {
78
+ const tokenBeforeOpeningCurly = sourceCode.getTokenBefore(openingCurly);
79
+ const tokenAfterOpeningCurly = sourceCode.getTokenAfter(openingCurly);
80
+ const tokenBeforeClosingCurly = sourceCode.getTokenBefore(closingCurly);
81
+ const singleLineException = params.allowSingleLine && astUtils.isTokenOnSameLine(openingCurly, closingCurly);
145
82
 
146
- if (curlyTokenEnd.loc.start.line === block.body[block.body.length - 1].loc.end.line) {
147
- context.report({
148
- node: block.body[block.body.length - 1],
149
- message: CLOSE_MESSAGE_SINGLE,
150
- fix: fixer => fixer.insertTextBefore(curlyTokenEnd, "\n")
151
- });
152
- }
83
+ if (style !== "allman" && !astUtils.isTokenOnSameLine(tokenBeforeOpeningCurly, openingCurly)) {
84
+ context.report({
85
+ node: openingCurly,
86
+ message: OPEN_MESSAGE,
87
+ fix: removeNewlineBetween(tokenBeforeOpeningCurly, openingCurly)
153
88
  });
154
- };
155
- }
156
-
157
- /**
158
- * Enforces the configured brace style on IfStatements
159
- * @param {ASTNode} node An IfStatement node.
160
- * @returns {void}
161
- * @private
162
- */
163
- function checkIfStatement(node) {
164
- checkBlock("consequent", "alternate")(node);
165
-
166
- if (node.alternate) {
167
-
168
- const tokens = sourceCode.getTokensBefore(node.alternate, 2);
169
-
170
- if (style === "1tbs") {
171
- if (tokens[0].loc.start.line !== tokens[1].loc.start.line &&
172
- node.consequent.type === "BlockStatement" &&
173
- isCurlyPunctuator(tokens[0])) {
174
- reportExtraNewline(node.alternate, CLOSE_MESSAGE, tokens[0]);
175
- }
176
- } else if (tokens[0].loc.start.line === tokens[1].loc.start.line) {
177
- context.report({
178
- node: node.alternate,
179
- message: CLOSE_MESSAGE_STROUSTRUP_ALLMAN,
180
- fix: fixer => fixer.insertTextAfter(tokens[0], "\n")
181
- });
182
- }
183
-
184
89
  }
185
- }
186
-
187
- /**
188
- * Enforces the configured brace style on TryStatements
189
- * @param {ASTNode} node A TryStatement node.
190
- * @returns {void}
191
- * @private
192
- */
193
- function checkTryStatement(node) {
194
- checkBlock("block", "finalizer")(node);
195
90
 
196
- if (isBlock(node.finalizer)) {
197
- const tokens = sourceCode.getTokensBefore(node.finalizer, 2);
198
-
199
- if (style === "1tbs") {
200
- if (tokens[0].loc.start.line !== tokens[1].loc.start.line) {
201
- reportExtraNewline(node.finalizer, CLOSE_MESSAGE, tokens[0]);
202
- }
203
- } else if (tokens[0].loc.start.line === tokens[1].loc.start.line) {
204
- context.report({
205
- node: node.finalizer,
206
- message: CLOSE_MESSAGE_STROUSTRUP_ALLMAN,
207
- fix: fixer => fixer.insertTextAfter(tokens[0], "\n")
208
- });
209
- }
91
+ if (style === "allman" && astUtils.isTokenOnSameLine(tokenBeforeOpeningCurly, openingCurly) && !singleLineException) {
92
+ context.report({
93
+ node: openingCurly,
94
+ message: OPEN_MESSAGE_ALLMAN,
95
+ fix: fixer => fixer.insertTextBefore(openingCurly, "\n")
96
+ });
210
97
  }
211
- }
212
-
213
- /**
214
- * Enforces the configured brace style on CatchClauses
215
- * @param {ASTNode} node A CatchClause node.
216
- * @returns {void}
217
- * @private
218
- */
219
- function checkCatchClause(node) {
220
- const previousToken = sourceCode.getTokenBefore(node),
221
- firstToken = sourceCode.getFirstToken(node);
222
98
 
223
- checkBlock("body")(node);
99
+ if (astUtils.isTokenOnSameLine(openingCurly, tokenAfterOpeningCurly) && tokenAfterOpeningCurly !== closingCurly && !singleLineException) {
100
+ context.report({
101
+ node: openingCurly,
102
+ message: BODY_MESSAGE,
103
+ fix: fixer => fixer.insertTextAfter(openingCurly, "\n")
104
+ });
105
+ }
224
106
 
225
- if (isBlock(node.body)) {
226
- if (style === "1tbs") {
227
- if (previousToken.loc.start.line !== firstToken.loc.start.line) {
228
- reportExtraNewline(node, CLOSE_MESSAGE, previousToken);
229
- }
230
- } else {
231
- if (previousToken.loc.start.line === firstToken.loc.start.line) {
232
- context.report({
233
- node,
234
- message: CLOSE_MESSAGE_STROUSTRUP_ALLMAN,
235
- fix: fixer => fixer.insertTextAfter(previousToken, "\n")
236
- });
237
- }
238
- }
107
+ if (tokenBeforeClosingCurly !== openingCurly && !singleLineException && astUtils.isTokenOnSameLine(tokenBeforeClosingCurly, closingCurly)) {
108
+ context.report({
109
+ node: closingCurly,
110
+ message: CLOSE_MESSAGE_SINGLE,
111
+ fix: fixer => fixer.insertTextBefore(closingCurly, "\n")
112
+ });
239
113
  }
240
114
  }
241
115
 
242
116
  /**
243
- * Enforces the configured brace style on SwitchStatements
244
- * @param {ASTNode} node A SwitchStatement node.
245
- * @returns {void}
246
- * @private
247
- */
248
- function checkSwitchStatement(node) {
249
- let tokens;
117
+ * Validates the location of a token that appears before a keyword (e.g. a newline before `else`)
118
+ * @param {Token} curlyToken The closing curly token. This is assumed to precede a keyword token (such as `else` or `finally`).
119
+ * @returns {void}
120
+ */
121
+ function validateCurlyBeforeKeyword(curlyToken) {
122
+ const keywordToken = sourceCode.getTokenAfter(curlyToken);
250
123
 
251
- if (node.cases && node.cases.length) {
252
- tokens = sourceCode.getTokensBefore(node.cases[0], 2);
253
- } else {
254
- tokens = sourceCode.getLastTokens(node, 3);
124
+ if (style === "1tbs" && !astUtils.isTokenOnSameLine(curlyToken, keywordToken)) {
125
+ context.report({
126
+ node: curlyToken,
127
+ message: CLOSE_MESSAGE,
128
+ fix: removeNewlineBetween(curlyToken, keywordToken)
129
+ });
255
130
  }
256
131
 
257
- if (style !== "allman" && tokens[0].loc.start.line !== tokens[1].loc.start.line) {
258
- reportExtraNewline(node, OPEN_MESSAGE, tokens[0]);
259
- } else if (style === "allman" && tokens[0].loc.start.line === tokens[1].loc.start.line) {
132
+ if (style !== "1tbs" && astUtils.isTokenOnSameLine(curlyToken, keywordToken)) {
260
133
  context.report({
261
- node,
262
- message: OPEN_MESSAGE_ALLMAN,
263
- fix: fixer => fixer.insertTextBefore(tokens[1], "\n")
134
+ node: curlyToken,
135
+ message: CLOSE_MESSAGE_STROUSTRUP_ALLMAN,
136
+ fix: fixer => fixer.insertTextAfter(curlyToken, "\n")
264
137
  });
265
138
  }
266
139
  }
@@ -270,20 +143,38 @@ module.exports = {
270
143
  //--------------------------------------------------------------------------
271
144
 
272
145
  return {
273
- FunctionDeclaration: checkBlock("body"),
274
- FunctionExpression: checkBlock("body"),
275
- ArrowFunctionExpression: checkBlock("body"),
276
- IfStatement: checkIfStatement,
277
- TryStatement: checkTryStatement,
278
- CatchClause: checkCatchClause,
279
- DoWhileStatement: checkBlock("body"),
280
- WhileStatement: checkBlock("body"),
281
- WithStatement: checkBlock("body"),
282
- ForStatement: checkBlock("body"),
283
- ForInStatement: checkBlock("body"),
284
- ForOfStatement: checkBlock("body"),
285
- SwitchStatement: checkSwitchStatement
286
- };
146
+ BlockStatement(node) {
147
+ if (!astUtils.STATEMENT_LIST_PARENTS.has(node.parent.type)) {
148
+ validateCurlyPair(sourceCode.getFirstToken(node), sourceCode.getLastToken(node));
149
+ }
150
+ },
151
+ ClassBody(node) {
152
+ validateCurlyPair(sourceCode.getFirstToken(node), sourceCode.getLastToken(node));
153
+ },
154
+ SwitchStatement(node) {
155
+ const closingCurly = sourceCode.getLastToken(node);
156
+ const openingCurly = sourceCode.getTokenBefore(node.cases.length ? node.cases[0] : closingCurly);
157
+
158
+ validateCurlyPair(openingCurly, closingCurly);
159
+ },
160
+ IfStatement(node) {
161
+ if (node.consequent.type === "BlockStatement" && node.alternate) {
162
+
163
+ // Handle the keyword after the `if` block (before `else`)
164
+ validateCurlyBeforeKeyword(sourceCode.getLastToken(node.consequent));
165
+ }
166
+ },
167
+ TryStatement(node) {
168
+
169
+ // Handle the keyword after the `try` block (before `catch` or `finally`)
170
+ validateCurlyBeforeKeyword(sourceCode.getLastToken(node.block));
287
171
 
172
+ if (node.handler && node.finalizer) {
173
+
174
+ // Handle the keyword after the `catch` block (before `finally`)
175
+ validateCurlyBeforeKeyword(sourceCode.getLastToken(node.handler.body));
176
+ }
177
+ }
178
+ };
288
179
  }
289
180
  };
@@ -163,8 +163,8 @@ module.exports = {
163
163
  * otherwise.
164
164
  */
165
165
  function isInlineComment(comment) {
166
- const previousToken = sourceCode.getTokenOrCommentBefore(comment),
167
- nextToken = sourceCode.getTokenOrCommentAfter(comment);
166
+ const previousToken = sourceCode.getTokenBefore(comment, { includeComments: true }),
167
+ nextToken = sourceCode.getTokenAfter(comment, { includeComments: true });
168
168
 
169
169
  return Boolean(
170
170
  previousToken &&
@@ -181,7 +181,7 @@ module.exports = {
181
181
  * @returns {boolean} True if the comment follows a valid comment.
182
182
  */
183
183
  function isConsecutiveComment(comment) {
184
- const previousTokenOrComment = sourceCode.getTokenOrCommentBefore(comment);
184
+ const previousTokenOrComment = sourceCode.getTokenBefore(comment, { includeComments: true });
185
185
 
186
186
  return Boolean(
187
187
  previousTokenOrComment &&
@@ -264,9 +264,9 @@ module.exports = {
264
264
  commentValid = isCommentValid(comment, options);
265
265
 
266
266
  if (!commentValid) {
267
- const message = capitalize === "always" ?
268
- ALWAYS_MESSAGE :
269
- NEVER_MESSAGE;
267
+ const message = capitalize === "always"
268
+ ? ALWAYS_MESSAGE
269
+ : NEVER_MESSAGE;
270
270
 
271
271
  context.report({
272
272
  node: null, // Intentionally using loc instead
@@ -20,7 +20,7 @@ const DEFAULT_OPTIONS = Object.freeze({
20
20
  objects: "never",
21
21
  imports: "never",
22
22
  exports: "never",
23
- functions: "ignore",
23
+ functions: "ignore"
24
24
  });
25
25
 
26
26
  /**
@@ -53,7 +53,7 @@ function normalizeOptions(optionValue) {
53
53
  exports: optionValue,
54
54
 
55
55
  // For backward compatibility, always ignore functions.
56
- functions: "ignore",
56
+ functions: "ignore"
57
57
  };
58
58
  }
59
59
  if (typeof optionValue === "object" && optionValue !== null) {
@@ -62,7 +62,7 @@ function normalizeOptions(optionValue) {
62
62
  objects: optionValue.objects || DEFAULT_OPTIONS.objects,
63
63
  imports: optionValue.imports || DEFAULT_OPTIONS.imports,
64
64
  exports: optionValue.exports || DEFAULT_OPTIONS.exports,
65
- functions: optionValue.functions || DEFAULT_OPTIONS.functions,
65
+ functions: optionValue.functions || DEFAULT_OPTIONS.functions
66
66
  };
67
67
  }
68
68
 
@@ -121,7 +121,7 @@ module.exports = {
121
121
  additionalProperties: false
122
122
  }
123
123
  ]
124
- },
124
+ }
125
125
  ]
126
126
  },
127
127
 
@@ -312,7 +312,7 @@ module.exports = {
312
312
  "always-multiline": forceTrailingCommaIfMultiline,
313
313
  "only-multiline": allowTrailingCommaIfMultiline,
314
314
  never: forbidTrailingComma,
315
- ignore: lodash.noop,
315
+ ignore: lodash.noop
316
316
  };
317
317
 
318
318
  return {
@@ -330,7 +330,7 @@ module.exports = {
330
330
  FunctionExpression: predicate[options.functions],
331
331
  ArrowFunctionExpression: predicate[options.functions],
332
332
  CallExpression: predicate[options.functions],
333
- NewExpression: predicate[options.functions],
333
+ NewExpression: predicate[options.functions]
334
334
  };
335
335
  }
336
336
  };
@@ -78,27 +78,27 @@ module.exports = {
78
78
  if (options[dir]) {
79
79
  if (dir === "before") {
80
80
  return fixer.insertTextBefore(node, " ");
81
- } else {
82
- return fixer.insertTextAfter(node, " ");
83
81
  }
84
- } else {
85
- let start, end;
86
- const newText = "";
82
+ return fixer.insertTextAfter(node, " ");
87
83
 
88
- if (dir === "before") {
89
- start = otherNode.range[1];
90
- end = node.range[0];
91
- } else {
92
- start = node.range[1];
93
- end = otherNode.range[0];
94
- }
84
+ }
85
+ let start, end;
86
+ const newText = "";
95
87
 
96
- return fixer.replaceTextRange([start, end], newText);
88
+ if (dir === "before") {
89
+ start = otherNode.range[1];
90
+ end = node.range[0];
91
+ } else {
92
+ start = node.range[1];
93
+ end = otherNode.range[0];
97
94
  }
95
+
96
+ return fixer.replaceTextRange([start, end], newText);
97
+
98
98
  },
99
- message: options[dir] ?
100
- "A space is required {{dir}} ','." :
101
- "There should be no space {{dir}} ','.",
99
+ message: options[dir]
100
+ ? "A space is required {{dir}} ','."
101
+ : "There should be no space {{dir}} ','.",
102
102
  data: {
103
103
  dir
104
104
  }
@@ -48,7 +48,7 @@ module.exports = {
48
48
  FunctionDeclaration: true,
49
49
  FunctionExpression: true,
50
50
  ImportDeclaration: true,
51
- ObjectPattern: true,
51
+ ObjectPattern: true
52
52
  };
53
53
 
54
54
  if (context.options.length === 2 && context.options[1].hasOwnProperty("exceptions")) {
@@ -104,7 +104,7 @@ module.exports = {
104
104
  } else if (node.type === "ArrowFunctionExpression") {
105
105
 
106
106
  // `=>` token
107
- loc = context.getSourceCode().getTokenBefore(node.body).loc.start;
107
+ loc = context.getSourceCode().getTokenBefore(node.body, astUtils.isArrowToken).loc.start;
108
108
  type = "function";
109
109
  } else if (
110
110
  node.parent.type === "MethodDefinition" ||
@@ -209,9 +209,9 @@ module.exports = {
209
209
 
210
210
  if (!calledInEveryPaths) {
211
211
  context.report({
212
- message: calledInSomePaths ?
213
- "Lacked a call of 'super()' in some code paths." :
214
- "Expected to call 'super()'.",
212
+ message: calledInSomePaths
213
+ ? "Lacked a call of 'super()' in some code paths."
214
+ : "Expected to call 'super()'.",
215
215
  node: node.parent
216
216
  });
217
217
  }
@@ -94,19 +94,23 @@ module.exports = {
94
94
  return first.loc.start.line === last.loc.end.line;
95
95
  }
96
96
 
97
+ /**
98
+ * Checks if the given token is an `else` token or not.
99
+ *
100
+ * @param {Token} token - The token to check.
101
+ * @returns {boolean} `true` if the token is an `else` token.
102
+ */
103
+ function isElseKeywordToken(token) {
104
+ return token.value === "else" && token.type === "Keyword";
105
+ }
106
+
97
107
  /**
98
108
  * Gets the `else` keyword token of a given `IfStatement` node.
99
109
  * @param {ASTNode} node - A `IfStatement` node to get.
100
110
  * @returns {Token} The `else` keyword token.
101
111
  */
102
112
  function getElseKeyword(node) {
103
- let token = sourceCode.getTokenAfter(node.consequent);
104
-
105
- while (token.type !== "Keyword" || token.value !== "else") {
106
- token = sourceCode.getTokenAfter(token);
107
- }
108
-
109
- return token;
113
+ return node.alternate && sourceCode.getFirstTokenBetween(node.consequent, node.alternate, isElseKeywordToken);
110
114
  }
111
115
 
112
116
  /**
@@ -31,9 +31,9 @@ module.exports = {
31
31
 
32
32
  create(context) {
33
33
  const options = context.options[0] || {};
34
- const commentPattern = options.commentPattern ?
35
- new RegExp(options.commentPattern) :
36
- DEFAULT_COMMENT_PATTERN;
34
+ const commentPattern = options.commentPattern
35
+ ? new RegExp(options.commentPattern)
36
+ : DEFAULT_COMMENT_PATTERN;
37
37
 
38
38
  const sourceCode = context.getSourceCode();
39
39
 
@@ -5,6 +5,12 @@
5
5
 
6
6
  "use strict";
7
7
 
8
+ //------------------------------------------------------------------------------
9
+ // Requirements
10
+ //------------------------------------------------------------------------------
11
+
12
+ const astUtils = require("../ast-utils");
13
+
8
14
  //------------------------------------------------------------------------------
9
15
  // Rule Definition
10
16
  //------------------------------------------------------------------------------
@@ -57,9 +63,9 @@ module.exports = {
57
63
  const options = context.options[1] || {};
58
64
  const sourceCode = context.getSourceCode();
59
65
 
60
- const nullOption = (config === "always") ?
61
- options.null || "always" :
62
- "ignore";
66
+ const nullOption = (config === "always")
67
+ ? options.null || "always"
68
+ : "ignore";
63
69
  const enforceRuleForNull = (nullOption === "always");
64
70
  const enforceInverseRuleForNull = (nullOption === "never");
65
71
 
@@ -100,8 +106,7 @@ module.exports = {
100
106
  * @private
101
107
  */
102
108
  function isNullCheck(node) {
103
- return (node.right.type === "Literal" && node.right.value === null) ||
104
- (node.left.type === "Literal" && node.left.value === null);
109
+ return astUtils.isNullLiteral(node.right) || astUtils.isNullLiteral(node.left);
105
110
  }
106
111
 
107
112
  /**
@@ -134,7 +139,11 @@ module.exports = {
134
139
 
135
140
  // If the comparison is a `typeof` comparison or both sides are literals with the same type, then it's safe to fix.
136
141
  if (isTypeOfBinary(node) || areLiteralsAndSameType(node)) {
137
- const operatorToken = sourceCode.getTokensBetween(node.left, node.right).find(token => token.value === node.operator);
142
+ const operatorToken = sourceCode.getFirstTokenBetween(
143
+ node.left,
144
+ node.right,
145
+ token => token.value === node.operator
146
+ );
138
147
 
139
148
  return fixer.replaceText(operatorToken, expectedOperator);
140
149
  }
@@ -5,6 +5,12 @@
5
5
 
6
6
  "use strict";
7
7
 
8
+ //------------------------------------------------------------------------------
9
+ // Requirements
10
+ //------------------------------------------------------------------------------
11
+
12
+ const astUtils = require("../ast-utils");
13
+
8
14
  //------------------------------------------------------------------------------
9
15
  // Rule Definition
10
16
  //------------------------------------------------------------------------------
@@ -67,28 +73,19 @@ module.exports = {
67
73
  * @private
68
74
  */
69
75
  function checkSpacing(node) {
76
+ const lastToken = sourceCode.getLastToken(node);
70
77
  const lastCalleeToken = sourceCode.getLastToken(node.callee);
71
- let prevToken = lastCalleeToken;
72
- let parenToken = sourceCode.getTokenAfter(lastCalleeToken);
73
-
74
- // advances to an open parenthesis.
75
- while (
76
- parenToken &&
77
- parenToken.range[1] < node.range[1] &&
78
- parenToken.value !== "("
79
- ) {
80
- prevToken = parenToken;
81
- parenToken = sourceCode.getTokenAfter(parenToken);
82
- }
78
+ const parenToken = sourceCode.getFirstTokenBetween(lastCalleeToken, lastToken, astUtils.isOpeningParenToken);
79
+ const prevToken = parenToken && sourceCode.getTokenBefore(parenToken);
83
80
 
84
81
  // Parens in NewExpression are optional
85
82
  if (!(parenToken && parenToken.range[1] < node.range[1])) {
86
83
  return;
87
84
  }
88
85
 
89
- const hasWhitespace = sourceCode.isSpaceBetweenTokens(prevToken, parenToken);
90
- const hasNewline = hasWhitespace &&
91
- /\n/.test(text.slice(prevToken.range[1], parenToken.range[0]).replace(/\/\*.*?\*\//g, ""));
86
+ const textBetweenTokens = text.slice(prevToken.range[1], parenToken.range[0]).replace(/\/\*.*?\*\//g, "");
87
+ const hasWhitespace = /\s/.test(textBetweenTokens);
88
+ const hasNewline = hasWhitespace && astUtils.LINEBREAK_MATCHER.test(textBetweenTokens);
92
89
 
93
90
  /*
94
91
  * never allowNewlines hasWhitespace hasNewline message
@@ -155,7 +155,7 @@ module.exports = {
155
155
  return;
156
156
  }
157
157
 
158
- const isProp = node.left.type === "MemberExpression" ? true : false;
158
+ const isProp = node.left.type === "MemberExpression";
159
159
  const name = isProp ? astUtils.getStaticPropertyName(node.left) : node.left.name;
160
160
 
161
161
  if (node.right.id && isIdentifier(name) && shouldWarn(name, node.right.id.name)) {