eslint 7.0.0-alpha.1 → 7.0.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 (84) hide show
  1. package/CHANGELOG.md +329 -0
  2. package/README.md +7 -7
  3. package/bin/eslint.js +115 -77
  4. package/conf/category-list.json +2 -3
  5. package/conf/environments.js +2 -1
  6. package/conf/eslint-recommended.js +3 -0
  7. package/lib/api.js +2 -0
  8. package/lib/cli-engine/cascading-config-array-factory.js +16 -2
  9. package/lib/cli-engine/cli-engine.js +53 -47
  10. package/lib/cli-engine/config-array/config-array.js +30 -1
  11. package/lib/cli-engine/config-array/ignore-pattern.js +7 -1
  12. package/lib/cli-engine/config-array-factory.js +244 -235
  13. package/lib/cli.js +181 -95
  14. package/lib/eslint/eslint.js +656 -0
  15. package/lib/eslint/index.js +7 -0
  16. package/lib/init/autoconfig.js +4 -4
  17. package/lib/init/config-file.js +2 -2
  18. package/lib/init/config-initializer.js +2 -4
  19. package/lib/init/source-code-utils.js +2 -2
  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 +178 -23
  23. package/lib/rules/accessor-pairs.js +2 -2
  24. package/lib/rules/array-callback-return.js +3 -18
  25. package/lib/rules/arrow-body-style.js +26 -15
  26. package/lib/rules/callback-return.js +4 -0
  27. package/lib/rules/camelcase.js +38 -1
  28. package/lib/rules/comma-style.js +3 -8
  29. package/lib/rules/computed-property-spacing.js +2 -2
  30. package/lib/rules/curly.js +124 -40
  31. package/lib/rules/func-call-spacing.js +4 -3
  32. package/lib/rules/func-names.js +31 -24
  33. package/lib/rules/getter-return.js +2 -12
  34. package/lib/rules/global-require.js +4 -0
  35. package/lib/rules/handle-callback-err.js +4 -0
  36. package/lib/rules/id-blacklist.js +140 -64
  37. package/lib/rules/id-length.js +14 -4
  38. package/lib/rules/indent-legacy.js +0 -16
  39. package/lib/rules/key-spacing.js +1 -1
  40. package/lib/rules/new-cap.js +1 -1
  41. package/lib/rules/newline-per-chained-call.js +6 -3
  42. package/lib/rules/no-alert.js +5 -3
  43. package/lib/rules/no-buffer-constructor.js +4 -0
  44. package/lib/rules/no-dupe-else-if.js +1 -1
  45. package/lib/rules/no-empty-function.js +4 -2
  46. package/lib/rules/no-eval.js +3 -2
  47. package/lib/rules/no-extra-bind.js +1 -1
  48. package/lib/rules/no-extra-boolean-cast.js +168 -38
  49. package/lib/rules/no-extra-parens.js +9 -5
  50. package/lib/rules/no-implied-eval.js +83 -101
  51. package/lib/rules/no-import-assign.js +1 -1
  52. package/lib/rules/no-inner-declarations.js +31 -39
  53. package/lib/rules/no-lone-blocks.js +1 -1
  54. package/lib/rules/no-magic-numbers.js +72 -37
  55. package/lib/rules/no-mixed-requires.js +4 -0
  56. package/lib/rules/no-new-object.js +15 -3
  57. package/lib/rules/no-new-require.js +4 -0
  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 +52 -18
  66. package/lib/rules/no-setter-return.js +1 -1
  67. package/lib/rules/no-sync.js +4 -0
  68. package/lib/rules/no-underscore-dangle.js +1 -1
  69. package/lib/rules/no-unexpected-multiline.js +22 -12
  70. package/lib/rules/no-useless-concat.js +1 -1
  71. package/lib/rules/operator-assignment.js +3 -3
  72. package/lib/rules/operator-linebreak.js +4 -16
  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/space-before-function-paren.js +5 -2
  77. package/lib/rules/template-curly-spacing.js +59 -42
  78. package/lib/rules/utils/ast-utils.js +65 -4
  79. package/lib/rules/wrap-iife.js +54 -17
  80. package/lib/rules/yoda.js +101 -51
  81. package/lib/shared/relative-module-resolver.js +1 -0
  82. package/lib/shared/types.js +9 -2
  83. package/messages/plugin-conflict.txt +7 -0
  84. package/package.json +27 -26
