needlessDisables.cjs 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. // NOTICE: This file is generated by Rollup. To modify it,
  2. // please instead edit the ESM counterpart and rebuild with Rollup (npm run build).
  3. 'use strict';
  4. const constants = require('./constants.cjs');
  5. const optionsMatches = require('./utils/optionsMatches.cjs');
  6. const putIfAbsent = require('./utils/putIfAbsent.cjs');
  7. const reportCommentProblem = require('./utils/reportCommentProblem.cjs');
  8. const validateDisableSettings = require('./validateDisableSettings.cjs');
  9. /**
  10. * @param {import('stylelint').PostcssResult} postcssResult
  11. * @returns {void}
  12. */
  13. function needlessDisables(postcssResult) {
  14. const [enabled, options] = validateDisableSettings(postcssResult, 'reportNeedlessDisables');
  15. if (!options) return;
  16. const {
  17. disabledRanges,
  18. disabledRanges: { [constants.RULE_NAME_ALL]: disabledRangesAll = [] },
  19. disabledWarnings = [],
  20. } = postcssResult.stylelint;
  21. // A map from `stylelint-disable` comments to the set of rules that
  22. // are usefully disabled by each comment. We track this
  23. // comment-by-comment rather than range-by-range because ranges that
  24. // disable *all* rules are duplicated for each rule they apply to in
  25. // practice.
  26. /** @type {Map<import('postcss').Node, Set<string>>}} */
  27. const usefulDisables = new Map();
  28. for (const warning of disabledWarnings) {
  29. const rule = warning.rule;
  30. const ruleRanges = disabledRanges[rule];
  31. if (ruleRanges) {
  32. for (const range of ruleRanges) {
  33. if (isWarningInRange(warning, range)) {
  34. putIfAbsent(usefulDisables, range.node, () => new Set()).add(rule);
  35. }
  36. }
  37. }
  38. for (const range of disabledRangesAll) {
  39. if (isWarningInRange(warning, range)) {
  40. putIfAbsent(usefulDisables, range.node, () => new Set()).add(rule);
  41. }
  42. }
  43. }
  44. const allRangeNodes = new Set(disabledRangesAll.map((range) => range.node));
  45. for (const [rule, ranges] of Object.entries(disabledRanges)) {
  46. for (const range of ranges) {
  47. const node = range.node;
  48. if (rule !== constants.RULE_NAME_ALL && allRangeNodes.has(node)) continue;
  49. if (enabled === optionsMatches(options, 'except', rule)) continue;
  50. const useful = usefulDisables.get(node) || new Set();
  51. // Only emit a warning if this range's comment isn't useful for this rule.
  52. // For the special rule "all", only emit a warning if it's not useful for
  53. // *any* rules, because it covers all of them.
  54. if (rule === constants.RULE_NAME_ALL ? useful.size !== 0 : useful.has(rule)) continue;
  55. reportCommentProblem({
  56. rule: '--report-needless-disables',
  57. message: `Needless disable for "${rule}"`,
  58. severity: options.severity,
  59. node,
  60. postcssResult,
  61. });
  62. }
  63. }
  64. }
  65. /**
  66. * @param {import('stylelint').DisabledWarning} warning
  67. * @param {import('stylelint').DisabledRange} range
  68. * @returns {boolean}
  69. */
  70. function isWarningInRange(warning, range) {
  71. const line = warning.line;
  72. // Need to check if range.end exist, because line number type cannot be compared to undefined
  73. return (
  74. range.start <= line &&
  75. ((range.end !== undefined && range.end >= line) || range.end === undefined)
  76. );
  77. }
  78. module.exports = needlessDisables;