eslint 3.14.0 → 3.16.1

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 (119) hide show
  1. package/CHANGELOG.md +68 -0
  2. package/README.md +1 -1
  3. package/conf/{eslint.json → eslint-recommended.js} +86 -71
  4. package/lib/ast-utils.js +192 -24
  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 +3 -3
  8. package/lib/config/config-file.js +31 -24
  9. package/lib/config/config-initializer.js +1 -1
  10. package/lib/config/config-validator.js +6 -6
  11. package/lib/config.js +3 -2
  12. package/lib/eslint.js +18 -18
  13. package/lib/formatters/checkstyle.js +2 -2
  14. package/lib/formatters/compact.js +2 -2
  15. package/lib/formatters/junit.js +2 -2
  16. package/lib/formatters/tap.js +2 -2
  17. package/lib/formatters/unix.js +2 -2
  18. package/lib/formatters/visualstudio.js +2 -2
  19. package/lib/rules/arrow-body-style.js +7 -4
  20. package/lib/rules/arrow-spacing.js +7 -6
  21. package/lib/rules/block-spacing.js +2 -2
  22. package/lib/rules/brace-style.js +42 -22
  23. package/lib/rules/capitalized-comments.js +6 -6
  24. package/lib/rules/comma-spacing.js +16 -16
  25. package/lib/rules/consistent-return.js +1 -1
  26. package/lib/rules/constructor-super.js +3 -3
  27. package/lib/rules/curly.js +11 -7
  28. package/lib/rules/default-case.js +3 -3
  29. package/lib/rules/eqeqeq.js +15 -6
  30. package/lib/rules/func-call-spacing.js +10 -13
  31. package/lib/rules/func-name-matching.js +1 -1
  32. package/lib/rules/generator-star-spacing.js +18 -19
  33. package/lib/rules/global-require.js +2 -2
  34. package/lib/rules/id-blacklist.js +2 -2
  35. package/lib/rules/id-length.js +3 -3
  36. package/lib/rules/id-match.js +2 -2
  37. package/lib/rules/indent.js +21 -20
  38. package/lib/rules/key-spacing.js +20 -23
  39. package/lib/rules/keyword-spacing.js +2 -13
  40. package/lib/rules/line-comment-position.js +1 -1
  41. package/lib/rules/linebreak-style.js +7 -1
  42. package/lib/rules/lines-around-comment.js +4 -4
  43. package/lib/rules/lines-around-directive.js +3 -3
  44. package/lib/rules/max-lines.js +2 -2
  45. package/lib/rules/max-statements-per-line.js +7 -6
  46. package/lib/rules/new-cap.js +2 -2
  47. package/lib/rules/newline-after-var.js +7 -2
  48. package/lib/rules/newline-before-return.js +2 -2
  49. package/lib/rules/newline-per-chained-call.js +3 -1
  50. package/lib/rules/no-cond-assign.js +3 -3
  51. package/lib/rules/no-extend-native.js +3 -3
  52. package/lib/rules/no-extra-bind.js +3 -4
  53. package/lib/rules/no-extra-boolean-cast.js +8 -0
  54. package/lib/rules/no-extra-parens.js +29 -8
  55. package/lib/rules/no-inner-declarations.js +4 -4
  56. package/lib/rules/no-irregular-whitespace.js +7 -1
  57. package/lib/rules/no-lone-blocks.js +10 -10
  58. package/lib/rules/no-mixed-operators.js +1 -7
  59. package/lib/rules/no-mixed-requires.js +4 -4
  60. package/lib/rules/no-multi-spaces.js +4 -1
  61. package/lib/rules/no-multi-str.js +7 -3
  62. package/lib/rules/no-redeclare.js +7 -7
  63. package/lib/rules/no-return-assign.js +7 -14
  64. package/lib/rules/no-sequences.js +7 -6
  65. package/lib/rules/no-trailing-spaces.js +8 -2
  66. package/lib/rules/no-undefined.js +45 -6
  67. package/lib/rules/no-unexpected-multiline.js +9 -8
  68. package/lib/rules/no-unneeded-ternary.js +5 -1
  69. package/lib/rules/no-unused-labels.js +17 -2
  70. package/lib/rules/no-unused-vars.js +34 -19
  71. package/lib/rules/no-use-before-define.js +33 -29
  72. package/lib/rules/no-useless-computed-key.js +8 -3
  73. package/lib/rules/no-useless-concat.js +10 -7
  74. package/lib/rules/no-useless-escape.js +1 -1
  75. package/lib/rules/no-useless-return.js +1 -7
  76. package/lib/rules/no-var.js +11 -0
  77. package/lib/rules/no-whitespace-before-property.js +5 -16
  78. package/lib/rules/object-curly-newline.js +2 -2
  79. package/lib/rules/object-curly-spacing.js +7 -25
  80. package/lib/rules/object-property-newline.js +3 -3
  81. package/lib/rules/object-shorthand.js +10 -10
  82. package/lib/rules/operator-assignment.js +2 -2
  83. package/lib/rules/operator-linebreak.js +8 -10
  84. package/lib/rules/padded-blocks.js +7 -4
  85. package/lib/rules/prefer-spread.js +1 -1
  86. package/lib/rules/prefer-template.js +1 -1
  87. package/lib/rules/quotes.js +10 -6
  88. package/lib/rules/semi-spacing.js +4 -0
  89. package/lib/rules/sort-imports.js +4 -4
  90. package/lib/rules/sort-vars.js +2 -2
  91. package/lib/rules/space-before-function-paren.js +8 -5
  92. package/lib/rules/space-in-parens.js +8 -8
  93. package/lib/rules/spaced-comment.js +10 -10
  94. package/lib/rules/strict.js +2 -2
  95. package/lib/rules/template-tag-spacing.js +77 -0
  96. package/lib/rules/unicode-bom.js +1 -1
  97. package/lib/rules/wrap-iife.js +5 -5
  98. package/lib/rules/yoda.js +2 -7
  99. package/lib/rules.js +2 -2
  100. package/lib/testers/rule-tester.js +25 -18
  101. package/lib/token-store/backward-token-comment-cursor.js +57 -0
  102. package/lib/token-store/backward-token-cursor.js +56 -0
  103. package/lib/token-store/cursor.js +76 -0
  104. package/lib/token-store/cursors.js +92 -0
  105. package/lib/token-store/decorative-cursor.js +39 -0
  106. package/lib/token-store/filter-cursor.js +43 -0
  107. package/lib/token-store/forward-token-comment-cursor.js +57 -0
  108. package/lib/token-store/forward-token-cursor.js +61 -0
  109. package/lib/token-store/index.js +604 -0
  110. package/lib/token-store/limit-cursor.js +40 -0
  111. package/lib/token-store/padded-token-cursor.js +38 -0
  112. package/lib/token-store/skip-cursor.js +42 -0
  113. package/lib/token-store/utils.js +100 -0
  114. package/lib/util/glob.js +1 -1
  115. package/lib/util/source-code-fixer.js +46 -44
  116. package/lib/util/source-code.js +35 -19
  117. package/messages/extend-config-missing.txt +3 -0
  118. package/package.json +3 -3
  119. package/lib/token-store.js +0 -203
