From a6d75beeb4edc59532c5c8bbcc6a853fc7e34a5f Mon Sep 17 00:00:00 2001 From: William Leung Date: Sun, 15 Aug 2021 12:53:45 +0800 Subject: [PATCH 1/2] breaking: change to support webpack 5 Also - fix eslint config to use standard js - npm ignore all unused filed --- .eslintignore | 3 +- .eslintrc | 6 +-- .gitignore | 1 + .npmignore | 6 +-- README.md | 21 ++++---- example/basic/webpack.config.js | 22 +++++---- example/less/webpack.config.js | 22 ++++++--- lib/index.js | 85 +++++++++++++-------------------- package.json | 32 +++++-------- test/spec/index.spec.js | 61 ++++++++++++----------- 10 files changed, 126 insertions(+), 133 deletions(-) diff --git a/.eslintignore b/.eslintignore index a67b8ff..de4d1f0 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,3 +1,2 @@ -dist/**/* +dist node_modules -example diff --git a/.eslintrc b/.eslintrc index 2d4a216..042234e 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,4 +1,4 @@ extends: - - metalab -rules: - metalab/filenames/match-exported: 0 + - standard +env: + mocha: true diff --git a/.gitignore b/.gitignore index 8ad03e1..d40f1ab 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.idea node_modules *.log dist diff --git a/.npmignore b/.npmignore index 97db4e6..5464530 100644 --- a/.npmignore +++ b/.npmignore @@ -1,4 +1,2 @@ -node_modules -examples -*.log -coverage +/* +!/lib diff --git a/README.md b/README.md index c5038eb..832763f 100644 --- a/README.md +++ b/README.md @@ -18,13 +18,14 @@ npm install --save fast-css-split-webpack-plugin ## Usage -Simply add an instance of `FastCSSSplitWebpackPlugin` to your list of plugins in your webpack configuration file _after_ `ExtractTextPlugin`. That's it! +Simply add an instance of `FastCSSSplitWebpackPlugin` to your list of plugins in your webpack configuration file _after_ `MiniCssExtractPlugin`. That's it! ```javascript -var ExtractTextPlugin = require('extract-text-webpack-plugin'); -var FastCSSSplitWebpackPlugin = require('../'); +var MiniCssExtractPlugin = require('mini-css-extract-plugin'); +var FastCSSSplitWebpackPlugin = require('fast-css-split-webpack-plugin'); module.exports = { + mode: 'development', entry: './index.js', context: __dirname, output: { @@ -33,13 +34,16 @@ module.exports = { filename: 'bundle.js', }, module: { - loaders: [{ + rules: [{ test: /\.css$/, - loader: ExtractTextPlugin.extract('style-loader', 'css-loader'), - }], + use: [MiniCssExtractPlugin.loader, { + loader: 'css-loader', + options: {sourceMap: true} + }] + }] }, plugins: [ - new ExtractTextPlugin('styles.css'), + new MiniCssExtractPlugin({filename: 'styles.css'}), new FastCSSSplitWebpackPlugin({size: 4000}), ], }; @@ -55,9 +59,8 @@ The following configuration options are available: **preserve**: `default: false`. Keep the original unsplit file as well. Sometimes this is desirable if you want to target a specific browser (IE) with the split files and then serve the unsplit ones to everyone else. -**defer**: `default: 'false'`. You can pass `true` here to cause this plugin to split the CSS on the `emit` phase. Sometimes this is needed if you have other plugins that operate on the CSS also in the emit phase. Unfortunately by doing this you potentially lose chunk linking and source maps. Use only when necessary. +**defer**: `default: 'false'`. You can pass `true` here to cause this plugin to split the CSS on the `afterProcessAssets` phase. Sometimes this is needed if you have other plugins that operate on the CSS also in the same phase. Unfortunately by doing this you potentially lose chunk linking and source maps. Use only when necessary. [webpack]: http://webpack.github.io/ -[herp]: https://github.com/ONE001/css-file-rules-webpack-separator [postcss]: https://github.com/postcss/postcss [postcss-chunk]: https://github.com/mattfysh/postcss-chunk diff --git a/example/basic/webpack.config.js b/example/basic/webpack.config.js index 6d1e6eb..be1ff7c 100644 --- a/example/basic/webpack.config.js +++ b/example/basic/webpack.config.js @@ -1,7 +1,11 @@ -var ExtractTextPlugin = require('extract-text-webpack-plugin') -var FastCSSSplitWebpackPlugin = require('../../') +const MiniCssExtractPlugin = require('mini-css-extract-plugin') +const FastCSSSplitWebpackPlugin = require('../../') + +/* eslint object-curly-spacing: [error, never] */ +/* eslint node/no-path-concat: off */ module.exports = { + mode: 'development', entry: './index.js', context: __dirname, output: { @@ -10,19 +14,17 @@ module.exports = { filename: 'bundle.js' }, module: { - loaders: [{ + rules: [{ test: /\.css$/, - loader: ExtractTextPlugin.extract.length !== 1 - ? ExtractTextPlugin.extract('style-loader', 'css-loader') - : ExtractTextPlugin.extract({ - fallbackLoader: 'style-loader', - loader: 'css-loader' - }) + use: [MiniCssExtractPlugin.loader, { + loader: 'css-loader', + options: {sourceMap: true} + }] }] }, devtool: 'source-map', plugins: [ - new ExtractTextPlugin('styles.css'), + new MiniCssExtractPlugin({filename: 'styles.css'}), new FastCSSSplitWebpackPlugin({size: 3, imports: true}) ] } diff --git a/example/less/webpack.config.js b/example/less/webpack.config.js index db04498..7f3405f 100644 --- a/example/less/webpack.config.js +++ b/example/less/webpack.config.js @@ -1,7 +1,11 @@ -var ExtractTextPlugin = require('extract-text-webpack-plugin') -var FastCSSSplitWebpackPlugin = require('../../') +const MiniCssExtractPlugin = require('mini-css-extract-plugin') +const FastCSSSplitWebpackPlugin = require('../../') + +/* eslint object-curly-spacing: [error, never] */ +/* eslint node/no-path-concat: off */ module.exports = { + mode: 'development', entry: './index.js', context: __dirname, output: { @@ -10,16 +14,20 @@ module.exports = { filename: 'bundle.js' }, module: { - loaders: [{ + rules: [{ test: /\.less$/, - loader: ExtractTextPlugin.extract( - 'css?-url&-autoprefixer&sourceMap!less?sourceMap' - ) + use: [MiniCssExtractPlugin.loader, { + loader: 'css-loader', + options: {sourceMap: true, url: false} + }, { + loader: 'less-loader', + options: {sourceMap: true} + }] }] }, devtool: 'source-map', plugins: [ - new ExtractTextPlugin('styles.css'), + new MiniCssExtractPlugin({filename: 'styles.css'}), new FastCSSSplitWebpackPlugin({size: 3}) ] } diff --git a/lib/index.js b/lib/index.js index bc6bb06..76d5387 100644 --- a/lib/index.js +++ b/lib/index.js @@ -2,7 +2,7 @@ const path = require('path') const split = require('css-split') -const { RawSource } = require('webpack-sources') +const { sources: { RawSource }, Compilation } = require('webpack') const { interpolateName } = require('loader-utils') /** @@ -68,27 +68,32 @@ const normalizeImports = (input, preserve) => { class FastCSSSplitWebpackPlugin { /** * Create new instance of FastCSSSplitWebpackPlugin. - * @param {Number} size Maximum number of rules for a single file. - * @param {Boolean|String} imports Truish to generate an additional import + * @param [options] + * @param {Number} [options.size] Maximum number of rules for a single file. + * @param {Boolean|String} [options.imports] Truish to generate an additional import * asset. When a boolean use the default name for the asset. - * @param {String} filename Control the generated split file name. - * @param {Boolean} defer Defer splitting until the `emit` phase. Normally - * only needed if something else in your pipeline is mangling things at - * the emit phase too. - * @param {Boolean} preserve True to keep the original unsplit file. + * @param {String} [options.filename] Control the generated split file name. + * @param {Number} [options.stage] only valid while [options.defer] is false + * see + * @param {Boolean} [options.defer] Defer splitting until the `afterProcessAssets` + * phase. Normally only needed if something else in your pipeline is mangling + * things at the same phase too. + * @param {Boolean} [options.preserve] True to keep the original unsplit file. */ constructor ({ size = 4000, imports = false, filename = '[name]-[part].[ext]', preserve, + stage = Compilation.PROCESS_ASSETS_STAGE_DEV_TOOLING, defer = false - }) { + } = {}) { this.options = { size, imports: normalizeImports(imports, preserve), filename: nameInterpolator(filename), preserve, + stage, defer } } @@ -97,7 +102,6 @@ class FastCSSSplitWebpackPlugin { * Generate the split chunks for a given CSS file. * @param {String} key Name of the file. * @param {Object} asset Valid webpack Source object. - * @returns {Promise} Promise generating array of new files. */ file (key, asset) { const input = { @@ -124,12 +128,12 @@ class FastCSSSplitWebpackPlugin { } } - chunksMapping (compilation, chunks, done) { + chunksMapping (compilation) { const assets = compilation.assets const publicPath = strip(compilation.options.output.publicPath || './') - chunks.map((chunk) => { - const input = chunk.files.filter(isCSS) + for (const chunk of compilation.chunks) { + const input = [...chunk.files].filter(isCSS) const entries = input.map((name) => this.file(name, assets[name])) entries.forEach((entry) => { @@ -145,7 +149,7 @@ class FastCSSSplitWebpackPlugin { // - ... entry.chunks.forEach((file) => { assets[file.fullname] = file - chunk.files.push(file.fullname) + chunk.files.add(file.fullname) }) // generate imports file content @@ -162,7 +166,7 @@ class FastCSSSplitWebpackPlugin { // if chunks.length == 1, the original chunk will be always preserved if (entry.chunks.length > 1 && !this.options.preserve) { - chunk.files.splice(chunk.files.indexOf(entry.file), 1) + chunk.files.delete(entry.file) delete assets[entry.file] } @@ -176,12 +180,10 @@ class FastCSSSplitWebpackPlugin { imports = `${entry.dirname}/${imports}` } assets[imports] = importsFile - chunk.files.push(imports) + chunk.files.add(imports) } }) - }) - - done() + } } /** @@ -195,44 +197,25 @@ class FastCSSSplitWebpackPlugin { * @returns {void} */ apply (compiler) { - // for webpack 4 - if (compiler.hooks) { - const plugin = { - name: 'FastCssSplitPlugin' - } - - if (this.options.defer) { - // Run on `emit` when user specifies the compiler phase + const name = 'FastCssSplitPlugin' + const { defer, stage } = this.options + + // Only run on `thisCompilation` to avoid injecting the plugin into + // sub-compilers as happens when using the `mini-css-extract-plugin`. + compiler.hooks.thisCompilation.tap(name, compilation => { + if (defer) { + // Run on `afterProcessAssets` when user specifies the compiler phase // Due to the incorrect css split + optimization behavior // Expected: css split should happen after optimization - compiler.hooks.emit.tapAsync(plugin, (compilation, done) => { - this.chunksMapping(compilation, compilation.chunks, done) + compilation.hooks.afterProcessAssets.tap(name, () => { + this.chunksMapping(compilation) }) } else { - // use compilation instead of this-compilation, just like other plugins do - compiler.hooks.compilation.tap(plugin, compilation => { - compilation.hooks.optimizeChunkAssets.tapAsync(plugin, (chunks, done) => { - this.chunksMapping(compilation, chunks, done) - }) + compilation.hooks.processAssets.tap({ name, stage }, () => { + this.chunksMapping(compilation) }) } - } else { - if (this.options.defer) { - // Run on `emit` when user specifies the compiler phase - // Due to the incorrect css split + optimization behavior - // Expected: css split should happen after optimization - compiler.plugin('emit', (compilation, done) => { - return this.chunksMapping(compilation, compilation.chunks, done) - }) - } else { - // use compilation instead of this-compilation, just like other plugins do - compiler.plugin('compilation', (compilation) => { - compilation.plugin('optimize-chunk-assets', (chunks, done) => { - return this.chunksMapping(compilation, chunks, done) - }) - }) - } - } + }) } } diff --git a/package.json b/package.json index 589ab11..2b342d3 100644 --- a/package.json +++ b/package.json @@ -15,15 +15,8 @@ "spec": "NODE_ENV=test ./node_modules/.bin/_mocha --timeout 5000 -r adana-dump -R spec --recursive test/spec/**/*.spec.js" }, "standard": { - "global": [ - "describe", - "it", - "beforeEach", - "afterEach" - ], - "ignore": [ - "/test", - "/example" + "env": [ + "mocha" ] }, "devDependencies": { @@ -31,23 +24,22 @@ "adana-dump": "^0.1.0", "adana-format-lcov": "^0.1.1", "chai": "^3.5.0", - "css-loader": "^0.23.1", - "extract-text-webpack-plugin": "^1.0.1", - "less": "^2.7.1", - "less-loader": "^2.2.3", + "css-loader": "^6.2.0", + "mini-css-extract-plugin": "^2.2.0", + "less": "^4.1.1", + "less-loader": "^10.0.1", "memory-fs": "^0.3.0", "mocha": "3.x", - "optimize-css-assets-webpack-plugin": "^3.2.0", - "standard": "^11.0.1", - "style-loader": "^0.13.1", - "webpack": "^1.13.0" + "css-minimizer-webpack-plugin": "^3.0.2", + "standard": "^16.0.3", + "style-loader": "^3.2.1", + "webpack": "^5.50.0" }, "dependencies": { "css-split": "^1.1.3", - "loader-utils": "^1.1.0", - "webpack-sources": "^1.0.2" + "loader-utils": "^1.4.0" }, "peerDependencies": { - "webpack": ">=1" + "webpack": ">=5" } } diff --git a/test/spec/index.spec.js b/test/spec/index.spec.js index f266c7b..0128367 100644 --- a/test/spec/index.spec.js +++ b/test/spec/index.spec.js @@ -1,6 +1,6 @@ const _webpack = require('webpack') -const ExtractTextPlugin = require('extract-text-webpack-plugin') -const OptimizeCssPlugin = require('optimize-css-assets-webpack-plugin') +const MiniCssExtractPlugin = require('mini-css-extract-plugin') +const CssMinimizerPlugin = require('css-minimizer-webpack-plugin') const CSSSplitWebpackPlugin = require('../../lib') const path = require('path') const MemoryFileSystem = require('memory-fs') @@ -9,20 +9,16 @@ const {expect} = require('chai') const basic = path.join('.', 'basic', 'index.js') const less = path.join('.', 'less', 'index.js') -const extract = ExtractTextPlugin.extract.length !== 1 - ? (a, b) => ExtractTextPlugin.extract(a, b) - : (fallbackLoader, loader) => loader ? ExtractTextPlugin.extract({ - fallbackLoader, - loader - }) : ExtractTextPlugin.extract({ - loader: fallbackLoader - }) +/* eslint object-curly-spacing: [error, never] */ +/* eslint no-unused-expressions: off */ +/* eslint comma-dangle: [error, only-multiline] */ const config = (options, entry = basic, extra = {devtool: 'source-map'}) => { const plugins = extra.plugins delete extra.plugins return Object.assign({ + mode: 'development', entry: path.join(__dirname, '..', '..', 'example', entry), context: path.join(__dirname, '..', '..', 'example'), output: { @@ -31,21 +27,25 @@ const config = (options, entry = basic, extra = {devtool: 'source-map'}) => { filename: 'bundle.js' }, module: { - loaders: [{ + rules: [{ test: /\.css$/, - loader: extract( - 'style-loader', - 'css-loader?sourceMap' - ) + use: [MiniCssExtractPlugin.loader, { + loader: 'css-loader', + options: {sourceMap: true}, + }], }, { test: /\.less$/, - loader: extract( - 'css?-url&-autoprefixer&sourceMap!less?sourceMap' - ) - }] + use: [MiniCssExtractPlugin.loader, { + loader: 'css-loader', + options: {sourceMap: true, url: false}, + }, { + loader: 'less-loader', + options: {sourceMap: true}, + }], + }], }, plugins: [ - new ExtractTextPlugin('styles.css'), + new MiniCssExtractPlugin({filename: 'styles.css'}), new CSSSplitWebpackPlugin(options), ].concat((plugins || [])), }, extra) @@ -72,10 +72,12 @@ const webpack = (options, inst, extra) => { describe('CSSSplitWebpackPlugin', () => { it('should split files when needed', () => - webpack({size: 3, imports: true}).then(({stats}) => { + webpack({size: 3, imports: true}).then(({stats, files}) => { expect(stats).to.not.be.null expect(stats.assetsByChunkName).to.have.property('main') .to.contain('styles-2.css') + expect(files).to.have.property('styles.css') + expect(files).to.not.have.property('styles.css.map') }) ) it('should ignore files that do not need splitting', () => @@ -93,10 +95,12 @@ describe('CSSSplitWebpackPlugin', () => { }) ) it('should remove the original asset when splitting', () => - webpack({size: 3, imports: false}).then(({stats}) => { + webpack({size: 3, imports: false}).then(({stats, files}) => { expect(stats).to.not.be.null expect(stats.assetsByChunkName).to.have.property('main') .to.not.contain('styles.css') + expect(files).to.not.have.property('styles.css') + expect(files).to.not.have.property('styles.css.map') }) ) it('should allow customization of import name', () => @@ -121,7 +125,7 @@ describe('CSSSplitWebpackPlugin', () => { }) }) it('should handle cases when there are no source maps', () => - webpack({size: 3}, less, {devtool: null}).then(({files}) => { + webpack({size: 3}, less, {devtool: false}).then(({files}) => { expect(files).to.not.have.property('styles-1.css.map') }) ) @@ -159,11 +163,14 @@ describe('CSSSplitWebpackPlugin', () => { size: 3, defer: true }, basic, { - devtool: null, - plugins: [ - new OptimizeCssPlugin() - ] + mode: 'production', + devtool: false, + optimization: { + minimizer: [new CssMinimizerPlugin()], + }, }).then(({stats, files}) => { + expect(files['styles-1.css'].toString()).to.equal('.foo{color:green}.bar{color:red}.baz{color:#ff0}') + expect(files['styles-2.css'].toString()).to.equal('.qux{color:blue}') expect(files).to.not.have.property('styles-1.css.map') expect(stats.assetsByChunkName) .to.have.property('main') From a1a02710fafcc8489bad167b0303d11949fcccca Mon Sep 17 00:00:00 2001 From: William Leung Date: Sun, 15 Aug 2021 12:53:45 +0800 Subject: [PATCH 2/2] chore: bump to 2.0.0 Also - fix repository url --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 2b342d3..1a3c90f 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,9 @@ { "name": "fast-css-split-webpack-plugin", - "version": "1.0.4", + "version": "2.0.0", "author": "yibn2008 ", "license": "CC0-1.0", - "repository": "metalabdesign/css-split-webpack-plugin", + "repository": "yibn2008/fast-css-split-webpack-plugin", "main": "lib/index.js", "keywords": [ "webpack-plugin",