eslint 6.3.0 → 6.6.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 (189) hide show
  1. package/CHANGELOG.md +126 -0
  2. package/README.md +11 -10
  3. package/bin/eslint.js +38 -12
  4. package/lib/cli-engine/cascading-config-array-factory.js +2 -1
  5. package/lib/cli-engine/cli-engine.js +11 -11
  6. package/lib/cli-engine/config-array/config-array.js +0 -4
  7. package/lib/cli-engine/config-array/config-dependency.js +2 -0
  8. package/lib/cli-engine/config-array/override-tester.js +4 -2
  9. package/lib/cli-engine/config-array-factory.js +7 -1
  10. package/lib/cli-engine/file-enumerator.js +5 -3
  11. package/lib/cli-engine/formatters/html.js +1 -0
  12. package/lib/cli-engine/ignored-paths.js +4 -3
  13. package/lib/cli-engine/lint-result-cache.js +0 -1
  14. package/lib/cli.js +13 -12
  15. package/lib/init/autoconfig.js +1 -11
  16. package/lib/init/config-file.js +0 -1
  17. package/lib/init/config-initializer.js +0 -1
  18. package/lib/init/config-rule.js +3 -7
  19. package/lib/init/npm-utils.js +1 -6
  20. package/lib/linter/code-path-analysis/code-path-analyzer.js +24 -38
  21. package/lib/linter/code-path-analysis/code-path-segment.js +17 -25
  22. package/lib/linter/code-path-analysis/code-path-state.js +40 -81
  23. package/lib/linter/code-path-analysis/code-path.js +10 -11
  24. package/lib/linter/code-path-analysis/debug-helpers.js +9 -13
  25. package/lib/linter/code-path-analysis/fork-context.js +23 -34
  26. package/lib/linter/code-path-analysis/id-generator.js +2 -2
  27. package/lib/linter/linter.js +121 -96
  28. package/lib/linter/node-event-generator.js +3 -2
  29. package/lib/options.js +6 -0
  30. package/lib/rule-tester/rule-tester.js +7 -10
  31. package/lib/rules/accessor-pairs.js +59 -19
  32. package/lib/rules/array-bracket-newline.js +12 -15
  33. package/lib/rules/array-bracket-spacing.js +12 -12
  34. package/lib/rules/array-callback-return.js +6 -11
  35. package/lib/rules/array-element-newline.js +5 -8
  36. package/lib/rules/arrow-parens.js +0 -1
  37. package/lib/rules/block-scoped-var.js +3 -3
  38. package/lib/rules/block-spacing.js +4 -4
  39. package/lib/rules/capitalized-comments.js +2 -9
  40. package/lib/rules/class-methods-use-this.js +3 -3
  41. package/lib/rules/comma-dangle.js +15 -23
  42. package/lib/rules/comma-spacing.js +1 -1
  43. package/lib/rules/computed-property-spacing.js +28 -11
  44. package/lib/rules/consistent-return.js +4 -5
  45. package/lib/rules/consistent-this.js +5 -5
  46. package/lib/rules/constructor-super.js +14 -16
  47. package/lib/rules/curly.js +3 -5
  48. package/lib/rules/default-param-last.js +62 -0
  49. package/lib/rules/dot-location.js +11 -12
  50. package/lib/rules/eqeqeq.js +7 -19
  51. package/lib/rules/func-names.js +6 -6
  52. package/lib/rules/function-call-argument-newline.js +5 -5
  53. package/lib/rules/generator-star-spacing.js +4 -9
  54. package/lib/rules/getter-return.js +4 -7
  55. package/lib/rules/indent-legacy.js +1 -1
  56. package/lib/rules/indent.js +46 -8
  57. package/lib/rules/index.js +3 -0
  58. package/lib/rules/init-declarations.js +2 -2
  59. package/lib/rules/jsx-quotes.js +1 -1
  60. package/lib/rules/keyword-spacing.js +32 -56
  61. package/lib/rules/lines-around-directive.js +1 -1
  62. package/lib/rules/max-len.js +0 -5
  63. package/lib/rules/max-statements-per-line.js +3 -7
  64. package/lib/rules/multiline-ternary.js +3 -3
  65. package/lib/rules/new-parens.js +5 -1
  66. package/lib/rules/newline-after-var.js +6 -7
  67. package/lib/rules/newline-before-return.js +8 -9
  68. package/lib/rules/newline-per-chained-call.js +2 -4
  69. package/lib/rules/no-class-assign.js +2 -2
  70. package/lib/rules/no-compare-neg-zero.js +1 -2
  71. package/lib/rules/no-confusing-arrow.js +2 -2
  72. package/lib/rules/no-console.js +4 -8
  73. package/lib/rules/no-const-assign.js +1 -1
  74. package/lib/rules/no-dupe-args.js +1 -1
  75. package/lib/rules/no-dupe-class-members.js +3 -4
  76. package/lib/rules/no-dupe-keys.js +6 -5
  77. package/lib/rules/no-duplicate-imports.js +14 -18
  78. package/lib/rules/no-else-return.js +0 -8
  79. package/lib/rules/no-empty-function.js +2 -4
  80. package/lib/rules/no-eval.js +10 -18
  81. package/lib/rules/no-ex-assign.js +1 -1
  82. package/lib/rules/no-extra-bind.js +12 -13
  83. package/lib/rules/no-extra-boolean-cast.js +1 -3
  84. package/lib/rules/no-extra-label.js +13 -10
  85. package/lib/rules/no-extra-parens.js +32 -17
  86. package/lib/rules/no-extra-semi.js +5 -6
  87. package/lib/rules/no-fallthrough.js +6 -6
  88. package/lib/rules/no-func-assign.js +3 -3
  89. package/lib/rules/no-global-assign.js +4 -4
  90. package/lib/rules/no-implicit-coercion.js +10 -10
  91. package/lib/rules/no-implied-eval.js +0 -1
  92. package/lib/rules/no-import-assign.js +238 -0
  93. package/lib/rules/no-invalid-this.js +1 -3
  94. package/lib/rules/no-labels.js +3 -6
  95. package/lib/rules/no-lone-blocks.js +7 -2
  96. package/lib/rules/no-loop-func.js +6 -11
  97. package/lib/rules/no-magic-numbers.js +6 -6
  98. package/lib/rules/no-misleading-character-class.js +14 -7
  99. package/lib/rules/no-mixed-operators.js +13 -22
  100. package/lib/rules/no-mixed-requires.js +0 -1
  101. package/lib/rules/no-multi-spaces.js +1 -1
  102. package/lib/rules/no-native-reassign.js +4 -4
  103. package/lib/rules/no-obj-calls.js +29 -9
  104. package/lib/rules/no-octal-escape.js +14 -8
  105. package/lib/rules/no-param-reassign.js +28 -7
  106. package/lib/rules/no-redeclare.js +1 -1
  107. package/lib/rules/no-regex-spaces.js +105 -45
  108. package/lib/rules/no-restricted-imports.js +11 -11
  109. package/lib/rules/no-self-assign.js +18 -18
  110. package/lib/rules/no-sequences.js +5 -5
  111. package/lib/rules/no-shadow.js +1 -4
  112. package/lib/rules/no-tabs.js +8 -2
  113. package/lib/rules/no-this-before-super.js +12 -13
  114. package/lib/rules/no-trailing-spaces.js +19 -7
  115. package/lib/rules/no-undef-init.js +7 -1
  116. package/lib/rules/no-unmodified-loop-condition.js +16 -29
  117. package/lib/rules/no-unneeded-ternary.js +3 -3
  118. package/lib/rules/no-unreachable.js +7 -7
  119. package/lib/rules/no-unsafe-finally.js +4 -7
  120. package/lib/rules/no-unsafe-negation.js +34 -19
  121. package/lib/rules/no-unused-expressions.js +11 -7
  122. package/lib/rules/no-unused-labels.js +3 -6
  123. package/lib/rules/no-unused-vars.js +22 -29
  124. package/lib/rules/no-use-before-define.js +10 -15
  125. package/lib/rules/no-useless-call.js +4 -4
  126. package/lib/rules/no-useless-concat.js +4 -4
  127. package/lib/rules/no-useless-constructor.js +14 -22
  128. package/lib/rules/no-useless-escape.js +3 -5
  129. package/lib/rules/no-useless-rename.js +32 -20
  130. package/lib/rules/no-useless-return.js +11 -17
  131. package/lib/rules/no-var.js +12 -25
  132. package/lib/rules/no-warning-comments.js +0 -1
  133. package/lib/rules/no-whitespace-before-property.js +3 -3
  134. package/lib/rules/object-curly-newline.js +7 -10
  135. package/lib/rules/object-curly-spacing.js +14 -15
  136. package/lib/rules/object-shorthand.js +36 -10
  137. package/lib/rules/one-var-declaration-per-line.js +2 -2
  138. package/lib/rules/operator-assignment.js +22 -1
  139. package/lib/rules/padded-blocks.js +1 -1
  140. package/lib/rules/padding-line-between-statements.js +0 -16
  141. package/lib/rules/prefer-arrow-callback.js +6 -6
  142. package/lib/rules/prefer-const.js +13 -21
  143. package/lib/rules/prefer-destructuring.js +1 -7
  144. package/lib/rules/prefer-named-capture-group.js +3 -16
  145. package/lib/rules/prefer-numeric-literals.js +35 -3
  146. package/lib/rules/prefer-object-spread.js +7 -7
  147. package/lib/rules/prefer-regex-literals.js +125 -0
  148. package/lib/rules/prefer-rest-params.js +3 -6
  149. package/lib/rules/prefer-spread.js +4 -4
  150. package/lib/rules/prefer-template.js +5 -6
  151. package/lib/rules/quote-props.js +1 -1
  152. package/lib/rules/quotes.js +11 -6
  153. package/lib/rules/radix.js +5 -10
  154. package/lib/rules/require-await.js +2 -5
  155. package/lib/rules/require-yield.js +2 -2
  156. package/lib/rules/rest-spread-spacing.js +1 -1
  157. package/lib/rules/sort-imports.js +3 -4
  158. package/lib/rules/sort-keys.js +1 -3
  159. package/lib/rules/space-before-blocks.js +1 -2
  160. package/lib/rules/space-before-function-paren.js +12 -1
  161. package/lib/rules/space-in-parens.js +81 -75
  162. package/lib/rules/space-infix-ops.js +5 -5
  163. package/lib/rules/spaced-comment.js +15 -18
  164. package/lib/rules/strict.js +2 -4
  165. package/lib/rules/symbol-description.js +1 -2
  166. package/lib/rules/template-curly-spacing.js +2 -2
  167. package/lib/rules/use-isnan.js +104 -6
  168. package/lib/rules/utils/ast-utils.js +53 -81
  169. package/lib/rules/utils/fix-tracker.js +0 -6
  170. package/lib/rules/utils/lazy-loading-rule-map.js +0 -1
  171. package/lib/rules/vars-on-top.js +11 -11
  172. package/lib/shared/config-ops.js +2 -2
  173. package/lib/shared/logging.js +2 -0
  174. package/lib/shared/runtime-info.js +163 -0
  175. package/lib/shared/traverser.js +2 -0
  176. package/lib/source-code/source-code.js +11 -12
  177. package/lib/source-code/token-store/backward-token-comment-cursor.js +5 -5
  178. package/lib/source-code/token-store/backward-token-cursor.js +5 -5
  179. package/lib/source-code/token-store/cursors.js +17 -19
  180. package/lib/source-code/token-store/decorative-cursor.js +1 -1
  181. package/lib/source-code/token-store/filter-cursor.js +2 -2
  182. package/lib/source-code/token-store/forward-token-comment-cursor.js +5 -5
  183. package/lib/source-code/token-store/forward-token-cursor.js +5 -5
  184. package/lib/source-code/token-store/index.js +86 -92
  185. package/lib/source-code/token-store/limit-cursor.js +2 -2
  186. package/lib/source-code/token-store/padded-token-cursor.js +7 -7
  187. package/lib/source-code/token-store/skip-cursor.js +2 -2
  188. package/lib/source-code/token-store/utils.js +9 -13
  189. package/package.json +8 -5
