diff --git a/example/index.css b/example/basic/index.css similarity index 100% rename from example/index.css rename to example/basic/index.css diff --git a/example/index.js b/example/basic/index.js similarity index 100% rename from example/index.js rename to example/basic/index.js diff --git a/example/webpack.config.js b/example/basic/webpack.config.js similarity index 85% rename from example/webpack.config.js rename to example/basic/webpack.config.js index 87d2c2f..c75840f 100644 --- a/example/webpack.config.js +++ b/example/basic/webpack.config.js @@ -1,5 +1,5 @@ var ExtractTextPlugin = require('extract-text-webpack-plugin'); -var CSSSplitWebpackPlugin = require('../').default; +var CSSSplitWebpackPlugin = require('../../').default; module.exports = { entry: './index.js', @@ -15,6 +15,7 @@ module.exports = { loader: ExtractTextPlugin.extract('style-loader', 'css-loader'), }], }, + devtool: 'source-map', plugins: [ new ExtractTextPlugin("styles.css"), new CSSSplitWebpackPlugin({size: 3, imports: true}), diff --git a/example/less/index.html b/example/less/index.html new file mode 100644 index 0000000..6caa0d7 --- /dev/null +++ b/example/less/index.html @@ -0,0 +1,15 @@ + + + + + LESS Example + + + + +
foo
+
bar
+
baz
+
qux
+ + diff --git a/example/less/index.js b/example/less/index.js new file mode 100644 index 0000000..ff9c989 --- /dev/null +++ b/example/less/index.js @@ -0,0 +1 @@ +require('./index.less'); diff --git a/example/less/index.less b/example/less/index.less new file mode 100644 index 0000000..bb5df77 --- /dev/null +++ b/example/less/index.less @@ -0,0 +1,20 @@ +@green: green; +@red: red; +@yellow: yellow; +@blue: blue; + +.foo { + color: @green; +} + +.bar { + color: @red; +} + +.baz { + color: @yellow; +} + +.qux { + color: @blue; +} diff --git a/example/less/webpack.config.js b/example/less/webpack.config.js new file mode 100644 index 0000000..7d55b97 --- /dev/null +++ b/example/less/webpack.config.js @@ -0,0 +1,25 @@ +var ExtractTextPlugin = require('extract-text-webpack-plugin'); +var CSSSplitWebpackPlugin = require('../../').default; + +module.exports = { + entry: './index.js', + context: __dirname, + output: { + path: __dirname + '/dist', + publicPath: '/foo', + filename: 'bundle.js', + }, + module: { + loaders: [{ + test: /\.less$/, + loader: ExtractTextPlugin.extract( + 'css?-url&-autoprefixer&sourceMap!less?sourceMap' + ), + }], + }, + devtool: 'source-map', + plugins: [ + new ExtractTextPlugin("styles.css"), + new CSSSplitWebpackPlugin({size: 3}), + ], +}; diff --git a/package.json b/package.json index 3eae86e..c27ef49 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "adana-cli": "^0.1.1", "adana-dump": "^0.1.0", "adana-format-lcov": "^0.1.1", + "autoprefixer-loader": "^3.2.0", "babel-cli": "^6.7.7", "babel-core": "^6.7.7", "babel-preset-metalab": "^0.2.1", @@ -30,9 +31,12 @@ "eslint-plugin-import": "^1.6.0", "eslint-plugin-react": "^4.2.3", "extract-text-webpack-plugin": "^1.0.1", + "less": "^2.7.1", + "less-loader": "^2.2.3", "memory-fs": "^0.3.0", "mocha": "^2.4.5", "style-loader": "^0.13.1", + "url-loader": "^0.5.7", "webpack": "^1.13.0" }, "dependencies": { diff --git a/src/index.js b/src/index.js index 4249749..37a29b1 100644 --- a/src/index.js +++ b/src/index.js @@ -104,20 +104,10 @@ export default class CSSSplitWebpackPlugin { file: key, index: i, }); - return postcss([chunk({ - ...this.options, - result: (i) => { - return { - to: name(i), - from: key, - map: { - inline: false, - prev: input.map, - }, - }; + return postcss([chunk(this.options)]).process(input.source, { + map: { + prev: input.map, }, - })]).process(input.source, { - from: key, }).then((result) => { return Promise.resolve({ file: key, diff --git a/test/spec/index.spec.js b/test/spec/index.spec.js index 5115f1a..8d38a6d 100644 --- a/test/spec/index.spec.js +++ b/test/spec/index.spec.js @@ -5,9 +5,12 @@ import path from 'path'; import MemoryFileSystem from 'memory-fs'; import {expect} from 'chai'; -const config = (options) => { +const basic = path.join('.', 'basic', 'index.js'); +const less = path.join('.', 'less', 'index.js'); + +const config = (options, entry = basic, extra) => { return { - entry: './index.js', + entry: path.join(__dirname, '..', '..', 'example', entry), context: path.join(__dirname, '..', '..', 'example'), output: { path: path.join(__dirname, 'dist'), @@ -17,75 +20,114 @@ const config = (options) => { module: { loaders: [{ test: /\.css$/, - loader: ExtractTextPlugin.extract('style-loader', 'css-loader'), + loader: ExtractTextPlugin.extract( + 'style-loader', + 'css-loader?sourceMap' + ), + }, { + test: /\.less$/, + loader: ExtractTextPlugin.extract( + 'css?-url&-autoprefixer&sourceMap!less?sourceMap' + ), }], }, + devtool: 'source-map', plugins: [ new ExtractTextPlugin('styles.css'), new CSSSplitWebpackPlugin(options), ], + ...extra, }; }; -const webpack = (options) => { - const compiler = _webpack(config(options)); - compiler.fs = new MemoryFileSystem(); +const webpack = (options, inst, extra) => { + const configuration = config(options, inst, extra); + const compiler = _webpack(configuration); + compiler.outputFileSystem = new MemoryFileSystem(); return new Promise((resolve) => { - compiler.run((err, stats) => { + compiler.run((err, _stats) => { expect(err).to.be.null; - resolve(stats.toJson()); + const stats = _stats.toJson(); + const files = {}; + stats.assets.forEach((asset) => { + files[asset.name] = compiler.outputFileSystem.readFileSync( + path.join(configuration.output.path, asset.name) + ); + }); + resolve({stats, files}); }); }); }; describe('CSSSplitWebpackPlugin', () => { - it('should split files when needed', () => { - return webpack({size: 3, imports: true}).then((stats) => { + it('should split files when needed', () => + webpack({size: 3, imports: true}).then(({stats}) => { expect(stats).to.not.be.null; expect(stats.assetsByChunkName).to.have.property('main') .to.contain('styles-2.css'); - }); - }); - it('should ignore files that do not need splitting', () => { - return webpack({size: 10, imports: true}).then((stats) => { + }) + ); + it('should ignore files that do not need splitting', () => + webpack({size: 10, imports: true}).then(({stats}) => { expect(stats).to.not.be.null; expect(stats.assetsByChunkName).to.have.property('main') .to.not.contain('styles-2.css'); - }); - }); - it('should generate an import file when requested', () => { - return webpack({size: 3, imports: true}).then((stats) => { + }) + ); + it('should generate an import file when requested', () => + webpack({size: 3, imports: true}).then(({stats}) => { expect(stats).to.not.be.null; expect(stats.assetsByChunkName).to.have.property('main') .to.contain('styles.css'); - }); - }); - it('should remove the original asset when splitting', () => { - return webpack({size: 3, imports: false}).then((stats) => { + }) + ); + it('should remove the original asset when splitting', () => + webpack({size: 3, imports: false}).then(({stats}) => { expect(stats).to.not.be.null; expect(stats.assetsByChunkName).to.have.property('main') .to.not.contain('styles.css'); - }); - }); - it('should allow customization of import name', () => { - return webpack({size: 3, imports: 'potato.css'}).then((stats) => { + }) + ); + it('should allow customization of import name', () => + webpack({size: 3, imports: 'potato.css'}).then(({stats}) => { expect(stats).to.not.be.null; expect(stats.assetsByChunkName).to.have.property('main') .to.contain('potato.css'); - }); - }); - it('should allow preservation of the original unsplit file', () => { - return webpack({size: 3, imports: false, preserve: true}).then((stats) => { + }) + ); + it('should allow preservation of the original unsplit file', () => + webpack({size: 3, imports: false, preserve: true}).then(({stats}) => { expect(stats).to.not.be.null; expect(stats.assetsByChunkName).to.have.property('main') .to.contain('styles.css'); - }); - }); + }) + ); it('should give sensible names by default', () => { - return webpack({size: 3, imports: true, preserve: true}).then((stats) => { + return webpack({size: 3, imports: true, preserve: true}).then(({stats}) => { expect(stats).to.not.be.null; expect(stats.assetsByChunkName).to.have.property('main') .to.contain('styles-split.css'); }); }); + it('should handle source maps properly', () => + webpack({size: 3}, less).then(({files}) => { + expect(files).to.have.property('styles-1.css.map'); + const map = JSON.parse(files['styles-1.css.map'].toString('utf8')); + expect(map).to.have.property('version', 3); + expect(map).to.have.property('sources') + .to.have.property(0) + .to.match(/index.less$/); + }) + ); + it('should handle cases when there are no source maps', () => + webpack({size: 3}, less, {devtool: null}).then(({files}) => { + expect(files).to.not.have.property('styles-1.css.map'); + }) + ); + + it('should fail with bad imports', () => { + expect(() => + new CSSSplitWebpackPlugin({imports: () => {}}) + ).to.throw(TypeError); + }); });