1 | "use strict";
|
2 |
|
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");
|
10 |
|
11 | const MARGIN_WIDTHS = 9;
|
12 |
|
13 | const levelColors = {
|
14 | info: "blue",
|
15 | warning: "yellow",
|
16 | error: "red"
|
17 | };
|
18 |
|
19 | function 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 |
|
40 | function 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 |
|
54 | function 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 |
|
63 | function 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 |
|
73 | if (availableWidth > fullWidth + MARGIN_WIDTHS) {
|
74 | return columnWidths[3];
|
75 | }
|
76 |
|
77 | return availableWidth - (fullWidth - columnWidths[3] + MARGIN_WIDTHS);
|
78 | }
|
79 |
|
80 | function formatter(messages, source) {
|
81 | if (!messages.length) return "";
|
82 |
|
83 | const orderedMessages = _.sortBy(
|
84 | messages,
|
85 | m => (m.line ? 2 : 1),
|
86 | m => m.line,
|
87 | m => m.column
|
88 | );
|
89 |
|
90 |
|
91 |
|
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 |
|
124 | .replace(/[\x01-\x1A]+/g, " ")
|
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 |
|
163 | module.exports = function(results) {
|
164 | let output = invalidOptionsFormatter(results);
|
165 |
|
166 | output += deprecationsFormatter(results);
|
167 |
|
168 | output = results.reduce((output, result) => {
|
169 |
|
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 |
|
188 | output = output.trim();
|
189 |
|
190 | if (output !== "") {
|
191 | output = "\n" + output + "\n\n";
|
192 | }
|
193 |
|
194 | return output;
|
195 | };
|