eslint 8.5.0 → 8.9.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.
@@ -59,6 +59,7 @@ const globals = require("../../conf/globals");
59
59
  /** @typedef {import("../shared/types").Environment} Environment */
60
60
  /** @typedef {import("../shared/types").GlobalConf} GlobalConf */
61
61
  /** @typedef {import("../shared/types").LintMessage} LintMessage */
62
+ /** @typedef {import("../shared/types").SuppressedLintMessage} SuppressedLintMessage */
62
63
  /** @typedef {import("../shared/types").ParserOptions} ParserOptions */
63
64
  /** @typedef {import("../shared/types").LanguageOptions} LanguageOptions */
64
65
  /** @typedef {import("../shared/types").Processor} Processor */
@@ -77,6 +78,7 @@ const globals = require("../../conf/globals");
77
78
  * @property {number} line The line number
78
79
  * @property {number} column The column number
79
80
  * @property {(string|null)} ruleId The rule ID
81
+ * @property {string} justification The justification of directive
80
82
  */
81
83
 
82
84
  /**
@@ -84,6 +86,7 @@ const globals = require("../../conf/globals");
84
86
  * @typedef {Object} LinterInternalSlots
85
87
  * @property {ConfigArray|null} lastConfigArray The `ConfigArray` instance that the last `verify()` call used.
86
88
  * @property {SourceCode|null} lastSourceCode The `SourceCode` instance that the last `verify()` call used.
89
+ * @property {SuppressedLintMessage[]} lastSuppressedMessages The `SuppressedLintMessage[]` instance that the last `verify()` call produced.
87
90
  * @property {Map<string, Parser>} parserMap The loaded parsers.
88
91
  * @property {Rules} ruleMap The loaded rules.
89
92
  */
