diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..d066b85 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,16 @@ +root = true + +[*] + +# Change these settings to your own preference +indent_style = space +indent_size = 2 + +# We recommend you to keep these unchanged +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +insert_final_newline = false diff --git a/.gitignore b/.gitignore index 5171c54..cf5db45 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ node_modules -npm-debug.log \ No newline at end of file +npm-debug.log +.nyc_output +coverage diff --git a/.travis.yml b/.travis.yml index 998f9b6..4e23218 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,7 @@ +sudo: false language: node_js node_js: - - '4' \ No newline at end of file + - 4 + - "stable" + +after_success: "cat ./coverage/lcov.info | ./node_modules/.bin/coveralls" diff --git a/README.md b/README.md index 3b9d9d5..4eb8dab 100644 --- a/README.md +++ b/README.md @@ -18,13 +18,13 @@ $ npm install stylelint-webpack-plugin In your webpack configuration ```js -var styleLintPlugin = require('stylelint-webpack-plugin'); +var StyleLintPlugin = require('stylelint-webpack-plugin'); module.exports = { // ... plugins: [ - new styleLintPlugin(), + new StyleLintPlugin(), ], // ... } @@ -47,7 +47,7 @@ See [stylelint options](http://stylelint.io/user-guide/node-api/#options), for t // Default settings module.exports = { plugins: [ - new styleLintPlugin({ + new StyleLintPlugin({ configFile: '.stylelintrc', context: 'inherits from webpack', files: '**/*.s?(a|c)ss', diff --git a/index.js b/index.js index 2491d08..06b6c19 100644 --- a/index.js +++ b/index.js @@ -1,74 +1,44 @@ +'use strict'; + // Dependencies -var loaderUtils = require('loader-utils'); -var assign = require('object-assign'); var path = require('path'); +var assign = require('object-assign'); var formatter = require('stylelint/dist/formatters/stringFormatter').default; -var chalk = require('chalk'); +var arrify = require('arrify'); // Modules -var linter = require('./lib/linter'); +var runCompilation = require('./lib/run-compilation'); function apply(options, compiler) { var context = options.context || compiler.context; - var errors = []; options = Object.assign({}, options, { // TODO: make it work with arrays - files: options.files.map(function(file) { + files: options.files.map(function (file) { return path.join(context, '/', file); }) }); - function runCompilation(compilation, done) { - linter(options).then(function(lint) { - if (lint.errored) { - errors = lint.results.filter(function(f) { - return f.errored; - }).map(function(f) { - return f.source; // send error instead - }); - - (options.quiet !== true) && console.log(chalk.yellow(options.formatter(lint.results))); - } - - if (options.failOnError && errors.length) { - done(new Error('Failed because of a stylelint error.\n')); - } else { - done(); - } - }).catch(function(e) { - if (options.failOnError && errors.length) { - done(new Error('Failed because of a stylelint error.\n')); - } else { - done(); - } - console.log(chalk.red(e)); - }); - - compilation.plugin && compilation.plugin('compilation', function(compilation) { - errors.forEach(function(err) { - compilation.errors.push(err); - }); - }); - } - - compiler.plugin('run', runCompilation); - compiler.plugin('watch-run', runCompilation); + compiler.plugin('run', runCompilation.bind(this, options)); + compiler.plugin('watch-run', runCompilation.bind(this, options)); } // makes it easier to pass and check options to the plugin thank you webpack doc // [https://webpack.github.io/docs/plugins.html#the-compiler-instance] -module.exports = function(options) { - options = options || {}; - // Default Glob is any directory level of scss and/or sass file, - // under webpack's context and specificity changed via globbing patterns - options.files = options.files || '**/*.s?(c|a)ss'; - !Array.isArray(options.files) && (options.files = [options.files]); - options.configFile = options.configFile || '.stylelintrc'; - options.formatter = options.formatter || formatter; - options.quiet = options.quiet || false; - +module.exports = function (options) { return { - apply: apply.bind(this, options) + apply: apply.bind(this, buildOptions(options)) }; -}; \ No newline at end of file +}; + +function buildOptions(options) { + return assign({ + configFile: '.stylelintrc', + formatter: formatter, + quiet: false + }, options, { + // Default Glob is any directory level of scss and/or sass file, + // under webpack's context and specificity changed via globbing patterns + files: arrify(options.files || '**/*.s?(c|a)ss') + }); +} diff --git a/lib/linter.js b/lib/linter.js index f6c0d51..11eb619 100644 --- a/lib/linter.js +++ b/lib/linter.js @@ -1,14 +1,10 @@ +'use strict'; + // Dependencies var stylelint = require('stylelint'); function lint(options) { - return new Promise(function(resolve, reject) { - stylelint.lint(options).then(function(data) { - resolve(data); - }).catch(function(e) { - reject(e); - }); - }); + return stylelint.lint(options); } -module.exports = lint; \ No newline at end of file +module.exports = lint; diff --git a/lib/run-compilation.js b/lib/run-compilation.js new file mode 100644 index 0000000..908f9be --- /dev/null +++ b/lib/run-compilation.js @@ -0,0 +1,53 @@ +'use strict'; + +var chalk = require('chalk'); +var linter = require('./linter'); + +/** + * Function bound to the plugin `apply` method to run the linter and report any + * errors (and their source file locations) + * @param options - from the apply method, the options passed in + * @param compilation - the compiler object + * @param done - callback + */ +module.exports = function runCompilation(options, compilation, done) { + var errors = []; + + linter(options) + .then(function (lint) { + if (lint.errored) { + errors = lint.results + .filter(function (f) { + return f.errored; + }) + .map(function (f) { + return f.source; // send error instead + }); + + if (!options.quiet) { + console.log(chalk.yellow(options.formatter(lint.results))); + } + } + + if (options.failOnError && errors.length) { + done(new Error('Failed because of a stylelint error.\n')); + } else { + done(); + } + }) + .catch(function (err) { + if (options.failOnError && errors.length) { + done(new Error('Failed because of a stylelint error.\n')); + } else { + done(); + } + console.log(chalk.red(err)); + }); + + // eslint-disable-next-line no-unused-expressions + compilation.plugin && compilation.plugin('compilation', function (compilation) { + errors.forEach(function (err) { + compilation.errors.push(err); + }); + }); +}; diff --git a/package.json b/package.json index 07828d8..81b71ed 100644 --- a/package.json +++ b/package.json @@ -22,26 +22,48 @@ "url": "https://github.com/vieron/stylelint-webpack-plugin/issues" }, "homepage": "https://github.com/vieron/stylelint-webpack-plugin#readme", + "peerDependencies": { + "stylelint": "^7.0.1" + }, "dependencies": { + "arrify": "^1.0.1", "chalk": "^1.1.1", "extract-text-webpack-plugin": "^1.0.1", "loader-utils": "~0.2.10", "object-assign": "~4.0.1", - "stylelint": "^6.0.1", + "stylelint": "^7.0.1", "webpack": "^1.12.10" }, "devDependencies": { "chai": "^3.4.1", "chai-as-promised": "^5.2.0", + "coveralls": "^2.11.12", "memory-fs": "^0.3.0", - "mocha": "^2.3.4" - }, - "peerDependencies": { - "stylelint": "^6.0.1" + "mocha": "^2.3.4", + "nyc": "^7.1.0", + "xo": "^0.16.0" }, "scripts": { - "test": "mocha --harmony --full-trace --check-leaks", + "pretest": "xo", + "test": "nyc mocha", "preversion": "npm run test", "version": "git add -A ." + }, + "nyc": { + "reporter": [ + "lcov", + "text-summary" + ] + }, + "xo": { + "space": true, + "envs": [ + "node", + "mocha" + ], + "globals": [ + "getPath", + "expect" + ] } } diff --git a/test/.stylelintrc b/test/.stylelintrc index ffed810..b6521d3 100644 --- a/test/.stylelintrc +++ b/test/.stylelintrc @@ -30,7 +30,7 @@ "declaration-colon-newline-after": "always-multi-line", "declaration-colon-space-after": "always-single-line", "declaration-colon-space-before": "never", - "font-family-name-quotes": "double-where-recommended", + "font-family-name-quotes": "always-unless-keyword", "function-calc-no-unspaced-operator": true, "function-comma-newline-after": "always-multi-line", "function-comma-space-after": "always-single-line", @@ -38,25 +38,24 @@ "function-linear-gradient-no-nonstandard-direction": true, "function-parentheses-newline-inside": "always-multi-line", "function-parentheses-space-inside": "never-single-line", - "function-url-quotes": "double", "function-whitespace-after": "always", "indentation": 2, + "length-zero-no-unit": true, "max-empty-lines": 1, "media-feature-colon-space-after": "always", "media-feature-colon-space-before": "never", "media-feature-no-missing-punctuation": true, + "media-feature-parentheses-space-inside": "never", "media-feature-range-operator-space-after": "always", "media-feature-range-operator-space-before": "always", "media-query-list-comma-newline-after": "always-multi-line", "media-query-list-comma-space-after": "always-single-line", "media-query-list-comma-space-before": "never", - "media-query-parentheses-space-inside": "never", "no-eol-whitespace": true, "no-invalid-double-slash-comments": true, - "no-missing-eof-newline": true, + "no-missing-end-of-source-newline": true, "number-leading-zero": "always", "number-no-trailing-zeros": true, - "number-zero-length-no-unit": true, "rule-non-nested-empty-line-before": [ "always-multi-line", { "ignore": ["after-comment"], } ], @@ -71,4 +70,4 @@ "value-list-comma-space-after": "always-single-line", "value-list-comma-space-before": "never", }, -} \ No newline at end of file +} diff --git a/test/fixtures/test1/index.js b/test/fixtures/test1/index.js new file mode 100644 index 0000000..daac3c2 --- /dev/null +++ b/test/fixtures/test1/index.js @@ -0,0 +1,3 @@ +require(getPath('./../../../node_modules/file-loader/index') + '!./test.scss'); + +console.log('test1'); diff --git a/test/testfiles/test1/test.scss b/test/fixtures/test1/test.scss similarity index 100% rename from test/testfiles/test1/test.scss rename to test/fixtures/test1/test.scss diff --git a/test/fixtures/test2/index.js b/test/fixtures/test2/index.js new file mode 100644 index 0000000..c1d394b --- /dev/null +++ b/test/fixtures/test2/index.js @@ -0,0 +1,3 @@ +require(getPath('./../../../node_modules/file-loader/index') + '!./test.scss'); + +console.log('test2'); diff --git a/test/testfiles/test2/test.scss b/test/fixtures/test2/test.scss similarity index 100% rename from test/testfiles/test2/test.scss rename to test/fixtures/test2/test.scss diff --git a/test/fixtures/test3/index.js b/test/fixtures/test3/index.js new file mode 100644 index 0000000..ab763be --- /dev/null +++ b/test/fixtures/test3/index.js @@ -0,0 +1,3 @@ +require(getPath('./../../../node_modules/file-loader/index') + '!./test.scss'); + +console.log('test3'); diff --git a/test/testfiles/test3/test.scss b/test/fixtures/test3/test.scss similarity index 100% rename from test/testfiles/test3/test.scss rename to test/fixtures/test3/test.scss diff --git a/test/fixtures/test4/index.js b/test/fixtures/test4/index.js new file mode 100644 index 0000000..82c89c3 --- /dev/null +++ b/test/fixtures/test4/index.js @@ -0,0 +1,3 @@ +require(getPath('./../../../node_modules/file-loader/index') + '!./test.scss'); + +console.log('test4'); diff --git a/test/testfiles/test4/test.scss b/test/fixtures/test4/test.scss similarity index 100% rename from test/testfiles/test4/test.scss rename to test/fixtures/test4/test.scss diff --git a/test/fixtures/test5/index.js b/test/fixtures/test5/index.js new file mode 100644 index 0000000..8a383a6 --- /dev/null +++ b/test/fixtures/test5/index.js @@ -0,0 +1,3 @@ +require(getPath('./../../../node_modules/file-loader/index') + '!./test.scss'); + +console.log('test5'); diff --git a/test/testfiles/test5/test.scss b/test/fixtures/test5/test.scss similarity index 100% rename from test/testfiles/test5/test.scss rename to test/fixtures/test5/test.scss diff --git a/test/fixtures/test6/index.js b/test/fixtures/test6/index.js new file mode 100644 index 0000000..aa35728 --- /dev/null +++ b/test/fixtures/test6/index.js @@ -0,0 +1,3 @@ +require(getPath('./../../../node_modules/file-loader/index') + '!./test.scss'); + +console.log('test6'); diff --git a/test/testfiles/test6/test.scss b/test/fixtures/test6/test.scss similarity index 100% rename from test/testfiles/test6/test.scss rename to test/fixtures/test6/test.scss diff --git a/test/testfiles/test7/_second.scss b/test/fixtures/test7/_second.scss similarity index 100% rename from test/testfiles/test7/_second.scss rename to test/fixtures/test7/_second.scss diff --git a/test/fixtures/test7/index.js b/test/fixtures/test7/index.js new file mode 100644 index 0000000..aa35728 --- /dev/null +++ b/test/fixtures/test7/index.js @@ -0,0 +1,3 @@ +require(getPath('./../../../node_modules/file-loader/index') + '!./test.scss'); + +console.log('test6'); diff --git a/test/testfiles/test7/test.scss b/test/fixtures/test7/test.scss similarity index 100% rename from test/testfiles/test7/test.scss rename to test/fixtures/test7/test.scss diff --git a/test/helpers/setup.js b/test/helpers/setup.js new file mode 100644 index 0000000..ec6e08b --- /dev/null +++ b/test/helpers/setup.js @@ -0,0 +1,10 @@ +'use strict'; + +var chai = require('chai'); + +global.expect = chai.expect; + +chai.use(require('chai-as-promised')); + +// get path from the './test' directory +global.getPath = require('path').join.bind(this, __dirname, '..'); diff --git a/test/index.js b/test/index.js index ee16b81..b3f0f55 100644 --- a/test/index.js +++ b/test/index.js @@ -1,28 +1,24 @@ -/* global __dirname, describe, it */ -var chai = require('chai'); -var expect = chai.expect; -var chaiAsPromised = require('chai-as-promised'); +/* eslint no-unused-expressions: 0 */ + +'use strict'; + var assign = require('object-assign'); var webpack = require('webpack'); var MemoryFileSystem = require('memory-fs'); -var ExtractTextPlugin = require('extract-text-webpack-plugin'); -var path = require('path'); - -chai.use(chaiAsPromised); // _dirname is the test directory -var styleLintPlugin = require(path.join(__dirname, '../index')); +var StyleLintPlugin = require(getPath('../index')); var outputFileSystem = new MemoryFileSystem(); -var configFilePath = path.join(__dirname, './.stylelintrc'); +var configFilePath = getPath('./.stylelintrc'); var baseConfig = { debug: false, output: { - path: path.join(__dirname, 'output') + path: getPath('output') }, plugins: [ - new styleLintPlugin({ + new StyleLintPlugin({ quiet: true, configFile: configFilePath }) @@ -38,30 +34,26 @@ function pack(testConfig, callback) { compiler.run(callback); } - -/** - * Test Suite - */ describe('sasslint-loader', function () { it('works with a simple file', function (done) { var config = { - context: './test/testfiles/test1', + context: './test/fixtures/test1', entry: './index' }; pack(assign({}, baseConfig, config), function (err, stats) { expect(err).to.not.exist; - expect(stats.compilation.errors.length).to.equal(0); - expect(stats.compilation.warnings.length).to.equal(0); + expect(stats.compilation.errors).to.have.length(0); + expect(stats.compilation.warnings).to.have.length(0); done(err); }); }); it('sends errors properly', function (done) { var config = { - context: './test/testfiles/test3', + context: './test/fixtures/test3', entry: './index', - plugins: [ new styleLintPlugin({ + plugins: [new StyleLintPlugin({ quiet: true, configFile: configFilePath })] @@ -69,26 +61,28 @@ describe('sasslint-loader', function () { pack(assign({}, baseConfig, config), function (err, stats) { expect(err).to.not.exist; - expect(stats.compilation.errors.length).to.equal(1); + expect(stats.compilation.errors).to.have.length(1); done(err); }); }); it('fails on errors', function () { var config = { - context: './test/testfiles/test3', + context: './test/fixtures/test3', entry: './index', - plugins: [ new styleLintPlugin({ - configFile: configFilePath, - quiet: true, - failOnError: true - })] + plugins: [ + new StyleLintPlugin({ + configFile: configFilePath, + quiet: true, + failOnError: true + }) + ] }; - return expect(new Promise(function(resolve, reject) { + return expect(new Promise(function (resolve, reject) { var compiler = webpack(assign({}, baseConfig, config)); compiler.outputFileSystem = outputFileSystem; - compiler.run(function(err) { + compiler.run(function (err) { reject(err); }); })).to.eventually.be.rejectedWith('Error: Failed because of a stylelint error.\n'); @@ -96,12 +90,14 @@ describe('sasslint-loader', function () { it('can specify a JSON config file via config', function (done) { var config = { - context: './test/testfiles/test5', + context: './test/fixtures/test5', entry: './index', - plugins: [ new styleLintPlugin({ - configFile: configFilePath, - quiet: true - })] + plugins: [ + new StyleLintPlugin({ + configFile: configFilePath, + quiet: true + }) + ] }; pack(assign({}, baseConfig, config), function (err, stats) { @@ -111,9 +107,9 @@ describe('sasslint-loader', function () { }); }); - it('should work with multiple files', function(done) { + it('should work with multiple files', function (done) { var config = { - context: './test/testfiles/test7', + context: './test/fixtures/test7', entry: './index' }; @@ -126,9 +122,9 @@ describe('sasslint-loader', function () { // it('should work with multiple context', function(done) { // var config = { - // context: './test/testfiles/test5', + // context: './test/fixtures/test5', // entry: './index', - // plugins: [ new styleLintPlugin({ + // plugins: [ new StyleLintPlugin({ // configFile: configFilePath, // context: ['./test/testFiles/test5', './test/testFiles/test7'] // })] @@ -141,4 +137,4 @@ describe('sasslint-loader', function () { // done(err); // }); // }); -}); \ No newline at end of file +}); diff --git a/test/mocha.opts b/test/mocha.opts new file mode 100644 index 0000000..f165676 --- /dev/null +++ b/test/mocha.opts @@ -0,0 +1,4 @@ +--require ./test/helpers/setup.js +--harmony +--full-trace +--check-leaks diff --git a/test/testfiles/test1/index.js b/test/testfiles/test1/index.js deleted file mode 100644 index 6ea93a5..0000000 --- a/test/testfiles/test1/index.js +++ /dev/null @@ -1,3 +0,0 @@ -require(path.join(__dirname, './../../../node_modules/file-loader/index') + '!./test.scss'); - -console.log('test1'); \ No newline at end of file diff --git a/test/testfiles/test2/index.js b/test/testfiles/test2/index.js deleted file mode 100644 index 905d6fc..0000000 --- a/test/testfiles/test2/index.js +++ /dev/null @@ -1,3 +0,0 @@ -require(path.join(__dirname, './../../../node_modules/file-loader/index') + '!./test.scss'); - -console.log('test2'); \ No newline at end of file diff --git a/test/testfiles/test3/index.js b/test/testfiles/test3/index.js deleted file mode 100644 index 945d34c..0000000 --- a/test/testfiles/test3/index.js +++ /dev/null @@ -1,3 +0,0 @@ -require(path.join(__dirname, './../../../node_modules/file-loader/index') + '!./test.scss'); - -console.log('test3'); \ No newline at end of file diff --git a/test/testfiles/test4/index.js b/test/testfiles/test4/index.js deleted file mode 100644 index f62d7fc..0000000 --- a/test/testfiles/test4/index.js +++ /dev/null @@ -1,3 +0,0 @@ -require(path.join(__dirname, './../../../node_modules/file-loader/index') + '!./test.scss'); - -console.log('test4'); \ No newline at end of file diff --git a/test/testfiles/test5/index.js b/test/testfiles/test5/index.js deleted file mode 100644 index d7feb83..0000000 --- a/test/testfiles/test5/index.js +++ /dev/null @@ -1,3 +0,0 @@ -require(path.join(__dirname, './../../../node_modules/file-loader/index') + '!./test.scss'); - -console.log('test5'); \ No newline at end of file diff --git a/test/testfiles/test6/index.js b/test/testfiles/test6/index.js deleted file mode 100644 index 71e2cdf..0000000 --- a/test/testfiles/test6/index.js +++ /dev/null @@ -1,3 +0,0 @@ -require(path.join(__dirname, './../../../node_modules/file-loader/index') + '!./test.scss'); - -console.log('test6'); \ No newline at end of file diff --git a/test/testfiles/test7/index.js b/test/testfiles/test7/index.js deleted file mode 100644 index 71e2cdf..0000000 --- a/test/testfiles/test7/index.js +++ /dev/null @@ -1,3 +0,0 @@ -require(path.join(__dirname, './../../../node_modules/file-loader/index') + '!./test.scss'); - -console.log('test6'); \ No newline at end of file