├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── example.png ├── index.js ├── lib └── processResult.js ├── package.json └── test ├── forVisual.css ├── index.js ├── processResult.js └── visual.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | charset = utf-8 11 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "node": true 4 | }, 5 | "rules": { 6 | "curly": 0, 7 | "radix": 2, 8 | "wrap-iife": 2, 9 | "brace-style": 0, 10 | "comma-style": 2, 11 | "consistent-this": [2, "_that"], 12 | "no-lonely-if": 2, 13 | "no-nested-ternary": 2, 14 | "no-process-exit": 0, 15 | "no-spaced-func": 2, 16 | "no-use-before-define": [2, "nofunc"], 17 | "quotes": [2, "single"], 18 | "space-after-keywords": [2, "always"], 19 | "space-before-blocks": [2, "always"], 20 | "space-in-parens": [2, "never"], 21 | "space-unary-ops": 2 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | tmp 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.12" 4 | - "iojs" 5 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## v0.3.1 4 | - Fix bug causing error if warning is on root node. 5 | 6 | ## v0.3.0 7 | - Throw error instead of exiting process when `throwError: true`. 8 | 9 | ## v0.2.3 10 | - Remove async. 11 | 12 | ## v0.2.0 13 | - Change the print format slightly. 14 | - Clear warnings from postcssResult.messages after logging them (configurable with option `keepWarnings`). 15 | 16 | ## v0.1.3 17 | - Add message when exiting process with code 1 to clarify reason. 18 | 19 | ## v0.1.2 20 | - Only log if there is something to log (don't log undefined). 21 | 22 | ## v0.1.1 23 | - Only exit process if code is non-zero. 24 | 25 | ## v0.1.0 26 | - First release. 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 David Clark 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # postcss-log-warnings [![Build Status](https://travis-ci.org/davidtheclark/postcss-log-warnings.svg?branch=master)](https://travis-ci.org/davidtheclark/postcss-log-warnings) 2 | 3 | --- 4 | 5 | **DEPRECATED** -- **DEPRECATED** -- **DEPRECATED** 6 | 7 | This plugin has grown up and become [postcss-reporter](https://github.com/postcss/postcss-reporter)! 8 | 9 | Please switch to the new official PostCSS plugin! 10 | 11 | **DEPRECATED** -- **DEPRECATED** -- **DEPRECATED** 12 | 13 | --- 14 | 15 | Log PostCSS warnings in the console. 16 | 17 | ## Purpose 18 | 19 | As of PostCSS 4.1, a single PostCSS process can accumulate warnings from all of the plugins it uses. 20 | Presumably, plugin authors want you to see those warnings. 21 | So this plugin exists to read the accumulated warnings (or warnings from only the plugins you've specified), format them for human legibility, and print them to the console. 22 | 23 | ## Example Output 24 | 25 | ![Example](example.png?raw=true) 26 | 27 | ## Installation 28 | 29 | ``` 30 | npm install postcss-log-warnings 31 | ``` 32 | 33 | ## Usage 34 | 35 | Add it to your plugin list *after any plugins whose warnings you want to log*, and pass it an object of options. 36 | 37 | For example, using [gulp-postcss](https://github.com/w0rm/gulp-postcss): 38 | 39 | ```js 40 | gulp.task('css', function() { 41 | return gulp.src('./src/*.css') 42 | .pipe(postcss([ 43 | bemLinter(), 44 | customProperties(), 45 | calc(), 46 | rejectAllColors(), 47 | logWarnings(myOptions) // <------ ding 48 | ])) 49 | .pipe(gulp.dest('./dist')); 50 | }); 51 | ``` 52 | 53 | *By default, this plugin will clear the warnings after it logs them*. Otherwise, your other plugins or your PostCSS runner might re-print the same warnings, causing some confusion. This can be changed by setting the option `{ keepWarnings: true }`. 54 | 55 | You can also use this module as a library: 56 | 57 | ```js 58 | var processResult = require('postcss-log-warnings/lib/processResult'); 59 | var warningLog = processResult(postcssResult, options); 60 | ``` 61 | 62 | ### Options 63 | 64 | - **keepWarnings** (boolean, default = `false`) 65 | 66 | If true, the plugin will *not* clear the warnings after it logs them (by default, it will clear them). Other plugins will then have access to these warnings and might re-print them. 67 | 68 | - **plugins** (array of strings, default = `[]`) 69 | 70 | If empty, the plugin will log every warning, regardless of which plugin registered it. 71 | To limit output, name the plugins whose warnings you would like to see. 72 | For example, `{ plugins: ['postcss-bem-linter'] }` will only log warnings from the `postcss-bem-linter` plugin. 73 | 74 | - **throwError** (boolean, default = `false`) 75 | 76 | If `true`, after the plugin logs your warnings it will throw an error if it found any warnings. 77 | (Not part of the library options.) 78 | -------------------------------------------------------------------------------- /example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidtheclark/postcss-log-warnings/c8bf820fb1c0031e6e30390ddbbb914e805d4b38/example.png -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var postcss = require('postcss'); 4 | var chalk = require('chalk'); 5 | var processResult = require('./lib/processResult'); 6 | 7 | module.exports = postcss.plugin('postcss-log-warnings', function(options) { 8 | options = options || {}; 9 | 10 | return function(css, result) { 11 | var warningLog = processResult(result, options); 12 | 13 | if (!warningLog) return; 14 | 15 | console.log(warningLog); 16 | 17 | if (options.throwError) { 18 | throw new Error(chalk.red.bold('\n** postcss-log-warnings: warnings were found **')); 19 | } 20 | }; 21 | }); 22 | -------------------------------------------------------------------------------- /lib/processResult.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var chalk = require('chalk'); 4 | var path = require('path'); 5 | 6 | module.exports = function(postcssResult, options) { 7 | options = options || {}; 8 | options.plugins = options.plugins || []; 9 | 10 | var warnings = postcssResult.warnings(); 11 | 12 | if (!warnings.length) return undefined; 13 | 14 | var output = '\n# postcss-log-warnings\n\n'; 15 | 16 | output += chalk.bold.underline(logFrom(postcssResult.root.source.input.from)) + '\n'; 17 | 18 | var filteredWarnings = warnings.filter(shouldLog); 19 | filteredWarnings.forEach(function(w) { 20 | output += warningToString(w) + '\n'; 21 | }); 22 | 23 | // Unless user has set `keepWarnings` option, 24 | // clear all these warnings that were just stringified 25 | if (!options.keepWarnings) { 26 | postcssResult.messages = postcssResult.messages.filter(function(message) { 27 | return message.type !== 'warning'; 28 | }); 29 | } 30 | 31 | return output; 32 | 33 | function shouldLog(warning) { 34 | if (options.plugins.length && options.plugins.indexOf(warning.plugin) === -1) { 35 | return false; 36 | } 37 | return true; 38 | } 39 | 40 | function warningToString(warning) { 41 | var str = ''; 42 | if (warning.node && warning.node.type !== 'root') { 43 | str += chalk.bold( 44 | warning.node.source.start.line + ':' + 45 | warning.node.source.start.column + '\t' 46 | ); 47 | } 48 | str += warning.text; 49 | if (options.plugins.length !== 1) { 50 | str += chalk.yellow(' [' + warning.plugin + ']'); 51 | } 52 | return str; 53 | } 54 | 55 | function logFrom(f) { 56 | if (f.charAt(0) === '<') return f; 57 | return path.relative(process.cwd(), f); 58 | } 59 | }; 60 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "postcss-log-warnings", 3 | "version": "0.3.1", 4 | "description": "Log PostCSS warnings in the console", 5 | "main": "index.js", 6 | "scripts": { 7 | "lint": "eslint .", 8 | "test": "npm run lint && tape test", 9 | "visual": "node test/visual.js" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/davidtheclark/postcss-log-warnings.git" 14 | }, 15 | "author": { 16 | "name": "David Clark", 17 | "email": "david.dave.clark@gmail.com", 18 | "url": "http://davidtheclark.com" 19 | }, 20 | "license": "MIT", 21 | "bugs": { 22 | "url": "https://github.com/davidtheclark/postcss-log-warnings/issues" 23 | }, 24 | "homepage": "https://github.com/davidtheclark/postcss-log-warnings", 25 | "devDependencies": { 26 | "eslint": "0.20.0", 27 | "lodash": "3.8.0", 28 | "tape": "4.0.0" 29 | }, 30 | "dependencies": { 31 | "chalk": "^1.0.0", 32 | "postcss": "^4.1.0" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /test/forVisual.css: -------------------------------------------------------------------------------- 1 | /** @define HamSandwich */ 2 | .Ham.Sandwich { 3 | color: pink; 4 | } 5 | 6 | :root a { 7 | font-weight: bold; 8 | } 9 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | require('./processResult'); 4 | -------------------------------------------------------------------------------- /test/processResult.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var test = require('tape'); 4 | var processResult = require('../lib/processResult'); 5 | var chalk = require('chalk'); 6 | var _ = require('lodash'); 7 | var path = require('path'); 8 | 9 | var mockSimpleResult = { 10 | messages: [{ 11 | type: 'warning', 12 | plugin: 'foo', 13 | text: 'foo warning' 14 | }, { 15 | type: 'warning', 16 | plugin: 'bar', 17 | text: 'bar warning' 18 | }, { 19 | type: 'warning', 20 | plugin: 'baz', 21 | text: 'baz warning' 22 | }, { 23 | type: 'error', 24 | plugin: 'baz', 25 | text: 'baz error' 26 | }], 27 | root: { 28 | source: { 29 | input: { 30 | from: '' 31 | } 32 | } 33 | } 34 | }; 35 | 36 | mockSimpleResult.warnings = function() { 37 | return this.messages.filter(function(m) { 38 | return m.type === 'warning'; 39 | }); 40 | }; 41 | 42 | var simpleOutput = '\n# postcss-log-warnings\n' + 43 | '\n' + 44 | '\nfoo warning [foo]' + 45 | '\nbar warning [bar]' + 46 | '\nbaz warning [baz]\n'; 47 | 48 | var simpleOutputNoBar = '\n# postcss-log-warnings\n' + 49 | '\n' + 50 | '\nfoo warning [foo]' + 51 | '\nbaz warning [baz]\n'; 52 | 53 | test('processResult with simple mock', function(t) { 54 | t.equal( 55 | chalk.stripColor(processResult(_.cloneDeep(mockSimpleResult))), 56 | simpleOutput, 57 | 'basic' 58 | ); 59 | 60 | t.equal( 61 | chalk.stripColor( 62 | processResult(_.cloneDeep(mockSimpleResult), { plugins: ['foo', 'baz']}) 63 | ), 64 | simpleOutputNoBar, 65 | 'excluding bar' 66 | ); 67 | 68 | t.end(); 69 | }); 70 | 71 | test('clearing warnings from result.messages', function(t) { 72 | var resultA = _.cloneDeep(mockSimpleResult); 73 | var resultB = _.cloneDeep(mockSimpleResult); 74 | 75 | t.equal(resultA.warnings().length, 3, 'initial length accurate'); 76 | 77 | processResult(resultA); 78 | 79 | t.equal(resultA.warnings().length, 0, 'warnings are cleared'); 80 | 81 | processResult(resultB, { keepWarnings: true }); 82 | 83 | t.deepEqual(mockSimpleResult.messages, resultB.messages, 84 | 'keepWarnings option preserves messages exactly'); 85 | 86 | t.end(); 87 | }); 88 | 89 | var mockComplexResult = { 90 | messages: [{ 91 | type: 'warning', 92 | plugin: 'foo', 93 | text: 'foo warning', 94 | node: { 95 | source: { 96 | start: { 97 | line: 3, 98 | column: 5 99 | } 100 | } 101 | } 102 | }, { 103 | type: 'warning', 104 | plugin: 'bar', 105 | text: 'bar warning', 106 | node: { 107 | source: { 108 | start: { 109 | line: 1, 110 | column: 99 111 | } 112 | } 113 | } 114 | }, { 115 | type: 'error', 116 | plugin: 'baz', 117 | text: 'baz error' 118 | }], 119 | root: { 120 | source: { 121 | input: { 122 | from: path.resolve(process.cwd(), 'style/rainbows/horses.css') 123 | } 124 | } 125 | } 126 | }; 127 | 128 | mockComplexResult.warnings = function() { 129 | return this.messages.filter(function(m) { 130 | return m.type === 'warning'; 131 | }); 132 | }; 133 | 134 | var complexOutput = '\n# postcss-log-warnings\n' + 135 | '\nstyle/rainbows/horses.css' + 136 | '\n3:5\tfoo warning [foo]' + 137 | '\n1:99\tbar warning [bar]\n'; 138 | 139 | var complexOutputNoBar = '\n# postcss-log-warnings\n' + 140 | '\nstyle/rainbows/horses.css' + 141 | '\n3:5\tfoo warning\n'; 142 | 143 | 144 | test('processResult with complex mock', function(t) { 145 | t.equal( 146 | chalk.stripColor(processResult(_.cloneDeep(mockComplexResult))), 147 | complexOutput, 148 | 'basic' 149 | ); 150 | 151 | t.equal( 152 | chalk.stripColor(processResult(_.cloneDeep(mockComplexResult), { plugins: ['foo'] })), 153 | complexOutputNoBar, 154 | 'excluding bar' 155 | ); 156 | 157 | t.end(); 158 | }); 159 | 160 | var mockWarningOnRootResult = { 161 | messages: [{ 162 | type: 'warning', 163 | text: 'blergh', 164 | node: { 165 | type: 'root', 166 | // warnings on root do not have start position 167 | source: {} 168 | }, 169 | plugin: 'reject-root' 170 | }], 171 | root: { 172 | source: { 173 | input: { 174 | from: '' 175 | } 176 | } 177 | } 178 | }; 179 | 180 | mockWarningOnRootResult.warnings = function() { 181 | return this.messages.filter(function(m) { 182 | return m.type === 'warning'; 183 | }); 184 | }; 185 | 186 | var warningOnRootResultOutput = '\n# postcss-log-warnings\n' + 187 | '\n' + 188 | '\nblergh [reject-root]\n'; 189 | 190 | test('processResult with mocked warning on root', function(t) { 191 | console.log(chalk.stripColor(processResult(_.cloneDeep(mockWarningOnRootResult)))); 192 | t.equal( 193 | chalk.stripColor(processResult(_.cloneDeep(mockWarningOnRootResult))), 194 | warningOnRootResultOutput 195 | ); 196 | t.end(); 197 | }); 198 | -------------------------------------------------------------------------------- /test/visual.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var postcss = require('postcss'); 4 | var logWarnings = require('..'); 5 | var fs = require('fs'); 6 | 7 | var rejectColors = postcss.plugin('reject-colors', function() { 8 | return function(css, result) { 9 | css.eachDecl(function(decl) { 10 | if (decl.prop === 'color') { 11 | result.warn('no colors allowed', { node: decl }); 12 | } 13 | }); 14 | }; 15 | }); 16 | 17 | var rejectBackgrounds = postcss.plugin('reject-backgrounds', function() { 18 | return function(css, result) { 19 | css.eachDecl(function(decl) { 20 | if (decl.prop === 'background') { 21 | result.warn('no backgrounds allowed', { node: decl }); 22 | } 23 | }); 24 | }; 25 | }); 26 | 27 | var rejectRoot = postcss.plugin('reject-root', function() { 28 | return function(css, result) { 29 | result.warn('blergh', { node: css }); 30 | }; 31 | }); 32 | 33 | fs.readFile('test/forVisual.css', { encoding: 'utf8' }, function(err, data) { 34 | if (err) throw err; 35 | postcss() 36 | .use(rejectColors()) 37 | .use(rejectBackgrounds()) 38 | .use(rejectRoot()) 39 | .use(logWarnings({ throwError: true })) 40 | .process(data, { from: 'test/forVisual.css' }) 41 | .then(function() { 42 | console.error('`throwError: true` failed!'); 43 | }) 44 | .catch(function(ourErr) { 45 | console.log(ourErr); 46 | console.log('There\'s your visual confirmation that it works.'); 47 | }) 48 | .catch(function(error) { 49 | console.log(error.stack); 50 | }); 51 | }); 52 | --------------------------------------------------------------------------------