verboseFormatter.js 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  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. const fixable = meta && meta.fixable ? ' (maybe fixable)' : '';
  58. output += dim(` ${ruleLink(rule, meta)}: ${list.length}${fixable}\n`);
  59. if (!fixableProblemsFound && meta && meta.fixable) {
  60. fixableProblemsFound = true;
  61. }
  62. }
  63. };
  64. printProblems('error');
  65. printProblems('warning');
  66. if (fixableProblemsFound) {
  67. output += yellow('\nYou may fix some problems with the "--fix" option.\n');
  68. }
  69. }
  70. return `${output}\n`;
  71. };
  72. /**
  73. * @template {string} K
  74. * @param {Warning[]} array
  75. * @param {(w: Warning) => K} keyFn
  76. * @returns {Record<K, Warning[]>}
  77. */
  78. function groupBy(array, keyFn) {
  79. /** @type {Record<string, Warning[]>} */
  80. const result = {};
  81. for (const item of array) {
  82. const key = keyFn(item);
  83. let warnings = result[key];
  84. if (warnings === undefined) {
  85. result[key] = warnings = [];
  86. }
  87. warnings.push(item);
  88. }
  89. return result;
  90. }
  91. /**
  92. * @param {string | undefined} source
  93. * @returns {string}
  94. */
  95. function fileLink(source) {
  96. if (!source || source.startsWith('<')) {
  97. return `${source}`;
  98. }
  99. return terminalLink(source, `file://${source}`);
  100. }
  101. /**
  102. * @param {string} rule
  103. * @param {Partial<RuleMeta> | undefined} metadata
  104. * @returns {string}
  105. */
  106. function ruleLink(rule, metadata) {
  107. if (metadata && metadata.url) {
  108. return terminalLink(rule, metadata.url);
  109. }
  110. return rule;
  111. }