eslint 3.15.0 → 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 (97) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/conf/{eslint.json → eslint-recommended.js} +85 -71
  3. package/lib/ast-utils.js +185 -19
  4. package/lib/code-path-analysis/code-path-state.js +2 -2
  5. package/lib/config/autoconfig.js +3 -3
  6. package/lib/config/config-file.js +14 -7
  7. package/lib/config/config-initializer.js +1 -1
  8. package/lib/config.js +3 -2
  9. package/lib/eslint.js +4 -4
  10. package/lib/rules/arrow-body-style.js +7 -4
  11. package/lib/rules/arrow-spacing.js +7 -6
  12. package/lib/rules/block-spacing.js +2 -2
  13. package/lib/rules/brace-style.js +2 -6
  14. package/lib/rules/capitalized-comments.js +6 -6
  15. package/lib/rules/comma-spacing.js +3 -3
  16. package/lib/rules/consistent-return.js +1 -1
  17. package/lib/rules/constructor-super.js +3 -3
  18. package/lib/rules/curly.js +11 -7
  19. package/lib/rules/default-case.js +3 -3
  20. package/lib/rules/eqeqeq.js +15 -6
  21. package/lib/rules/func-call-spacing.js +10 -13
  22. package/lib/rules/generator-star-spacing.js +18 -19
  23. package/lib/rules/id-blacklist.js +2 -2
  24. package/lib/rules/id-length.js +3 -3
  25. package/lib/rules/id-match.js +2 -2
  26. package/lib/rules/indent.js +7 -6
  27. package/lib/rules/key-spacing.js +12 -16
  28. package/lib/rules/keyword-spacing.js +2 -13
  29. package/lib/rules/line-comment-position.js +1 -1
  30. package/lib/rules/linebreak-style.js +7 -1
  31. package/lib/rules/lines-around-comment.js +4 -4
  32. package/lib/rules/lines-around-directive.js +3 -3
  33. package/lib/rules/max-lines.js +2 -2
  34. package/lib/rules/max-statements-per-line.js +7 -6
  35. package/lib/rules/newline-after-var.js +7 -2
  36. package/lib/rules/newline-per-chained-call.js +3 -1
  37. package/lib/rules/no-cond-assign.js +3 -3
  38. package/lib/rules/no-extend-native.js +3 -3
  39. package/lib/rules/no-extra-bind.js +3 -4
  40. package/lib/rules/no-extra-boolean-cast.js +8 -0
  41. package/lib/rules/no-extra-parens.js +1 -2
  42. package/lib/rules/no-inner-declarations.js +4 -4
  43. package/lib/rules/no-irregular-whitespace.js +7 -1
  44. package/lib/rules/no-lone-blocks.js +10 -10
  45. package/lib/rules/no-mixed-operators.js +1 -7
  46. package/lib/rules/no-multi-spaces.js +4 -1
  47. package/lib/rules/no-multi-str.js +7 -3
  48. package/lib/rules/no-return-assign.js +7 -14
  49. package/lib/rules/no-sequences.js +7 -6
  50. package/lib/rules/no-trailing-spaces.js +8 -2
  51. package/lib/rules/no-undefined.js +45 -6
  52. package/lib/rules/no-unexpected-multiline.js +9 -8
  53. package/lib/rules/no-unneeded-ternary.js +5 -1
  54. package/lib/rules/no-unused-labels.js +17 -2
  55. package/lib/rules/no-unused-vars.js +2 -16
  56. package/lib/rules/no-useless-computed-key.js +8 -3
  57. package/lib/rules/no-useless-concat.js +10 -7
  58. package/lib/rules/no-useless-escape.js +1 -1
  59. package/lib/rules/no-useless-return.js +1 -7
  60. package/lib/rules/no-var.js +1 -3
  61. package/lib/rules/no-whitespace-before-property.js +5 -16
  62. package/lib/rules/object-curly-newline.js +2 -2
  63. package/lib/rules/object-curly-spacing.js +7 -25
  64. package/lib/rules/object-property-newline.js +3 -3
  65. package/lib/rules/object-shorthand.js +2 -2
  66. package/lib/rules/operator-assignment.js +1 -1
  67. package/lib/rules/operator-linebreak.js +8 -10
  68. package/lib/rules/padded-blocks.js +4 -4
  69. package/lib/rules/prefer-spread.js +1 -1
  70. package/lib/rules/prefer-template.js +1 -1
  71. package/lib/rules/quotes.js +10 -6
  72. package/lib/rules/semi-spacing.js +4 -0
  73. package/lib/rules/space-before-function-paren.js +8 -5
  74. package/lib/rules/spaced-comment.js +2 -2
  75. package/lib/rules/strict.js +2 -2
  76. package/lib/rules/unicode-bom.js +1 -1
  77. package/lib/rules/wrap-iife.js +5 -5
  78. package/lib/rules/yoda.js +2 -7
  79. package/lib/testers/rule-tester.js +13 -6
  80. package/lib/token-store/backward-token-comment-cursor.js +57 -0
  81. package/lib/token-store/backward-token-cursor.js +56 -0
  82. package/lib/token-store/cursor.js +76 -0
  83. package/lib/token-store/cursors.js +92 -0
  84. package/lib/token-store/decorative-cursor.js +39 -0
  85. package/lib/token-store/filter-cursor.js +43 -0
  86. package/lib/token-store/forward-token-comment-cursor.js +57 -0
  87. package/lib/token-store/forward-token-cursor.js +61 -0
  88. package/lib/token-store/index.js +604 -0
  89. package/lib/token-store/limit-cursor.js +40 -0
  90. package/lib/token-store/padded-token-cursor.js +38 -0
  91. package/lib/token-store/skip-cursor.js +42 -0
  92. package/lib/token-store/utils.js +100 -0
  93. package/lib/util/source-code-fixer.js +35 -39
  94. package/lib/util/source-code.js +31 -15
  95. package/messages/extend-config-missing.txt +3 -0
  96. package/package.json +2 -2
  97. package/lib/token-store.js +0 -203
