report.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. 'use strict';
  2. const util = require('util');
  3. /**
  4. * Report a problem.
  5. *
  6. * This function accounts for `disabledRanges` attached to the result.
  7. * That is, if the reported problem is within a disabledRange,
  8. * it is ignored. Otherwise, it is attached to the result as a
  9. * postcss warning.
  10. *
  11. * It also accounts for the rule's severity.
  12. *
  13. * You *must* pass *either* a node or a line number.
  14. *
  15. * @type {typeof import('stylelint').utils.report}
  16. */
  17. module.exports = function report(problem) {
  18. const { ruleName, result, message, messageArgs, line, node, index, endIndex, word } = problem;
  19. result.stylelint = result.stylelint || {
  20. ruleSeverities: {},
  21. customMessages: {},
  22. ruleMetadata: {},
  23. };
  24. // In quiet mode, mere warnings are ignored
  25. if (result.stylelint.quiet && result.stylelint.ruleSeverities[ruleName] !== 'error') {
  26. return;
  27. }
  28. const { start } = (node && node.rangeBy({ index, endIndex })) || {};
  29. // If a line is not passed, use the node.rangeBy method to get the
  30. // line number that the complaint pertains to
  31. const startLine = line || (start && start.line);
  32. if (!startLine) {
  33. throw new Error('You must pass either a node or a line number');
  34. }
  35. const { ignoreDisables } = result.stylelint.config || {};
  36. if (result.stylelint.disabledRanges) {
  37. const ranges =
  38. result.stylelint.disabledRanges[ruleName] || result.stylelint.disabledRanges.all || [];
  39. for (const range of ranges) {
  40. if (
  41. // If the problem is within a disabledRange,
  42. // and that disabledRange's rules include this one,
  43. // do not register a warning
  44. range.start <= startLine &&
  45. (range.end === undefined || range.end >= startLine) &&
  46. (!range.rules || range.rules.includes(ruleName))
  47. ) {
  48. // Collect disabled warnings
  49. // Used to report `needlessDisables` in subsequent processing.
  50. const disabledWarnings =
  51. result.stylelint.disabledWarnings || (result.stylelint.disabledWarnings = []);
  52. disabledWarnings.push({
  53. rule: ruleName,
  54. line: startLine,
  55. });
  56. if (!ignoreDisables) {
  57. return;
  58. }
  59. break;
  60. }
  61. }
  62. }
  63. const severity = result.stylelint.ruleSeverities && result.stylelint.ruleSeverities[ruleName];
  64. if (!result.stylelint.stylelintError && severity === 'error') {
  65. result.stylelint.stylelintError = true;
  66. }
  67. /** @type {import('stylelint').WarningOptions} */
  68. const warningProperties = {
  69. severity,
  70. rule: ruleName,
  71. };
  72. if (node) {
  73. warningProperties.node = node;
  74. }
  75. if (problem.start) {
  76. warningProperties.start = problem.start;
  77. } else if (index) {
  78. warningProperties.index = index;
  79. }
  80. if (problem.end) {
  81. warningProperties.end = problem.end;
  82. } else if (endIndex) {
  83. warningProperties.endIndex = endIndex;
  84. }
  85. if (word) {
  86. warningProperties.word = word;
  87. }
  88. const { customMessages } = result.stylelint;
  89. const warningMessage = buildWarningMessage(
  90. (customMessages && customMessages[ruleName]) || message,
  91. messageArgs,
  92. );
  93. result.warn(warningMessage, warningProperties);
  94. };
  95. /**
  96. * @param {import('stylelint').RuleMessage} message
  97. * @param {import('stylelint').Problem['messageArgs']} messageArgs
  98. * @returns {string}
  99. */
  100. function buildWarningMessage(message, messageArgs) {
  101. const args = messageArgs || [];
  102. if (typeof message === 'string') {
  103. return util.format(message, ...args);
  104. }
  105. return message(...args);
  106. }