verboseFormatter.js 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. 'use strict';
  2. const { underline, red, yellow, dim, green } = require('picocolors');
  3. const pluralize = require('../utils/pluralize');
  4. const stringFormatter = require('./stringFormatter');
  5. const terminalLink = require('./terminalLink');
  6. /** @typedef {import('stylelint').Formatter} Formatter */
  7. /** @typedef {import('stylelint').LintResult} LintResult */
  8. /** @typedef {import('stylelint').Warning} Warning */
  9. /** @typedef {import('stylelint').Severity} Severity */
  10. /** @typedef {import('stylelint').RuleMeta} RuleMeta */
  11. /**
  12. * @type {Formatter}
  13. */
  14. module.exports = function verboseFormatter(results, returnValue) {
  15. let output = stringFormatter(results, returnValue);
  16. if (output === '') {
  17. output = '\n';
  18. }
  19. const ignoredCount = results.filter((result) => result.ignored).length;
  20. const checkedDisplay = ignoredCount
  21. ? `${results.length - ignoredCount} of ${results.length}`
  22. : results.length;
  23. output += underline(`${checkedDisplay} ${pluralize('source', results.length)} checked\n`);
  24. for (const result of results) {
  25. let formatting = green;
  26. if (result.errored) {
  27. formatting = red;
  28. } else if (result.warnings.length) {
  29. formatting = yellow;
  30. } else if (result.ignored) {
  31. formatting = dim;
  32. }
  33. let sourceText = fileLink(result.source);
  34. if (result.ignored) {
  35. sourceText += ' (ignored)';
  36. }
  37. output += formatting(` ${sourceText}\n`);
  38. }
  39. const warnings = results.flatMap((r) => r.warnings);
  40. if (warnings.length === 0) {
  41. output += '\n0 problems found\n';
  42. } else {
  43. const warningsBySeverity = groupBy(warnings, (w) => w.severity);
  44. let fixableProblemsFound = false;
  45. /**
  46. * @param {Severity} severity
  47. */
  48. const printProblems = (severity) => {
  49. const problems = warningsBySeverity[severity];
  50. if (problems === undefined) return;
  51. output += '\n';
  52. output += underline(`${problems.length} ${pluralize(severity, problems.length)} found\n`);
  53. const problemsByRule = groupBy(problems, (w) => w.rule);
  54. const metadata = returnValue.ruleMetadata;
  55. for (const [rule, list] of Object.entries(problemsByRule)) {
  56. const meta = metadata[rule] || {};
  57. let additional = [meta.fixable ? 'maybe fixable' : '', meta.deprecated ? 'deprecated' : '']
  58. .filter(Boolean)
  59. .join(', ');
  60. additional = additional ? ` (${additional})` : '';
  61. output += dim(` ${ruleLink(rule, meta)}: ${list.length}${additional}\n`);
  62. if (!fixableProblemsFound && meta.fixable) {
  63. fixableProblemsFound = true;
  64. }
  65. }
  66. };
  67. printProblems('error');
  68. printProblems('warning');
  69. if (fixableProblemsFound) {
  70. output += yellow('\nYou may fix some problems with the "--fix" option.\n');
  71. }
  72. }
  73. return `${output}\n`;
  74. };
  75. /**
  76. * @template {string} K
  77. * @param {Warning[]} array
  78. * @param {(w: Warning) => K} keyFn
  79. * @returns {Record<K, Warning[]>}
  80. */
  81. function groupBy(array, keyFn) {
  82. /** @type {Record<string, Warning[]>} */
  83. const result = {};
  84. for (const item of array) {
  85. const key = keyFn(item);
  86. let warnings = result[key];
  87. if (warnings === undefined) {
  88. result[key] = warnings = [];
  89. }
  90. warnings.push(item);
  91. }
  92. return result;
  93. }
  94. /**
  95. * @param {string | undefined} source
  96. * @returns {string}
  97. */
  98. function fileLink(source) {
  99. if (!source || source.startsWith('<')) {
  100. return `${source}`;
  101. }
  102. return terminalLink(source, `file://${source}`);
  103. }
  104. /**
  105. * @param {string} rule
  106. * @param {Partial<RuleMeta> | undefined} metadata
  107. * @returns {string}
  108. */
  109. function ruleLink(rule, metadata) {
  110. if (metadata && metadata.url) {
  111. return terminalLink(rule, metadata.url);
  112. }
  113. return rule;
  114. }