eslint 4.18.0 → 4.19.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 (65) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/README.md +2 -2
  3. package/conf/environments.js +3 -1
  4. package/conf/eslint-recommended.js +0 -0
  5. package/lib/ast-utils.js +25 -29
  6. package/lib/cli-engine.js +29 -28
  7. package/lib/code-path-analysis/code-path-state.js +5 -5
  8. package/lib/code-path-analysis/code-path.js +11 -6
  9. package/lib/code-path-analysis/fork-context.js +10 -12
  10. package/lib/config/config-file.js +20 -11
  11. package/lib/config/config-ops.js +8 -10
  12. package/lib/config/config-rule.js +2 -3
  13. package/lib/config/plugins.js +20 -0
  14. package/lib/config.js +7 -8
  15. package/lib/file-finder.js +9 -10
  16. package/lib/ignored-paths.js +4 -4
  17. package/lib/linter.js +397 -406
  18. package/lib/load-rules.js +6 -7
  19. package/lib/rules/accessor-pairs.js +4 -4
  20. package/lib/rules/array-callback-return.js +8 -6
  21. package/lib/rules/array-element-newline.js +4 -4
  22. package/lib/rules/curly.js +11 -10
  23. package/lib/rules/generator-star-spacing.js +1 -2
  24. package/lib/rules/indent-legacy.js +7 -10
  25. package/lib/rules/indent.js +51 -29
  26. package/lib/rules/keyword-spacing.js +6 -18
  27. package/lib/rules/max-len.js +12 -5
  28. package/lib/rules/no-await-in-loop.js +1 -1
  29. package/lib/rules/no-buffer-constructor.js +1 -1
  30. package/lib/rules/no-control-regex.js +51 -72
  31. package/lib/rules/no-else-return.js +7 -6
  32. package/lib/rules/no-empty-character-class.js +1 -1
  33. package/lib/rules/no-eval.js +7 -8
  34. package/lib/rules/no-extra-parens.js +5 -4
  35. package/lib/rules/no-implicit-coercion.js +6 -9
  36. package/lib/rules/no-invalid-regexp.js +53 -36
  37. package/lib/rules/no-irregular-whitespace.js +1 -1
  38. package/lib/rules/no-loop-func.js +9 -11
  39. package/lib/rules/no-magic-numbers.js +17 -10
  40. package/lib/rules/no-return-assign.js +4 -3
  41. package/lib/rules/no-unexpected-multiline.js +1 -1
  42. package/lib/rules/no-unsafe-finally.js +7 -4
  43. package/lib/rules/no-useless-escape.js +2 -2
  44. package/lib/rules/no-useless-return.js +10 -9
  45. package/lib/rules/no-var.js +8 -8
  46. package/lib/rules/object-curly-newline.js +2 -1
  47. package/lib/rules/one-var.js +140 -97
  48. package/lib/rules/padding-line-between-statements.js +6 -4
  49. package/lib/rules/prefer-arrow-callback.js +5 -4
  50. package/lib/rules/prefer-template.js +5 -3
  51. package/lib/rules/space-unary-ops.js +1 -3
  52. package/lib/rules/spaced-comment.js +3 -7
  53. package/lib/rules/template-tag-spacing.js +0 -0
  54. package/lib/rules/valid-jsdoc.js +6 -6
  55. package/lib/rules/vars-on-top.js +7 -17
  56. package/lib/timing.js +3 -5
  57. package/lib/util/glob-util.js +11 -11
  58. package/lib/util/interpolate.js +5 -1
  59. package/lib/util/naming.js +11 -10
  60. package/lib/util/npm-util.js +4 -6
  61. package/lib/util/path-util.js +6 -8
  62. package/lib/util/source-code-util.js +23 -26
  63. package/lib/util/source-code.js +4 -3
  64. package/package.json +4 -3
  65. package/conf/default-config-options.js +0 -29
package/lib/load-rules.js CHANGED
@@ -20,16 +20,15 @@ const rulesDirCache = {};
20
20
 
