3 const _ = require("lodash");
4 const chalk = require("chalk");
5 const path = require("path");
6 const stringWidth = require("string-width");
7 const symbols = require("log-symbols");
8 const table = require("table");
9 const utils = require("postcss-reporter/lib/util");
11 const MARGIN_WIDTHS = 9;
19 function deprecationsFormatter(results) {
20 const allDeprecationWarnings = _.flatMap(results, "deprecations");
21 const uniqueDeprecationWarnings = _.uniqBy(allDeprecationWarnings, "text");
23 if (!uniqueDeprecationWarnings || !uniqueDeprecationWarnings.length) {
27 return uniqueDeprecationWarnings.reduce((output, warning) => {
28 output += chalk.yellow("Deprecation Warning: ");
29 output += warning.text;
30 if (warning.reference) {
31 output += chalk.dim(" See: ");
32 output += chalk.dim.underline(warning.reference);
38 function invalidOptionsFormatter(results) {
39 const allInvalidOptionWarnings = _.flatMap(results, r =>
40 r.invalidOptionWarnings.map(w => w.text)
42 const uniqueInvalidOptionWarnings = _.uniq(allInvalidOptionWarnings);
44 return uniqueInvalidOptionWarnings.reduce((output, warning) => {
45 output += chalk.red("Invalid Option: ");
51 function logFrom(fromValue) {
52 if (fromValue.charAt(0) === "<") return fromValue;
54 .relative(process.cwd(), fromValue)
59 function getMessageWidth(columnWidths) {
60 if (!process.stdout.isTTY) {
61 return columnWidths[3];
64 const availableWidth =
65 process.stdout.columns < 80 ? 80 : process.stdout.columns;
66 const fullWidth = _.sum(_.values(columnWidths));
68 // If there is no reason to wrap the text, we won't align the last column to the right
69 if (availableWidth > fullWidth + MARGIN_WIDTHS) {
70 return columnWidths[3];
73 return availableWidth - (fullWidth - columnWidths[3] + MARGIN_WIDTHS);
76 function formatter(messages, source) {
77 if (!messages.length) return "";
79 const orderedMessages = _.sortBy(
81 m => (m.line ? 2 : 1), // positionless first
86 // Create a list of column widths, needed to calculate
87 // the size of the message column and if needed wrap it.
88 const columnWidths = { 0: 1, 1: 1, 2: 1, 3: 1, 4: 1 };
90 const calculateWidths = function(columns) {
91 _.forOwn(columns, (value, key) => {
92 const normalisedValue = value ? value.toString() : value;
93 columnWidths[key] = Math.max(
95 stringWidth(normalisedValue)
105 output += chalk.underline(logFrom(source)) + "\n";
108 const cleanedMessages = orderedMessages.map(message => {
109 const location = utils.getLocation(message);
110 const severity = message.severity;
113 location.column || "",
115 ? chalk[levelColors[severity]](symbols[severity])
118 // Remove all control characters (newline, tab and etc)
119 .replace(/[\x01-\x1A]+/g, " ") // eslint-disable-line
122 new RegExp(_.escapeRegExp("(" + message.rule + ")") + "$"),
125 chalk.dim(message.rule || "")
128 calculateWidths(row);
134 .table(cleanedMessages, {
135 border: table.getBorderCharacters("void"),
137 0: { alignment: "right", width: columnWidths[0], paddingRight: 0 },
138 1: { alignment: "left", width: columnWidths[1] },
139 2: { alignment: "center", width: columnWidths[2] },
142 width: getMessageWidth(columnWidths),
145 4: { alignment: "left", width: columnWidths[4], paddingRight: 0 }
147 drawHorizontalLine: () => false
151 el.replace(/(\d+)\s+(\d+)/, (m, p1, p2) => chalk.dim(p1 + ":" + p2))
158 module.exports = function(results) {
159 let output = invalidOptionsFormatter(results);
160 output += deprecationsFormatter(results);
162 output = results.reduce((output, result) => {
163 // Treat parseErrors as warnings
164 if (result.parseErrors) {
165 result.parseErrors.forEach(error =>
166 result.warnings.push({
168 column: error.column,
169 rule: error.stylelintType,
171 text: `${error.text} (${error.stylelintType})`
175 output += formatter(result.warnings, result.source);
179 // Ensure consistent padding
180 output = output.trim();
183 output = "\n" + output + "\n\n";