├── .gitignore ├── .gitattributes ├── test ├── .jshintrc ├── index.js └── report.js ├── .travis.yml ├── .jshintrc ├── .editorconfig ├── package.json ├── license ├── readme.md └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /test/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../.jshintrc", 3 | "mocha": true 4 | } 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | node_js: 4 | - 'iojs' 5 | - '0.12' 6 | - '0.10' 7 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, 3 | "esnext": true, 4 | "bitwise": true, 5 | "camelcase": true, 6 | "curly": true, 7 | "immed": true, 8 | "newcap": true, 9 | "noarg": true, 10 | "undef": true, 11 | "unused": "vars", 12 | "strict": true 13 | } 14 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = tab 5 | end_of_line = lf 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [package.json] 11 | indent_style = space 12 | indent_size = 2 13 | 14 | [*.md] 15 | trim_trailing_whitespace = false 16 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gulp-recess", 3 | "version": "1.2.0", 4 | "description": "Lint CSS and LESS with RECESS", 5 | "license": "MIT", 6 | "repository": "sindresorhus/gulp-recess", 7 | "author": { 8 | "name": "Sindre Sorhus", 9 | "email": "sindresorhus@gmail.com", 10 | "url": "http://sindresorhus.com" 11 | }, 12 | "engines": { 13 | "node": ">=0.10.0" 14 | }, 15 | "scripts": { 16 | "test": "mocha && jshint index.js test" 17 | }, 18 | "files": [ 19 | "index.js" 20 | ], 21 | "keywords": [ 22 | "gulpplugin", 23 | "css", 24 | "less", 25 | "stylesheet", 26 | "recess", 27 | "lint", 28 | "validate" 29 | ], 30 | "dependencies": { 31 | "chalk": "^1.0.0", 32 | "gulp-util": "^3.0.0", 33 | "rcloader": "^0.1.2", 34 | "recess": "^1.1.9", 35 | "through2": "^2.0.0" 36 | }, 37 | "devDependencies": { 38 | "jshint": "*", 39 | "mocha": "*" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Sindre Sorhus (sindresorhus.com) 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 13 | all 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 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var assert = require('assert'); 3 | var gutil = require('gulp-util'); 4 | var recess = require('../'); 5 | 6 | describe('gulp-recess', function () { 7 | describe('index', function () { 8 | it('should lint valid CSS', function (done) { 9 | var stream = recess(); 10 | var called = 0; 11 | 12 | stream.on('data', function (data) { 13 | assert(data.hasOwnProperty('recess')); // exists 14 | assert(data.recess.success === true); // it passed 15 | // FRAGILE: changes in recess could change these: 16 | assert(data.recess.status === 'Perfect!'); 17 | assert(data.recess.failureCount === 0); 18 | assert(data.recess.results.length === 0); 19 | called++; 20 | }); 21 | 22 | stream.on('end', function () { 23 | assert(called === 1); 24 | done(); 25 | }); 26 | 27 | stream.write(new gutil.File({ 28 | path: 'fixture.css', 29 | contents: new Buffer('.test-less{font-size:14px;background-color:green;}') 30 | })); 31 | 32 | stream.end(); 33 | }); 34 | 35 | it('should lint invalid CSS', function (done) { 36 | var stream = recess(); 37 | var called = 0; 38 | 39 | stream.on('data', function (data) { 40 | assert(data.hasOwnProperty('recess')); // exists 41 | assert(data.recess.success === false); // it failed 42 | // FRAGILE: changes in recess could change these: 43 | assert(data.recess.status === 'Busted'); 44 | assert(data.recess.failureCount > 0); 45 | assert(data.recess.results.length > 0); 46 | called++; 47 | }); 48 | 49 | stream.on('end', function () { 50 | assert(called === 1); 51 | done(); 52 | }); 53 | 54 | stream.write(new gutil.File({ 55 | path: 'fixture-invalid.css', 56 | contents: new Buffer('#test{background-color:green;font-size:0px;}') 57 | })); 58 | 59 | stream.end(); 60 | }); 61 | 62 | it('should handle LESS parse errors', function (done) { 63 | var stream = recess(); 64 | var called = 0; 65 | 66 | stream.on('data', function (data) { 67 | assert(data.hasOwnProperty('recess') === false); // doesn't exist 68 | called++; 69 | }); 70 | 71 | stream.on('error', function (err) { 72 | called++; 73 | assert(called === 1); 74 | assert.strictEqual(err.message, '.woops is undefined'); 75 | done(); 76 | }); 77 | 78 | stream.write(new gutil.File({ 79 | path: 'fixture.less', 80 | contents: new Buffer('div {\n.woops\n}') 81 | })); 82 | 83 | stream.end(); 84 | }); 85 | }); 86 | }); 87 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Deprecated since RECESS is [no longer maintained](https://github.com/twitter/recess) 2 | 3 | --- 4 | 5 | # gulp-recess [![Build Status](https://travis-ci.org/sindresorhus/gulp-recess.svg?branch=master)](https://travis-ci.org/sindresorhus/gulp-recess) 6 | 7 | > Lint CSS and LESS with [RECESS](https://github.com/twitter/recess) 8 | 9 | *Issues with the output should be reported on the RECESS [issue tracker](https://github.com/twitter/recess/issues).* 10 | 11 | 12 | ## Install 13 | 14 | ``` 15 | $ npm install --save-dev gulp-recess 16 | ``` 17 | 18 | 19 | ## Usage 20 | 21 | ```js 22 | var gulp = require('gulp'); 23 | var recess = require('gulp-recess'); 24 | 25 | gulp.task('default', function () { 26 | return gulp.src('src/app.css') 27 | .pipe(recess()) 28 | .pipe(recess.reporter()) 29 | .pipe(gulp.dest('dist')); 30 | }); 31 | ``` 32 | 33 | 34 | ## API 35 | 36 | Options can be specified programmatically as part of the task configuration using the options below. 37 | Additionally or alternatively, you can use a `.recessrc` file to specify the options. 38 | 39 | The `compress` and `compile` options from RECESS are intentionally missing. Separate tasks 40 | like [gulp-csso](https://github.com/ben-eb/gulp-csso) and [gulp-less](https://github.com/plus3network/gulp-less) 41 | will do a much better job. 42 | 43 | ### recess(options) 44 | 45 | Run recess on each file 46 | 47 | ```js 48 | // default options 49 | includePath: [] // Additional paths to look for `@import`'ed LESS files. 50 | strictPropertyOrder: true // Complains if not strict property order 51 | noIDs: true // Doesn't complain about using IDs in your stylesheets 52 | noJSPrefix: true // Doesn't complain about styling .js- prefixed classnames 53 | noOverqualifying: true // Doesn't complain about overqualified selectors (ie: div#foo.bar) 54 | noUnderscores: true // Doesn't complain about using underscores in your class names 55 | noUniversalSelectors: true // Doesn't complain about using the universal * selector 56 | zeroUnits: true // Doesn't complain if you add units to values of 0 57 | ``` 58 | 59 | Writes `.recess` object to each vinyl object: 60 | 61 | ```js 62 | { 63 | success: true, // Did it succeed? 64 | status: 'Perfect!', // Recess status 65 | failureCount: 0, // Number of errors 66 | results: [], // Recess failure details 67 | errors: [], // Recess errors 68 | options: {} // The options passed to Recess 69 | } 70 | ``` 71 | 72 | ### recess.reporter(options) 73 | 74 | Write the report on each failing file. (Passing files write no output.) 75 | 76 | ```js 77 | // default options 78 | fail: true // If true, it writes error event on failure 79 | minimal: false // If true, it only lists failure filenames omitting details 80 | ``` 81 | 82 | 83 | ## License 84 | 85 | MIT © [Sindre Sorhus](http://sindresorhus.com) 86 | -------------------------------------------------------------------------------- /test/report.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var assert = require('assert'); 3 | var gutil = require('gulp-util'); 4 | var recess = require('../'); 5 | 6 | var out = process.stdout.write.bind(process.stdout); 7 | 8 | describe('gulp-recess', function () { 9 | describe('report', function () { 10 | var stdoutData = []; 11 | 12 | beforeEach(function () { 13 | stdoutData = []; 14 | process.stdout.write = function () { 15 | stdoutData.push(arguments); 16 | }; 17 | }); 18 | 19 | afterEach(function () { 20 | process.stdout.write = out; // put it back even if test fails 21 | stdoutData = null; 22 | }); 23 | 24 | it('should lint valid CSS', function (done) { 25 | var stream = recess(); 26 | var reporter = recess.reporter(); 27 | 28 | reporter.on('finish', function () { 29 | assert(stdoutData.length === 0); 30 | done(); 31 | }); 32 | 33 | stream.pipe(reporter); 34 | 35 | stream.write(new gutil.File({ 36 | path: 'fixture.css', 37 | contents: new Buffer('.test-less{font-size:14px;background-color:green;}') 38 | })); 39 | 40 | stream.end(); 41 | }); 42 | 43 | it('should lint invalid CSS', function (done) { 44 | var stream = recess(); 45 | var reporter = recess.reporter({fail:false}); 46 | var actualData; 47 | 48 | stream.on('data', function (data) { 49 | actualData = data; 50 | }); 51 | 52 | reporter.on('finish', function () { 53 | process.stdout.write = out; 54 | assert(stdoutData.length > 0); 55 | done(); 56 | }); 57 | 58 | stream.pipe(reporter); 59 | 60 | stream.write(new gutil.File({ 61 | path: 'fixture-invalid.css', 62 | contents: new Buffer('#test{background-color:green;font-size:0px;}') 63 | })); 64 | 65 | stream.end(); 66 | }); 67 | 68 | it('should lint invalid CSS and output minimal', function (done) { 69 | var stream = recess(); 70 | var reporter = recess.reporter({fail:false, minimal:true}); 71 | var actualData; 72 | 73 | stream.on('data', function (data) { 74 | actualData = data; 75 | }); 76 | 77 | reporter.on('finish', function () { 78 | process.stdout.write = out; 79 | assert(stdoutData.length === 1); 80 | done(); 81 | }); 82 | 83 | stream.pipe(reporter); 84 | 85 | stream.write(new gutil.File({ 86 | path: 'fixture-invalid.css', 87 | contents: new Buffer('#test{background-color:green;font-size:0px;}') 88 | })); 89 | 90 | stream.end(); 91 | }); 92 | 93 | it('should error on invalid CSS', function (done) { 94 | var stream = recess(); 95 | var reporter = recess.reporter(); 96 | 97 | reporter.on('error', function (err) { 98 | process.stdout.write = out; 99 | assert(err.message.indexOf('fixture-invalid.css') > -1); 100 | done(); 101 | }); 102 | 103 | stream.pipe(reporter); 104 | 105 | stream.write(new gutil.File({ 106 | path: 'fixture-invalid.css', 107 | contents: new Buffer('#test{background-color:green;font-size:0px;}') 108 | })); 109 | 110 | stream.end(); 111 | }); 112 | }); 113 | }); 114 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var gutil = require('gulp-util'); 3 | var through = require('through2'); 4 | var chalk = require('chalk'); 5 | var recess = require('recess'); 6 | var RcLoader = require('rcloader'); 7 | 8 | // prevent RECESS from reading files 9 | recess.Constructor.prototype.read = function () { 10 | setImmediate(function () { 11 | this.data = this.options.contents; 12 | this.parse(); 13 | }.bind(this)); 14 | }; 15 | 16 | module.exports = function (options) { 17 | options = options || {}; 18 | 19 | var rcLoader = new RcLoader('.recessrc', options); 20 | 21 | return through.obj(function (file, enc, cb) { 22 | if (file.isNull()) { 23 | cb(null, file); 24 | return; 25 | } 26 | 27 | if (file.isStream()) { 28 | cb(new gutil.PluginError('gulp-recess', 'Streaming not supported')); 29 | return; 30 | } 31 | 32 | options = rcLoader.for(file.path); 33 | options.contents = file.contents.toString(); 34 | 35 | recess(file.path, options, function (err, results) { 36 | if (err) { 37 | err.forEach(function (el) { 38 | var realError = new Error(el.message); 39 | 40 | // el is an instance of LessError, which does not inherit 41 | // from Error. PluginError expects an instance of Error. 42 | // create a real Error and map LessError properties to it. 43 | realError.columnNumber = el.column; 44 | realError.fileName = el.filename; 45 | realError.lineNumber = el.line; 46 | realError.stack = el.stack; 47 | realError.type = el.name; 48 | 49 | var recessError = new gutil.PluginError('gulp-recess', realError, { 50 | showStack: false 51 | }); 52 | 53 | recessError.recess = { 54 | message: el.message, 55 | filename: el.filename, 56 | line: el.line, 57 | col: el.column 58 | }; 59 | 60 | this.emit('error', recessError); 61 | }, this); 62 | this.push(file); 63 | return; 64 | } 65 | 66 | var data = results[0]; // we only linted one file 67 | var failureCount = 0; 68 | 69 | if (data.output.length > 2) { 70 | var match = /([0-9]+) fail/i.exec(chalk.stripColor(data.output[2])); 71 | 72 | if (match) { 73 | failureCount = parseInt(match[1]); 74 | } 75 | } 76 | 77 | file.recess = { 78 | success: (data.output[1].indexOf('Busted') === -1), 79 | status: chalk.stripColor(data.output[1]).replace(/status: /i,'').trim(), 80 | failureCount: failureCount, 81 | results: data.output.slice(3), 82 | errors: data.errors, 83 | opt: options 84 | }; 85 | 86 | cb(null, file); 87 | }.bind(this)); 88 | }); 89 | }; 90 | 91 | module.exports.reporter = function (options) { 92 | options = options || {}; 93 | 94 | return through.obj(function (file, enc, cb) { 95 | if (file.isNull()) { 96 | cb(null, file); 97 | return; 98 | } 99 | 100 | if (file.isStream()) { 101 | cb(new gutil.PluginError('gulp-recess', 'Streaming not supported')); 102 | return; 103 | } 104 | 105 | var recessDataForThisFile = file.recess; 106 | 107 | if (recessDataForThisFile && !recessDataForThisFile.success) { 108 | console.log(' [' + chalk.green('gulp-recess') + '] ' + file.relative + ': ' + chalk.red(recessDataForThisFile.status) + ' ' + recessDataForThisFile.failureCount+' failures'); 109 | 110 | if (!options.minimal) { 111 | console.log(file.recess.results.join('\n')); 112 | } 113 | 114 | if (!options.hasOwnProperty('fail') || options.fail) { 115 | cb(new gutil.PluginError('gulp-recess', file.relative + ': ' + file.recess.status + ' ' + recessDataForThisFile.failureCount + ' failures', { 116 | fileName: file.path, 117 | showStack: false 118 | })); 119 | return; 120 | } 121 | } 122 | 123 | cb(null, file); 124 | }); 125 | }; 126 | --------------------------------------------------------------------------------