21
21
  /**
22
22
  * Load all rule modules from specified directory.
23
- * @param {string} [rulesDir] Path to rules directory, may be relative. Defaults to `lib/rules`.
23
+ * @param {string} [relativeRulesDir] Path to rules directory, may be relative. Defaults to `lib/rules`.
24
24
  * @param {string} cwd Current working directory
25
25
  * @returns {Object} Loaded rule modules by rule ids (file names).
26
26
  */
27
- module.exports = function(rulesDir, cwd) {
28
- if (!rulesDir) {
29
- rulesDir = path.join(__dirname, "rules");
30
- } else {
31
- rulesDir = path.resolve(cwd, rulesDir);
32
- }
27
+ module.exports = function(relativeRulesDir, cwd) {
28
+
29
+ const rulesDir = relativeRulesDir
30
+ ? path.resolve(cwd, relativeRulesDir)
31
+ : path.join(__dirname, "rules");
33
32
 
34
33
  // cache will help performance as IO operation are expensive
35
34
  if (rulesDirCache[rulesDir]) {
@@ -58,11 +58,11 @@ function isPropertyDescriptor(node) {
58
58
  * Object.defineProperties(obj, {foo: {set: ...}})
59
59
  * Object.create(proto, {foo: {set: ...}})
60
60
  */
61
- node = node.parent.parent;
61
+ const grandparent = node.parent.parent;
62
62
 
63
- return node.type === "ObjectExpression" && (
64
- isArgumentOfMethodCall(node, 1, "Object", "create") ||
65
- isArgumentOfMethodCall(node, 1, "Object", "defineProperties")
63
+ return grandparent.type === "ObjectExpression" && (
64
+ isArgumentOfMethodCall(grandparent, 1, "Object", "create") ||
65
+ isArgumentOfMethodCall(grandparent, 1, "Object", "defineProperties")
66
66
  );
67
67
  }
68
68
 
@@ -71,8 +71,10 @@ function isTargetMethod(node) {
71
71
  * @returns {boolean} `true` if the node is the callback of an array method.
72
72
  */
73
73
  function isCallbackOfArrayMethod(node) {
74
- while (node) {
75
- const parent = node.parent;
74
+ let currentNode = node;
75
+
76
+ while (currentNode) {
77
+ const parent = currentNode.parent;
76
78
 
77
79
  switch (parent.type) {
78
80
 
@@ -82,7 +84,7 @@ function isCallbackOfArrayMethod(node) {
82
84
  */
83
85
  case "LogicalExpression":
84
86
  case "ConditionalExpression":
85
- node = parent;
87
+ currentNode = parent;
86
88
  break;
87
89
 
88
90
  /*
@@ -99,7 +101,7 @@ function isCallbackOfArrayMethod(node) {
99
101
  if (func === null || !astUtils.isCallee(func)) {
100
102
  return false;
101
103
  }
102
- node = func.parent;
104
+ currentNode = func.parent;
103
105
  break;
104
106
  }
105
107
 
@@ -112,13 +114,13 @@ function isCallbackOfArrayMethod(node) {
112
114
  if (astUtils.isArrayFromMethod(parent.callee)) {
113
115
  return (
114
116
  parent.arguments.length >= 2 &&
115
- parent.arguments[1] === node
117
+ parent.arguments[1] === currentNode
116
118
  );
117
119
  }
118
120
  if (isTargetMethod(parent.callee)) {
119
121
  return (
120
122
  parent.arguments.length >= 1 &&
121
- parent.arguments[0] === node
123
+ parent.arguments[0] === currentNode
122
124
  );
123
125
  }
124
126
  return false;
@@ -59,16 +59,16 @@ module.exports = {
59
59
  /**
60
60
  * Normalizes a given option value.
61
61
  *
62
- * @param {string|Object|undefined} option - An option value to parse.
62
+ * @param {string|Object|undefined} providedOption - An option value to parse.
63
63
  * @returns {{multiline: boolean, minItems: number}} Normalized option object.
64
64
  */
65
- function normalizeOptionValue(option) {
65
+ function normalizeOptionValue(providedOption) {
66
66
  let multiline = false;
67
67
  let minItems;
68
68
 
69
- option = option || "always";
69
+ const option = providedOption || "always";
70
70
 
71
- if (option === "always" || option.minItems === 0) {
71
+ if (!option || option === "always" || option.minItems === 0) {
72
72
  minItems = 0;
73
73
  } else if (option === "never") {
74
74
  minItems = Number.POSITIVE_INFINITY;
@@ -137,12 +137,14 @@ module.exports = {
137
137
  return true;
138
138
  }
139
139
 
140
- node = node.consequent.body[0];
141
- while (node) {
142
- if (node.type === "IfStatement" && !node.alternate) {
140
+ for (
141
+ let currentNode = node.consequent.body[0];
142
+ currentNode;
143
+ currentNode = astUtils.getTrailingStatement(currentNode)
144
+ ) {
145
+ if (currentNode.type === "IfStatement" && !currentNode.alternate) {
143
146
  return true;
144
147
  }
145
- node = astUtils.getTrailingStatement(node);
146
148
  }
147
149
  }
148
150
 
@@ -311,14 +313,13 @@ module.exports = {
311
313
  function prepareIfChecks(node) {
312
314
  const preparedChecks = [];
313
315
 
314
- do {
315
- preparedChecks.push(prepareCheck(node, node.consequent, "if", { condition: true }));
316
- if (node.alternate && node.alternate.type !== "IfStatement") {
317
- preparedChecks.push(prepareCheck(node, node.alternate, "else"));
316
+ for (let currentNode = node; currentNode; currentNode = currentNode.alternate) {
317
+ preparedChecks.push(prepareCheck(currentNode, currentNode.consequent, "if", { condition: true }));
318
+ if (currentNode.alternate && currentNode.alternate.type !== "IfStatement") {
319
+ preparedChecks.push(prepareCheck(currentNode, currentNode.alternate, "else"));
318
320
  break;
319
321
  }
320
- node = node.alternate;
321
- } while (node);
322
+ }
322
323
 
323
324
  if (consistent) {
324
325
 
@@ -85,7 +85,6 @@ module.exports = {
85
85
  }
86
86
 
87
87
  const modes = (function(option) {
88
- option = option || {};
89
88
  const defaults = optionToDefinition(option, optionDefinitions.before);
90
89
 
91
90
  return {
@@ -93,7 +92,7 @@ module.exports = {
93
92
  anonymous: optionToDefinition(option.anonymous, defaults),
94
93
  method: optionToDefinition(option.method, defaults)
95
94
  };
96
- }(context.options[0]));
95
+ }(context.options[0] || {}));
97
96
 
98
97
  const sourceCode = context.getSourceCode();
99
98
 
@@ -505,12 +505,9 @@ module.exports = {
505
505
  */
506
506
  function getParentNodeByType(node, type, stopAtList) {
507
507
  let parent = node.parent;
508
+ const stopAtSet = new Set(stopAtList || ["Program"]);
508
509
 
509
- if (!stopAtList) {
510
- stopAtList = ["Program"];
511
- }
512
-
513
- while (parent.type !== type && stopAtList.indexOf(parent.type) === -1 && parent.type !== "Program") {
510
+ while (parent.type !== type && !stopAtSet.has(parent.type) && parent.type !== "Program") {
514
511
  parent = parent.parent;
515
512
  }
516
513
 
@@ -941,19 +938,19 @@ module.exports = {
941
938
  /**
942
939
  * Returns the expected indentation for the case statement
943
940
  * @param {ASTNode} node node to examine
944
- * @param {int} [switchIndent] indent for switch statement
941
+ * @param {int} [providedSwitchIndent] indent for switch statement
945
942
  * @returns {int} indent size
946
943
  */
947
- function expectedCaseIndent(node, switchIndent) {
944
+ function expectedCaseIndent(node, providedSwitchIndent) {
948
945
  const switchNode = (node.type === "SwitchStatement") ? node : node.parent;
946
+ const switchIndent = typeof providedSwitchIndent === "undefined"
947
+ ? getNodeIndent(switchNode).goodChar
948
+ : providedSwitchIndent;
949
949
  let caseIndent;
950
950
 
951
951
  if (caseIndentStore[switchNode.loc.start.line]) {
952
952
  return caseIndentStore[switchNode.loc.start.line];
953
953
  }
954
- if (typeof switchIndent === "undefined") {
955
- switchIndent = getNodeIndent(switchNode).goodChar;
956
- }
957
954
 
958
955
  if (switchNode.cases.length > 0 && options.SwitchCase === 0) {
959
956
  caseIndent = switchIndent;
@@ -779,6 +779,19 @@ module.exports = {
779
779
  return (statement.type === "ExpressionStatement" || statement.type === "VariableDeclaration") && statement.parent.type === "Program";
780
780
  }
781
781
 
782
+ /**
783
+ * Counts the number of linebreaks that follow the last non-whitespace character in a string
784
+ * @param {string} string The string to check
785
+ * @returns {number} The number of JavaScript linebreaks that follow the last non-whitespace character,
786
+ * or the total number of linebreaks if the string is all whitespace.
787
+ */
788
+ function countTrailingLinebreaks(string) {
789
+ const trailingWhitespace = string.match(/\s*$/)[0];
790
+ const linebreakMatches = trailingWhitespace.match(astUtils.createGlobalLinebreakMatcher());
791
+
792
+ return linebreakMatches === null ? 0 : linebreakMatches.length;
793
+ }
794
+
782
795
  /**
783
796
  * Check indentation for lists of elements (arrays, objects, function params)
784
797
  * @param {ASTNode[]} elements List of elements that should be offset
@@ -836,8 +849,12 @@ module.exports = {
836
849
  } else {
837
850
  const previousElement = elements[index - 1];
838
851
  const firstTokenOfPreviousElement = previousElement && getFirstToken(previousElement);
852
+ const previousElementLastToken = previousElement && sourceCode.getLastToken(previousElement);
839
853
 
840
- if (previousElement && sourceCode.getLastToken(previousElement).loc.end.line > startToken.loc.end.line) {
854
+ if (
855
+ previousElement &&
856
+ previousElementLastToken.loc.end.line - countTrailingLinebreaks(previousElementLastToken.value) > startToken.loc.end.line
857
+ ) {
841
858
  offsets.setDesiredOffsets(element.range, firstTokenOfPreviousElement, 0);
842
859
  }
843
860
  }
@@ -980,6 +997,8 @@ module.exports = {
980
997
  return !node || node.loc.start.line === token.loc.start.line;
981
998
  }
982
999
 
1000
+ const ignoredNodeFirstTokens = new Set();
1001
+
983
1002
  const baseOffsetListeners = {
984
1003
  "ArrayExpression, ArrayPattern"(node) {
985
1004
  const openingBracket = sourceCode.getFirstToken(node);
@@ -1010,15 +1029,6 @@ module.exports = {
1010
1029
  addElementListIndent(node.params, openingParen, closingParen, options.FunctionExpression.parameters);
1011
1030
  }
1012
1031
  addBlocklessNodeIndent(node.body);
1013
-
1014
- let arrowToken;
1015
-
1016
- if (node.params.length) {
1017
- arrowToken = sourceCode.getTokenAfter(node.params[node.params.length - 1], astUtils.isArrowToken);
1018
- } else {
1019
- arrowToken = sourceCode.getFirstToken(node, astUtils.isArrowToken);
1020
- }
1021
- offsets.setDesiredOffset(arrowToken, sourceCode.getFirstToken(node), 0);
1022
1032
  },
1023
1033
 
1024
1034
  AssignmentExpression(node) {
@@ -1128,9 +1138,6 @@ module.exports = {
1128
1138
  */
1129
1139
  offsets.setDesiredOffset(firstAlternateToken, firstToken, 1);
1130
1140
  }
1131
-
1132
- offsets.setDesiredOffsets([questionMarkToken.range[1], colonToken.range[0]], firstConsequentToken, 0);
1133
- offsets.setDesiredOffsets([colonToken.range[1], node.range[1]], firstAlternateToken, 0);
1134
1141
  }
1135
1142
  },
1136
1143
 
@@ -1272,20 +1279,9 @@ module.exports = {
1272
1279
  SwitchStatement(node) {
1273
1280
  const openingCurly = sourceCode.getTokenAfter(node.discriminant, astUtils.isOpeningBraceToken);
1274
1281
  const closingCurly = sourceCode.getLastToken(node);
1275
- const caseKeywords = node.cases.map(switchCase => sourceCode.getFirstToken(switchCase));
1276
1282
 
1277
1283
  offsets.setDesiredOffsets([openingCurly.range[1], closingCurly.range[0]], openingCurly, options.SwitchCase);
1278
1284
 
1279
- node.cases.forEach((switchCase, index) => {
1280
- const caseKeyword = caseKeywords[index];
1281
-
1282
- if (!(switchCase.consequent.length === 1 && switchCase.consequent[0].type === "BlockStatement")) {
1283
- const tokenAfterCurrentCase = index === node.cases.length - 1 ? closingCurly : caseKeywords[index + 1];
1284
-
1285
- offsets.setDesiredOffsets([caseKeyword.range[1], tokenAfterCurrentCase.range[0]], caseKeyword, 1);
1286
- }
1287
- });
1288
-
1289
1285
  if (node.cases.length) {
1290
1286
  sourceCode.getTokensBetween(
1291
1287
  node.cases[node.cases.length - 1],
@@ -1295,6 +1291,15 @@ module.exports = {
1295
1291
  }
1296
1292
  },
1297
1293
 
1294
+ SwitchCase(node) {
1295
+ if (!(node.consequent.length === 1 && node.consequent[0].type === "BlockStatement")) {
1296
+ const caseKeyword = sourceCode.getFirstToken(node);
1297
+ const tokenAfterCurrentCase = sourceCode.getTokenAfter(node);
1298
+
1299
+ offsets.setDesiredOffsets([caseKeyword.range[1], tokenAfterCurrentCase.range[0]], caseKeyword, 1);
1300
+ }
1301
+ },
1302
+
1298
1303
  TemplateLiteral(node) {
1299
1304
  node.expressions.forEach((expression, index) => {
1300
1305
  const previousQuasi = node.quasis[index];
@@ -1385,7 +1390,6 @@ module.exports = {
1385
1390
  const firstToken = sourceCode.getFirstToken(node);
1386
1391
 
1387
1392
  offsets.setDesiredOffsets(node.name.range, firstToken, 1);
1388
- offsets.setDesiredOffset(sourceCode.getLastToken(node), firstToken, 0);
1389
1393
  },
1390
1394
 
1391
1395
  JSXExpressionContainer(node) {
@@ -1397,7 +1401,15 @@ module.exports = {
1397
1401
  openingCurly,
1398
1402
  1
1399
1403
  );
1400
- offsets.setDesiredOffset(closingCurly, openingCurly, 0);
1404
+ },
1405
+
1406
+ "*"(node) {
1407
+ const firstToken = sourceCode.getFirstToken(node);
1408
+
1409
+ // Ensure that the children of every node are indented at least as much as the first token.
1410
+ if (firstToken && !ignoredNodeFirstTokens.has(firstToken)) {
1411
+ offsets.setDesiredOffsets(node.range, firstToken, 0);
1412
+ }
1401
1413
  }
1402
1414
  };
1403
1415
 
@@ -1406,7 +1418,8 @@ module.exports = {
1406
1418
  /*
1407
1419
  * To ignore the indentation of a node:
1408
1420
  * 1. Don't call the node's listener when entering it (if it has a listener)
1409
- * 2. Call `ignoreNode` on the node sometime after exiting it and before validating offsets.
1421
+ * 2. Don't set any offsets against the first token of the node.
1422
+ * 3. Call `ignoreNode` on the node sometime after exiting it and before validating offsets.
1410
1423
  */
1411
1424
  const offsetListeners = lodash.mapValues(
1412
1425
  baseOffsetListeners,
@@ -1434,7 +1447,16 @@ module.exports = {
1434
1447
 
1435
1448
  // For each ignored node selector, set up a listener to collect it into the `ignoredNodes` set.
1436
1449
  const ignoredNodes = new Set();
1437
- const addToIgnoredNodes = ignoredNodes.add.bind(ignoredNodes);
1450
+
1451
+ /**
1452
+ * Ignores a node
1453
+ * @param {ASTNode} node The node to ignore
1454
+ * @returns {void}
1455
+ */
1456
+ function addToIgnoredNodes(node) {
1457
+ ignoredNodes.add(node);
1458
+ ignoredNodeFirstTokens.add(sourceCode.getFirstToken(node));
1459
+ }
1438
1460
 
1439
1461
  const ignoredNodeListeners = options.ignoredNodes.reduce(
1440
1462
  (listeners, ignoredSelector) => Object.assign(listeners, { [ignoredSelector]: addToIgnoredNodes }),
@@ -1457,7 +1479,7 @@ module.exports = {
1457
1479
 
1458
1480
  // If a node's type is nonstandard, we can't tell how its children should be offset, so ignore it.
1459
1481
  if (!KNOWN_NODES.has(node.type)) {
1460
- ignoredNodes.add(node);
1482
+ addToIgnoredNodes(node);
1461
1483
  }
1462
1484
  },
1463
1485
  "Program:exit"() {
@@ -108,13 +108,10 @@ module.exports = {
108
108
  * Reports a given token if there are not space(s) before the token.
109
109
  *
110
110
  * @param {Token} token - A token to report.
111
- * @param {RegExp|undefined} pattern - Optional. A pattern of the previous
112
- * token to check.
111
+ * @param {RegExp} pattern - A pattern of the previous token to check.
113
112
  * @returns {void}
114
113
  */
115
114
  function expectSpaceBefore(token, pattern) {
116
- pattern = pattern || PREV_TOKEN;
117
-
118
115
  const prevToken = sourceCode.getTokenBefore(token);
119
116
 
120
117
  if (prevToken &&
@@ -138,13 +135,10 @@ module.exports = {
138
135
  * Reports a given token if there are space(s) before the token.
139
136
  *
140
137
  * @param {Token} token - A token to report.
141
- * @param {RegExp|undefined} pattern - Optional. A pattern of the previous
142
- * token to check.
138
+ * @param {RegExp} pattern - A pattern of the previous token to check.
143
139
  * @returns {void}
144
140
  */
145
141
  function unexpectSpaceBefore(token, pattern) {
146
- pattern = pattern || PREV_TOKEN;
147
-
148
142
  const prevToken = sourceCode.getTokenBefore(token);
149
143
 
150
144
  if (prevToken &&
@@ -168,13 +162,10 @@ module.exports = {
168
162
  * Reports a given token if there are not space(s) after the token.
169
163
  *
170
164
  * @param {Token} token - A token to report.
171
- * @param {RegExp|undefined} pattern - Optional. A pattern of the next
172
- * token to check.
165
+ * @param {RegExp} pattern - A pattern of the next token to check.
173
166
  * @returns {void}
174
167
  */
175
168
  function expectSpaceAfter(token, pattern) {
176
- pattern = pattern || NEXT_TOKEN;
177
-
178
169
  const nextToken = sourceCode.getTokenAfter(token);
179
170
 
180
171
  if (nextToken &&
@@ -198,13 +189,10 @@ module.exports = {
198
189
  * Reports a given token if there are space(s) after the token.
199
190
  *
200
191
  * @param {Token} token - A token to report.
201
- * @param {RegExp|undefined} pattern - Optional. A pattern of the next
202
- * token to check.
192
+ * @param {RegExp} pattern - A pattern of the next token to check.
203
193
  * @returns {void}
204
194
  */
205
195
  function unexpectSpaceAfter(token, pattern) {
206
- pattern = pattern || NEXT_TOKEN;
207
-
208
196
  const nextToken = sourceCode.getTokenAfter(token);
209
197
 
210
198
  if (nextToken &&
@@ -274,7 +262,7 @@ module.exports = {
274
262
  * @returns {void}
275
263
  */
276
264
  function checkSpacingBefore(token, pattern) {
277
- checkMethodMap[token.value].before(token, pattern);
265
+ checkMethodMap[token.value].before(token, pattern || PREV_TOKEN);
278
266
  }
279
267
 
280
268
  /**
@@ -287,7 +275,7 @@ module.exports = {
287
275
  * @returns {void}
288
276
  */
289
277
  function checkSpacingAfter(token, pattern) {
290
- checkMethodMap[token.value].after(token, pattern);
278
+ checkMethodMap[token.value].after(token, pattern || NEXT_TOKEN);
291
279
  }
292
280
 
293
281
  /**
@@ -213,7 +213,8 @@ module.exports = {
213
213
  * @returns {ASTNode[]} An array of string nodes.
214
214
  */
215
215
  function getAllStrings() {
216
- return sourceCode.ast.tokens.filter(token => token.type === "String");
216
+ return sourceCode.ast.tokens.filter(token => (token.type === "String" ||
217
+ (token.type === "JSXText" && sourceCode.getNodeByRangeIndex(token.range[0] - 1).type === "JSXAttribute")));
217
218
  }
218
219
 
219
220
  /**
@@ -287,6 +288,7 @@ module.exports = {
287
288
  * line is a comment
288
289
  */
289
290
  let lineIsComment = false;
291
+ let textToMeasure;
290
292
 
291
293
  /*
292
294
  * We can short-circuit the comment checks if we're already out of
@@ -305,12 +307,17 @@ module.exports = {
305
307
 
306
308
  if (isFullLineComment(line, lineNumber, comment)) {
307
309
  lineIsComment = true;
310
+ textToMeasure = line;
308
311
  } else if (ignoreTrailingComments && isTrailingComment(line, lineNumber, comment)) {
309
- line = stripTrailingComment(line, comment);
312
+ textToMeasure = stripTrailingComment(line, comment);
313
+ } else {
314
+ textToMeasure = line;
310
315
  }
316
+ } else {
317
+ textToMeasure = line;
311
318
  }
312
- if (ignorePattern && ignorePattern.test(line) ||
313
- ignoreUrls && URL_REGEXP.test(line) ||
319
+ if (ignorePattern && ignorePattern.test(textToMeasure) ||
320
+ ignoreUrls && URL_REGEXP.test(textToMeasure) ||
314
321
  ignoreStrings && stringsByLine[lineNumber] ||
315
322
  ignoreTemplateLiterals && templateLiteralsByLine[lineNumber] ||
316
323
  ignoreRegExpLiterals && regExpLiteralsByLine[lineNumber]
@@ -320,7 +327,7 @@ module.exports = {
320
327
  return;
321
328
  }
322
329
 
323
- const lineLength = computeLineLength(line, tabWidth);
330
+ const lineLength = computeLineLength(textToMeasure, tabWidth);
324
331
  const commentLengthApplies = lineIsComment && maxCommentLength;
325
332
 
326
333
  if (lineIsComment && ignoreComments) {
@@ -84,7 +84,7 @@ module.exports = {
84
84
  while (parent && !isBoundary(parent)) {
85
85
  if (isLooped(node, parent)) {
86
86
  context.report({
87
- node,
87
+ node: awaitNode,
88
88
  messageId: "unexpectedAwait"
89
89
  });
90
90
  return;
@@ -11,7 +11,7 @@
11
11
  module.exports = {
12
12
  meta: {
13
13
  docs: {
14
- description: "disallow use of the Buffer() constructor",
14
+ description: "disallow use of the `Buffer()` constructor",
15
15
  category: "Node.js and CommonJS",
16
16
  recommended: false,
17
17
  url: "https://eslint.org/docs/rules/no-buffer-constructor"
@@ -5,6 +5,44 @@
5
5
 
6
6
  "use strict";
7
7
 
8
+ const RegExpValidator = require("regexpp").RegExpValidator;
9
+ const collector = new class {
10
+ constructor() {
11
+ this.ecmaVersion = 2018;
12
+ this._source = "";
13
+ this._controlChars = [];
14
+ this._validator = new RegExpValidator(this);
15
+ }
16
+
17
+ onPatternEnter() {
18
+ this._controlChars = [];
19
+ }
20
+
21
+ onCharacter(start, end, cp) {
22
+ if (cp >= 0x00 &&
23
+ cp <= 0x1F &&
24
+ (
25
+ this._source.codePointAt(start) === cp ||
26
+ this._source.slice(start, end).startsWith("\\x") ||
27
+ this._source.slice(start, end).startsWith("\\u")
28
+ )
29
+ ) {
30
+ this._controlChars.push(`\\x${`0${cp.toString(16)}`.slice(-2)}`);
31
+ }
32
+ }
33
+
34
+ collectControlChars(regexpStr) {
35
+ try {
36
+ this._source = regexpStr;
37
+ this._validator.validatePattern(regexpStr); // Call onCharacter hook
38
+ } catch (err) {
39
+
40
+ // Ignore syntax errors in RegExp.
41
+ }
42
+ return this._controlChars;
43
+ }
44
+ }();
45
+
8
46
  //------------------------------------------------------------------------------
9
47
  // Rule Definition
10
48
  //------------------------------------------------------------------------------
@@ -33,87 +71,28 @@ module.exports = {
33
71
  * @returns {RegExp|null} Regex if found else null
34
72
  * @private
35
73
  */
36
- function getRegExp(node) {
37
- if (node.value instanceof RegExp) {
38
- return node.value;
74
+ function getRegExpPattern(node) {
75
+ if (node.regex) {
76
+ return node.regex.pattern;
39
77
  }
40
- if (typeof node.value === "string") {
41
-
42
- const parent = context.getAncestors().pop();
43
-
44
- if ((parent.type === "NewExpression" || parent.type === "CallExpression") &&
45
- parent.callee.type === "Identifier" && parent.callee.name === "RegExp"
46
- ) {
47
-
48
- // there could be an invalid regular expression string
49
- try {
50
- return new RegExp(node.value);
51
- } catch (ex) {
52
- return null;
53
- }
54
- }
78
+ if (typeof node.value === "string" &&
79
+ (node.parent.type === "NewExpression" || node.parent.type === "CallExpression") &&
80
+ node.parent.callee.type === "Identifier" &&
81
+ node.parent.callee.name === "RegExp" &&
82
+ node.parent.arguments[0] === node
83
+ ) {
84
+ return node.value;
55
85
  }
56
86
 
57
87
  return null;
58
88
  }
59
89
 
60
-
61
- const controlChar = /[\x00-\x1f]/g; // eslint-disable-line no-control-regex
62
- const consecutiveSlashes = /\\+/g;
63
- const consecutiveSlashesAtEnd = /\\+$/g;
64
- const stringControlChar = /\\x[01][0-9a-f]/ig;
65
- const stringControlCharWithoutSlash = /x[01][0-9a-f]/ig;
66
-
67
- /**
68
- * Return a list of the control characters in the given regex string
69
- * @param {string} regexStr regex as string to check
70
- * @returns {array} returns a list of found control characters on given string
71
- * @private
72
- */
73
- function getControlCharacters(regexStr) {
74
-
75
- // check control characters, if RegExp object used
76
- const controlChars = regexStr.match(controlChar) || [];
77
-
78
- let stringControlChars = [];
79
-
80
- // check substr, if regex literal used
81
- const subStrIndex = regexStr.search(stringControlChar);
82
-
83
- if (subStrIndex > -1) {
84
-
85
- // is it escaped, check backslash count
86
- const possibleEscapeCharacters = regexStr.slice(0, subStrIndex).match(consecutiveSlashesAtEnd);
87
-
88
- const hasControlChars = possibleEscapeCharacters === null || !(possibleEscapeCharacters[0].length % 2);
89
-
90
- if (hasControlChars) {
91
- stringControlChars = regexStr.slice(subStrIndex, -1)
92
- .split(consecutiveSlashes)
93
- .filter(Boolean)
94
- .map(x => {
95
- const match = x.match(stringControlCharWithoutSlash) || [x];
96
-
97
- return `\\${match[0]}`;
98
- });
99
- }
100
- }
101
-
102
- return controlChars.map(x => {
103
- const hexCode = `0${x.charCodeAt(0).toString(16)}`.slice(-2);
104
-
105
- return `\\x${hexCode}`;
106
- }).concat(stringControlChars);
107
- }
108
-
109
90
  return {
110
91
  Literal(node) {
111
- const regex = getRegExp(node);
112
-
113
- if (regex) {
114
- const computedValue = regex.toString();
92
+ const pattern = getRegExpPattern(node);
115
93
 
116
- const controlCharacters = getControlCharacters(computedValue);
94
+ if (pattern) {
95
+ const controlCharacters = collector.collectControlChars(pattern);
117
96
 
118
97
  if (controlCharacters.length > 0) {
119
98
  context.report({