@@ -287,11 +290,12 @@ function createLintingProblem(options) {
287
290
  * @param {token} options.commentToken The Comment token
288
291
  * @param {string} options.value The value after the directive in the comment
289
292
  * comment specified no specific rules, so it applies to all rules (e.g. `eslint-disable`)
293
+ * @param {string} options.justification The justification of the directive
290
294
  * @param {function(string): {create: Function}} options.ruleMapper A map from rule IDs to defined rules
291
295
  * @returns {Object} Directives and problems from the comment
292
296
  */
293
297
  function createDisableDirectives(options) {
294
- const { commentToken, type, value, ruleMapper } = options;
298
+ const { commentToken, type, value, justification, ruleMapper } = options;
295
299
  const ruleIds = Object.keys(commentParser.parseListConfig(value));
296
300
  const directiveRules = ruleIds.length ? ruleIds : [null];
297
301
  const result = {
@@ -305,7 +309,25 @@ function createDisableDirectives(options) {
305
309
 
306
310
  // push to directives, if the rule is defined(including null, e.g. /*eslint enable*/)
307
311
  if (ruleId === null || !!ruleMapper(ruleId)) {
308
- result.directives.push({ parentComment, type, line: commentToken.loc.start.line, column: commentToken.loc.start.column + 1, ruleId });
312
+ if (type === "disable-next-line") {
313
+ result.directives.push({
314
+ parentComment,
315
+ type,
316
+ line: commentToken.loc.end.line,
317
+ column: commentToken.loc.end.column + 1,
318
+ ruleId,
319
+ justification
320
+ });
321
+ } else {
322
+ result.directives.push({
323
+ parentComment,
324
+ type,
325
+ line: commentToken.loc.start.line,
326
+ column: commentToken.loc.start.column + 1,
327
+ ruleId,
328
+ justification
329
+ });
330
+ }
309
331
  } else {
310
332
  result.directiveProblems.push(createLintingProblem({ ruleId, loc: commentToken.loc }));
311
333
  }
@@ -314,26 +336,34 @@ function createDisableDirectives(options) {
314
336
  }
315
337
 
316
338
  /**
317
- * Remove the ignored part from a given directive comment and trim it.
318
- * @param {string} value The comment text to strip.
319
- * @returns {string} The stripped text.
339
+ * Extract the directive and the justification from a given directive comment and trim them.
340
+ * @param {string} value The comment text to extract.
341
+ * @returns {{directivePart: string, justificationPart: string}} The extracted directive and justification.
320
342
  */
321
- function stripDirectiveComment(value) {
322
- return value.split(/\s-{2,}\s/u)[0].trim();
343
+ function extractDirectiveComment(value) {
344
+ const match = /\s-{2,}\s/u.exec(value);
345
+
346
+ if (!match) {
347
+ return { directivePart: value.trim(), justificationPart: "" };
348
+ }
349
+
350
+ const directive = value.slice(0, match.index).trim();
351
+ const justification = value.slice(match.index + match[0].length).trim();
352
+
353
+ return { directivePart: directive, justificationPart: justification };
323
354
  }
324
355
 
325
356
  /**
326
357
  * Parses comments in file to extract file-specific config of rules, globals
327
358
  * and environments and merges them with global config; also code blocks
328
359
  * where reporting is disabled or enabled and merges them with reporting config.
329
- * @param {string} filename The file being checked.
330
360
  * @param {ASTNode} ast The top node of the AST.
331
361
  * @param {function(string): {create: Function}} ruleMapper A map from rule IDs to defined rules
332
362
  * @param {string|null} warnInlineConfig If a string then it should warn directive comments as disabled. The string value is the config name what the setting came from.
333
363
  * @returns {{configuredRules: Object, enabledGlobals: {value:string,comment:Token}[], exportedVariables: Object, problems: Problem[], disableDirectives: DisableDirective[]}}
334
364
  * A collection of the directive comments that were found, along with any problems that occurred when parsing
335
365
  */
336
- function getDirectiveComments(filename, ast, ruleMapper, warnInlineConfig) {
366
+ function getDirectiveComments(ast, ruleMapper, warnInlineConfig) {
337
367
  const configuredRules = {};
338
368
  const enabledGlobals = Object.create(null);
339
369
  const exportedVariables = {};
@@ -344,8 +374,9 @@ function getDirectiveComments(filename, ast, ruleMapper, warnInlineConfig) {
344
374
  });
345
375
 
346
376
  ast.comments.filter(token => token.type !== "Shebang").forEach(comment => {
347
- const trimmedCommentText = stripDirectiveComment(comment.value);
348
- const match = /^(eslint(?:-env|-enable|-disable(?:(?:-next)?-line)?)?|exported|globals?)(?:\s|$)/u.exec(trimmedCommentText);
377
+ const { directivePart, justificationPart } = extractDirectiveComment(comment.value);
378
+
379
+ const match = /^(eslint(?:-env|-enable|-disable(?:(?:-next)?-line)?)?|exported|globals?)(?:\s|$)/u.exec(directivePart);
349
380
 
350
381
  if (!match) {
351
382
  return;
@@ -369,7 +400,7 @@ function getDirectiveComments(filename, ast, ruleMapper, warnInlineConfig) {
369
400
  return;
370
401
  }
371
402
 
372
- if (lineCommentSupported && comment.loc.start.line !== comment.loc.end.line) {
403
+ if (directiveText === "eslint-disable-line" && comment.loc.start.line !== comment.loc.end.line) {
373
404
  const message = `${directiveText} comment should not span multiple lines.`;
374
405
 
375
406
  problems.push(createLintingProblem({
@@ -380,7 +411,7 @@ function getDirectiveComments(filename, ast, ruleMapper, warnInlineConfig) {
380
411
  return;
381
412
  }
382
413
 
383
- const directiveValue = trimmedCommentText.slice(match.index + directiveText.length);
414
+ const directiveValue = directivePart.slice(match.index + directiveText.length);
384
415
 
385
416
  switch (directiveText) {
386
417
  case "eslint-disable":
@@ -388,7 +419,7 @@ function getDirectiveComments(filename, ast, ruleMapper, warnInlineConfig) {
388
419
  case "eslint-disable-next-line":
389
420
  case "eslint-disable-line": {
390
421
  const directiveType = directiveText.slice("eslint-".length);
391
- const options = { commentToken: comment, type: directiveType, value: directiveValue, ruleMapper };
422
+ const options = { commentToken: comment, type: directiveType, value: directiveValue, justification: justificationPart, ruleMapper };
392
423
  const { directives, directiveProblems } = createDisableDirectives(options);
393
424
 
394
425
  disableDirectives.push(...directives);
@@ -545,7 +576,7 @@ function findEslintEnv(text) {
545
576
  if (match[0].endsWith("*/")) {
546
577
  retv = Object.assign(
547
578
  retv || {},
548
- commentParser.parseListConfig(stripDirectiveComment(match[1]))
579
+ commentParser.parseListConfig(extractDirectiveComment(match[1]).directivePart)
549
580
  );
550
581
  }
551
582
  }
@@ -1220,6 +1251,7 @@ class Linter {
1220
1251
  cwd: normalizeCwd(cwd),
1221
1252
  lastConfigArray: null,
1222
1253
  lastSourceCode: null,
1254
+ lastSuppressedMessages: [],
1223
1255
  configType, // TODO: Remove after flat config conversion
1224
1256
  parserMap: new Map([["espree", espree]]),
1225
1257
  ruleMap: new Rules()
@@ -1243,7 +1275,7 @@ class Linter {
1243
1275
  * @param {ConfigData} providedConfig An ESLintConfig instance to configure everything.
1244
1276
  * @param {VerifyOptions} [providedOptions] The optional filename of the file being checked.
1245
1277
  * @throws {Error} If during rule execution.
1246
- * @returns {LintMessage[]} The results as an array of messages or an empty array if no messages.
1278
+ * @returns {(LintMessage|SuppressedLintMessage)[]} The results as an array of messages or an empty array if no messages.
1247
1279
  */
1248
1280
  _verifyWithoutProcessors(textOrSourceCode, providedConfig, providedOptions) {
1249
1281
  const slots = internalSlotsMap.get(this);
@@ -1332,7 +1364,7 @@ class Linter {
1332
1364
 
1333
1365
  const sourceCode = slots.lastSourceCode;
1334
1366
  const commentDirectives = options.allowInlineConfig
1335
- ? getDirectiveComments(options.filename, sourceCode.ast, ruleId => getRule(slots, ruleId), options.warnInlineConfig)
1367
+ ? getDirectiveComments(sourceCode.ast, ruleId => getRule(slots, ruleId), options.warnInlineConfig)
1336
1368
  : { configuredRules: {}, enabledGlobals: {}, exportedVariables: {}, problems: [], disableDirectives: [] };
1337
1369
 
1338
1370
  // augment global scope with declared global variables
@@ -1425,11 +1457,11 @@ class Linter {
1425
1457
  configArray.normalizeSync();
1426
1458
  }
1427
1459
 
1428
- return this._verifyWithFlatConfigArray(textOrSourceCode, configArray, options, true);
1460
+ return this._distinguishSuppressedMessages(this._verifyWithFlatConfigArray(textOrSourceCode, configArray, options, true));
1429
1461
  }
1430
1462
 
1431
1463
  if (typeof config.extractConfig === "function") {
1432
- return this._verifyWithConfigArray(textOrSourceCode, config, options);
1464
+ return this._distinguishSuppressedMessages(this._verifyWithConfigArray(textOrSourceCode, config, options));
1433
1465
  }
1434
1466
  }
1435
1467
 
@@ -1443,9 +1475,9 @@ class Linter {
1443
1475
  * So we cannot apply multiple processors.
1444
1476
  */
1445
1477
  if (options.preprocess || options.postprocess) {
1446
- return this._verifyWithProcessor(textOrSourceCode, config, options);
1478
+ return this._distinguishSuppressedMessages(this._verifyWithProcessor(textOrSourceCode, config, options));
1447
1479
  }
1448
- return this._verifyWithoutProcessors(textOrSourceCode, config, options);
1480
+ return this._distinguishSuppressedMessages(this._verifyWithoutProcessors(textOrSourceCode, config, options));
1449
1481
  }
1450
1482
 
1451
1483
  /**
@@ -1454,7 +1486,7 @@ class Linter {
1454
1486
  * @param {FlatConfig} config The config array.
1455
1487
  * @param {VerifyOptions&ProcessorOptions} options The options.
1456
1488
  * @param {FlatConfigArray} [configForRecursive] The `ConfigArray` object to apply multiple processors recursively.
1457
- * @returns {LintMessage[]} The found problems.
1489
+ * @returns {(LintMessage|SuppressedLintMessage)[]} The found problems.
1458
1490
  */
1459
1491
  _verifyWithFlatConfigArrayAndProcessor(textOrSourceCode, config, options, configForRecursive) {
1460
1492
  const filename = options.filename || "<input>";
@@ -1511,7 +1543,7 @@ class Linter {
1511
1543
  * @param {FlatConfig} providedConfig An ESLintConfig instance to configure everything.
1512
1544
  * @param {VerifyOptions} [providedOptions] The optional filename of the file being checked.
1513
1545
  * @throws {Error} If during rule execution.
1514
- * @returns {LintMessage[]} The results as an array of messages or an empty array if no messages.
1546
+ * @returns {(LintMessage|SuppressedLintMessage)[]} The results as an array of messages or an empty array if no messages.
1515
1547
  */
1516
1548
  _verifyWithFlatConfigArrayAndWithoutProcessors(textOrSourceCode, providedConfig, providedOptions) {
1517
1549
  const slots = internalSlotsMap.get(this);
@@ -1593,7 +1625,6 @@ class Linter {
1593
1625
  const sourceCode = slots.lastSourceCode;
1594
1626
  const commentDirectives = options.allowInlineConfig
1595
1627
  ? getDirectiveComments(
1596
- options.filename,
1597
1628
  sourceCode.ast,
1598
1629
  ruleId => getRuleFromConfig(ruleId, config),
1599
1630
  options.warnInlineConfig
@@ -1661,7 +1692,7 @@ class Linter {
1661
1692
  * @param {string|SourceCode} textOrSourceCode The source code.
1662
1693
  * @param {ConfigArray} configArray The config array.
1663
1694
  * @param {VerifyOptions&ProcessorOptions} options The options.
1664
- * @returns {LintMessage[]} The found problems.
1695
+ * @returns {(LintMessage|SuppressedLintMessage)[]} The found problems.
1665
1696
  */
1666
1697
  _verifyWithConfigArray(textOrSourceCode, configArray, options) {
1667
1698
  debug("With ConfigArray: %s", options.filename);
@@ -1698,7 +1729,7 @@ class Linter {
1698
1729
  * @param {VerifyOptions&ProcessorOptions} options The options.
1699
1730
  * @param {boolean} [firstCall=false] Indicates if this is being called directly
1700
1731
  * from verify(). (TODO: Remove once eslintrc is removed.)
1701
- * @returns {LintMessage[]} The found problems.
1732
+ * @returns {(LintMessage|SuppressedLintMessage)[]} The found problems.
1702
1733
  */
1703
1734
  _verifyWithFlatConfigArray(textOrSourceCode, configArray, options, firstCall = false) {
1704
1735
  debug("With flat config: %s", options.filename);
@@ -1738,7 +1769,7 @@ class Linter {
1738
1769
  * @param {ConfigData|ExtractedConfig} config The config array.
1739
1770
  * @param {VerifyOptions&ProcessorOptions} options The options.
1740
1771
  * @param {ConfigArray} [configForRecursive] The `ConfigArray` object to apply multiple processors recursively.
1741
- * @returns {LintMessage[]} The found problems.
1772
+ * @returns {(LintMessage|SuppressedLintMessage)[]} The found problems.
1742
1773
  */
1743
1774
  _verifyWithProcessor(textOrSourceCode, config, options, configForRecursive) {
1744
1775
  const filename = options.filename || "<input>";
@@ -1790,6 +1821,30 @@ class Linter {
1790
1821
  return postprocess(messageLists, filenameToExpose);
1791
1822
  }
1792
1823
 
1824
+ /**
1825
+ * Given a list of reported problems, distinguish problems between normal messages and suppressed messages.
1826
+ * The normal messages will be returned and the suppressed messages will be stored as lastSuppressedMessages.
1827
+ * @param {Problem[]} problems A list of reported problems.
1828
+ * @returns {LintMessage[]} A list of LintMessage.
1829
+ */
1830
+ _distinguishSuppressedMessages(problems) {
1831
+ const messages = [];
1832
+ const suppressedMessages = [];
1833
+ const slots = internalSlotsMap.get(this);
1834
+
1835
+ for (const problem of problems) {
1836
+ if (problem.suppressions) {
1837
+ suppressedMessages.push(problem);
1838
+ } else {
1839
+ messages.push(problem);
1840
+ }
1841
+ }
1842
+
1843
+ slots.lastSuppressedMessages = suppressedMessages;
1844
+
1845
+ return messages;
1846
+ }
1847
+
1793
1848
  /**
1794
1849
  * Gets the SourceCode object representing the parsed source.
1795
1850
  * @returns {SourceCode} The SourceCode object.
@@ -1798,6 +1853,14 @@ class Linter {
1798
1853
  return internalSlotsMap.get(this).lastSourceCode;
1799
1854
  }
1800
1855
 
1856
+ /**
1857
+ * Gets the list of SuppressedLintMessage produced in the last running.
1858
+ * @returns {SuppressedLintMessage[]} The list of SuppressedLintMessage
1859
+ */
1860
+ getSuppressedMessages() {
1861
+ return internalSlotsMap.get(this).lastSuppressedMessages;
1862
+ }
1863
+
1801
1864
  /**
1802
1865
  * Defines a new linting rule.
1803
1866
  * @param {string} ruleId A unique rule identifier