eslint 6.5.0 → 6.7.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 (196) hide show
  1. package/CHANGELOG.md +115 -0
  2. package/README.md +10 -9
  3. package/conf/config-schema.js +1 -0
  4. package/conf/default-cli-options.js +1 -1
  5. package/lib/cli-engine/cascading-config-array-factory.js +40 -14
  6. package/lib/cli-engine/cli-engine.js +49 -21
  7. package/lib/cli-engine/config-array/config-array.js +13 -4
  8. package/lib/cli-engine/config-array/config-dependency.js +2 -0
  9. package/lib/cli-engine/config-array/extracted-config.js +27 -0
  10. package/lib/cli-engine/config-array/ignore-pattern.js +231 -0
  11. package/lib/cli-engine/config-array/index.js +2 -0
  12. package/lib/cli-engine/config-array/override-tester.js +2 -0
  13. package/lib/cli-engine/config-array-factory.js +120 -2
  14. package/lib/cli-engine/file-enumerator.js +51 -30
  15. package/lib/cli-engine/formatters/html.js +1 -0
  16. package/lib/init/autoconfig.js +1 -11
  17. package/lib/init/config-file.js +0 -1
  18. package/lib/init/config-initializer.js +4 -4
  19. package/lib/init/config-rule.js +1 -5
  20. package/lib/init/npm-utils.js +0 -5
  21. package/lib/linter/code-path-analysis/code-path-analyzer.js +24 -38
  22. package/lib/linter/code-path-analysis/code-path-segment.js +17 -25
  23. package/lib/linter/code-path-analysis/code-path-state.js +40 -81
  24. package/lib/linter/code-path-analysis/code-path.js +10 -11
  25. package/lib/linter/code-path-analysis/debug-helpers.js +8 -12
  26. package/lib/linter/code-path-analysis/fork-context.js +23 -34
  27. package/lib/linter/code-path-analysis/id-generator.js +2 -2
  28. package/lib/linter/linter.js +121 -95
  29. package/lib/linter/node-event-generator.js +3 -2
  30. package/lib/linter/report-translator.js +73 -7
  31. package/lib/rule-tester/rule-tester.js +46 -14
  32. package/lib/rules/accessor-pairs.js +8 -8
  33. package/lib/rules/array-bracket-newline.js +12 -15
  34. package/lib/rules/array-bracket-spacing.js +12 -12
  35. package/lib/rules/array-callback-return.js +6 -11
  36. package/lib/rules/array-element-newline.js +5 -8
  37. package/lib/rules/arrow-parens.js +0 -1
  38. package/lib/rules/block-scoped-var.js +3 -3
  39. package/lib/rules/block-spacing.js +4 -4
  40. package/lib/rules/camelcase.js +19 -6
  41. package/lib/rules/capitalized-comments.js +0 -7
  42. package/lib/rules/class-methods-use-this.js +3 -3
  43. package/lib/rules/comma-dangle.js +20 -25
  44. package/lib/rules/comma-spacing.js +1 -1
  45. package/lib/rules/computed-property-spacing.js +14 -14
  46. package/lib/rules/consistent-return.js +4 -5
  47. package/lib/rules/consistent-this.js +5 -5
  48. package/lib/rules/constructor-super.js +14 -16
  49. package/lib/rules/curly.js +12 -9
  50. package/lib/rules/default-param-last.js +1 -0
  51. package/lib/rules/dot-location.js +11 -12
  52. package/lib/rules/func-names.js +6 -6
  53. package/lib/rules/function-call-argument-newline.js +8 -6
  54. package/lib/rules/generator-star-spacing.js +4 -9
  55. package/lib/rules/getter-return.js +4 -7
  56. package/lib/rules/grouped-accessor-pairs.js +224 -0
  57. package/lib/rules/indent.js +13 -2
  58. package/lib/rules/index.js +5 -0
  59. package/lib/rules/init-declarations.js +2 -2
  60. package/lib/rules/jsx-quotes.js +1 -1
  61. package/lib/rules/keyword-spacing.js +32 -56
  62. package/lib/rules/lines-around-directive.js +1 -1
  63. package/lib/rules/max-len.js +0 -5
  64. package/lib/rules/max-statements-per-line.js +3 -7
  65. package/lib/rules/multiline-comment-style.js +237 -106
  66. package/lib/rules/multiline-ternary.js +3 -3
  67. package/lib/rules/newline-after-var.js +6 -7
  68. package/lib/rules/newline-before-return.js +8 -9
  69. package/lib/rules/newline-per-chained-call.js +2 -4
  70. package/lib/rules/no-class-assign.js +2 -2
  71. package/lib/rules/no-compare-neg-zero.js +1 -2
  72. package/lib/rules/no-cond-assign.js +14 -4
  73. package/lib/rules/no-confusing-arrow.js +2 -2
  74. package/lib/rules/no-console.js +4 -8
  75. package/lib/rules/no-const-assign.js +1 -1
  76. package/lib/rules/no-constructor-return.js +62 -0
  77. package/lib/rules/no-dupe-args.js +1 -1
  78. package/lib/rules/no-dupe-class-members.js +3 -4
  79. package/lib/rules/no-dupe-else-if.js +122 -0
  80. package/lib/rules/no-dupe-keys.js +6 -5
  81. package/lib/rules/no-duplicate-imports.js +14 -18
  82. package/lib/rules/no-else-return.js +0 -8
  83. package/lib/rules/no-empty-function.js +2 -4
  84. package/lib/rules/no-eval.js +10 -18
  85. package/lib/rules/no-ex-assign.js +1 -1
  86. package/lib/rules/no-extra-bind.js +5 -12
  87. package/lib/rules/no-extra-boolean-cast.js +0 -2
  88. package/lib/rules/no-extra-label.js +4 -9
  89. package/lib/rules/no-extra-parens.js +17 -15
  90. package/lib/rules/no-extra-semi.js +5 -6
  91. package/lib/rules/no-fallthrough.js +6 -6
  92. package/lib/rules/no-func-assign.js +3 -3
  93. package/lib/rules/no-global-assign.js +4 -4
  94. package/lib/rules/no-implicit-coercion.js +10 -10
  95. package/lib/rules/no-implicit-globals.js +90 -8
  96. package/lib/rules/no-implied-eval.js +0 -1
  97. package/lib/rules/no-inline-comments.js +25 -11
  98. package/lib/rules/no-invalid-this.js +17 -5
  99. package/lib/rules/no-labels.js +3 -6
  100. package/lib/rules/no-lone-blocks.js +1 -1
  101. package/lib/rules/no-loop-func.js +6 -11
  102. package/lib/rules/no-magic-numbers.js +6 -6
  103. package/lib/rules/no-misleading-character-class.js +14 -7
  104. package/lib/rules/no-mixed-operators.js +13 -22
  105. package/lib/rules/no-mixed-requires.js +0 -1
  106. package/lib/rules/no-multi-spaces.js +1 -1
  107. package/lib/rules/no-native-reassign.js +4 -4
  108. package/lib/rules/no-octal-escape.js +1 -1
  109. package/lib/rules/no-param-reassign.js +28 -7
  110. package/lib/rules/no-redeclare.js +1 -1
  111. package/lib/rules/no-regex-spaces.js +0 -1
  112. package/lib/rules/no-restricted-imports.js +11 -11
  113. package/lib/rules/no-self-assign.js +12 -13
  114. package/lib/rules/no-sequences.js +3 -3
  115. package/lib/rules/no-setter-return.js +227 -0
  116. package/lib/rules/no-shadow.js +1 -4
  117. package/lib/rules/no-tabs.js +8 -2
  118. package/lib/rules/no-this-before-super.js +12 -13
  119. package/lib/rules/no-trailing-spaces.js +19 -7
  120. package/lib/rules/no-underscore-dangle.js +23 -4
  121. package/lib/rules/no-unmodified-loop-condition.js +16 -29
  122. package/lib/rules/no-unneeded-ternary.js +3 -3
  123. package/lib/rules/no-unreachable.js +7 -7
  124. package/lib/rules/no-unsafe-finally.js +4 -7
  125. package/lib/rules/no-unsafe-negation.js +32 -9
  126. package/lib/rules/no-unused-expressions.js +11 -7
  127. package/lib/rules/no-unused-labels.js +3 -6
  128. package/lib/rules/no-unused-vars.js +22 -29
  129. package/lib/rules/no-use-before-define.js +10 -15
  130. package/lib/rules/no-useless-call.js +4 -4
  131. package/lib/rules/no-useless-computed-key.js +60 -33
  132. package/lib/rules/no-useless-concat.js +4 -4
  133. package/lib/rules/no-useless-constructor.js +14 -22
  134. package/lib/rules/no-useless-escape.js +29 -8
  135. package/lib/rules/no-useless-rename.js +15 -7
  136. package/lib/rules/no-useless-return.js +8 -15
  137. package/lib/rules/no-var.js +12 -25
  138. package/lib/rules/no-warning-comments.js +0 -1
  139. package/lib/rules/no-whitespace-before-property.js +3 -3
  140. package/lib/rules/object-curly-newline.js +7 -10
  141. package/lib/rules/object-curly-spacing.js +21 -22
  142. package/lib/rules/object-shorthand.js +1 -1
  143. package/lib/rules/one-var-declaration-per-line.js +2 -2
  144. package/lib/rules/operator-assignment.js +33 -3
  145. package/lib/rules/padded-blocks.js +1 -1
  146. package/lib/rules/padding-line-between-statements.js +0 -16
  147. package/lib/rules/prefer-arrow-callback.js +6 -6
  148. package/lib/rules/prefer-const.js +27 -28
  149. package/lib/rules/prefer-destructuring.js +1 -7
  150. package/lib/rules/prefer-exponentiation-operator.js +189 -0
  151. package/lib/rules/prefer-named-capture-group.js +0 -1
  152. package/lib/rules/prefer-numeric-literals.js +32 -4
  153. package/lib/rules/prefer-object-spread.js +7 -7
  154. package/lib/rules/prefer-rest-params.js +3 -6
  155. package/lib/rules/prefer-spread.js +4 -4
  156. package/lib/rules/prefer-template.js +5 -6
  157. package/lib/rules/quote-props.js +1 -1
  158. package/lib/rules/quotes.js +5 -6
  159. package/lib/rules/radix.js +5 -10
  160. package/lib/rules/require-await.js +10 -5
  161. package/lib/rules/require-yield.js +2 -2
  162. package/lib/rules/rest-spread-spacing.js +1 -1
  163. package/lib/rules/semi.js +6 -3
  164. package/lib/rules/sort-imports.js +3 -4
  165. package/lib/rules/sort-keys.js +1 -3
  166. package/lib/rules/space-before-blocks.js +1 -2
  167. package/lib/rules/space-in-parens.js +4 -4
  168. package/lib/rules/space-infix-ops.js +6 -6
  169. package/lib/rules/spaced-comment.js +20 -22
  170. package/lib/rules/strict.js +2 -4
  171. package/lib/rules/symbol-description.js +1 -2
  172. package/lib/rules/template-curly-spacing.js +2 -2
  173. package/lib/rules/use-isnan.js +40 -3
  174. package/lib/rules/utils/ast-utils.js +84 -85
  175. package/lib/rules/utils/fix-tracker.js +0 -6
  176. package/lib/rules/utils/lazy-loading-rule-map.js +0 -1
  177. package/lib/rules/vars-on-top.js +11 -11
  178. package/lib/shared/config-ops.js +2 -2
  179. package/lib/shared/runtime-info.js +8 -8
  180. package/lib/shared/traverser.js +2 -0
  181. package/lib/shared/types.js +9 -0
  182. package/lib/source-code/source-code.js +62 -17
  183. package/lib/source-code/token-store/backward-token-comment-cursor.js +5 -5
  184. package/lib/source-code/token-store/backward-token-cursor.js +5 -5
  185. package/lib/source-code/token-store/cursors.js +17 -19
  186. package/lib/source-code/token-store/decorative-cursor.js +1 -1
  187. package/lib/source-code/token-store/filter-cursor.js +2 -2
  188. package/lib/source-code/token-store/forward-token-comment-cursor.js +5 -5
  189. package/lib/source-code/token-store/forward-token-cursor.js +5 -5
  190. package/lib/source-code/token-store/index.js +86 -92
  191. package/lib/source-code/token-store/limit-cursor.js +2 -2
  192. package/lib/source-code/token-store/padded-token-cursor.js +7 -7
  193. package/lib/source-code/token-store/skip-cursor.js +2 -2
  194. package/lib/source-code/token-store/utils.js +9 -13
  195. package/package.json +9 -7
  196. package/lib/cli-engine/ignored-paths.js +0 -362