@@ -128,8 +128,8 @@ module.exports = {
128
128
  const options = normalizedOptions[node.type];
129
129
  const openBrace = sourceCode.getFirstToken(node);
130
130
  const closeBrace = sourceCode.getLastToken(node);
131
- let first = sourceCode.getTokenOrCommentAfter(openBrace);
132
- let last = sourceCode.getTokenOrCommentBefore(closeBrace);
131
+ let first = sourceCode.getTokenAfter(openBrace, { includeComments: true });
132
+ let last = sourceCode.getTokenBefore(closeBrace, { includeComments: true });
133
133
  const needsLinebreaks = (
134
134
  node.properties.length >= options.minProperties ||
135
135
  (
@@ -206,14 +206,8 @@ module.exports = {
206
206
  */
207
207
  function getClosingBraceOfObject(node) {
208
208
  const lastProperty = node.properties[node.properties.length - 1];
209
- let token = sourceCode.getTokenAfter(lastProperty);
210
209
 
211
- // skip ')' and trailing commas.
212
- while (token.type !== "Punctuator" || token.value !== "}") {
213
- token = sourceCode.getTokenAfter(token);
214
- }
215
-
216
- return token;
210
+ return sourceCode.getTokenAfter(lastProperty, astUtils.isClosingBraceToken);
217
211
  }
218
212
 
219
213
  /**
@@ -254,15 +248,9 @@ module.exports = {
254
248
  firstSpecifier = node.specifiers[1];
255
249
  }
256
250
 
257
- const first = sourceCode.getTokenBefore(firstSpecifier);
258
- let last = sourceCode.getTokenAfter(lastSpecifier);
259
-
260
- // to support a trailing comma.
261
- if (last.value === ",") {
262
- last = sourceCode.getTokenAfter(last);
263
- }
264
-
265
- const second = sourceCode.getTokenAfter(first),
251
+ const first = sourceCode.getTokenBefore(firstSpecifier),
252
+ last = sourceCode.getTokenAfter(lastSpecifier, astUtils.isNotCommaToken),
253
+ second = sourceCode.getTokenAfter(first),
266
254
  penultimate = sourceCode.getTokenBefore(last);
267
255
 
268
256
  validateBraceSpacing(node, first, second, penultimate, last);
@@ -280,15 +268,9 @@ module.exports = {
280
268
 
281
269
  const firstSpecifier = node.specifiers[0],
282
270
  lastSpecifier = node.specifiers[node.specifiers.length - 1],
283
- first = sourceCode.getTokenBefore(firstSpecifier);
284
- let last = sourceCode.getTokenAfter(lastSpecifier);
285
-
286
- // to support a trailing comma.
287
- if (last.value === ",") {
288
- last = sourceCode.getTokenAfter(last);
289
- }
290
-
291
- const second = sourceCode.getTokenAfter(first),
271
+ first = sourceCode.getTokenBefore(firstSpecifier),
272
+ last = sourceCode.getTokenAfter(lastSpecifier, astUtils.isNotCommaToken),
273
+ second = sourceCode.getTokenAfter(first),
292
274
  penultimate = sourceCode.getTokenBefore(last);
293
275
 
294
276
  validateBraceSpacing(node, first, second, penultimate, last);
@@ -34,9 +34,9 @@ module.exports = {
34
34
 
35
35
  create(context) {
36
36
  const allowSameLine = context.options[0] && Boolean(context.options[0].allowMultiplePropertiesPerLine);
37
- const errorMessage = allowSameLine ?
38
- "Object properties must go on a new line if they aren't all on the same line." :
39
- "Object properties must go on a new line.";
37
+ const errorMessage = allowSameLine
38
+ ? "Object properties must go on a new line if they aren't all on the same line."
39
+ : "Object properties must go on a new line.";
40
40
 
41
41
  const sourceCode = context.getSourceCode();
42
42
 
@@ -215,8 +215,8 @@ module.exports = {
215
215
  * @returns {Object} A fix for this node
216
216
  */
217
217
  function makeFunctionShorthand(fixer, node) {
218
- const firstKeyToken = node.computed ? sourceCode.getTokens(node).find(token => token.value === "[") : sourceCode.getFirstToken(node.key);
219
- const lastKeyToken = node.computed ? sourceCode.getTokensBetween(node.key, node.value).find(token => token.value === "]") : sourceCode.getLastToken(node.key);
218
+ const firstKeyToken = node.computed ? sourceCode.getFirstToken(node, astUtils.isOpeningBracketToken) : sourceCode.getFirstToken(node.key);
219
+ const lastKeyToken = node.computed ? sourceCode.getFirstTokenBetween(node.key, node.value, astUtils.isClosingBracketToken) : sourceCode.getLastToken(node.key);
220
220
  const keyText = sourceCode.text.slice(firstKeyToken.range[0], lastKeyToken.range[1]);
221
221
  let keyPrefix = "";
222
222
 
@@ -108,7 +108,7 @@ module.exports = {
108
108
  * @returns {Token} The operator token in the node
109
109
  */
110
110
  function getOperatorToken(node) {
111
- return sourceCode.getTokensBetween(node.left, node.right).find(token => token.value === node.operator);
111
+ return sourceCode.getFirstTokenBetween(node.left, node.right, token => token.value === node.operator);
112
112
  }
113
113
 
114
114
  /**
@@ -5,14 +5,16 @@
5
5
 
6
6
  "use strict";
7
7
 
8
+ //------------------------------------------------------------------------------
9
+ // Requirements
10
+ //------------------------------------------------------------------------------
11
+
8
12
  const astUtils = require("../ast-utils");
9
13
 
10
14
  //------------------------------------------------------------------------------
11
15
  // Rule Definition
12
16
  //------------------------------------------------------------------------------
13
17
 
14
- const LINEBREAK_REGEX = /\r\n|\r|\n|\u2028|\u2029/g;
15
-
16
18
  module.exports = {
17
19
  meta: {
18
20
  docs: {
@@ -85,7 +87,7 @@ module.exports = {
85
87
  if (hasLinebreakBefore !== hasLinebreakAfter && desiredStyle !== "none") {
86
88
 
87
89
  // If there is a comment before and after the operator, don't do a fix.
88
- if (sourceCode.getTokenOrCommentBefore(operatorToken) !== tokenBefore && sourceCode.getTokenOrCommentAfter(operatorToken) !== tokenAfter) {
90
+ if (sourceCode.getTokenBefore(operatorToken, { includeComments: true }) !== tokenBefore && sourceCode.getTokenAfter(operatorToken, { includeComments: true }) !== tokenAfter) {
89
91
  return null;
90
92
  }
91
93
 
@@ -100,6 +102,7 @@ module.exports = {
100
102
  newTextBefore = textAfter;
101
103
  newTextAfter = textBefore;
102
104
  } else {
105
+ const LINEBREAK_REGEX = astUtils.createGlobalLinebreakMatcher();
103
106
 
104
107
  // Otherwise, if no linebreak is desired and no comments interfere, replace the linebreaks with empty strings.
105
108
  newTextBefore = desiredStyle === "before" || textBefore.trim() ? textBefore : textBefore.replace(LINEBREAK_REGEX, "");
@@ -129,19 +132,14 @@ module.exports = {
129
132
  * @returns {void}
130
133
  */
131
134
  function validateNode(node, leftSide) {
132
- let leftToken = sourceCode.getLastToken(leftSide);
133
- let operatorToken = sourceCode.getTokenAfter(leftToken);
134
135
 
135
136
  // When the left part of a binary expression is a single expression wrapped in
136
137
  // parentheses (ex: `(a) + b`), leftToken will be the last token of the expression
137
138
  // and operatorToken will be the closing parenthesis.
138
139
  // The leftToken should be the last closing parenthesis, and the operatorToken
139
140
  // should be the token right after that.
140
- while (operatorToken.value === ")") {
141
- leftToken = operatorToken;
142
- operatorToken = sourceCode.getTokenAfter(operatorToken);
143
- }
144
-
141
+ const operatorToken = sourceCode.getTokenAfter(leftSide, astUtils.isNotClosingParenToken);
142
+ const leftToken = sourceCode.getTokenBefore(operatorToken);
145
143
  const rightToken = sourceCode.getTokenAfter(operatorToken);
146
144
  const operator = operatorToken.value;
147
145
  const operatorStyleOverride = styleOverrides[operator];
@@ -101,7 +101,7 @@ module.exports = {
101
101
  let first = token;
102
102
 
103
103
  do {
104
- first = sourceCode.getTokenOrCommentAfter(first);
104
+ first = sourceCode.getTokenAfter(first, { includeComments: true });
105
105
  } while (isComment(first) && first.loc.start.line === tokenStartLine);
106
106
 
107
107
  const firstLine = first.loc.start.line;
@@ -120,7 +120,7 @@ module.exports = {
120
120
  let last = token;
121
121
 
122
122
  do {
123
- last = sourceCode.getTokenOrCommentBefore(last);
123
+ last = sourceCode.getTokenBefore(last, { includeComments: true });
124
124
  } while (isComment(last) && last.loc.end.line === blockEnd);
125
125
 
126
126
  const lastLine = last.loc.end.line;
@@ -182,7 +182,7 @@ module.exports = {
182
182
  }
183
183
  } else {
184
184
  if (blockHasTopPadding) {
185
- const nextToken = sourceCode.getTokenOrCommentAfter(openBrace);
185
+ const nextToken = sourceCode.getTokenAfter(openBrace, { includeComments: true });
186
186
 
187
187
  context.report({
188
188
  node,
@@ -195,7 +195,7 @@ module.exports = {
195
195
  }
196
196
 
197
197
  if (blockHasBottomPadding) {
198
- const previousToken = sourceCode.getTokenOrCommentBefore(closeBrace);
198
+ const previousToken = sourceCode.getTokenBefore(closeBrace, { includeComments: true });
199
199
 
200
200
  context.report({
201
201
  node,
@@ -108,7 +108,7 @@ module.exports = {
108
108
  return null;
109
109
  }
110
110
 
111
- const propertyDot = sourceCode.getTokensBetween(applied, node.callee.property).find(token => token.value === ".");
111
+ const propertyDot = sourceCode.getFirstTokenBetween(applied, node.callee.property, token => token.value === ".");
112
112
 
113
113
  return fixer.replaceTextRange([propertyDot.range[0], node.range[1]], `(...${sourceCode.getText(node.arguments[1])})`);
114
114
  }
@@ -157,7 +157,7 @@ module.exports = {
157
157
  }
158
158
 
159
159
  if (isConcatenation(currentNode) && hasStringLiteral(currentNode) && hasNonStringLiteral(currentNode)) {
160
- const plusSign = sourceCode.getTokensBetween(currentNode.left, currentNode.right).find(token => token.value === "+");
160
+ const plusSign = sourceCode.getFirstTokenBetween(currentNode.left, currentNode.right, token => token.value === "+");
161
161
  const textBeforePlus = getTextBetween(currentNode.left, plusSign);
162
162
  const textAfterPlus = getTextBetween(plusSign, currentNode.right);
163
163
  const leftEndsWithCurly = endsWithTemplateCurly(currentNode.left);
@@ -33,6 +33,9 @@ const QUOTE_SETTINGS = {
33
33
  }
34
34
  };
35
35
 
36
+ // An unescaped newline is a newline preceded by an even number of backslashes.
37
+ const UNESCAPED_LINEBREAK_PATTERN = new RegExp(String.raw`(^|[^\\])(\\\\)*[${Array.from(astUtils.LINEBREAKS).join("")}]`);
38
+
36
39
  /**
37
40
  * Switches quoting of javascript string between ' " and `
38
41
  * escaping and unescaping as necessary.
@@ -254,15 +257,16 @@ module.exports = {
254
257
  TemplateLiteral(node) {
255
258
 
256
259
  // If backticks are expected or it's a tagged template, then this shouldn't throw an errors
257
- if (allowTemplateLiterals || quoteOption === "backtick" || node.parent.type === "TaggedTemplateExpression") {
260
+ if (
261
+ allowTemplateLiterals ||
262
+ quoteOption === "backtick" ||
263
+ node.parent.type === "TaggedTemplateExpression" && node === node.parent.quasi
264
+ ) {
258
265
  return;
259
266
  }
260
267
 
261
- /*
262
- * A warning should be produced if the template literal only has one TemplateElement, and has no unescaped newlines.
263
- * An unescaped newline is a newline preceded by an even number of backslashes.
264
- */
265
- const shouldWarn = node.quasis.length === 1 && !/(^|[^\\])(\\\\)*[\r\n\u2028\u2029]/.test(node.quasis[0].value.raw);
268
+ // A warning should be produced if the template literal only has one TemplateElement, and has no unescaped newlines.
269
+ const shouldWarn = node.quasis.length === 1 && !UNESCAPED_LINEBREAK_PATTERN.test(node.quasis[0].value.raw);
266
270
 
267
271
  if (shouldWarn) {
268
272
  context.report({
@@ -206,6 +206,10 @@ module.exports = {
206
206
  DebuggerStatement: checkNode,
207
207
  ReturnStatement: checkNode,
208
208
  ThrowStatement: checkNode,
209
+ ImportDeclaration: checkNode,
210
+ ExportNamedDeclaration: checkNode,
211
+ ExportAllDeclaration: checkNode,
212
+ ExportDefaultDeclaration: checkNode,
209
213
  ForStatement(node) {
210
214
  if (node.init) {
211
215
  checkSemicolonSpacing(sourceCode.getTokenAfter(node.init), node);
@@ -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
  //------------------------------------------------------------------------------
@@ -104,7 +110,7 @@ module.exports = {
104
110
  const isAnonymousGenerator = node.generator && !isNamed;
105
111
  const isNormalArrow = isArrow && !node.async;
106
112
  const isArrowWithoutParens = isArrow && sourceCode.getFirstToken(node, 1).value !== "(";
107
- let forbidSpacing, requireSpacing, rightToken;
113
+ let forbidSpacing, requireSpacing;
108
114
 
109
115
  // isAnonymousGenerator → `generator-star-spacing` should warn it. E.g. `function* () {}`
110
116
  // isNormalArrow → ignore always.
@@ -124,10 +130,7 @@ module.exports = {
124
130
  requireSpacing = requireAnonymousFunctionSpacing;
125
131
  }
126
132
 
127
- rightToken = sourceCode.getFirstToken(node);
128
- while (rightToken.value !== "(") {
129
- rightToken = sourceCode.getTokenAfter(rightToken);
130
- }
133
+ const rightToken = sourceCode.getFirstToken(node, astUtils.isOpeningParenToken);
131
134
  const leftToken = sourceCode.getTokenBefore(rightToken);
132
135
  const location = leftToken.loc.end;
133
136
 
@@ -5,6 +5,7 @@
5
5
  "use strict";
6
6
 
7
7
  const lodash = require("lodash");
8
+ const astUtils = require("../ast-utils");
8
9
 
9
10
  //------------------------------------------------------------------------------
10
11
  // Helpers
@@ -88,8 +89,7 @@ function createExceptionsPattern(exceptions) {
88
89
  pattern += exceptions.map(escapeAndRepeat).join("|");
89
90
  pattern += ")";
90
91
  }
91
-
92
- pattern += "(?:$|[\n\r]))";
92
+ pattern += `(?:$|[${Array.from(astUtils.LINEBREAKS).join("")}]))`;
93
93
  }
94
94
 
95
95
  return pattern;
@@ -212,8 +212,8 @@ module.exports = {
212
212
  */
213
213
  function enterFunction(node) {
214
214
  const isBlock = node.body.type === "BlockStatement",
215
- useStrictDirectives = isBlock ?
216
- getUseStrictDirectives(node.body.body) : [];
215
+ useStrictDirectives = isBlock
216
+ ? getUseStrictDirectives(node.body.body) : [];
217
217
 
218
218
  if (mode === "function") {
219
219
  enterFunctionInFunctionMode(node, useStrictDirectives);
@@ -45,7 +45,7 @@ module.exports = {
45
45
  loc: location,
46
46
  message: "Expected Unicode BOM (Byte Order Mark).",
47
47
  fix(fixer) {
48
- return fixer.insertTextBefore(node, "\uFEFF");
48
+ return fixer.insertTextBeforeRange([0, 1], "\uFEFF");
49
49
  }
50
50
  });
51
51
  } else if (sourceCode.hasBOM && (requireBOM === "never")) {
@@ -5,6 +5,10 @@
5
5
 
6
6
  "use strict";
7
7
 
8
+ //------------------------------------------------------------------------------
9
+ // Requirements
10
+ //------------------------------------------------------------------------------
11
+
8
12
  const astUtils = require("../ast-utils");
9
13
 
10
14
  //------------------------------------------------------------------------------
@@ -51,11 +55,7 @@ module.exports = {
51
55
  * @private
52
56
  */
53
57
  function wrapped(node) {
54
- const previousToken = sourceCode.getTokenBefore(node),
55
- nextToken = sourceCode.getTokenAfter(node);
56
-
57
- return previousToken && previousToken.value === "(" &&
58
- nextToken && nextToken.value === ")";
58
+ return astUtils.isParenthesised(sourceCode, node);
59
59
  }
60
60
 
61
61
  /**
package/lib/rules/yoda.js CHANGED
@@ -235,12 +235,7 @@ module.exports = {
235
235
  * paren token.
236
236
  */
237
237
  function isParenWrapped() {
238
- let tokenBefore, tokenAfter;
239
-
240
- return ((tokenBefore = sourceCode.getTokenBefore(node)) &&
241
- tokenBefore.value === "(" &&
242
- (tokenAfter = sourceCode.getTokenAfter(node)) &&
243
- tokenAfter.value === ")");
238
+ return astUtils.isParenthesised(sourceCode, node);
244
239
  }
245
240
 
246
241
  return (node.type === "LogicalExpression" &&
@@ -269,7 +264,7 @@ module.exports = {
269
264
  * @returns {string} A string representation of the node with the sides and operator flipped
270
265
  */
271
266
  function getFlippedString(node) {
272
- const operatorToken = sourceCode.getTokensBetween(node.left, node.right).find(token => token.value === node.operator);
267
+ const operatorToken = sourceCode.getFirstTokenBetween(node.left, node.right, token => token.value === node.operator);
273
268
  const textBeforeOperator = sourceCode.getText().slice(sourceCode.getTokenBefore(operatorToken).range[1], operatorToken.range[0]);
274
269
  const textAfterOperator = sourceCode.getText().slice(operatorToken.range[1], sourceCode.getTokenAfter(operatorToken).range[0]);
275
270
  const leftText = sourceCode.getText().slice(sourceCode.getFirstToken(node).range[0], sourceCode.getTokenBefore(operatorToken).range[1]);
@@ -147,6 +147,12 @@ function RuleTester(testerConfig) {
147
147
  lodash.cloneDeep(defaultConfig),
148
148
  testerConfig
149
149
  );
150
+
151
+ /**
152
+ * Rule definitions to define before tests.
153
+ * @type {Object}
154
+ */
155
+ this.rules = {};
150
156
  }
151
157
 
152
158
  /**
@@ -239,7 +245,7 @@ RuleTester.prototype = {
239
245
  * @returns {void}
240
246
  */
241
247
  defineRule(name, rule) {
242
- eslint.defineRule(name, rule);
248
+ this.rules[name] = rule;
243
249
  },
244
250
 
245
251
  /**
@@ -488,13 +494,12 @@ RuleTester.prototype = {
488
494
  assert.fail(messages[i], null, "Error should be a string or object.");
489
495
  }
490
496
  }
497
+ }
491
498
 
492
- if (item.hasOwnProperty("output")) {
493
- const fixResult = SourceCodeFixer.applyFixes(eslint.getSourceCode(), messages);
494
-
495
- assert.equal(fixResult.output, item.output, "Output is incorrect.");
496
- }
499
+ if (item.hasOwnProperty("output")) {
500
+ const fixResult = SourceCodeFixer.applyFixes(eslint.getSourceCode(), messages);
497
501
 
502
+ assert.equal(fixResult.output, item.output, "Output is incorrect.");
498
503
  }
499
504
 
500
505
  assertASTDidntChange(result.beforeAST, result.afterAST);
@@ -508,6 +513,7 @@ RuleTester.prototype = {
508
513
  RuleTester.describe("valid", () => {
509
514
  test.valid.forEach(valid => {
510
515
  RuleTester.it(valid.code || valid, () => {
516
+ eslint.defineRules(this.rules);
511
517
  testValidTemplate(ruleName, valid);
512
518
  });
513
519
  });
@@ -516,6 +522,7 @@ RuleTester.prototype = {
516
522
  RuleTester.describe("invalid", () => {
517
523
  test.invalid.forEach(invalid => {
518
524
  RuleTester.it(invalid.code, () => {
525
+ eslint.defineRules(this.rules);
519
526
  testInvalidTemplate(ruleName, invalid);
520
527
  });
521
528
  });
@@ -0,0 +1,57 @@
1
+ /**
2
+ * @fileoverview Define the cursor which iterates tokens and comments in reverse.
3
+ * @author Toru Nagashima
4
+ */
5
+ "use strict";
6
+
7
+ //------------------------------------------------------------------------------
8
+ // Requirements
9
+ //------------------------------------------------------------------------------
10
+
11
+ const Cursor = require("./cursor");
12
+ const utils = require("./utils");
13
+
14
+ //------------------------------------------------------------------------------
15
+ // Exports
16
+ //------------------------------------------------------------------------------
17
+
18
+ /**
19
+ * The cursor which iterates tokens and comments in reverse.
20
+ */
21
+ module.exports = class BackwardTokenCommentCursor extends Cursor {
22
+
23
+ /**
24
+ * Initializes this cursor.
25
+ * @param {Token[]} tokens - The array of tokens.
26
+ * @param {Comment[]} comments - The array of comments.
27
+ * @param {Object} indexMap - The map from locations to indices in `tokens`.
28
+ * @param {number} startLoc - The start location of the iteration range.
29
+ * @param {number} endLoc - The end location of the iteration range.
30
+ */
31
+ constructor(tokens, comments, indexMap, startLoc, endLoc) {
32
+ super();
33
+ this.tokens = tokens;
34
+ this.comments = comments;
35
+ this.tokenIndex = utils.getLastIndex(tokens, indexMap, endLoc);
36
+ this.commentIndex = utils.search(comments, endLoc) - 1;
37
+ this.border = startLoc;
38
+ }
39
+
40
+ /** @inheritdoc */
41
+ moveNext() {
42
+ const token = (this.tokenIndex >= 0) ? this.tokens[this.tokenIndex] : null;
43
+ const comment = (this.commentIndex >= 0) ? this.comments[this.commentIndex] : null;
44
+
45
+ if (token && (!comment || token.range[1] > comment.range[1])) {
46
+ this.current = token;
47
+ this.tokenIndex -= 1;
48
+ } else if (comment) {
49
+ this.current = comment;
50
+ this.commentIndex -= 1;
51
+ } else {
52
+ this.current = null;
53
+ }
54
+
55
+ return Boolean(this.current) && (this.border === -1 || this.current.range[0] >= this.border);
56
+ }
57
+ };
@@ -0,0 +1,56 @@
1
+ /**
2
+ * @fileoverview Define the cursor which iterates tokens only in reverse.
3
+ * @author Toru Nagashima
4
+ */
5
+ "use strict";
6
+
7
+ //------------------------------------------------------------------------------
8
+ // Requirements
9
+ //------------------------------------------------------------------------------
10
+
11
+ const Cursor = require("./cursor");
12
+ const utils = require("./utils");
13
+
14
+ //------------------------------------------------------------------------------
15
+ // Exports
16
+ //------------------------------------------------------------------------------
17
+
18
+ /**
19
+ * The cursor which iterates tokens only in reverse.
20
+ */
21
+ module.exports = class BackwardTokenCursor extends Cursor {
22
+
23
+ /**
24
+ * Initializes this cursor.
25
+ * @param {Token[]} tokens - The array of tokens.
26
+ * @param {Comment[]} comments - The array of comments.
27
+ * @param {Object} indexMap - The map from locations to indices in `tokens`.
28
+ * @param {number} startLoc - The start location of the iteration range.
29
+ * @param {number} endLoc - The end location of the iteration range.
30
+ */
31
+ constructor(tokens, comments, indexMap, startLoc, endLoc) {
32
+ super();
33
+ this.tokens = tokens;
34
+ this.index = utils.getLastIndex(tokens, indexMap, endLoc);
35
+ this.indexEnd = utils.getFirstIndex(tokens, indexMap, startLoc);
36
+ }
37
+
38
+ /** @inheritdoc */
39
+ moveNext() {
40
+ if (this.index >= this.indexEnd) {
41
+ this.current = this.tokens[this.index];
42
+ this.index -= 1;
43
+ return true;
44
+ }
45
+ return false;
46
+ }
47
+
48
+ //
49
+ // Shorthand for performance.
50
+ //
51
+
52
+ /** @inheritdoc */
53
+ getOneToken() {
54
+ return (this.index >= this.indexEnd) ? this.tokens[this.index] : null;
55
+ }
56
+ };
@@ -0,0 +1,76 @@
1
+ /**
2
+ * @fileoverview Define the abstract class about cursors which iterate tokens.
3
+ * @author Toru Nagashima
4
+ */
5
+ "use strict";
6
+
7
+ //------------------------------------------------------------------------------
8
+ // Exports
9
+ //------------------------------------------------------------------------------
10
+
11
+ /**
12
+ * The abstract class about cursors which iterate tokens.
13
+ *
14
+ * This class has 2 abstract methods.
15
+ *
16
+ * - `current: Token | Comment | null` ... The current token.
17
+ * - `moveNext(): boolean` ... Moves this cursor to the next token. If the next token didn't exist, it returns `false`.
18
+ *
19
+ * This is similar to ES2015 Iterators.
20
+ * However, Iterators were slow (at 2017-01), so I created this class as similar to C# IEnumerable.
21
+ *
22
+ * There are the following known sub classes.
23
+ *
24
+ * - ForwardTokenCursor .......... The cursor which iterates tokens only.
25
+ * - BackwardTokenCursor ......... The cursor which iterates tokens only in reverse.
26
+ * - ForwardTokenCommentCursor ... The cursor which iterates tokens and comments.
27
+ * - BackwardTokenCommentCursor .. The cursor which iterates tokens and comments in reverse.
28
+ * - DecorativeCursor
29
+ * - FilterCursor ............ The cursor which ignores the specified tokens.
30
+ * - SkipCursor .............. The cursor which ignores the first few tokens.
31
+ * - LimitCursor ............. The cursor which limits the count of tokens.
32
+ *
33
+ */
34
+ module.exports = class Cursor {
35
+
36
+ /**
37
+ * Initializes this cursor.
38
+ */
39
+ constructor() {
40
+ this.current = null;
41
+ }
42
+
43
+ /**
44
+ * Gets the first token.
45
+ * This consumes this cursor.
46
+ * @returns {Token|Comment} The first token or null.
47
+ */
48
+ getOneToken() {
49
+ return this.moveNext() ? this.current : null;
50
+ }
51
+
52
+ /**
53
+ * Gets the first tokens.
54
+ * This consumes this cursor.
55
+ * @returns {(Token|Comment)[]} All tokens.
56
+ */
57
+ getAllTokens() {
58
+ const tokens = [];
59
+
60
+ while (this.moveNext()) {
61
+ tokens.push(this.current);
62
+ }
63
+
64
+ return tokens;
65
+ }
66
+
67
+ /**
68
+ * Moves this cursor to the next token.
69
+ * @returns {boolean} `true` if the next token exists.
70
+ * @abstract
71
+ */
72
+ /* istanbul ignore next */
73
+ moveNext() { // eslint-disable-line class-methods-use-this
74
+ throw new Error("Not implemented.");
75
+ }
76
+ };