@@ -40,8 +40,7 @@ const TARGET_NODE_TYPE = /^(?:Binary|Logical|Conditional)Expression$/u;
40
40
 
41
41
  /**
42
42
  * Normalizes options.
43
- *
44
- * @param {Object|undefined} options - A options object to normalize.
43
+ * @param {Object|undefined} options A options object to normalize.
45
44
  * @returns {Object} Normalized option object.
46
45
  */
47
46
  function normalizeOptions(options = {}) {
@@ -57,10 +56,9 @@ function normalizeOptions(options = {}) {
57
56
 
58
57
  /**
59
58
  * Checks whether any group which includes both given operator exists or not.
60
- *
61
- * @param {Array.<string[]>} groups - A list of groups to check.
62
- * @param {string} left - An operator.
63
- * @param {string} right - Another operator.
59
+ * @param {Array.<string[]>} groups A list of groups to check.
60
+ * @param {string} left An operator.
61
+ * @param {string} right Another operator.
64
62
  * @returns {boolean} `true` if such group existed.
65
63
  */
66
64
  function includesBothInAGroup(groups, left, right) {
@@ -69,8 +67,7 @@ function includesBothInAGroup(groups, left, right) {
69
67
 
70
68
  /**
71
69
  * Checks whether the given node is a conditional expression and returns the test node else the left node.
72
- *
73
- * @param {ASTNode} node - A node which can be a BinaryExpression or a LogicalExpression node.
70
+ * @param {ASTNode} node A node which can be a BinaryExpression or a LogicalExpression node.
74
71
  * This parent node can be BinaryExpression, LogicalExpression
75
72
  * , or a ConditionalExpression node
76
73
  * @returns {ASTNode} node the appropriate node(left or test).
@@ -124,8 +121,7 @@ module.exports = {
124
121
 
125
122
  /**
126
123
  * Checks whether a given node should be ignored by options or not.
127
- *
128
- * @param {ASTNode} node - A node to check. This is a BinaryExpression
124
+ * @param {ASTNode} node A node to check. This is a BinaryExpression
129
125
  * node or a LogicalExpression node. This parent node is one of
130
126
  * them, too.
131
127
  * @returns {boolean} `true` if the node should be ignored.
@@ -146,8 +142,7 @@ module.exports = {
146
142
  /**
147
143
  * Checks whether the operator of a given node is mixed with parent
148
144
  * node's operator or not.
149
- *
150
- * @param {ASTNode} node - A node to check. This is a BinaryExpression
145
+ * @param {ASTNode} node A node to check. This is a BinaryExpression
151
146
  * node or a LogicalExpression node. This parent node is one of
152
147
  * them, too.
153
148
  * @returns {boolean} `true` if the node was mixed.
@@ -163,8 +158,7 @@ module.exports = {
163
158
  /**
164
159
  * Checks whether the operator of a given node is mixed with a
165
160
  * conditional expression.
166
- *
167
- * @param {ASTNode} node - A node to check. This is a conditional
161
+ * @param {ASTNode} node A node to check. This is a conditional
168
162
  * expression node
169
163
  * @returns {boolean} `true` if the node was mixed.
170
164
  */
@@ -174,8 +168,7 @@ module.exports = {
174
168
 
175
169
  /**
176
170
  * Gets the operator token of a given node.
177
- *
178
- * @param {ASTNode} node - A node to check. This is a BinaryExpression
171
+ * @param {ASTNode} node A node to check. This is a BinaryExpression
179
172
  * node or a LogicalExpression node.
180
173
  * @returns {Token} The operator token of the node.
181
174
  */
@@ -186,8 +179,7 @@ module.exports = {
186
179
  /**
187
180
  * Reports both the operator of a given node and the operator of the
188
181
  * parent node.
189
- *
190
- * @param {ASTNode} node - A node to check. This is a BinaryExpression
182
+ * @param {ASTNode} node A node to check. This is a BinaryExpression
191
183
  * node or a LogicalExpression node. This parent node is one of
192
184
  * them, too.
193
185
  * @returns {void}
@@ -205,13 +197,13 @@ module.exports = {
205
197
 
206
198
  context.report({
207
199
  node: left,
208
- loc: getOperatorToken(left).loc.start,
200
+ loc: getOperatorToken(left).loc,
209
201
  message,
210
202
  data
211
203
  });
212
204
  context.report({
213
205
  node: right,
214
- loc: getOperatorToken(right).loc.start,
206
+ loc: getOperatorToken(right).loc,
215
207
  message,
216
208
  data
217
209
  });
@@ -220,8 +212,7 @@ module.exports = {
220
212
  /**
221
213
  * Checks between the operator of this node and the operator of the
222
214
  * parent node.
223
- *
224
- * @param {ASTNode} node - A node to check.
215
+ * @param {ASTNode} node A node to check.
225
216
  * @returns {void}
226
217
  */
227
218
  function check(node) {
@@ -58,7 +58,6 @@ module.exports = {
58
58
 
59
59
  /**
60
60
  * Returns the list of built-in modules.
61
- *
62
61
  * @returns {string[]} An array of built-in Node.js modules.
63
62
  */
64
63
  function getBuiltinModules() {
@@ -121,7 +121,7 @@ module.exports = {
121
121
 
122
122
  context.report({
123
123
  node: rightToken,
124
- loc: rightToken.loc.start,
124
+ loc: { start: leftToken.loc.end, end: rightToken.loc.start },
125
125
  message: "Multiple spaces found before '{{displayValue}}'.",
126
126
  data: { displayValue },
127
127
  fix: fixer => fixer.replaceTextRange([leftToken.range[1], rightToken.range[0]], " ")
@@ -46,9 +46,9 @@ module.exports = {
46
46
 
47
47
  /**
48
48
  * Reports write references.
49
- * @param {Reference} reference - A reference to check.
50
- * @param {int} index - The index of the reference in the references.
51
- * @param {Reference[]} references - The array that the reference belongs to.
49
+ * @param {Reference} reference A reference to check.
50
+ * @param {int} index The index of the reference in the references.
51
+ * @param {Reference[]} references The array that the reference belongs to.
52
52
  * @returns {void}
53
53
  */
54
54
  function checkReference(reference, index, references) {
@@ -73,7 +73,7 @@ module.exports = {
73
73
 
74
74
  /**
75
75
  * Reports write references if a given variable is read-only builtin.
76
- * @param {Variable} variable - A variable to check.
76
+ * @param {Variable} variable A variable to check.
77
77
  * @returns {void}
78
78
  */
79
79
  function checkVariable(variable) {
@@ -5,6 +5,18 @@
5
5
 
6
6
  "use strict";
7
7
 
8
+ //------------------------------------------------------------------------------
9
+ // Requirements
10
+ //------------------------------------------------------------------------------
11
+
12
+ const { CALL, ReferenceTracker } = require("eslint-utils");
13
+
14
+ //------------------------------------------------------------------------------
15
+ // Helpers
16
+ //------------------------------------------------------------------------------
17
+
18
+ const nonCallableGlobals = ["Atomics", "JSON", "Math", "Reflect"];
19
+
8
20
  //------------------------------------------------------------------------------
9
21
  // Rule Definition
10
22
  //------------------------------------------------------------------------------
@@ -20,23 +32,31 @@ module.exports = {
20
32
  url: "https://eslint.org/docs/rules/no-obj-calls"
21
33
  },
22
34
 
23
- schema: []
35
+ schema: [],
36
+
37
+ messages: {
38
+ unexpectedCall: "'{{name}}' is not a function."
39
+ }
24
40
  },
25
41
 
26
42
  create(context) {
27
43
 
28
44
  return {
29
- CallExpression(node) {
30
-
31
- if (node.callee.type === "Identifier") {
32
- const name = node.callee.name;
45
+ Program() {
46
+ const scope = context.getScope();
47
+ const tracker = new ReferenceTracker(scope);
48
+ const traceMap = {};
49
+
50
+ for (const global of nonCallableGlobals) {
51
+ traceMap[global] = {
52
+ [CALL]: true
53
+ };
54
+ }
33
55
 
34
- if (name === "Math" || name === "JSON" || name === "Reflect") {
35
- context.report({ node, message: "'{{name}}' is not a function.", data: { name } });
36
- }
56
+ for (const { node } of tracker.iterateGlobalReferences(traceMap)) {
57
+ context.report({ node, messageId: "unexpectedCall", data: { name: node.callee.name } });
37
58
  }
38
59
  }
39
60
  };
40
-
41
61
  }
42
62
  };
@@ -20,7 +20,11 @@ module.exports = {
20
20
  url: "https://eslint.org/docs/rules/no-octal-escape"
21
21
  },
22
22
 
23
- schema: []
23
+ schema: [],
24
+
25
+ messages: {
26
+ octalEscapeSequence: "Don't use octal: '\\{{sequence}}'. Use '\\u....' instead."
27
+ }
24
28
  },
25
29
 
26
30
  create(context) {
@@ -32,15 +36,17 @@ module.exports = {
32
36
  return;
33
37
  }
34
38
 
35
- const match = node.raw.match(/^([^\\]|\\[^0-7])*\\([0-3][0-7]{1,2}|[4-7][0-7]|[0-7])/u);
39
+ // \0 represents a valid NULL character if it isn't followed by a digit.
40
+ const match = node.raw.match(
41
+ /^(?:[^\\]|\\.)*?\\([0-3][0-7]{1,2}|[4-7][0-7]|[1-7])/u
42
+ );
36
43
 
37
44
  if (match) {
38
- const octalDigit = match[2];
39
-
40
- // \0 is actually not considered an octal
41
- if (match[2] !== "0" || typeof match[3] !== "undefined") {
42
- context.report({ node, message: "Don't use octal: '\\{{octalDigit}}'. Use '\\u....' instead.", data: { octalDigit } });
43
- }
45
+ context.report({
46
+ node,
47
+ messageId: "octalEscapeSequence",
48
+ data: { sequence: match[1] }
49
+ });
44
50
  }
45
51
  }
46
52
 
@@ -45,6 +45,13 @@ module.exports = {
45
45
  type: "string"
46
46
  },
47
47
  uniqueItems: true
48
+ },
49
+ ignorePropertyModificationsForRegex: {
50
+ type: "array",
51
+ items: {
52
+ type: "string"
53
+ },
54
+ uniqueItems: true
48
55
  }
49
56
  },
50
57
  additionalProperties: false
@@ -57,10 +64,11 @@ module.exports = {
57
64
  create(context) {
58
65
  const props = context.options[0] && context.options[0].props;
59
66
  const ignoredPropertyAssignmentsFor = context.options[0] && context.options[0].ignorePropertyModificationsFor || [];
67
+ const ignoredPropertyAssignmentsForRegex = context.options[0] && context.options[0].ignorePropertyModificationsForRegex || [];
60
68
 
61
69
  /**
62
70
  * Checks whether or not the reference modifies properties of its variable.
63
- * @param {Reference} reference - A reference to check.
71
+ * @param {Reference} reference A reference to check.
64
72
  * @returns {boolean} Whether or not the reference modifies properties of its variable.
65
73
  */
66
74
  function isModifyingProp(reference) {
@@ -136,11 +144,24 @@ module.exports = {
136
144
  return false;
137
145
  }
138
146
 
147
+ /**
148
+ * Tests that an identifier name matches any of the ignored property assignments.
149
+ * First we test strings in ignoredPropertyAssignmentsFor.
150
+ * Then we instantiate and test RegExp objects from ignoredPropertyAssignmentsForRegex strings.
151
+ * @param {string} identifierName A string that describes the name of an identifier to
152
+ * ignore property assignments for.
153
+ * @returns {boolean} Whether the string matches an ignored property assignment regular expression or not.
154
+ */
155
+ function isIgnoredPropertyAssignment(identifierName) {
156
+ return ignoredPropertyAssignmentsFor.includes(identifierName) ||
157
+ ignoredPropertyAssignmentsForRegex.some(ignored => new RegExp(ignored, "u").test(identifierName));
158
+ }
159
+
139
160
  /**
140
161
  * Reports a reference if is non initializer and writable.
141
- * @param {Reference} reference - A reference to check.
142
- * @param {int} index - The index of the reference in the references.
143
- * @param {Reference[]} references - The array that the reference belongs to.
162
+ * @param {Reference} reference A reference to check.
163
+ * @param {int} index The index of the reference in the references.
164
+ * @param {Reference[]} references The array that the reference belongs to.
144
165
  * @returns {void}
145
166
  */
146
167
  function checkReference(reference, index, references) {
@@ -157,7 +178,7 @@ module.exports = {
157
178
  ) {
158
179
  if (reference.isWrite()) {
159
180
  context.report({ node: identifier, message: "Assignment to function parameter '{{name}}'.", data: { name: identifier.name } });
160
- } else if (props && isModifyingProp(reference) && ignoredPropertyAssignmentsFor.indexOf(identifier.name) === -1) {
181
+ } else if (props && isModifyingProp(reference) && !isIgnoredPropertyAssignment(identifier.name)) {
161
182
  context.report({ node: identifier, message: "Assignment to property of function parameter '{{name}}'.", data: { name: identifier.name } });
162
183
  }
163
184
  }
@@ -165,7 +186,7 @@ module.exports = {
165
186
 
166
187
  /**
167
188
  * Finds and reports references that are non initializer and writable.
168
- * @param {Variable} variable - A variable to check.
189
+ * @param {Variable} variable A variable to check.
169
190
  * @returns {void}
170
191
  */
171
192
  function checkVariable(variable) {
@@ -176,7 +197,7 @@ module.exports = {
176
197
 
177
198
  /**
178
199
  * Checks parameters of a given function node.
179
- * @param {ASTNode} node - A function node to check.
200
+ * @param {ASTNode} node A function node to check.
180
201
  * @returns {void}
181
202
  */
182
203
  function checkForFunction(node) {
@@ -86,7 +86,7 @@ module.exports = {
86
86
 
87
87
  /**
88
88
  * Find variables in a given scope and flag redeclared ones.
89
- * @param {Scope} scope - An eslint-scope scope object.
89
+ * @param {Scope} scope An eslint-scope scope object.
90
90
  * @returns {void}
91
91
  * @private
92
92
  */
@@ -5,7 +5,29 @@
5
5
 
6
6
  "use strict";
7
7
 
8
+ //------------------------------------------------------------------------------
9
+ // Requirements
10
+ //------------------------------------------------------------------------------
11
+
8
12
  const astUtils = require("./utils/ast-utils");
13
+ const regexpp = require("regexpp");
14
+
15
+ //------------------------------------------------------------------------------
16
+ // Helpers
17
+ //------------------------------------------------------------------------------
18
+
19
+ const regExpParser = new regexpp.RegExpParser();
20
+ const DOUBLE_SPACE = / {2}/u;
21
+
22
+ /**
23
+ * Check if node is a string
24
+ * @param {ASTNode} node node to evaluate
25
+ * @returns {boolean} True if its a string
26
+ * @private
27
+ */
28
+ function isString(node) {
29
+ return node && node.type === "Literal" && typeof node.value === "string";
30
+ }
9
31
 
10
32
  //------------------------------------------------------------------------------
11
33
  // Rule Definition
@@ -27,40 +49,69 @@ module.exports = {
27
49
  },
28
50
 
29
51
  create(context) {
30
- const sourceCode = context.getSourceCode();
31
52
 
32
53
  /**
33
- * Validate regular expressions
34
- * @param {ASTNode} node node to validate
35
- * @param {string} value regular expression to validate
36
- * @param {number} valueStart The start location of the regex/string literal. It will always be the case that
37
- * `sourceCode.getText().slice(valueStart, valueStart + value.length) === value`
54
+ * Validate regular expression
55
+ * @param {ASTNode} nodeToReport Node to report.
56
+ * @param {string} pattern Regular expression pattern to validate.
57
+ * @param {string} rawPattern Raw representation of the pattern in the source code.
58
+ * @param {number} rawPatternStartRange Start range of the pattern in the source code.
59
+ * @param {string} flags Regular expression flags.
38
60
  * @returns {void}
39
61
  * @private
40
62
  */
41
- function checkRegex(node, value, valueStart) {
42
- const multipleSpacesRegex = /( {2,})( [+*{?]|[^+*{?]|$)/u,
43
- regexResults = multipleSpacesRegex.exec(value);
63
+ function checkRegex(nodeToReport, pattern, rawPattern, rawPatternStartRange, flags) {
44
64
 
45
- if (regexResults !== null) {
46
- const count = regexResults[1].length;
65
+ // Skip if there are no consecutive spaces in the source code, to avoid reporting e.g., RegExp(' \ ').
66
+ if (!DOUBLE_SPACE.test(rawPattern)) {
67
+ return;
68
+ }
47
69
 
48
- context.report({
49
- node,
50
- message: "Spaces are hard to count. Use {{{count}}}.",
51
- data: { count },
52
- fix(fixer) {
53
- return fixer.replaceTextRange(
54
- [valueStart + regexResults.index, valueStart + regexResults.index + count],
55
- ` {${count}}`
56
- );
57
- }
58
- });
59
-
60
- /*
61
- * TODO: (platinumazure) Fix message to use rule message
62
- * substitution when api.report is fixed in lib/eslint.js.
63
- */
70
+ const characterClassNodes = [];
71
+ let regExpAST;
72
+
73
+ try {
74
+ regExpAST = regExpParser.parsePattern(pattern, 0, pattern.length, flags.includes("u"));
75
+ } catch (e) {
76
+
77
+ // Ignore regular expressions with syntax errors
78
+ return;
79
+ }
80
+
81
+ regexpp.visitRegExpAST(regExpAST, {
82
+ onCharacterClassEnter(ccNode) {
83
+ characterClassNodes.push(ccNode);
84
+ }
85
+ });
86
+
87
+ const spacesPattern = /( {2,})(?: [+*{?]|[^+*{?]|$)/gu;
88
+ let match;
89
+
90
+ while ((match = spacesPattern.exec(pattern))) {
91
+ const { 1: { length }, index } = match;
92
+
93
+ // Report only consecutive spaces that are not in character classes.
94
+ if (
95
+ characterClassNodes.every(({ start, end }) => index < start || end <= index)
96
+ ) {
97
+ context.report({
98
+ node: nodeToReport,
99
+ message: "Spaces are hard to count. Use {{{length}}}.",
100
+ data: { length },
101
+ fix(fixer) {
102
+ if (pattern !== rawPattern) {
103
+ return null;
104
+ }
105
+ return fixer.replaceTextRange(
106
+ [rawPatternStartRange + index, rawPatternStartRange + index + length],
107
+ ` {${length}}`
108
+ );
109
+ }
110
+ });
111
+
112
+ // Report only the first occurence of consecutive spaces
113
+ return;
114
+ }
64
115
  }
65
116
  }
66
117
 
@@ -71,25 +122,22 @@ module.exports = {
71
122
  * @private
72
123
  */
73
124
  function checkLiteral(node) {
74
- const token = sourceCode.getFirstToken(node),
75
- nodeType = token.type,
76
- nodeValue = token.value;
125
+ if (node.regex) {
126
+ const pattern = node.regex.pattern;
127
+ const rawPattern = node.raw.slice(1, node.raw.lastIndexOf("/"));
128
+ const rawPatternStartRange = node.range[0] + 1;
129
+ const flags = node.regex.flags;
77
130
 
78
- if (nodeType === "RegularExpression") {
79
- checkRegex(node, nodeValue, token.range[0]);
131
+ checkRegex(
132
+ node,
133
+ pattern,
134
+ rawPattern,
135
+ rawPatternStartRange,
136
+ flags
137
+ );
80
138
  }
81
139
  }
82
140
 
83
- /**
84
- * Check if node is a string
85
- * @param {ASTNode} node node to evaluate
86
- * @returns {boolean} True if its a string
87
- * @private
88
- */
89
- function isString(node) {
90
- return node && node.type === "Literal" && typeof node.value === "string";
91
- }
92
-
93
141
  /**
94
142
  * Validate strings passed to the RegExp constructor
95
143
  * @param {ASTNode} node node to validate
@@ -100,9 +148,22 @@ module.exports = {
100
148
  const scope = context.getScope();
101
149
  const regExpVar = astUtils.getVariableByName(scope, "RegExp");
102
150
  const shadowed = regExpVar && regExpVar.defs.length > 0;
151
+ const patternNode = node.arguments[0];
152
+ const flagsNode = node.arguments[1];
103
153
 
104
- if (node.callee.type === "Identifier" && node.callee.name === "RegExp" && isString(node.arguments[0]) && !shadowed) {
105
- checkRegex(node, node.arguments[0].value, node.arguments[0].range[0] + 1);
154
+ if (node.callee.type === "Identifier" && node.callee.name === "RegExp" && isString(patternNode) && !shadowed) {
155
+ const pattern = patternNode.value;
156
+ const rawPattern = patternNode.raw.slice(1, -1);
157
+ const rawPatternStartRange = patternNode.range[0] + 1;
158
+ const flags = isString(flagsNode) ? flagsNode.value : "";
159
+
160
+ checkRegex(
161
+ node,
162
+ pattern,
163
+ rawPattern,
164
+ rawPatternStartRange,
165
+ flags
166
+ );
106
167
  }
107
168
  }
108
169
 
@@ -111,6 +172,5 @@ module.exports = {
111
172
  CallExpression: checkFunction,
112
173
  NewExpression: checkFunction
113
174
  };
114
-
115
175
  }
116
176
  };
@@ -116,7 +116,7 @@ module.exports = {
116
116
 
117
117
  /**
118
118
  * Checks to see if "*" is being used to import everything.
119
- * @param {Set.<string>} importNames - Set of import names that are being imported
119
+ * @param {Set.<string>} importNames Set of import names that are being imported
120
120
  * @returns {boolean} whether everything is imported or not
121
121
  */
122
122
  function isEverythingImported(importNames) {
@@ -145,7 +145,7 @@ module.exports = {
145
145
 
146
146
  /**
147
147
  * Report a restricted path specifically for patterns.
148
- * @param {node} node - representing the restricted path reference
148
+ * @param {node} node representing the restricted path reference
149
149
  * @returns {void}
150
150
  * @private
151
151
  */
@@ -163,8 +163,8 @@ module.exports = {
163
163
 
164
164
  /**
165
165
  * Report a restricted path specifically when using the '*' import.
166
- * @param {string} importSource - path of the import
167
- * @param {node} node - representing the restricted path reference
166
+ * @param {string} importSource path of the import
167
+ * @param {node} node representing the restricted path reference
168
168
  * @returns {void}
169
169
  * @private
170
170
  */
@@ -185,8 +185,8 @@ module.exports = {
185
185
 
186
186
  /**
187
187
  * Check if the given importSource is restricted because '*' is being imported.
188
- * @param {string} importSource - path of the import
189
- * @param {Set.<string>} importNames - Set of import names that are being imported
188
+ * @param {string} importSource path of the import
189
+ * @param {Set.<string>} importNames Set of import names that are being imported
190
190
  * @returns {boolean} whether the path is restricted
191
191
  * @private
192
192
  */
@@ -198,8 +198,8 @@ module.exports = {
198
198
 
199
199
  /**
200
200
  * Check if the given importNames are restricted given a list of restrictedImportNames.
201
- * @param {Set.<string>} importNames - Set of import names that are being imported
202
- * @param {string[]} restrictedImportNames - array of import names that are restricted for this import
201
+ * @param {Set.<string>} importNames Set of import names that are being imported
202
+ * @param {string[]} restrictedImportNames array of import names that are restricted for this import
203
203
  * @returns {boolean} whether the objectName is restricted
204
204
  * @private
205
205
  */
@@ -211,8 +211,8 @@ module.exports = {
211
211
 
212
212
  /**
213
213
  * Check if the given importSource is a restricted path.
214
- * @param {string} importSource - path of the import
215
- * @param {Set.<string>} importNames - Set of import names that are being imported
214
+ * @param {string} importSource path of the import
215
+ * @param {Set.<string>} importNames Set of import names that are being imported
216
216
  * @returns {boolean} whether the variable is a restricted path or not
217
217
  * @private
218
218
  */
@@ -232,7 +232,7 @@ module.exports = {
232
232
 
233
233
  /**
234
234
  * Check if the given importSource is restricted by a pattern.
235
- * @param {string} importSource - path of the import
235
+ * @param {string} importSource path of the import
236
236
  * @returns {boolean} whether the variable is a restricted pattern or not
237
237
  * @private
238
238
  */