@@ -22,8 +22,7 @@ const assert = require("assert"),
22
22
 
23
23
  /**
24
24
  * Gets whether or not a given segment is reachable.
25
- *
26
- * @param {CodePathSegment} segment - A segment to get.
25
+ * @param {CodePathSegment} segment A segment to get.
27
26
  * @returns {boolean} `true` if the segment is reachable.
28
27
  */
29
28
  function isReachable(segment) {
@@ -36,11 +35,10 @@ function isReachable(segment) {
36
35
  * When `context.segmentsList` is `[[a, b], [c, d], [e, f]]`, `begin` is `0`, and
37
36
  * `end` is `-1`, this creates `[g, h]`. This `g` is from `a`, `c`, and `e`.
38
37
  * This `h` is from `b`, `d`, and `f`.
39
- *
40
- * @param {ForkContext} context - An instance.
41
- * @param {number} begin - The first index of the previous segments.
42
- * @param {number} end - The last index of the previous segments.
43
- * @param {Function} create - A factory function of new segments.
38
+ * @param {ForkContext} context An instance.
39
+ * @param {number} begin The first index of the previous segments.
40
+ * @param {number} end The last index of the previous segments.
41
+ * @param {Function} create A factory function of new segments.
44
42
  * @returns {CodePathSegment[]} New segments.
45
43
  */
46
44
  function makeSegments(context, begin, end, create) {
@@ -69,9 +67,8 @@ function makeSegments(context, begin, end, create) {
69
67
  * control statement (such as `break`, `continue`) from the `finally` block, the
70
68
  * destination's segments may be half of the source segments. In that case, this
71
69
  * merges segments.
72
- *
73
- * @param {ForkContext} context - An instance.
74
- * @param {CodePathSegment[]} segments - Segments to merge.
70
+ * @param {ForkContext} context An instance.
71
+ * @param {CodePathSegment[]} segments Segments to merge.
75
72
  * @returns {CodePathSegment[]} The merged segments.
76
73
  */
77
74
  function mergeExtraSegments(context, segments) {
@@ -100,10 +97,11 @@ function mergeExtraSegments(context, segments) {
100
97
  */
101
98
  class ForkContext {
102
99
 
100
+ // eslint-disable-next-line jsdoc/require-description
103
101
  /**
104
- * @param {IdGenerator} idGenerator - An identifier generator for segments.
105
- * @param {ForkContext|null} upper - An upper fork context.
106
- * @param {number} count - A number of parallel segments.
102
+ * @param {IdGenerator} idGenerator An identifier generator for segments.
103
+ * @param {ForkContext|null} upper An upper fork context.
104
+ * @param {number} count A number of parallel segments.
107
105
  */
108
106
  constructor(idGenerator, upper, count) {
109
107
  this.idGenerator = idGenerator;
@@ -142,9 +140,8 @@ class ForkContext {
142
140
 
143
141
  /**
144
142
  * Creates new segments from this context.
145
- *
146
- * @param {number} begin - The first index of previous segments.
147
- * @param {number} end - The last index of previous segments.
143
+ * @param {number} begin The first index of previous segments.
144
+ * @param {number} end The last index of previous segments.
148
145
  * @returns {CodePathSegment[]} New segments.
149
146
  */
150
147
  makeNext(begin, end) {
@@ -154,9 +151,8 @@ class ForkContext {
154
151
  /**
155
152
  * Creates new segments from this context.
156
153
  * The new segments is always unreachable.
157
- *
158
- * @param {number} begin - The first index of previous segments.
159
- * @param {number} end - The last index of previous segments.
154
+ * @param {number} begin The first index of previous segments.
155
+ * @param {number} end The last index of previous segments.
160
156
  * @returns {CodePathSegment[]} New segments.
161
157
  */
162
158
  makeUnreachable(begin, end) {
@@ -167,9 +163,8 @@ class ForkContext {
167
163
  * Creates new segments from this context.
168
164
  * The new segments don't have connections for previous segments.
169
165
  * But these inherit the reachable flag from this context.
170
- *
171
- * @param {number} begin - The first index of previous segments.
172
- * @param {number} end - The last index of previous segments.
166
+ * @param {number} begin The first index of previous segments.
167
+ * @param {number} end The last index of previous segments.
173
168
  * @returns {CodePathSegment[]} New segments.
174
169
  */
175
170
  makeDisconnected(begin, end) {
@@ -179,8 +174,7 @@ class ForkContext {
179
174
  /**
180
175
  * Adds segments into this context.
181
176
  * The added segments become the head.
182
- *
183
- * @param {CodePathSegment[]} segments - Segments to add.
177
+ * @param {CodePathSegment[]} segments Segments to add.
184
178
  * @returns {void}
185
179
  */
186
180
  add(segments) {
@@ -192,8 +186,7 @@ class ForkContext {
192
186
  /**
193
187
  * Replaces the head segments with given segments.
194
188
  * The current head segments are removed.
195
- *
196
- * @param {CodePathSegment[]} segments - Segments to add.
189
+ * @param {CodePathSegment[]} segments Segments to add.
197
190
  * @returns {void}
198
191
  */
199
192
  replaceHead(segments) {
@@ -204,8 +197,7 @@ class ForkContext {
204
197
 
205
198
  /**
206
199
  * Adds all segments of a given fork context into this context.
207
- *
208
- * @param {ForkContext} context - A fork context to add.
200
+ * @param {ForkContext} context A fork context to add.
209
201
  * @returns {void}
210
202
  */
211
203
  addAll(context) {
@@ -220,7 +212,6 @@ class ForkContext {
220
212
 
221
213
  /**
222
214
  * Clears all secments in this context.
223
- *
224
215
  * @returns {void}
225
216
  */
226
217
  clear() {
@@ -229,8 +220,7 @@ class ForkContext {
229
220
 
230
221
  /**
231
222
  * Creates the root fork context.
232
- *
233
- * @param {IdGenerator} idGenerator - An identifier generator for segments.
223
+ * @param {IdGenerator} idGenerator An identifier generator for segments.
234
224
  * @returns {ForkContext} New fork context.
235
225
  */
236
226
  static newRoot(idGenerator) {
@@ -243,9 +233,8 @@ class ForkContext {
243
233
 
244
234
  /**
245
235
  * Creates an empty fork context preceded by a given context.
246
- *
247
- * @param {ForkContext} parentContext - The parent fork context.
248
- * @param {boolean} forkLeavingPath - A flag which shows inside of `finally` block.
236
+ * @param {ForkContext} parentContext The parent fork context.
237
+ * @param {boolean} forkLeavingPath A flag which shows inside of `finally` block.
249
238
  * @returns {ForkContext} New fork context.
250
239
  */
251
240
  static newEmpty(parentContext, forkLeavingPath) {
@@ -18,8 +18,9 @@
18
18
  */
19
19
  class IdGenerator {
20
20
 
21
+ // eslint-disable-next-line jsdoc/require-description
21
22
  /**
22
- * @param {string} prefix - Optional. A prefix of generated ids.
23
+ * @param {string} prefix Optional. A prefix of generated ids.
23
24
  */
24
25
  constructor(prefix) {
25
26
  this.prefix = String(prefix);
@@ -28,7 +29,6 @@ class IdGenerator {
28
29
 
29
30
  /**
30
31
  * Generates id.
31
- *
32
32
  * @returns {string} A generated id.
33
33
  */
34
34
  next() {
@@ -292,10 +292,15 @@ function getDirectiveComments(filename, ast, ruleMapper, warnInlineConfig) {
292
292
  if (!match) {
293
293
  return;
294
294
  }
295
- const lineCommentSupported = /^eslint-disable-(next-)?line$/u.test(match[1]);
295
+ const directiveText = match[1];
296
+ const lineCommentSupported = /^eslint-disable-(next-)?line$/u.test(directiveText);
296
297
 
297
- if (warnInlineConfig && (lineCommentSupported || comment.type === "Block")) {
298
- const kind = comment.type === "Block" ? `/*${match[1]}*/` : `//${match[1]}`;
298
+ if (comment.type === "Line" && !lineCommentSupported) {
299
+ return;
300
+ }
301
+
302
+ if (warnInlineConfig) {
303
+ const kind = comment.type === "Block" ? `/*${directiveText}*/` : `//${directiveText}`;
299
304
 
300
305
  problems.push(createLintingProblem({
301
306
  ruleId: null,
@@ -306,108 +311,101 @@ function getDirectiveComments(filename, ast, ruleMapper, warnInlineConfig) {
306
311
  return;
307
312
  }
308
313
 
309
- const directiveValue = trimmedCommentText.slice(match.index + match[1].length);
310
- let directiveType = "";
314
+ if (lineCommentSupported && comment.loc.start.line !== comment.loc.end.line) {
315
+ const message = `${directiveText} comment should not span multiple lines.`;
311
316
 
312
- if (lineCommentSupported) {
313
- if (comment.loc.start.line === comment.loc.end.line) {
314
- directiveType = match[1].slice("eslint-".length);
315
- } else {
316
- const message = `${match[1]} comment should not span multiple lines.`;
317
+ problems.push(createLintingProblem({
318
+ ruleId: null,
319
+ message,
320
+ loc: comment.loc
321
+ }));
322
+ return;
323
+ }
317
324
 
318
- problems.push(createLintingProblem({
319
- ruleId: null,
320
- message,
321
- loc: comment.loc
322
- }));
325
+ const directiveValue = trimmedCommentText.slice(match.index + directiveText.length);
326
+
327
+ switch (directiveText) {
328
+ case "eslint-disable":
329
+ case "eslint-enable":
330
+ case "eslint-disable-next-line":
331
+ case "eslint-disable-line": {
332
+ const directiveType = directiveText.slice("eslint-".length);
333
+ const options = { type: directiveType, loc: comment.loc, value: directiveValue, ruleMapper };
334
+ const { directives, directiveProblems } = createDisableDirectives(options);
335
+
336
+ disableDirectives.push(...directives);
337
+ problems.push(...directiveProblems);
338
+ break;
323
339
  }
324
- } else if (comment.type === "Block") {
325
- switch (match[1]) {
326
- case "exported":
327
- Object.assign(exportedVariables, commentParser.parseStringConfig(directiveValue, comment));
328
- break;
329
340
 
330
- case "globals":
331
- case "global":
332
- for (const [id, { value }] of Object.entries(commentParser.parseStringConfig(directiveValue, comment))) {
333
- let normalizedValue;
341
+ case "exported":
342
+ Object.assign(exportedVariables, commentParser.parseStringConfig(directiveValue, comment));
343
+ break;
344
+
345
+ case "globals":
346
+ case "global":
347
+ for (const [id, { value }] of Object.entries(commentParser.parseStringConfig(directiveValue, comment))) {
348
+ let normalizedValue;
349
+
350
+ try {
351
+ normalizedValue = ConfigOps.normalizeConfigGlobal(value);
352
+ } catch (err) {
353
+ problems.push(createLintingProblem({
354
+ ruleId: null,
355
+ loc: comment.loc,
356
+ message: err.message
357
+ }));
358
+ continue;
359
+ }
360
+
361
+ if (enabledGlobals[id]) {
362
+ enabledGlobals[id].comments.push(comment);
363
+ enabledGlobals[id].value = normalizedValue;
364
+ } else {
365
+ enabledGlobals[id] = {
366
+ comments: [comment],
367
+ value: normalizedValue
368
+ };
369
+ }
370
+ }
371
+ break;
372
+
373
+ case "eslint": {
374
+ const parseResult = commentParser.parseJsonConfig(directiveValue, comment.loc);
375
+
376
+ if (parseResult.success) {
377
+ Object.keys(parseResult.config).forEach(name => {
378
+ const rule = ruleMapper(name);
379
+ const ruleValue = parseResult.config[name];
380
+
381
+ if (rule === null) {
382
+ problems.push(createLintingProblem({ ruleId: name, loc: comment.loc }));
383
+ return;
384
+ }
334
385
 
335
386
  try {
336
- normalizedValue = ConfigOps.normalizeConfigGlobal(value);
387
+ validator.validateRuleOptions(rule, name, ruleValue);
337
388
  } catch (err) {
338
389
  problems.push(createLintingProblem({
339
- ruleId: null,
340
- loc: comment.loc,
341
- message: err.message
390
+ ruleId: name,
391
+ message: err.message,
392
+ loc: comment.loc
342
393
  }));
343
- continue;
344
- }
345
394
 
346
- if (enabledGlobals[id]) {
347
- enabledGlobals[id].comments.push(comment);
348
- enabledGlobals[id].value = normalizedValue;
349
- } else {
350
- enabledGlobals[id] = {
351
- comments: [comment],
352
- value: normalizedValue
353
- };
395
+ // do not apply the config, if found invalid options.
396
+ return;
354
397
  }
355
- }
356
- break;
357
-
358
- case "eslint-disable":
359
- directiveType = "disable";
360
- break;
361
-
362
- case "eslint-enable":
363
- directiveType = "enable";
364
- break;
365
-
366
- case "eslint": {
367
- const parseResult = commentParser.parseJsonConfig(directiveValue, comment.loc);
368
-
369
- if (parseResult.success) {
370
- Object.keys(parseResult.config).forEach(name => {
371
- const rule = ruleMapper(name);
372
- const ruleValue = parseResult.config[name];
373
-
374
- if (rule === null) {
375
- problems.push(createLintingProblem({ ruleId: name, loc: comment.loc }));
376
- return;
377
- }
378
-
379
- try {
380
- validator.validateRuleOptions(rule, name, ruleValue);
381
- } catch (err) {
382
- problems.push(createLintingProblem({
383
- ruleId: name,
384
- message: err.message,
385
- loc: comment.loc
386
- }));
387
-
388
- // do not apply the config, if found invalid options.
389
- return;
390
- }
391
-
392
- configuredRules[name] = ruleValue;
393
- });
394
- } else {
395
- problems.push(parseResult.error);
396
- }
397
398
 
398
- break;
399
+ configuredRules[name] = ruleValue;
400
+ });
401
+ } else {
402
+ problems.push(parseResult.error);
399
403
  }
400
404
 
401
- // no default
405
+ break;
402
406
  }
403
- }
404
-
405
- if (directiveType !== "") {
406
- const options = { type: directiveType, loc: comment.loc, value: directiveValue, ruleMapper };
407
- const { directives, directiveProblems } = createDisableDirectives(options);
408
407
 
409
- disableDirectives.push(...directives);
410
- problems.push(...directiveProblems);
408
+ // no default
411
409
  }
412
410
  });
413
411
 
@@ -438,7 +436,7 @@ const eslintEnvPattern = /\/\*\s*eslint-env\s(.+?)\*\//gu;
438
436
 
439
437
  /**
440
438
  * Checks whether or not there is a comment which has "eslint-env *" in a given text.
441
- * @param {string} text - A source code text to check.
439
+ * @param {string} text A source code text to check.
442
440
  * @returns {Object|null} A result of parseListConfig() with "eslint-env *" comment.
443
441
  */
444
442
  function findEslintEnv(text) {
@@ -555,8 +553,7 @@ function resolveGlobals(providedGlobals, enabledEnvironments) {
555
553
 
556
554
  /**
557
555
  * Strips Unicode BOM from a given text.
558
- *
559
- * @param {string} text - A text to strip.
556
+ * @param {string} text A text to strip.
560
557
  * @returns {string} The stripped text.
561
558
  */
562
559
  function stripUnicodeBOM(text) {
@@ -669,6 +666,8 @@ function parse(text, parser, providedParserOptions, filePath) {
669
666
  // If the message includes a leading line number, strip it:
670
667
  const message = `Parsing error: ${ex.message.replace(/^line \d+:/iu, "").trim()}`;
671
668
 
669
+ debug("%s\n%s", message, ex.stack);
670
+
672
671
  return {
673
672
  success: false,
674
673
  error: {
@@ -813,9 +812,10 @@ const BASE_TRAVERSAL_CONTEXT = Object.freeze(
813
812
  * @param {Object} settings The settings that were enabled in the config
814
813
  * @param {string} filename The reported filename of the code
815
814
  * @param {boolean} disableFixes If true, it doesn't make `fix` properties.
815
+ * @param {string | undefined} cwd cwd of the cli
816
816
  * @returns {Problem[]} An array of reported problems
817
817
  */
818
- function runRules(sourceCode, configuredRules, ruleMapper, parserOptions, parserName, settings, filename, disableFixes) {
818
+ function runRules(sourceCode, configuredRules, ruleMapper, parserOptions, parserName, settings, filename, disableFixes, cwd) {
819
819
  const emitter = createEmitter();
820
820
  const nodeQueue = [];
821
821
  let currentNode = sourceCode.ast;
@@ -842,6 +842,7 @@ function runRules(sourceCode, configuredRules, ruleMapper, parserOptions, parser
842
842
  {
843
843
  getAncestors: () => getAncestors(currentNode),
844
844
  getDeclaredVariables: sourceCode.scopeManager.getDeclaredVariables.bind(sourceCode.scopeManager),
845
+ getCwd: () => cwd,
845
846
  getFilename: () => filename,
846
847
  getScope: () => getScope(sourceCode.scopeManager, currentNode),
847
848
  getSourceCode: () => sourceCode,
@@ -988,6 +989,24 @@ function getRule(slots, ruleId) {
988
989
  );
989
990
  }
990
991
 
992
+ /**
993
+ * Normalize the value of the cwd
994
+ * @param {string | undefined} cwd raw value of the cwd, path to a directory that should be considered as the current working directory, can be undefined.
995
+ * @returns {string | undefined} normalized cwd
996
+ */
997
+ function normalizeCwd(cwd) {
998
+ if (cwd) {
999
+ return cwd;
1000
+ }
1001
+ if (typeof process === "object") {
1002
+ return process.cwd();
1003
+ }
1004
+
1005
+ // It's more explicit to assign the undefined
1006
+ // eslint-disable-next-line no-undefined
1007
+ return undefined;
1008
+ }
1009
+
991
1010
  /**
992
1011
  * The map to store private data.
993
1012
  * @type {WeakMap<Linter, LinterInternalSlots>}
@@ -1004,8 +1023,14 @@ const internalSlotsMap = new WeakMap();
1004
1023
  */
1005
1024
  class Linter {
1006
1025
 
1007
- constructor() {
1026
+ /**
1027
+ * Initialize the Linter.
1028
+ * @param {Object} [config] the config object
1029
+ * @param {string} [config.cwd] path to a directory that should be considered as the current working directory, can be undefined.
1030
+ */
1031
+ constructor({ cwd } = {}) {
1008
1032
  internalSlotsMap.set(this, {
1033
+ cwd: normalizeCwd(cwd),
1009
1034
  lastConfigArray: null,
1010
1035
  lastSourceCode: null,
1011
1036
  parserMap: new Map([["espree", espree]]),
@@ -1137,7 +1162,8 @@ class Linter {
1137
1162
  parserName,
1138
1163
  settings,
1139
1164
  options.filename,
1140
- options.disableFixes
1165
+ options.disableFixes,
1166
+ slots.cwd
1141
1167
  );
1142
1168
  } catch (err) {
1143
1169
  err.message += `\nOccurred while linting ${options.filename}`;
@@ -202,6 +202,7 @@ const parseSelector = lodash.memoize(rawSelector => {
202
202
  */
203
203
  class NodeEventGenerator {
204
204
 
205
+ // eslint-disable-next-line jsdoc/require-description
205
206
  /**
206
207
  * @param {SafeEmitter} emitter
207
208
  * An SafeEmitter which is the destination of events. This emitter must already
@@ -286,7 +287,7 @@ class NodeEventGenerator {
286
287
 
287
288
  /**
288
289
  * Emits an event of entering AST node.
289
- * @param {ASTNode} node - A node which was entered.
290
+ * @param {ASTNode} node A node which was entered.
290
291
  * @returns {void}
291
292
  */
292
293
  enterNode(node) {
@@ -298,7 +299,7 @@ class NodeEventGenerator {
298
299
 
299
300
  /**
300
301
  * Emits an event of leaving AST node.
301
- * @param {ASTNode} node - A node which was left.
302
+ * @param {ASTNode} node A node which was left.
302
303
  * @returns {void}
303
304
  */
304
305
  leaveNode(node) {
@@ -26,6 +26,7 @@ const interpolate = require("./interpolate");
26
26
  * @property {Object} [data] Optional data to use to fill in placeholders in the
27
27
  * message.
28
28
  * @property {Function} [fix] The function to call that creates a fix command.
29
+ * @property {Array<{desc?: string, messageId?: string, fix: Function}>} suggest Suggestion descriptions and functions to create a the associated fixes.
29
30
  */
30
31
 
31
32
  /**
@@ -34,14 +35,15 @@ const interpolate = require("./interpolate");
34
35
  * @property {string} ruleId
35
36
  * @property {(0|1|2)} severity
36
37
  * @property {(string|undefined)} message
37
- * @property {(string|undefined)} messageId
38
+ * @property {(string|undefined)} [messageId]
38
39
  * @property {number} line
39
40
  * @property {number} column
40
- * @property {(number|undefined)} endLine
41
- * @property {(number|undefined)} endColumn
41
+ * @property {(number|undefined)} [endLine]
42
+ * @property {(number|undefined)} [endColumn]
42
43
  * @property {(string|null)} nodeType
43
44
  * @property {string} source
44
- * @property {({text: string, range: (number[]|null)}|null)} fix
45
+ * @property {({text: string, range: (number[]|null)}|null)} [fix]
46
+ * @property {Array<{text: string, range: (number[]|null)}|null>} [suggestions]
45
47
  */
46
48
 
47
49
  //------------------------------------------------------------------------------
@@ -182,6 +184,29 @@ function normalizeFixes(descriptor, sourceCode) {
182
184
  return fix;
183
185
  }
184
186
 
187
+ /**
188
+ * Gets an array of suggestion objects from the given descriptor.
189
+ * @param {MessageDescriptor} descriptor The report descriptor.
190
+ * @param {SourceCode} sourceCode The source code object to get text between fixes.
191
+ * @param {Object} messages Object of meta messages for the rule.
192
+ * @returns {Array<SuggestionResult>} The suggestions for the descriptor
193
+ */
194
+ function mapSuggestions(descriptor, sourceCode, messages) {
195
+ if (!descriptor.suggest || !Array.isArray(descriptor.suggest)) {
196
+ return [];
197
+ }
198
+
199
+ return descriptor.suggest.map(suggestInfo => {
200
+ const computedDesc = suggestInfo.desc || messages[suggestInfo.messageId];
201
+
202
+ return {
203
+ ...suggestInfo,
204
+ desc: interpolate(computedDesc, suggestInfo.data),
205
+ fix: normalizeFixes(suggestInfo, sourceCode)
206
+ };
207
+ });
208
+ }
209
+
185
210
  /**
186
211
  * Creates information about the report from a descriptor
187
212
  * @param {Object} options Information about the problem
@@ -192,6 +217,7 @@ function normalizeFixes(descriptor, sourceCode) {
192
217
  * @param {string} [options.messageId] The error message ID.
193
218
  * @param {{start: SourceLocation, end: (SourceLocation|null)}} options.loc Start and end location
194
219
  * @param {{text: string, range: (number[]|null)}} options.fix The fix object
220
+ * @param {Array<{text: string, range: (number[]|null)}>} options.suggestions The array of suggestions objects
195
221
  * @returns {function(...args): ReportInfo} Function that returns information about the report
196
222
  */
197
223
  function createProblem(options) {
@@ -221,9 +247,47 @@ function createProblem(options) {
221
247
  problem.fix = options.fix;
222
248
  }
223
249
 
250
+ if (options.suggestions && options.suggestions.length > 0) {
251
+ problem.suggestions = options.suggestions;
252
+ }
253
+
224
254
  return problem;
225
255
  }
226
256
 
257
+ /**
258
+ * Validates that suggestions are properly defined. Throws if an error is detected.
259
+ * @param {Array<{ desc?: string, messageId?: string }>} suggest The incoming suggest data.
260
+ * @param {Object} messages Object of meta messages for the rule.
261
+ * @returns {void}
262
+ */
263
+ function validateSuggestions(suggest, messages) {
264
+ if (suggest && Array.isArray(suggest)) {
265
+ suggest.forEach(suggestion => {
266
+ if (suggestion.messageId) {
267
+ const { messageId } = suggestion;
268
+
269
+ if (!messages) {
270
+ throw new TypeError(`context.report() called with a suggest option with a messageId '${messageId}', but no messages were present in the rule metadata.`);
271
+ }
272
+
273
+ if (!messages[messageId]) {
274
+ throw new TypeError(`context.report() called with a suggest option with a messageId '${messageId}' which is not present in the 'messages' config: ${JSON.stringify(messages, null, 2)}`);
275
+ }
276
+
277
+ if (suggestion.desc) {
278
+ throw new TypeError("context.report() called with a suggest option that defines both a 'messageId' and an 'desc'. Please only pass one.");
279
+ }
280
+ } else if (!suggestion.desc) {
281
+ throw new TypeError("context.report() called with a suggest option that doesn't have either a `desc` or `messageId`");
282
+ }
283
+
284
+ if (typeof suggestion.fix !== "function") {
285
+ throw new TypeError(`context.report() called with a suggest option without a fix function. See: ${suggestion}`);
286
+ }
287
+ });
288
+ }
289
+ }
290
+
227
291
  /**
228
292
  * Returns a function that converts the arguments of a `context.report` call from a rule into a reported
229
293
  * problem for the Node.js API.
@@ -242,17 +306,17 @@ module.exports = function createReportTranslator(metadata) {
242
306
  */
243
307
  return (...args) => {
244
308
  const descriptor = normalizeMultiArgReportCall(...args);
309
+ const messages = metadata.messageIds;
245
310
 
246
311
  assertValidNodeInfo(descriptor);
247
312
 
248
313
  let computedMessage;
249
314
 
250
315
  if (descriptor.messageId) {
251
- if (!metadata.messageIds) {
316
+ if (!messages) {
252
317
  throw new TypeError("context.report() called with a messageId, but no messages were present in the rule metadata.");
253
318
  }
254
319
  const id = descriptor.messageId;
255
- const messages = metadata.messageIds;
256
320
 
257
321
  if (descriptor.message) {
258
322
  throw new TypeError("context.report() called with a message and a messageId. Please only pass one.");
@@ -267,6 +331,7 @@ module.exports = function createReportTranslator(metadata) {
267
331
  throw new TypeError("Missing `message` property in report() call; add a message that describes the linting problem.");
268
332
  }
269
333
 
334
+ validateSuggestions(descriptor.suggest, messages);
270
335
 
271
336
  return createProblem({
272
337
  ruleId: metadata.ruleId,
@@ -275,7 +340,8 @@ module.exports = function createReportTranslator(metadata) {
275
340
  message: interpolate(computedMessage, descriptor.data),
276
341
  messageId: descriptor.messageId,
277
342
  loc: normalizeReportLoc(descriptor),
278
- fix: metadata.disableFixes ? null : normalizeFixes(descriptor, metadata.sourceCode)
343
+ fix: metadata.disableFixes ? null : normalizeFixes(descriptor, metadata.sourceCode),
344
+ suggestions: metadata.disableFixes ? [] : mapSuggestions(descriptor, metadata.sourceCode, messages)
279
345
  });
280
346
  };
281
347
  };