UNPKG

5.06 kBJavaScriptView Raw
1"use strict";
2
3const _ = require("lodash");
4const chalk = require("chalk");
5const path = require("path");
6const stringWidth = require("string-width");
7const symbols = require("log-symbols");
8const table = require("table");
9const utils = require("postcss-reporter/lib/util");
10
11const MARGIN_WIDTHS = 9;
12
13const levelColors = {
14 info: "blue",
15 warning: "yellow",
16 error: "red"
17};
18
19function deprecationsFormatter(results) {
20 const allDeprecationWarnings = _.flatMap(results, "deprecations");
21 const uniqueDeprecationWarnings = _.uniqBy(allDeprecationWarnings, "text");
22
23 if (!uniqueDeprecationWarnings || !uniqueDeprecationWarnings.length) {
24 return "";
25 }
26
27 return uniqueDeprecationWarnings.reduce((output, warning) => {
28 output += chalk.yellow("Deprecation Warning: ");
29 output += warning.text;
30
31 if (warning.reference) {
32 output += chalk.dim(" See: ");
33 output += chalk.dim.underline(warning.reference);
34 }
35
36 return output + "\n";
37 }, "\n");
38}
39
40function invalidOptionsFormatter(results) {
41 const allInvalidOptionWarnings = _.flatMap(results, r =>
42 r.invalidOptionWarnings.map(w => w.text)
43 );
44 const uniqueInvalidOptionWarnings = _.uniq(allInvalidOptionWarnings);
45
46 return uniqueInvalidOptionWarnings.reduce((output, warning) => {
47 output += chalk.red("Invalid Option: ");
48 output += warning;
49
50 return output + "\n";
51 }, "\n");
52}
53
54function logFrom(fromValue) {
55 if (fromValue.charAt(0) === "<") return fromValue;
56
57 return path
58 .relative(process.cwd(), fromValue)
59 .split(path.sep)
60 .join("/");
61}
62
63function getMessageWidth(columnWidths) {
64 if (!process.stdout.isTTY) {
65 return columnWidths[3];
66 }
67
68 const availableWidth =
69 process.stdout.columns < 80 ? 80 : process.stdout.columns;
70 const fullWidth = _.sum(_.values(columnWidths));
71
72 // If there is no reason to wrap the text, we won't align the last column to the right
73 if (availableWidth > fullWidth + MARGIN_WIDTHS) {
74 return columnWidths[3];
75 }
76
77 return availableWidth - (fullWidth - columnWidths[3] + MARGIN_WIDTHS);
78}
79
80function formatter(messages, source) {
81 if (!messages.length) return "";
82
83 const orderedMessages = _.sortBy(
84 messages,
85 m => (m.line ? 2 : 1), // positionless first
86 m => m.line,
87 m => m.column
88 );
89
90 // Create a list of column widths, needed to calculate
91 // the size of the message column and if needed wrap it.
92 const columnWidths = { 0: 1, 1: 1, 2: 1, 3: 1, 4: 1 };
93
94 const calculateWidths = function(columns) {
95 _.forOwn(columns, (value, key) => {
96 const normalisedValue = value ? value.toString() : value;
97
98 columnWidths[key] = Math.max(
99 columnWidths[key],
100 stringWidth(normalisedValue)
101 );
102 });
103
104 return columns;
105 };
106
107 let output = "\n";
108
109 if (source) {
110 output += chalk.underline(logFrom(source)) + "\n";
111 }
112
113 const cleanedMessages = orderedMessages.map(message => {
114 const location = utils.getLocation(message);
115 const severity = message.severity;
116 const row = [
117 location.line || "",
118 location.column || "",
119 symbols[severity]
120 ? chalk[levelColors[severity]](symbols[severity])
121 : severity,
122 message.text
123 // Remove all control characters (newline, tab and etc)
124 .replace(/[\x01-\x1A]+/g, " ") // eslint-disable-line no-control-regex
125 .replace(/\.$/, "")
126 .replace(
127 new RegExp(_.escapeRegExp("(" + message.rule + ")") + "$"),
128 ""
129 ),
130 chalk.dim(message.rule || "")
131 ];
132
133 calculateWidths(row);
134
135 return row;
136 });
137
138 output += table
139 .table(cleanedMessages, {
140 border: table.getBorderCharacters("void"),
141 columns: {
142 0: { alignment: "right", width: columnWidths[0], paddingRight: 0 },
143 1: { alignment: "left", width: columnWidths[1] },
144 2: { alignment: "center", width: columnWidths[2] },
145 3: {
146 alignment: "left",
147 width: getMessageWidth(columnWidths),
148 wrapWord: getMessageWidth(columnWidths) > 1
149 },
150 4: { alignment: "left", width: columnWidths[4], paddingRight: 0 }
151 },
152 drawHorizontalLine: () => false
153 })
154 .split("\n")
155 .map(el =>
156 el.replace(/(\d+)\s+(\d+)/, (m, p1, p2) => chalk.dim(p1 + ":" + p2))
157 )
158 .join("\n");
159
160 return output;
161}
162
163module.exports = function(results) {
164 let output = invalidOptionsFormatter(results);
165
166 output += deprecationsFormatter(results);
167
168 output = results.reduce((output, result) => {
169 // Treat parseErrors as warnings
170 if (result.parseErrors) {
171 result.parseErrors.forEach(error =>
172 result.warnings.push({
173 line: error.line,
174 column: error.column,
175 rule: error.stylelintType,
176 severity: "error",
177 text: `${error.text} (${error.stylelintType})`
178 })
179 );
180 }
181
182 output += formatter(result.warnings, result.source);
183
184 return output;
185 }, output);
186
187 // Ensure consistent padding
188 output = output.trim();
189
190 if (output !== "") {
191 output = "\n" + output + "\n\n";
192 }
193
194 return output;
195};