├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── index.js ├── package.json └── tests ├── .sass-lint.yml ├── Gulpfile.js └── sass ├── bar └── baz.sass └── foo.scss /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # node-waf configuration 20 | .lock-wscript 21 | 22 | # Compiled binary addons (http://nodejs.org/api/addons.html) 23 | build/Release 24 | 25 | # Dependency directory 26 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 27 | node_modules 28 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | 3 | language: node_js 4 | 5 | node_js: 6 | - current 7 | - 12 8 | - 10 9 | - 8 10 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # gulp-sass-lint Changelog 2 | 3 | ## v1.4.0 4 | - Drop dependency on deprecated `gulp-util` 5 | 6 | ## v1.3.4 7 | - Update sass-lint to 1.12.0 8 | 9 | ## v1.3.3 10 | 11 | - Update sass-lint to 1.11.0 12 | 13 | ## v1.3.1 14 | 15 | **Novemeber 7th, 2016** 16 | 17 | **Updates** 18 | 19 | * Patch release to sass-lint 1.10.1 20 | 21 | ## v1.3.0 22 | 23 | **November 7th, 2016** 24 | 25 | **Fixes** 26 | 27 | * Fixed an issue with a user config file being ignored 28 | * Fixed an issue with a format options being ignored 29 | 30 | **Updates** 31 | 32 | * Updated the way you can output results files from gulp-sass-lint, check the [README.md](README.md) for more information 33 | * Updated to sass-lint 1.10.0 which adds the ability to disable linters via source comments 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Sam Richard 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 | # Gulp Sass Lint [![npm version](https://badge.fury.io/js/gulp-sass-lint.svg)](http://badge.fury.io/js/gulp-sass-lint) 2 | 3 | [Gulp](http://gulpjs.com/) plugin for [Sass Lint](https://github.com/sasstools/sass-lint). 4 | 5 | ## Install 6 | 7 | ``` 8 | npm install gulp-sass-lint --save-dev 9 | ``` 10 | 11 | ## Usage 12 | 13 | `gulpfile.js` 14 | 15 | ```javascript 16 | 'use strict'; 17 | 18 | var gulp = require('gulp'), 19 | sassLint = require('gulp-sass-lint'); 20 | 21 | gulp.task('default', function () { 22 | return gulp.src('sass/**/*.s+(a|c)ss') 23 | .pipe(sassLint()) 24 | .pipe(sassLint.format()) 25 | .pipe(sassLint.failOnError()) 26 | }); 27 | ``` 28 | 29 | ## API 30 | 31 | ### sassLint(options) 32 | 33 | You can pass an object of options to configure gulp-sass-lint to your specific projects needs the options are listed below. 34 | 35 | #### options.options 36 | 37 | You can find out more about the specific SassLint options from the [SassLint Documentation](https://github.com/sasstools/sass-lint/tree/develop/docs/options) 38 | 39 | ```javascript 40 | { 41 | options: { 42 | formatter: 'stylish', 43 | 'merge-default-rules': false 44 | } 45 | } 46 | ``` 47 | 48 | By default SassLint includes it's own configuration file, if you provide one it attempts to merge everything except for the files section below. If you pass options directly into the plugin they take precedence. The order can be visualised below with the SassLint config being the base. 49 | 50 | `options > config file > SassLint default included config` 51 | 52 | You can disable this behaviour by setting `merge-default-rules` to false within the `options.options` object that you pass to `gulp-sass-lint` or you can include it in your config file options that you can pass into `gulp-sass-lint` with `options.configFile`. 53 | 54 | More info and examples can be found within the SassLint [docs](https://github.com/sasstools/sass-lint/blob/master/docs/options/merge-default-rules.md) 55 | 56 | #### options.files 57 | 58 | 59 | Type: `Object` 60 | 61 | Within the files object you can specify a glob pattern as a string or an array of glob pattern for both the `include` and `ignore` options. Please note that your include option will currently be ignored as you should be passing the glob patterns / file paths to be linted directly to gulp `gulp.src('sass/**/*.s+(a|c)ss')`. The ignore option however will still be respected if you'd rather specify them in your config rather than in the `gulp.src` method. 62 | 63 | ```javascript 64 | { 65 | files: { 66 | include: '**/*.scss', // This will be ignored by gulp-sass-lint 67 | ignore: 'test/**/*.scss' // This will still be respected and read 68 | } 69 | } 70 | ``` 71 | 72 | #### options.rules 73 | 74 | Type: `Object` 75 | 76 | You can define which rules you would like to use here and set a severity too. For more information see how to [configure](https://github.com/sasstools/sass-lint/tree/master#rules) and also the SassLint [rules](https://github.com/sasstools/sass-lint/tree/master/docs/rules) 77 | 78 | ```javascript 79 | { 80 | rules: { 81 | 'no-ids': 0, // Severity 0 (disabled) 82 | 'no-mergeable-selectors': 1, // Severity 1 (warning) 83 | 'hex-length': [ 84 | 2, // Severity 2 (error) 85 | { 86 | 'style': 'long' 87 | } 88 | ] 89 | } 90 | } 91 | ``` 92 | 93 | #### options.configFile 94 | 95 | You can pass the path to a custom config file via the `configFile` option. The path will be relative to where you're running gulp from. 96 | 97 | ```javascript 98 | { 99 | configFile: 'app/config/.my-config.yml' 100 | } 101 | ``` 102 | 103 | ### Example 104 | 105 | The following highlights all of the above options in use 106 | 107 | ```javascript 108 | 109 | 'use strict'; 110 | 111 | var gulp = require('gulp'), 112 | sassLint = require('gulp-sass-lint'); 113 | 114 | gulp.task('default', function () { 115 | return gulp.src('sass/**/*.s+(a|c)ss') 116 | .pipe(sassLint({ 117 | options: { 118 | formatter: 'stylish', 119 | 'merge-default-rules': false 120 | }, 121 | files: {ignore: '**/*.scss'}, 122 | rules: { 123 | 'no-ids': 1, 124 | 'no-mergeable-selectors': 0 125 | }, 126 | configFile: 'config/other/.sass-lint.yml' 127 | })) 128 | .pipe(sassLint.format()) 129 | .pipe(sassLint.failOnError()) 130 | }); 131 | 132 | ``` 133 | --- 134 | 135 | ### sassLint.format(writable) 136 | 137 | Formats the results dependent on your config file or the options you provided to the sassLint task above. The default format is `stylish` but you can choose any of the others that SassLint provides, see the [docs](https://github.com/sasstools/sass-lint/blob/master/docs/options/formatter.md). 138 | 139 | You can also choose to output to a file from within the options you provide or your config file. See the [output-file docs](https://github.com/sasstools/sass-lint/blob/master/docs/options/output-file.md) 140 | 141 | ```javascript 142 | gulp.task('lint_sass_jenkins', function () { 143 | var file = fs.createWriteStream('reports/lint_sass.xml'); 144 | var stream = gulp.src('public/sass/**/*.scss') 145 | .pipe(sassLint({ 146 | options: { 147 | configFile: '.sass-lint.yml', 148 | formatter: 'checkstyle' 149 | } 150 | })) 151 | .pipe(sassLint.format(file)); 152 | stream.on('finish', function() { 153 | file.end(); 154 | }); 155 | return stream; 156 | }); 157 | ``` 158 | 159 | --- 160 | 161 | ### sassLint.failOnError() 162 | 163 | Fails the task and emits a gulp error when all files have been linted if an error has been detected (rules set to severity 2). 164 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | ////////////////////////////// 2 | // Sass Lint 3 | // - A Gulp Plugin 4 | // 5 | // Lints Sass files 6 | ////////////////////////////// 7 | 8 | 'use strict'; 9 | 10 | ////////////////////////////// 11 | // Variables 12 | ////////////////////////////// 13 | 14 | var through = require('through2'), 15 | lint = require('sass-lint'), 16 | path = require('path'), 17 | PluginError = require('plugin-error'), 18 | PLUGIN_NAME = 'sass-lint'; 19 | 20 | ////////////////////////////// 21 | // Export 22 | ////////////////////////////// 23 | 24 | var sassLint = function (options) { 25 | var userOptions = options || {}; 26 | var configFile = userOptions.configFile; 27 | 28 | var compile = through.obj(function (file, encoding, cb) { 29 | 30 | if (file.isNull()) { 31 | return cb(); 32 | } 33 | if (file.isStream()) { 34 | this.emit('error', new PluginError(PLUGIN_NAME, 'Streams are not supported!')); 35 | return cb(); 36 | } 37 | 38 | // load our config from sassLint and the user provided options if available 39 | file.sassConfig = lint.getConfig(userOptions, configFile); 40 | // save the config file within the file object for access when this file is piped around 41 | file.userOptions = userOptions; 42 | file.configFile = configFile; 43 | 44 | // lint the file and pass the user defined options and config path to sass lint to handle 45 | try { 46 | file.sassLint = [ 47 | lint.lintFileText({ 48 | 'text': file.contents, 49 | 'format': path.extname(file.path).replace('.', ''), 50 | 'filename': path.relative(process.cwd(), file.path) 51 | }, userOptions, configFile)]; 52 | } catch(e) { 53 | this.emit('error', new PluginError(PLUGIN_NAME, e.message)); 54 | } 55 | 56 | this.push(file); 57 | cb(); 58 | }); 59 | return compile; 60 | } 61 | 62 | sassLint.format = function (writable) { 63 | var compile = through.obj(function (file, encoding, cb) { 64 | if (file.isNull()) { 65 | return cb(); 66 | } 67 | if (file.isStream()) { 68 | this.emit('error', new PluginError(PLUGIN_NAME, 'Streams are not supported!')); 69 | return cb(); 70 | } 71 | 72 | if (writable) { 73 | var result = lint.format(file.sassLint, file.userOptions, file.configFile); 74 | writable.write(result); 75 | } 76 | else { 77 | lint.outputResults(file.sassLint, file.userOptions, file.configFile); 78 | } 79 | 80 | this.push(file); 81 | cb(); 82 | }); 83 | return compile; 84 | } 85 | 86 | sassLint.failOnError = function () { 87 | var filesWithErrors = []; 88 | var compile = through({objectMode: true}, function (file, encoding, cb) { 89 | if (file.isNull()) { 90 | return cb(); 91 | } 92 | 93 | if (file.isStream()) { 94 | this.emit('error', new PluginError(PLUGIN_NAME, 'Streams are not supported!')); 95 | return cb(); 96 | } 97 | 98 | if (file.sassLint[0].errorCount > 0) { 99 | filesWithErrors.push(file); 100 | } 101 | 102 | this.push(file); 103 | cb(); 104 | }, function (cb) { 105 | var errorMessage; 106 | 107 | if (filesWithErrors.length > 0) { 108 | errorMessage = filesWithErrors.map(function (file) { 109 | return file.sassLint[0].errorCount + ' errors detected in ' + file.relative 110 | }).join('\n'); 111 | 112 | this.emit('error', new PluginError(PLUGIN_NAME, errorMessage)); 113 | } 114 | 115 | cb(); 116 | }); 117 | 118 | return compile; 119 | } 120 | 121 | module.exports = sassLint; 122 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gulp-sass-lint", 3 | "version": "1.4.0", 4 | "description": "Gulp plugin for Sass Lint", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/sasstools/gulp-sass-lint.git" 12 | }, 13 | "keywords": [ 14 | "gulpplugin", 15 | "sass", 16 | "gulp", 17 | "lint" 18 | ], 19 | "author": "Sam Richard", 20 | "license": "MIT", 21 | "bugs": { 22 | "url": "https://github.com/sasstools/gulp-sass-lint/issues" 23 | }, 24 | "homepage": "https://github.com/sasstools/gulp-sass-lint#readme", 25 | "dependencies": { 26 | "plugin-error": "^0.1.2", 27 | "sass-lint": "^1.12.0", 28 | "through2": "^2.0.2" 29 | }, 30 | "devDependencies": { 31 | "gulp": "^4.0.2" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tests/.sass-lint.yml: -------------------------------------------------------------------------------- 1 | options: 2 | formatter: stylish 3 | files: 4 | include: '**/*.s+(a|c)ss' 5 | rules: 6 | # Extends 7 | extends-before-mixins: 1 8 | extends-before-declarations: 1 9 | placeholder-in-extend: 1 10 | 11 | # Mixins 12 | mixins-before-declarations: 1 13 | 14 | # Line Spacing 15 | one-declaration-per-line: 1 16 | empty-line-between-blocks: 1 17 | single-line-per-selector: 1 18 | 19 | # Disallows 20 | no-color-keywords: 1 21 | no-color-literals: 1 22 | no-css-comments: 1 23 | no-debug: 1 24 | no-duplicate-properties: 1 25 | no-empty-rulesets: 1 26 | no-extends: 0 27 | no-ids: 1 28 | no-important: 1 29 | no-invalid-hex: 1 30 | no-mergeable-selectors: 1 31 | no-misspelled-properties: 1 32 | no-qualifying-elements: 1 33 | no-trailing-zero: 1 34 | no-transition-all: 1 35 | no-url-protocols: 1 36 | no-vendor-prefixes: 1 37 | no-warn: 1 38 | 39 | # Nesting 40 | force-attribute-nesting: 1 41 | force-element-nesting: 1 42 | force-pseudo-nesting: 1 43 | 44 | # Name Formats 45 | function-name-format: 1 46 | mixin-name-format: 1 47 | placeholder-name-format: 1 48 | variable-name-format: 49 | - 1 50 | - 51 | allow-leading-underscore: false 52 | 53 | # Style Guide 54 | border-zero: 1 55 | brace-style: 1 56 | clean-import-paths: 1 57 | empty-args: 1 58 | hex-length: 1 59 | hex-notation: 1 60 | indentation: 1 61 | leading-zero: 1 62 | nesting-depth: 1 63 | property-sort-order: 1 64 | quotes: 1 65 | shorthand-values: 1 66 | url-quotes: 1 67 | variable-for-property: 1 68 | zero-unit: 1 69 | 70 | # Inner Spacing 71 | space-after-comma: 1 72 | space-before-colon: 1 73 | space-after-colon: 1 74 | space-before-brace: 1 75 | space-before-bang: 1 76 | space-after-bang: 1 77 | space-between-parens: 1 78 | 79 | # Final Items 80 | trailing-semicolon: 1 81 | final-newline: 1 -------------------------------------------------------------------------------- /tests/Gulpfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var gulp = require('gulp'), 4 | lint = require('../index'); 5 | 6 | gulp.task('default', function () { 7 | gulp.src('sass/**/*.s+(a|c)ss') 8 | .pipe(lint()) 9 | .pipe(lint.format()) 10 | .pipe(lint.failOnError()) 11 | }); -------------------------------------------------------------------------------- /tests/sass/bar/baz.sass: -------------------------------------------------------------------------------- 1 | .foo 2 | background :red 3 | color: blue -------------------------------------------------------------------------------- /tests/sass/foo.scss: -------------------------------------------------------------------------------- 1 | $foo-color: #123; 2 | 3 | .foo { 4 | background: linear-gradient(top, #cc2, #44d); 5 | color: #fff; 6 | } 7 | 8 | $bar-color: #112233; 9 | 10 | .bar { 11 | background: linear-gradient(top, #cccc22, #4444dd); 12 | color: #ffffff; 13 | } 14 | 15 | // values that can't be shortened are ignored by the style:short rule 16 | 17 | .baz { 18 | border-color: #123456; 19 | } 20 | 21 | // color literals, rgb and hsl values currently don't get returned 22 | // by the AST's color type 23 | 24 | $qux-color: red; 25 | $rgb-color: rgb(255, 255, 255); 26 | $rgba-color: rgba(0, 0, 0, .1); 27 | $hsl-color: hsl(40, 50%, 50%); 28 | $hsla-color: hsla(40, 50%, 50%, .3); 29 | --------------------------------------------------------------------------------