@@ -23,7 +23,7 @@ const fs = require("fs"),
23
23
  stripBom = require("strip-bom"),
24
24
  stripComments = require("strip-json-comments"),
25
25
  stringify = require("json-stable-stringify"),
26
- defaultOptions = require("../../conf/eslint.json"),
26
+ defaultOptions = require("../../conf/eslint-recommended"),
27
27
  requireUncached = require("require-uncached");
28
28
 
29
29
  const debug = require("debug")("eslint:config-file");
@@ -364,10 +364,10 @@ function applyExtends(config, filePath, relativeTo) {
364
364
  if (parentPath === "eslint:recommended") {
365
365
 
366
366
  /*
367
- * Add an explicit substitution for eslint:recommended to conf/eslint.json
368
- * this lets us use the eslint.json file as the recommended rules
367
+ * Add an explicit substitution for eslint:recommended to
368
+ * conf/eslint-recommended.js.
369
369
  */
370
- parentPath = path.resolve(__dirname, "../../conf/eslint.json");
370
+ parentPath = path.resolve(__dirname, "../../conf/eslint-recommended.js");
371
371
  } else if (parentPath === "eslint:all") {
372
372
 
373
373
  /*
@@ -380,9 +380,9 @@ function applyExtends(config, filePath, relativeTo) {
380
380
  * If the `extends` path is relative, use the directory of the current configuration
381
381
  * file as the reference point. Otherwise, use as-is.
382
382
  */
383
- parentPath = (!path.isAbsolute(parentPath) ?
384
- path.join(relativeTo || path.dirname(filePath), parentPath) :
385
- parentPath
383
+ parentPath = (!path.isAbsolute(parentPath)
384
+ ? path.join(relativeTo || path.dirname(filePath), parentPath)
385
+ : parentPath
386
386
  );
387
387
  }
388
388
 
@@ -390,6 +390,13 @@ function applyExtends(config, filePath, relativeTo) {
390
390
  debug(`Loading ${parentPath}`);
391
391
  return ConfigOps.merge(load(parentPath, false, relativeTo), previousValue);
392
392
  } catch (e) {
393
+ if (parentPath.indexOf("plugin:") === 0 || parentPath.indexOf("eslint:") === 0) {
394
+ e.message = `Failed to load config "${parentPath}" to extend from.`;
395
+ e.messageTemplate = "extend-config-missing";
396
+ e.messageData = {
397
+ configName: parentPath
398
+ };
399
+ }
393
400
 
394
401
  /*
395
402
  * If the file referenced by `extends` failed to load, add the path
@@ -461,24 +468,24 @@ function normalizePackageName(name, prefix) {
461
468
  function resolve(filePath, relativeTo) {
462
469
  if (isFilePath(filePath)) {
463
470
  return { filePath: path.resolve(relativeTo || "", filePath) };
464
- } else {
465
- let normalizedPackageName;
466
-
467
- if (filePath.indexOf("plugin:") === 0) {
468
- const packagePath = filePath.substr(7, filePath.lastIndexOf("/") - 7);
469
- const configName = filePath.substr(filePath.lastIndexOf("/") + 1, filePath.length - filePath.lastIndexOf("/") - 1);
470
-
471
- normalizedPackageName = normalizePackageName(packagePath, "eslint-plugin");
472
- debug(`Attempting to resolve ${normalizedPackageName}`);
473
- filePath = resolver.resolve(normalizedPackageName, getLookupPath(relativeTo));
474
- return { filePath, configName };
475
- } else {
476
- normalizedPackageName = normalizePackageName(filePath, "eslint-config");
477
- debug(`Attempting to resolve ${normalizedPackageName}`);
478
- filePath = resolver.resolve(normalizedPackageName, getLookupPath(relativeTo));
479
- return { filePath };
480
- }
481
471
  }
472
+ let normalizedPackageName;
473
+
474
+ if (filePath.indexOf("plugin:") === 0) {
475
+ const packagePath = filePath.substr(7, filePath.lastIndexOf("/") - 7);
476
+ const configName = filePath.substr(filePath.lastIndexOf("/") + 1, filePath.length - filePath.lastIndexOf("/") - 1);
477
+
478
+ normalizedPackageName = normalizePackageName(packagePath, "eslint-plugin");
479
+ debug(`Attempting to resolve ${normalizedPackageName}`);
480
+ filePath = resolver.resolve(normalizedPackageName, getLookupPath(relativeTo));
481
+ return { filePath, configName };
482
+ }
483
+ normalizedPackageName = normalizePackageName(filePath, "eslint-config");
484
+ debug(`Attempting to resolve ${normalizedPackageName}`);
485
+ filePath = resolver.resolve(normalizedPackageName, getLookupPath(relativeTo));
486
+ return { filePath };
487
+
488
+
482
489
 
483
490
  }
484
491
 
@@ -17,7 +17,7 @@ const util = require("util"),
17
17
  ConfigOps = require("./config-ops"),
18
18
  getSourceCodeOfFiles = require("../util/source-code-util").getSourceCodeOfFiles,
19
19
  npmUtil = require("../util/npm-util"),
20
- recConfig = require("../../conf/eslint.json"),
20
+ recConfig = require("../../conf/eslint-recommended"),
21
21
  log = require("../logging");
22
22
 
23
23
  const debug = require("debug")("eslint:config-initializer");
@@ -40,13 +40,13 @@ function getRuleOptionsSchema(id) {
40
40
  minItems: 0,
41
41
  maxItems: schema.length
42
42
  };
43
- } else {
44
- return {
45
- type: "array",
46
- minItems: 0,
47
- maxItems: 0
48
- };
49
43
  }
44
+ return {
45
+ type: "array",
46
+ minItems: 0,
47
+ maxItems: 0
48
+ };
49
+
50
50
  }
51
51
 
52
52
  // Given a full schema, leave it alone
package/lib/config.js CHANGED
@@ -234,8 +234,9 @@ class Config {
234
234
  }
235
235
 
236
236
  /**
237
- * Build a config object merging the base config (conf/eslint.json), the
238
- * environments config (conf/environments.js) and eventually the user config.
237
+ * Build a config object merging the base config (conf/eslint-recommended),
238
+ * the environments config (conf/environments.js) and eventually the user
239
+ * config.
239
240
  * @param {string} filePath a file in whose directory we start looking for a local config
240
241
  * @returns {Object} config object
241
242
  */
package/lib/eslint.js CHANGED
@@ -14,7 +14,7 @@ const assert = require("assert"),
14
14
  escope = require("escope"),
15
15
  levn = require("levn"),
16
16
  blankScriptAST = require("../conf/blank-script.json"),
17
- DEFAULT_PARSER = require("../conf/eslint.json").parser,
17
+ DEFAULT_PARSER = require("../conf/eslint-recommended").parser,
18
18
  replacements = require("../conf/replacements.json"),
19
19
  CodePathAnalyzer = require("./code-path-analysis/code-path-analyzer"),
20
20
  ConfigOps = require("./config/config-ops"),
@@ -528,9 +528,9 @@ function createStubRule(message) {
528
528
 
529
529
  if (message) {
530
530
  return createRuleModule;
531
- } else {
532
- throw new Error("No message passed to stub rule");
533
531
  }
532
+ throw new Error("No message passed to stub rule");
533
+
534
534
  }
535
535
 
536
536
  /**
@@ -660,9 +660,9 @@ module.exports = (function() {
660
660
  try {
661
661
  if (typeof parser.parseForESLint === "function") {
662
662
  return parser.parseForESLint(text, parserOptions);
663
- } else {
664
- return parser.parse(text, parserOptions);
665
663
  }
664
+ return parser.parse(text, parserOptions);
665
+
666
666
  } catch (ex) {
667
667
 
668
668
  // If the message includes a leading line number, strip it:
@@ -695,9 +695,9 @@ module.exports = (function() {
695
695
  return ruleConfig;
696
696
  } else if (Array.isArray(ruleConfig)) {
697
697
  return ruleConfig[0];
698
- } else {
699
- return 0;
700
698
  }
699
+ return 0;
700
+
701
701
  }
702
702
 
703
703
  /**
@@ -708,9 +708,9 @@ module.exports = (function() {
708
708
  function getRuleOptions(ruleConfig) {
709
709
  if (Array.isArray(ruleConfig)) {
710
710
  return ruleConfig.slice(1);
711
- } else {
712
- return [];
713
711
  }
712
+ return [];
713
+
714
714
  }
715
715
 
716
716
  // set unlimited listeners (see https://github.com/eslint/eslint/issues/524)
@@ -864,8 +864,8 @@ module.exports = (function() {
864
864
  (parseResult && parseResult.services ? parseResult.services : {})
865
865
  );
866
866
 
867
- const rule = ruleCreator.create ? ruleCreator.create(ruleContext) :
868
- ruleCreator(ruleContext);
867
+ const rule = ruleCreator.create ? ruleCreator.create(ruleContext)
868
+ : ruleCreator(ruleContext);
869
869
 
870
870
  // add all the node types as listeners
871
871
  Object.keys(rule).forEach(nodeType => {
@@ -939,9 +939,9 @@ module.exports = (function() {
939
939
 
940
940
  if (lineDiff === 0) {
941
941
  return a.column - b.column;
942
- } else {
943
- return lineDiff;
944
942
  }
943
+ return lineDiff;
944
+
945
945
  });
946
946
 
947
947
  return messages;
@@ -1109,9 +1109,9 @@ module.exports = (function() {
1109
1109
  if (scope) {
1110
1110
  if (scope.type === "function-expression-name") {
1111
1111
  return scope.childScopes[0];
1112
- } else {
1113
- return scope;
1114
1112
  }
1113
+ return scope;
1114
+
1115
1115
  }
1116
1116
 
1117
1117
  }
@@ -1161,9 +1161,9 @@ module.exports = (function() {
1161
1161
  api.getFilename = function() {
1162
1162
  if (typeof currentFilename === "string") {
1163
1163
  return currentFilename;
1164
- } else {
1165
- return "<input>";
1166
1164
  }
1165
+ return "<input>";
1166
+
1167
1167
  };
1168
1168
 
1169
1169
  /**
@@ -1192,7 +1192,7 @@ module.exports = (function() {
1192
1192
  * @returns {Object} Object mapping rule IDs to their default configurations
1193
1193
  */
1194
1194
  api.defaults = function() {
1195
- return require("../conf/eslint.json");
1195
+ return require("../conf/eslint-recommended");
1196
1196
  };
1197
1197
 
1198
1198
  /**
@@ -19,9 +19,9 @@ const xmlEscape = require("../util/xml-escape");
19
19
  function getMessageType(message) {
20
20
  if (message.fatal || message.severity === 2) {
21
21
  return "error";
22
- } else {
23
- return "warning";
24
22
  }
23
+ return "warning";
24
+
25
25
  }
26
26
 
27
27
  //------------------------------------------------------------------------------
@@ -17,9 +17,9 @@
17
17
  function getMessageType(message) {
18
18
  if (message.fatal || message.severity === 2) {
19
19
  return "Error";
20
- } else {
21
- return "Warning";
22
20
  }
21
+ return "Warning";
22
+
23
23
  }
24
24
 
25
25
 
@@ -19,9 +19,9 @@ const xmlEscape = require("../util/xml-escape");
19
19
  function getMessageType(message) {
20
20
  if (message.fatal || message.severity === 2) {
21
21
  return "Error";
22
- } else {
23
- return "Warning";
24
22
  }
23
+ return "Warning";
24
+
25
25
  }
26
26
 
27
27
  //------------------------------------------------------------------------------
@@ -18,9 +18,9 @@ const yaml = require("js-yaml");
18
18
  function getMessageType(message) {
19
19
  if (message.fatal || message.severity === 2) {
20
20
  return "error";
21
- } else {
22
- return "warning";
23
21
  }
22
+ return "warning";
23
+
24
24
  }
25
25
 
26
26
  /**
@@ -16,9 +16,9 @@
16
16
  function getMessageType(message) {
17
17
  if (message.fatal || message.severity === 2) {
18
18
  return "Error";
19
- } else {
20
- return "Warning";
21
19
  }
20
+ return "Warning";
21
+
22
22
  }
23
23
 
24
24
 
@@ -18,9 +18,9 @@
18
18
  function getMessageType(message) {
19
19
  if (message.fatal || message.severity === 2) {
20
20
  return "error";
21
- } else {
22
- return "warning";
23
21
  }
22
+ return "warning";
23
+
24
24
  }
25
25
 
26
26
 
@@ -4,6 +4,12 @@
4
4
  */
5
5
  "use strict";
6
6
 
7
+ //------------------------------------------------------------------------------
8
+ // Requirements
9
+ //------------------------------------------------------------------------------
10
+
11
+ const astUtils = require("../ast-utils");
12
+
7
13
  //------------------------------------------------------------------------------
8
14
  // Rule Definition
9
15
  //------------------------------------------------------------------------------
@@ -136,10 +142,7 @@ module.exports = {
136
142
  loc: arrowBody.loc.start,
137
143
  message: "Expected block statement surrounding arrow body.",
138
144
  fix(fixer) {
139
- const lastTokenBeforeBody = sourceCode.getTokensBetween(sourceCode.getFirstToken(node), arrowBody)
140
- .reverse()
141
- .find(token => token.value !== "(");
142
-
145
+ const lastTokenBeforeBody = sourceCode.getLastTokenBetween(sourceCode.getFirstToken(node), arrowBody, astUtils.isNotOpeningParenToken);
143
146
  const firstBodyToken = sourceCode.getTokenAfter(lastTokenBeforeBody);
144
147
 
145
148
  return fixer.replaceTextRange(
@@ -4,6 +4,12 @@
4
4
  */
5
5
  "use strict";
6
6
 
7
+ //------------------------------------------------------------------------------
8
+ // Requirements
9
+ //------------------------------------------------------------------------------
10
+
11
+ const astUtils = require("../ast-utils");
12
+
7
13
  //------------------------------------------------------------------------------
8
14
  // Rule Definition
9
15
  //------------------------------------------------------------------------------
@@ -51,12 +57,7 @@ module.exports = {
51
57
  * @returns {Object} Tokens of arrow and before/after arrow.
52
58
  */
53
59
  function getTokens(node) {
54
- let arrow = sourceCode.getTokenBefore(node.body);
55
-
56
- // skip '(' tokens.
57
- while (arrow.value !== "=>") {
58
- arrow = sourceCode.getTokenBefore(arrow);
59
- }
60
+ const arrow = sourceCode.getTokenBefore(node.body, astUtils.isArrowToken);
60
61
 
61
62
  return {
62
63
  before: sourceCode.getTokenBefore(arrow),
@@ -74,8 +74,8 @@ module.exports = {
74
74
  // Gets braces and the first/last token of content.
75
75
  const openBrace = getOpenBrace(node);
76
76
  const closeBrace = sourceCode.getLastToken(node);
77
- const firstToken = sourceCode.getTokenOrCommentAfter(openBrace);
78
- const lastToken = sourceCode.getTokenOrCommentBefore(closeBrace);
77
+ const firstToken = sourceCode.getTokenAfter(openBrace, { includeComments: true });
78
+ const lastToken = sourceCode.getTokenBefore(closeBrace, { includeComments: true });
79
79
 
80
80
  // Skip if the node is invalid or empty.
81
81
  if (openBrace.type !== "Punctuator" ||
@@ -62,7 +62,7 @@ module.exports = {
62
62
  function removeNewlineBetween(firstToken, secondToken) {
63
63
  const textRange = [firstToken.range[1], secondToken.range[0]];
64
64
  const textBetween = sourceCode.text.slice(textRange[0], textRange[1]);
65
- const NEWLINE_REGEX = /\r\n|\r|\n|\u2028|\u2029/g;
65
+ const NEWLINE_REGEX = astUtils.createGlobalLinebreakMatcher();
66
66
 
67
67
  // Don't do a fix if there is a comment between the tokens
68
68
  return fixer => fixer.replaceTextRange(textRange, textBetween.trim() ? null : textBetween.replace(NEWLINE_REGEX, ""));
@@ -78,7 +78,6 @@ module.exports = {
78
78
  const tokenBeforeOpeningCurly = sourceCode.getTokenBefore(openingCurly);
79
79
  const tokenAfterOpeningCurly = sourceCode.getTokenAfter(openingCurly);
80
80
  const tokenBeforeClosingCurly = sourceCode.getTokenBefore(closingCurly);
81
- const tokenAfterClosingCurly = sourceCode.getTokenAfter(closingCurly);
82
81
  const singleLineException = params.allowSingleLine && astUtils.isTokenOnSameLine(openingCurly, closingCurly);
83
82
 
84
83
  if (style !== "allman" && !astUtils.isTokenOnSameLine(tokenBeforeOpeningCurly, openingCurly)) {
@@ -112,23 +111,30 @@ module.exports = {
112
111
  fix: fixer => fixer.insertTextBefore(closingCurly, "\n")
113
112
  });
114
113
  }
114
+ }
115
115
 
116
- if (tokenAfterClosingCurly && tokenAfterClosingCurly.type === "Keyword" && new Set(["else", "catch", "finally"]).has(tokenAfterClosingCurly.value)) {
117
- if (style === "1tbs" && !astUtils.isTokenOnSameLine(closingCurly, tokenAfterClosingCurly)) {
118
- context.report({
119
- node: closingCurly,
120
- message: CLOSE_MESSAGE,
121
- fix: removeNewlineBetween(closingCurly, tokenAfterClosingCurly)
122
- });
123
- }
116
+ /**
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);
124
123
 
125
- if (style !== "1tbs" && astUtils.isTokenOnSameLine(closingCurly, tokenAfterClosingCurly)) {
126
- context.report({
127
- node: closingCurly,
128
- message: CLOSE_MESSAGE_STROUSTRUP_ALLMAN,
129
- fix: fixer => fixer.insertTextAfter(closingCurly, "\n")
130
- });
131
- }
124
+ if (style === "1tbs" && !astUtils.isTokenOnSameLine(curlyToken, keywordToken)) {
125
+ context.report({
126
+ node: curlyToken,
127
+ message: CLOSE_MESSAGE,
128
+ fix: removeNewlineBetween(curlyToken, keywordToken)
129
+ });
130
+ }
131
+
132
+ if (style !== "1tbs" && astUtils.isTokenOnSameLine(curlyToken, keywordToken)) {
133
+ context.report({
134
+ node: curlyToken,
135
+ message: CLOSE_MESSAGE_STROUSTRUP_ALLMAN,
136
+ fix: fixer => fixer.insertTextAfter(curlyToken, "\n")
137
+ });
132
138
  }
133
139
  }
134
140
 
@@ -138,11 +144,7 @@ module.exports = {
138
144
 
139
145
  return {
140
146
  BlockStatement(node) {
141
- if (
142
- node.parent.type !== "BlockStatement" &&
143
- node.parent.type !== "SwitchCase" &&
144
- node.parent.type !== "Program"
145
- ) {
147
+ if (!astUtils.STATEMENT_LIST_PARENTS.has(node.parent.type)) {
146
148
  validateCurlyPair(sourceCode.getFirstToken(node), sourceCode.getLastToken(node));
147
149
  }
148
150
  },
@@ -154,6 +156,24 @@ module.exports = {
154
156
  const openingCurly = sourceCode.getTokenBefore(node.cases.length ? node.cases[0] : closingCurly);
155
157
 
156
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));
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
+ }
157
177
  }
158
178
  };
159
179
  }
@@ -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
@@ -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
  }
@@ -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