@@ -6,6 +6,41 @@
6
6
 
7
7
  "use strict";
8
8
 
9
+ //------------------------------------------------------------------------------
10
+ // Helpers
11
+ //------------------------------------------------------------------------------
12
+
13
+ /**
14
+ * Determines whether the given node is the update node of a `ForStatement`.
15
+ * @param {ASTNode} node The node to check.
16
+ * @returns {boolean} `true` if the node is `ForStatement` update.
17
+ */
18
+ function isForStatementUpdate(node) {
19
+ const parent = node.parent;
20
+
21
+ return parent.type === "ForStatement" && parent.update === node;
22
+ }
23
+
24
+ /**
25
+ * Determines whether the given node is considered to be a for loop "afterthought" by the logic of this rule.
26
+ * In particular, it returns `true` if the given node is either:
27
+ * - The update node of a `ForStatement`: for (;; i++) {}
28
+ * - An operand of a sequence expression that is the update node: for (;; foo(), i++) {}
29
+ * - An operand of a sequence expression that is child of another sequence expression, etc.,
30
+ * up to the sequence expression that is the update node: for (;; foo(), (bar(), (baz(), i++))) {}
31
+ * @param {ASTNode} node The node to check.
32
+ * @returns {boolean} `true` if the node is a for loop afterthought.
33
+ */
34
+ function isForLoopAfterthought(node) {
35
+ const parent = node.parent;
36
+
37
+ if (parent.type === "SequenceExpression") {
38
+ return isForLoopAfterthought(parent);
39
+ }
40
+
41
+ return isForStatementUpdate(node);
42
+ }
43
+
9
44
  //------------------------------------------------------------------------------
10
45
  // Rule Definition
11
46
  //------------------------------------------------------------------------------
