diff --git a/lib/formatter.js b/lib/formatter.js index 7358cc6..300efad 100644 --- a/lib/formatter.js +++ b/lib/formatter.js @@ -3,12 +3,6 @@ var path = require('path'); var firstBy = require('thenby'); var util = require('./util'); -var supportsLargeCharset = - process.platform !== 'win32' || - process.env.CI || - process.env.TERM === 'xterm-256color'; -var warningSymbol = supportsLargeCharset ? '⚠' : '!!'; - function createSortFunction(positionless, sortByPosition) { var positionValue = 0 @@ -76,8 +70,12 @@ module.exports = function (opts) { str += '\t'; } - if (!options.noIcon && message.type === 'warning') { - str += pico.yellow(warningSymbol + ' '); + if (!options.noIcon) { + if (message.type === 'warning') { + str += pico.yellow(util.warningSymbol + ' '); + } else if (message.type === 'error') { + str += pico.red(util.errorSymbol + ' '); + } } str += message.text; diff --git a/lib/reporter.js b/lib/reporter.js index 71872a9..ae709c7 100644 --- a/lib/reporter.js +++ b/lib/reporter.js @@ -32,7 +32,7 @@ module.exports = function (opts = {}) { }; } - var messageFilter = opts.filter || ((message) => message.type === 'warning'); + var messageFilter = opts.filter || ((message) => message.type === 'warning' || message.type === 'error'); return { postcssPlugin: 'postcss-reporter', @@ -45,6 +45,9 @@ module.exports = function (opts = {}) { ? '' : result.root.source.input.file || result.root.source.input.id; + let errorCount = 0; + let warningCount = 0; + var sourceGroupedMessages = messagesToLog.reduce((grouped, message) => { const key = util.getLocation(message).file || resultSource; @@ -52,6 +55,12 @@ module.exports = function (opts = {}) { grouped[key] = []; } + if (message.type === 'error') { + errorCount++; + } else if (message.type === 'warning') { + warningCount++; + } + grouped[key].push(message); return grouped; @@ -78,9 +87,15 @@ module.exports = function (opts = {}) { if (!report) return; + const summaryColor = errorCount > 0 ? 'red' : 'yellow'; + const summarySymbol = errorCount > 0 ? util.errorSymbol : util.warningSymbol; + const summary = `${summarySymbol} ${messagesToLog.length} ${util.plur('problem', messagesToLog.length)} (${errorCount} ${util.plur('error')}, ${warningCount} ${util.plur('warning')})` + + report += `\n ${pico[summaryColor](pico.bold(summary))}\n`; + console.log(report); - if (opts.throwError && shouldThrowError()) { + if (shouldThrowError()) { throw new Error( pico.red( pico.bold('\n** postcss-reporter: warnings or errors were found **') @@ -89,12 +104,7 @@ module.exports = function (opts = {}) { } function shouldThrowError() { - return ( - messagesToLog.length && - messagesToLog.some((message) => { - return message.type === 'warning' || message.type === 'error'; - }) - ); + return opts.throwError || errorCount > 0; } }, }; diff --git a/lib/util.js b/lib/util.js index 094b3ac..5433491 100644 --- a/lib/util.js +++ b/lib/util.js @@ -1,3 +1,8 @@ +var supportsLargeCharset = + process.platform !== 'win32' || + process.env.CI || + process.env.TERM === 'xterm-256color'; + exports.getLocation = function (message) { var messageNode = message.node; @@ -17,3 +22,10 @@ exports.getLocation = function (message) { location.file = messageInput.file || messageInput.id; return location; }; + +exports.plur = function plur(word, count) { + return (count === 1 ? word : `${word}s`); +} + +exports.warningSymbol = supportsLargeCharset ? '⚠' : '!!'; +exports.errorSymbol = supportsLargeCharset ? '✖' : 'xx'; diff --git a/test/formatter.js b/test/formatter.js index c3447dc..2282401 100644 --- a/test/formatter.js +++ b/test/formatter.js @@ -8,6 +8,7 @@ var stripColor = require('strip-color'); var defaultFormatter = formatter(); var colorlessWarning = '⚠'; +var colorlessError = '✖'; var basicMessages = [ { @@ -36,7 +37,7 @@ var basicOutput = '\n' + '\n' + colorlessWarning + ' foo warning [foo]' + '\n' + colorlessWarning + ' bar warning [bar]' + '\n' + colorlessWarning + ' baz warning [baz]' + - '\nbaz error [baz]' + + '\n' + colorlessError + ' baz error [baz]' + '\n'; var basicOutputMinimal = '\n' + @@ -104,14 +105,14 @@ var complexMessages = [ ]; var complexOutput = '\nstyle/rainbows/horses.css' + - '\nbaz error [baz]' + + '\n' + colorlessError + ' baz error [baz]' + '\n1:99\t' + colorlessWarning + ' bar warning [bar]' + '\n3:5\t' + colorlessWarning + ' foo warning [foo]' + '\n8:13\t' + colorlessWarning + ' ha warning [foo]' + '\n'; var noPositionSortOutput = '\nstyle/rainbows/horses.css' + - '\nbaz error [baz]' + + '\n' + colorlessError + ' baz error [baz]' + '\n3:5\t' + colorlessWarning + ' foo warning [foo]' + '\n1:99\t' + colorlessWarning + ' bar warning [bar]' + '\n8:13\t' + colorlessWarning + ' ha warning [foo]' + @@ -121,12 +122,12 @@ var positionlessLastOutput = '\nstyle/rainbows/horses.css' + '\n1:99\t' + colorlessWarning + ' bar warning [bar]' + '\n3:5\t' + colorlessWarning + ' foo warning [foo]' + '\n8:13\t' + colorlessWarning + ' ha warning [foo]' + - '\nbaz error [baz]' + + '\n' + colorlessError + ' baz error [baz]' + '\n'; var noSortOutput = '\nstyle/rainbows/horses.css' + '\n3:5\t' + colorlessWarning + ' foo warning [foo]' + - '\nbaz error [baz]' + + '\n' + colorlessError + ' baz error [baz]' + '\n1:99\t' + colorlessWarning + ' bar warning [bar]' + '\n8:13\t' + colorlessWarning + ' ha warning [foo]' + '\n'; diff --git a/test/reporter.js b/test/reporter.js index 2a069cf..1359a6a 100644 --- a/test/reporter.js +++ b/test/reporter.js @@ -84,11 +84,16 @@ test('reporter with simple mock result containing non warning typed message', fu plugin: 'foo', text: 'foo warning', }, + { + type: 'error', + plugin: 'foo', + text: 'foo error', + }, ]; var testReporter = reporter({ formatter: mockFormatter(tracker), }); - t.doesNotThrow(function() { + t.throws(function() { testReporter.OnceExit(null, { result: mockResultContainingNonWarningMessage, }); @@ -158,7 +163,11 @@ test('reporter with simple mock result and function-filtered plugins', function( formatter: mockFormatter(tracker), filter: function(message) { return message.type === 'error'; }, }); - testReporter.OnceExit(null, { result: cloneResult }); + t.throws(function() { + testReporter.OnceExit(null, { + result: cloneResult, + }); + }); t.deepEqual( tracker.messages, [ @@ -422,7 +431,11 @@ test('reporter with warnings that messages that each have nodes', function(t) { var testReporter = reporter({ formatter: mockMultiSourceFormatter(tracker), }); - testReporter.OnceExit(null, { result: mockWarningNodeResult }); + t.throws(function() { + testReporter.OnceExit(null, { + result: mockWarningNodeResult, + }); + }); t.deepEqual(tracker, [ { source: 'foo.css', @@ -470,6 +483,45 @@ test('reporter with warnings that messages that each have nodes', function(t) { }, ], }, + { + source: '', + messages: [ + { + type: 'error', + plugin: 'pat', + text: 'pat error', + node: { + source: { + input: { + id: '', + }, + }, + }, + }, + { + type: 'error', + plugin: 'hoo', + text: 'hoo error', + node: { + source: { + input: { + id: '', + }, + }, + }, + }, + ], + }, + { + source: '', + messages: [ + { + type: 'error', + plugin: 'hah', + text: 'hah error', + }, + ], + }, ]); t.end(); });