@@ -42,18 +77,19 @@ module.exports = {
42
77
  create(context) {
43
78
 
44
79
  const config = context.options[0];
45
- let allowInForAfterthought = false;
80
+ let allowForLoopAfterthoughts = false;
46
81
 
47
82
  if (typeof config === "object") {
48
- allowInForAfterthought = config.allowForLoopAfterthoughts === true;
83
+ allowForLoopAfterthoughts = config.allowForLoopAfterthoughts === true;
49
84
  }
50
85
 
51
86
  return {
52
87
 
53
88
  UpdateExpression(node) {
54
- if (allowInForAfterthought && node.parent.type === "ForStatement") {
89
+ if (allowForLoopAfterthoughts && isForLoopAfterthought(node)) {
55
90
  return;
56
91
  }
92
+
57
93
  context.report({
58
94
  node,
59
95
  messageId: "unexpectedUnaryOp",
@@ -10,6 +10,10 @@
10
10
 
11
11
  module.exports = {
12
12
  meta: {
13
+ deprecated: true,
14
+
15
+ replacedBy: ["node/no-process-env"],
16
+
13
17
  type: "suggestion",
14
18
 
15
19
  docs: {
@@ -10,6 +10,10 @@
10
10
 
11
11
  module.exports = {
12
12
  meta: {
13
+ deprecated: true,
14
+
15
+ replacedBy: ["node/no-process-exit"],
16
+
13
17
  type: "suggestion",
14
18
 
15
19
  docs: {
@@ -47,7 +47,7 @@ module.exports = {
47
47
  if (DISALLOWED_PROPS.indexOf(propName) > -1) {
48
48
  context.report({
49
49
  messageId: "prototypeBuildIn",
50
- loc: node.callee.property.loc.start,
50
+ loc: node.callee.property.loc,
51
51
  data: { prop: propName },
52
52
  node
53
53
  });
@@ -40,6 +40,10 @@ const arrayOfStringsOrObjects = {
40
40
 
41
41
  module.exports = {
42
42
  meta: {
43
+ deprecated: true,
44
+
45
+ replacedBy: ["node/no-restricted-require"],
46
+
43
47
  type: "suggestion",
44
48
 
45
49
  docs: {
@@ -106,10 +110,19 @@ module.exports = {
106
110
  * @param {ASTNode} node The node to check.
107
111
  * @returns {boolean} If the node is a string literal.
108
112
  */
109
- function isString(node) {
113
+ function isStringLiteral(node) {
110
114
  return node && node.type === "Literal" && typeof node.value === "string";
111
115
  }
112
116
 
117
+ /**
118
+ * Function to check if a node is a static string template literal.
119
+ * @param {ASTNode} node The node to check.
120
+ * @returns {boolean} If the node is a string template literal.
121
+ */
122
+ function isStaticTemplateLiteral(node) {
123
+ return node && node.type === "TemplateLiteral" && node.expressions.length === 0;
124
+ }
125
+
113
126
  /**
114
127
  * Function to check if a node is a require call.
115
128
  * @param {ASTNode} node The node to check.
@@ -119,14 +132,31 @@ module.exports = {
119
132
  return node.callee.type === "Identifier" && node.callee.name === "require";
120
133
  }
121
134
 
135
+ /**
136
+ * Extract string from Literal or TemplateLiteral node
137
+ * @param {ASTNode} node The node to extract from
138
+ * @returns {string|null} Extracted string or null if node doesn't represent a string
139
+ */
140
+ function getFirstArgumentString(node) {
141
+ if (isStringLiteral(node)) {
142
+ return node.value.trim();
143
+ }
144
+
145
+ if (isStaticTemplateLiteral(node)) {
146
+ return node.quasis[0].value.cooked.trim();
147
+ }
148
+
149
+ return null;
150
+ }
151
+
122
152
  /**
123
153
  * Report a restricted path.
124
154
  * @param {node} node representing the restricted path reference
155
+ * @param {string} name restricted path
125
156
  * @returns {void}
126
157
  * @private
127
158
  */
128
- function reportPath(node) {
129
- const name = node.arguments[0].value.trim();
159
+ function reportPath(node, name) {
130
160
  const customMessage = restrictedPathMessages[name];
131
161
  const messageId = customMessage
132
162
  ? "customMessage"
@@ -156,21 +186,25 @@ module.exports = {
156
186
  CallExpression(node) {
157
187
  if (isRequireCall(node)) {
158
188
 
159
- // node has arguments and first argument is string
160
- if (node.arguments.length && isString(node.arguments[0])) {
161
- const name = node.arguments[0].value.trim();
162
-
163
- // check if argument value is in restricted modules array
164
- if (isRestrictedPath(name)) {
165
- reportPath(node);
166
- }
167
-
168
- if (restrictedPatterns.length > 0 && ig.ignores(name)) {
169
- context.report({
170
- node,
171
- messageId: "patternMessage",
172
- data: { name }
173
- });
189
+ // node has arguments
190
+ if (node.arguments.length) {
191
+ const name = getFirstArgumentString(node.arguments[0]);
192
+
193
+ // if first argument is a string literal or a static string template literal
194
+ if (name) {
195
+
196
+ // check if argument value is in restricted modules array
197
+ if (isRestrictedPath(name)) {
198
+ reportPath(node, name);
199
+ }
200
+
201
+ if (restrictedPatterns.length > 0 && ig.ignores(name)) {
202
+ context.report({
203
+ node,
204
+ messageId: "patternMessage",
205
+ data: { name }
206
+ });
207
+ }
174
208
  }
175
209
  }
176
210
  }
@@ -145,7 +145,7 @@ module.exports = {
145
145
  docs: {
146
146
  description: "disallow returning values from setters",
147
147
  category: "Possible Errors",
148
- recommended: false,
148
+ recommended: true,
149
149
  url: "https://eslint.org/docs/rules/no-setter-return"
150
150
  },
151
151
 
@@ -13,6 +13,10 @@
13
13
 
14
14
  module.exports = {
15
15
  meta: {
16
+ deprecated: true,
17
+
18
+ replacedBy: ["node/no-sync"],
19
+
16
20
  type: "suggestion",
17
21
 
18
22
  docs: {
@@ -205,7 +205,7 @@ module.exports = {
205
205
  const identifier = node.key.name;
206
206
  const isMethod = node.type === "MethodDefinition" || node.type === "Property" && node.method;
207
207
 
208
- if (typeof identifier !== "undefined" && enforceInMethodNames && isMethod && hasTrailingUnderscore(identifier)) {
208
+ if (typeof identifier !== "undefined" && enforceInMethodNames && isMethod && hasTrailingUnderscore(identifier) && !isAllowed(identifier)) {
209
209
  context.report({
210
210
  node,
211
211
  messageId: "unexpectedUnderscore",
@@ -53,7 +53,11 @@ module.exports = {
53
53
  const nodeExpressionEnd = sourceCode.getTokenBefore(openParen);
54
54
 
55
55
  if (openParen.loc.start.line !== nodeExpressionEnd.loc.end.line) {
56
- context.report({ node, loc: openParen.loc.start, messageId, data: { char: openParen.value } });
56
+ context.report({
57
+ node,
58
+ loc: openParen.loc,
59
+ messageId
60
+ });
57
61
  }
58
62
  }
59
63
 
@@ -71,18 +75,24 @@ module.exports = {
71
75
  },
72
76
 
73
77
  TaggedTemplateExpression(node) {
74
- if (node.tag.loc.end.line === node.quasi.loc.start.line) {
75
- return;
76
- }
77
-
78
- // handle generics type parameters on template tags
79
- const tokenBefore = sourceCode.getTokenBefore(node.quasi);
80
-
81
- if (tokenBefore.loc.end.line === node.quasi.loc.start.line) {
82
- return;
78
+ const { quasi } = node;
79
+
80
+ // handles common tags, parenthesized tags, and typescript's generic type arguments
81
+ const tokenBefore = sourceCode.getTokenBefore(quasi);
82
+
83
+ if (tokenBefore.loc.end.line !== quasi.loc.start.line) {
84
+ context.report({
85
+ node,
86
+ loc: {
87
+ start: quasi.loc.start,
88
+ end: {
89
+ line: quasi.loc.start.line,
90
+ column: quasi.loc.start.column + 1
91
+ }
92
+ },
93
+ messageId: "taggedTemplate"
94
+ });
83
95
  }
84
-
85
- context.report({ node, loc: node.loc.start, messageId: "taggedTemplate" });
86
96
  },
87
97
 
88
98
  CallExpression(node) {
@@ -105,7 +105,7 @@ module.exports = {
105
105
 
106
106
  context.report({
107
107
  node,
108
- loc: operatorToken.loc.start,
108
+ loc: operatorToken.loc,
109
109
  messageId: "unexpectedConcat"
110
110
  });
111
111
  }
@@ -214,12 +214,12 @@ module.exports = {
214
214
  ) {
215
215
  rightText = `${sourceCode.text.slice(operatorToken.range[1], node.right.range[0])}(${sourceCode.getText(node.right)})`;
216
216
  } else {
217
- const firstRightToken = sourceCode.getFirstToken(node.right);
217
+ const tokenAfterOperator = sourceCode.getTokenAfter(operatorToken, { includeComments: true });
218
218
  let rightTextPrefix = "";
219
219
 
220
220
  if (
221
- operatorToken.range[1] === firstRightToken.range[0] &&
222
- !astUtils.canTokensBeAdjacent({ type: "Punctuator", value: newOperator }, firstRightToken)
221
+ operatorToken.range[1] === tokenAfterOperator.range[0] &&
222
+ !astUtils.canTokensBeAdjacent({ type: "Punctuator", value: newOperator }, tokenAfterOperator)
223
223
  ) {
224
224
  rightTextPrefix = " "; // foo+=+bar -> foo= foo+ +bar
225
225
  }
@@ -172,10 +172,7 @@ module.exports = {
172
172
  // lone operator
173
173
  context.report({
174
174
  node,
175
- loc: {
176
- line: operatorToken.loc.end.line,
177
- column: operatorToken.loc.end.column
178
- },
175
+ loc: operatorToken.loc,
179
176
  messageId: "badLinebreak",
180
177
  data: {
181
178
  operator
@@ -187,10 +184,7 @@ module.exports = {
187
184
 
188
185
  context.report({
189
186
  node,
190
- loc: {
191
- line: operatorToken.loc.end.line,
192
- column: operatorToken.loc.end.column
193
- },
187
+ loc: operatorToken.loc,
194
188
  messageId: "operatorAtBeginning",
195
189
  data: {
196
190
  operator
@@ -202,10 +196,7 @@ module.exports = {
202
196
 
203
197
  context.report({
204
198
  node,
205
- loc: {
206
- line: operatorToken.loc.end.line,
207
- column: operatorToken.loc.end.column
208
- },
199
+ loc: operatorToken.loc,
209
200
  messageId: "operatorAtEnd",
210
201
  data: {
211
202
  operator
@@ -217,10 +208,7 @@ module.exports = {
217
208
 
218
209
  context.report({
219
210
  node,
220
- loc: {
221
- line: operatorToken.loc.end.line,
222
- column: operatorToken.loc.end.column
223
- },
211
+ loc: operatorToken.loc,
224
212
  messageId: "noLinebreak",
225
213
  data: {
226
214
  operator
@@ -79,13 +79,13 @@ module.exports = {
79
79
 
80
80
  "CallExpression[arguments.length=2]"(node) {
81
81
  const [strNode, radixNode] = node.arguments,
82
- str = strNode.value,
82
+ str = astUtils.getStaticStringValue(strNode),
83
83
  radix = radixNode.value;
84
84
 
85
85
  if (
86
- strNode.type === "Literal" &&
86
+ str !== null &&
87
+ astUtils.isStringLiteral(strNode) &&
87
88
  radixNode.type === "Literal" &&
88
- typeof str === "string" &&
89
89
  typeof radix === "number" &&
90
90
  radixMap.has(radix) &&
91
91
  isParseInt(node.callee)
@@ -181,8 +181,8 @@ function defineFixer(node, sourceCode) {
181
181
  const leftParen = sourceCode.getTokenAfter(node.callee, isOpeningParenToken);
182
182
  const rightParen = sourceCode.getLastToken(node);
183
183
 
184
- // Remove the callee `Object.assign`
185
- yield fixer.remove(node.callee);
184
+ // Remove everything before the opening paren: callee `Object.assign`, type arguments, and whitespace between the callee and the paren.
185
+ yield fixer.removeRange([node.range[0], leftParen.range[0]]);
186
186
 
187
187
  // Replace the parens of argument list to braces.
188
188
  if (needsParens(node, sourceCode)) {
@@ -68,7 +68,7 @@ module.exports = {
68
68
  * @returns {void}
69
69
  */
70
70
  function exitFunction(node) {
71
- if (node.async && !scopeInfo.hasAwait && !astUtils.isEmptyFunction(node)) {
71
+ if (!node.generator && node.async && !scopeInfo.hasAwait && !astUtils.isEmptyFunction(node)) {
72
72
  context.report({
73
73
  node,
74
74
  loc: astUtils.getFunctionHeadLoc(node, sourceCode),
@@ -127,7 +127,10 @@ module.exports = {
127
127
  if (hasSpacing && functionConfig === "never") {
128
128
  context.report({
129
129
  node,
130
- loc: leftToken.loc.end,
130
+ loc: {
131
+ start: leftToken.loc.end,
132
+ end: rightToken.loc.start
133
+ },
131
134
  messageId: "unexpectedSpace",
132
135
  fix(fixer) {
133
136
  const comments = sourceCode.getCommentsBefore(rightToken);
@@ -145,7 +148,7 @@ module.exports = {
145
148
  } else if (!hasSpacing && functionConfig === "always") {
146
149
  context.report({
147
150
  node,
148
- loc: leftToken.loc.end,
151
+ loc: rightToken.loc,
149
152
  messageId: "missingSpace",
150
153
  fix: fixer => fixer.insertTextAfter(leftToken, " ")
151
154
  });
@@ -11,13 +11,6 @@
11
11
 
12
12
  const astUtils = require("./utils/ast-utils");
13
13
 
14
- //------------------------------------------------------------------------------
15
- // Helpers
16
- //------------------------------------------------------------------------------
17
-
18
- const OPEN_PAREN = /\$\{$/u;
19
- const CLOSE_PAREN = /^\}/u;
20
-
21
14
  //------------------------------------------------------------------------------
22
15
  // Rule Definition
23
16
  //------------------------------------------------------------------------------
@@ -49,7 +42,6 @@ module.exports = {
49
42
  create(context) {
50
43
  const sourceCode = context.getSourceCode();
51
44
  const always = context.options[0] === "always";
52
- const prefix = always ? "expected" : "unexpected";
53
45
 
54
46
  /**
55
47
  * Checks spacing before `}` of a given token.
@@ -57,25 +49,39 @@ module.exports = {
57
49
  * @returns {void}
58
50
  */
59
51
  function checkSpacingBefore(token) {
60
- const prevToken = sourceCode.getTokenBefore(token, { includeComments: true });
52
+ if (!token.value.startsWith("}")) {
53
+ return; // starts with a backtick, this is the first template element in the template literal
54
+ }
55
+
56
+ const prevToken = sourceCode.getTokenBefore(token, { includeComments: true }),
57
+ hasSpace = sourceCode.isSpaceBetween(prevToken, token);
58
+
59
+ if (!astUtils.isTokenOnSameLine(prevToken, token)) {
60
+ return;
61
+ }
61
62
 
62
- if (prevToken &&
63
- CLOSE_PAREN.test(token.value) &&
64
- astUtils.isTokenOnSameLine(prevToken, token) &&
65
- sourceCode.isSpaceBetweenTokens(prevToken, token) !== always
66
- ) {
63
+ if (always && !hasSpace) {
67
64
  context.report({
68
- loc: token.loc.start,
69
- messageId: `${prefix}Before`,
70
- fix(fixer) {
71
- if (always) {
72
- return fixer.insertTextBefore(token, " ");
65
+ loc: {
66
+ start: token.loc.start,
67
+ end: {
68
+ line: token.loc.start.line,
69
+ column: token.loc.start.column + 1
73
70
  }
74
- return fixer.removeRange([
75
- prevToken.range[1],
76
- token.range[0]
77
- ]);
78
- }
71
+ },
72
+ messageId: "expectedBefore",
73
+ fix: fixer => fixer.insertTextBefore(token, " ")
74
+ });
75
+ }
76
+
77
+ if (!always && hasSpace) {
78
+ context.report({
79
+ loc: {
80
+ start: prevToken.loc.end,
81
+ end: token.loc.start
82
+ },
83
+ messageId: "unexpectedBefore",
84
+ fix: fixer => fixer.removeRange([prevToken.range[1], token.range[0]])
79
85
  });
80
86
  }
81
87
  }
@@ -86,28 +92,39 @@ module.exports = {
86
92
  * @returns {void}
87
93
  */
88
94
  function checkSpacingAfter(token) {
89
- const nextToken = sourceCode.getTokenAfter(token, { includeComments: true });
95
+ if (!token.value.endsWith("${")) {
96
+ return; // ends with a backtick, this is the last template element in the template literal
97
+ }
98
+
99
+ const nextToken = sourceCode.getTokenAfter(token, { includeComments: true }),
100
+ hasSpace = sourceCode.isSpaceBetween(token, nextToken);
90
101
 
91
- if (nextToken &&
92
- OPEN_PAREN.test(token.value) &&
93
- astUtils.isTokenOnSameLine(token, nextToken) &&
94
- sourceCode.isSpaceBetweenTokens(token, nextToken) !== always
95
- ) {
102
+ if (!astUtils.isTokenOnSameLine(token, nextToken)) {
103
+ return;
104
+ }
105
+
106
+ if (always && !hasSpace) {
96
107
  context.report({
97
108
  loc: {
98
- line: token.loc.end.line,
99
- column: token.loc.end.column - 2
109
+ start: {
110
+ line: token.loc.end.line,
111
+ column: token.loc.end.column - 2
112
+ },
113
+ end: token.loc.end
100
114
  },
101
- messageId: `${prefix}After`,
102
- fix(fixer) {
103
- if (always) {
104
- return fixer.insertTextAfter(token, " ");
105
- }
106
- return fixer.removeRange([
107
- token.range[1],
108
- nextToken.range[0]
109
- ]);
110
- }
115
+ messageId: "expectedAfter",
116
+ fix: fixer => fixer.insertTextAfter(token, " ")
117
+ });
118
+ }
119
+
120
+ if (!always && hasSpace) {
121
+ context.report({
122
+ loc: {
123
+ start: token.loc.end,
124
+ end: nextToken.loc.start
125
+ },
126
+ messageId: "unexpectedAfter",
127
+ fix: fixer => fixer.removeRange([token.range[1], nextToken.range[0]])
111
128
  });
112
129
  }
113
130
  }
@@ -1357,17 +1357,65 @@ module.exports = {
1357
1357
  * next to each other, behavior is undefined (although it should return `true` in most cases).
1358
1358
  */
1359
1359
  canTokensBeAdjacent(leftValue, rightValue) {
1360
+ const espreeOptions = {
1361
+ ecmaVersion: espree.latestEcmaVersion,
1362
+ comment: true,
1363
+ range: true
1364
+ };
1365
+
1360
1366
  let leftToken;
1361
1367
 
1362
1368
  if (typeof leftValue === "string") {
1363
- const leftTokens = espree.tokenize(leftValue, { ecmaVersion: 2015 });
1369
+ let tokens;
1370
+
1371
+ try {
1372
+ tokens = espree.tokenize(leftValue, espreeOptions);
1373
+ } catch (e) {
1374
+ return false;
1375
+ }
1376
+
1377
+ const comments = tokens.comments;
1364
1378
 
1365
- leftToken = leftTokens[leftTokens.length - 1];
1379
+ leftToken = tokens[tokens.length - 1];
1380
+ if (comments.length) {
1381
+ const lastComment = comments[comments.length - 1];
1382
+
1383
+ if (lastComment.range[0] > leftToken.range[0]) {
1384
+ leftToken = lastComment;
1385
+ }
1386
+ }
1366
1387
  } else {
1367
1388
  leftToken = leftValue;
1368
1389
  }
1369
1390
 
1370
- const rightToken = typeof rightValue === "string" ? espree.tokenize(rightValue, { ecmaVersion: 2015 })[0] : rightValue;
1391
+ if (leftToken.type === "Shebang") {
1392
+ return false;
1393
+ }
1394
+
1395
+ let rightToken;
1396
+
1397
+ if (typeof rightValue === "string") {
1398
+ let tokens;
1399
+
1400
+ try {
1401
+ tokens = espree.tokenize(rightValue, espreeOptions);
1402
+ } catch (e) {
1403
+ return false;
1404
+ }
1405
+
1406
+ const comments = tokens.comments;
1407
+
1408
+ rightToken = tokens[0];
1409
+ if (comments.length) {
1410
+ const firstComment = comments[0];
1411
+
1412
+ if (firstComment.range[0] < rightToken.range[0]) {
1413
+ rightToken = firstComment;
1414
+ }
1415
+ }
1416
+ } else {
1417
+ rightToken = rightValue;
1418
+ }
1371
1419
 
1372
1420
  if (leftToken.type === "Punctuator" || rightToken.type === "Punctuator") {
1373
1421
  if (leftToken.type === "Punctuator" && rightToken.type === "Punctuator") {
@@ -1379,6 +1427,9 @@ module.exports = {
1379
1427
  MINUS_TOKENS.has(leftToken.value) && MINUS_TOKENS.has(rightToken.value)
1380
1428
  );
1381
1429
  }
1430
+ if (leftToken.type === "Punctuator" && leftToken.value === "/") {
1431
+ return !["Block", "Line", "RegularExpression"].includes(rightToken.type);
1432
+ }
1382
1433
  return true;
1383
1434
  }
1384
1435
 
@@ -1393,6 +1444,10 @@ module.exports = {
1393
1444
  return true;
1394
1445
  }
1395
1446
 
1447
+ if (leftToken.type === "Block" || rightToken.type === "Block" || rightToken.type === "Line") {
1448
+ return true;
1449
+ }
1450
+
1396
1451
  return false;
1397
1452
  },
1398
1453
 
@@ -1413,11 +1468,17 @@ module.exports = {
1413
1468
  const match = namePattern.exec(comment.value);
1414
1469
 
1415
1470
  // Convert the index to loc.
1416
- return sourceCode.getLocFromIndex(
1471
+ const start = sourceCode.getLocFromIndex(
1417
1472
  comment.range[0] +
1418
1473
  "/*".length +
1419
1474
  (match ? match.index + 1 : 0)
1420
1475
  );
1476
+ const end = {
1477
+ line: start.line,
1478
+ column: start.column + (match ? name.length : 1)
1479
+ };
1480
+
1481
+ return { start, end };
1421
1482
  },
1422
1483
 
1423
1484
  /**