From 9a41708925fd6fc40cb0ad540bbd7ea3b52fca94 Mon Sep 17 00:00:00 2001 From: zackjackson Date: Sat, 1 Sep 2018 00:31:49 -0700 Subject: [PATCH 1/8] fix: Fixed HMR issue when oneOf is used When using oneOf, HRM is never activated. This patch improved the lookup system used for determining if CSS can and should be reloaded. There have been some other tweaks for css modules and HMR fix #98,#80 --- .github/CODEOWNERS | 2 +- package.json | 2 +- src/index.js | 41 +++++++++++++++++++++++++---------------- 3 files changed, 27 insertions(+), 18 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 89eff985..57da4905 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,2 +1,2 @@ # extract-css-chunks-webpack-plugin maintainers -* @faceyspacey @ScriptedAlchemy +* @ScriptedAlchemy diff --git a/package.json b/package.json index 6d02885c..938ec28e 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "version": "0.0.0-placeholder", "author": "James Gillmore ", "contributors": [ - "Zack Jackson (https://github.com/ScriptedAlchemy)" + "Zack Jackson (https://github.com/ScriptedAlchemy)" ], "description": "Extract CSS from chunks into stylesheets + HMR. Supports Webpack 4", "engines": { diff --git a/src/index.js b/src/index.js index 3c018689..5e5aa741 100644 --- a/src/index.js +++ b/src/index.js @@ -150,22 +150,7 @@ class ExtractCssChunks { const isHOT = this.options.hot ? true : isHMR(compiler); if (isHOT && compiler.options.module && compiler.options.module.rules) { - const updatedRules = compiler.options.module.rules.reduce((rules, rule) => { - if (rule.use && Array.isArray(rule.use)) { - const isMiniCss = rule.use.some((l) => { - const needle = l.loader || l; - return needle.includes(pluginName); - }); - if (isMiniCss) { - rule.use.unshift(hotLoader); - } - } - rules.push(rule); - - return rules; - }, []); - - compiler.options.module.rules = updatedRules; + compiler.options.module.rules = this.updateWebpackConfig(compiler.options.module.rules); } } catch (e) { console.error('Something went wrong: contact the author', JSON.stringify(e)); // eslint-disable-line no-console @@ -412,6 +397,30 @@ class ExtractCssChunks { }); } + updateWebpackConfig(rulez) { + let isExtract = null; + return rulez.reduce((rules, rule) => { + if (rule.oneOf) { + rule.oneOf = this.updateWebpackConfig(rule.oneOf); + } + + if (rule.use && Array.isArray(rule.use)) { + isExtract = rule.use.some((l) => { + const needle = l.loader || l; + return needle.includes(pluginName); + }); + + if (isExtract) { + rule.use.unshift(hotLoader); + } + } + + rules.push(rule); + + return rules; + }, []); + } + getCssChunkObject(mainChunk) { const obj = {}; for (const chunk of mainChunk.getAllAsyncChunks()) { From 25668bae3ef0a2e972d1aed9d2a912dc6eb94567 Mon Sep 17 00:00:00 2001 From: zackjackson Date: Mon, 8 Oct 2018 17:28:41 -0700 Subject: [PATCH 2/8] fix: Applying rollback fixes to updateWebpackConfig, search loader and use object keys --- src/index.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/index.js b/src/index.js index 935d8203..4dd6a8e2 100644 --- a/src/index.js +++ b/src/index.js @@ -433,7 +433,17 @@ class ExtractCssChunks { node.use.unshift(this.hotLoaderObject); } } + if (node && node.loader && Array.isArray(node.loader)) { + const isMiniCss = node.loader.some((l) => { + const needle = l.loader || l; + return needle.includes(pluginName); + }); + if (isMiniCss) { + node.loader.unshift(this.hotLoaderObject); + } + } }); + rules.push(rule); return rules; From b25c2daf2fe6ac4ea2a4b21ce706d7edfdac0fd4 Mon Sep 17 00:00:00 2001 From: zackjackson Date: Mon, 8 Oct 2018 17:30:28 -0700 Subject: [PATCH 3/8] fix: Adding back exported out hot loader --- src/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/index.js b/src/index.js index 4dd6a8e2..f28f309f 100644 --- a/src/index.js +++ b/src/index.js @@ -602,5 +602,6 @@ class ExtractCssChunks { } ExtractCssChunks.loader = require.resolve('./loader'); +ExtractCssChunks.hotLoader = require.resolve('./hotLoader'); export default ExtractCssChunks; From 4eac0f6459d9a0b85495a75ca34cf3571e3dd928 Mon Sep 17 00:00:00 2001 From: zackjackson Date: Mon, 8 Oct 2018 17:49:25 -0700 Subject: [PATCH 4/8] docs(readme): Adding example of manual hotloader implementation In the event users cannot get it to work, theres an example in the docs to reference --- README.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/README.md b/README.md index dbce0e7b..4a649f3a 100644 --- a/README.md +++ b/README.md @@ -281,6 +281,30 @@ new ExtractCssChunk({ >Keep in mind, by default `[name].css` is used when `process.env.NODE_ENV === 'development'` and `[name].[contenthash].css` during production, so you can likely forget about having to pass anything. +### HMR Troubleshooting +**Blank array in HMR script?** + +If you have issues with HMR, but enabled the loader, plugin, and already tried `hot: true, reloadAll:true`, then your webpack config might be more abstract than my simple lookup functions. In this case, Ive exported out the actual hot loader, you can add this manually and as long as you've got the plugin and loader -- it'll work + +```js + rules: [ + { + test: /\.css$/, + use: [ + ExtractCssChunks.hotLoader, // for those who want to manually force hotLoading + ExtractCssChunks.loader, + { + loader: 'css-loader', + options: { + modules: true, + localIdentName: '[name]__[local]--[hash:base64:5]', + }, + }, + ], + }, + ] +``` + ### HMR Pitfall The most common workflow when working with webpack is to write a "development" / "production" value in to the From 1200397c96ec4f6c6ec7990c2945bc2d93e89a15 Mon Sep 17 00:00:00 2001 From: zackjackson Date: Tue, 9 Oct 2018 10:08:40 -0700 Subject: [PATCH 5/8] fix: handle functions as loaders Do not break when searching through `use` or `loader` that is a function --- package.json | 2 +- src/index.js | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 0773cf1f..54bdaa4e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "extract-css-chunks-webpack-plugin", - "version": "3.2.0", + "version": "3.2.1-alpha.0", "author": "James Gillmore ", "contributors": [ "Zack Jackson (https://github.com/ScriptedAlchemy)" diff --git a/src/index.js b/src/index.js index f28f309f..e2a77449 100644 --- a/src/index.js +++ b/src/index.js @@ -427,6 +427,9 @@ class ExtractCssChunks { if (node && node.use && Array.isArray(node.use)) { const isMiniCss = node.use.some((l) => { const needle = l.loader || l; + if (typeof l === 'function') { + return false; + } return needle.includes(pluginName); }); if (isMiniCss) { @@ -436,6 +439,9 @@ class ExtractCssChunks { if (node && node.loader && Array.isArray(node.loader)) { const isMiniCss = node.loader.some((l) => { const needle = l.loader || l; + if (typeof l === 'function') { + return false; + } return needle.includes(pluginName); }); if (isMiniCss) { From 7d591ac0b48c31da25059754495d292b1270ccbc Mon Sep 17 00:00:00 2001 From: Sebastian Seilund Date: Tue, 9 Oct 2018 10:10:30 -0700 Subject: [PATCH 6/8] fix(index.js): Support function loaders (#116) * docs(readme): Tone of Voice clairification on what hot:true does (#113) * fix(index.js): Support function loaders updateWebpackConfig previously expected `use` entries to be strings or arrays, and would throw `needle.includes is not a function` errors when given a function that returns a loader object. * Update README.md --- src/index.js | 3 +++ test/cases/function-loader/expected/main.css | 2 ++ test/cases/function-loader/index.js | 1 + test/cases/function-loader/style.css | 1 + test/cases/function-loader/webpack.config.js | 21 ++++++++++++++++++++ 5 files changed, 28 insertions(+) create mode 100644 test/cases/function-loader/expected/main.css create mode 100644 test/cases/function-loader/index.js create mode 100644 test/cases/function-loader/style.css create mode 100644 test/cases/function-loader/webpack.config.js diff --git a/src/index.js b/src/index.js index e2a77449..bd04fed9 100644 --- a/src/index.js +++ b/src/index.js @@ -426,6 +426,9 @@ class ExtractCssChunks { this.traverseDepthFirst(rule, (node) => { if (node && node.use && Array.isArray(node.use)) { const isMiniCss = node.use.some((l) => { + if (typeof l === 'function') { + return false; + } const needle = l.loader || l; if (typeof l === 'function') { return false; diff --git a/test/cases/function-loader/expected/main.css b/test/cases/function-loader/expected/main.css new file mode 100644 index 00000000..aea53e43 --- /dev/null +++ b/test/cases/function-loader/expected/main.css @@ -0,0 +1,2 @@ +body { background: red; } + diff --git a/test/cases/function-loader/index.js b/test/cases/function-loader/index.js new file mode 100644 index 00000000..aa3357bf --- /dev/null +++ b/test/cases/function-loader/index.js @@ -0,0 +1 @@ +import './style.css'; diff --git a/test/cases/function-loader/style.css b/test/cases/function-loader/style.css new file mode 100644 index 00000000..31fc5b8a --- /dev/null +++ b/test/cases/function-loader/style.css @@ -0,0 +1 @@ +body { background: red; } diff --git a/test/cases/function-loader/webpack.config.js b/test/cases/function-loader/webpack.config.js new file mode 100644 index 00000000..0137a9e4 --- /dev/null +++ b/test/cases/function-loader/webpack.config.js @@ -0,0 +1,21 @@ +const Self = require('../../../'); + +module.exports = { + entry: './index.js', + module: { + rules: [ + { + test: /\.css$/, + use: [ + () => ({ loader: 'css-loader' }), + ], + }, + ], + }, + plugins: [ + new Self({ + filename: '[name].css', + hot: true, + }), + ], +}; From 9f27f1eef51972fd4ddfe0d634dfc50b9b13fa6f Mon Sep 17 00:00:00 2001 From: zackjackson Date: Tue, 9 Oct 2018 10:22:53 -0700 Subject: [PATCH 7/8] test: Fixing function-loader tests --- .eslintrc.js | 3 ++- package.json | 2 +- src/index.js | 3 --- src/loader.js | 3 ++- test/cases/function-loader/index.js | 2 +- test/cases/function-loader/{style.css => main.css} | 0 test/cases/function-loader/webpack.config.js | 8 ++++++-- 7 files changed, 12 insertions(+), 9 deletions(-) rename test/cases/function-loader/{style.css => main.css} (100%) diff --git a/.eslintrc.js b/.eslintrc.js index 12a376a4..41c10cb5 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -21,6 +21,7 @@ module.exports = { 'prefer-destructuring': 0, 'array-callback-return': 0, 'prefer-template': 0, - 'class-methods-use-this': 0 + 'class-methods-use-this': 0, + 'no-plusplus': 0 } }; diff --git a/package.json b/package.json index 54bdaa4e..452f4e16 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "extract-css-chunks-webpack-plugin", - "version": "3.2.1-alpha.0", + "version": "3.2.1-alpha.1", "author": "James Gillmore ", "contributors": [ "Zack Jackson (https://github.com/ScriptedAlchemy)" diff --git a/src/index.js b/src/index.js index bd04fed9..e2a77449 100644 --- a/src/index.js +++ b/src/index.js @@ -426,9 +426,6 @@ class ExtractCssChunks { this.traverseDepthFirst(rule, (node) => { if (node && node.use && Array.isArray(node.use)) { const isMiniCss = node.use.some((l) => { - if (typeof l === 'function') { - return false; - } const needle = l.loader || l; if (typeof l === 'function') { return false; diff --git a/src/loader.js b/src/loader.js index 66e55049..f5d0b97d 100644 --- a/src/loader.js +++ b/src/loader.js @@ -12,7 +12,8 @@ const pluginName = 'extract-css-chunks-webpack-plugin'; const exec = (loaderContext, code, filename) => { const module = new NativeModule(filename, loaderContext); - module.paths = NativeModule._nodeModulePaths(loaderContext.context); // eslint-disable-line no-underscore-dangle + // eslint-disable-line no-underscore-dangle + module.paths = NativeModule._nodeModulePaths(loaderContext.context); module.filename = filename; module._compile(code, filename); // eslint-disable-line no-underscore-dangle return module.exports; diff --git a/test/cases/function-loader/index.js b/test/cases/function-loader/index.js index aa3357bf..74f07de1 100644 --- a/test/cases/function-loader/index.js +++ b/test/cases/function-loader/index.js @@ -1 +1 @@ -import './style.css'; +import './main.css'; diff --git a/test/cases/function-loader/style.css b/test/cases/function-loader/main.css similarity index 100% rename from test/cases/function-loader/style.css rename to test/cases/function-loader/main.css diff --git a/test/cases/function-loader/webpack.config.js b/test/cases/function-loader/webpack.config.js index 0137a9e4..821ab161 100644 --- a/test/cases/function-loader/webpack.config.js +++ b/test/cases/function-loader/webpack.config.js @@ -1,3 +1,5 @@ + + const Self = require('../../../'); module.exports = { @@ -7,7 +9,10 @@ module.exports = { { test: /\.css$/, use: [ - () => ({ loader: 'css-loader' }), + () => [ + Self.loader, + 'css-loader', + ], ], }, ], @@ -15,7 +20,6 @@ module.exports = { plugins: [ new Self({ filename: '[name].css', - hot: true, }), ], }; From ee15791b93db7cc64ab38ab531891755a9ba1b41 Mon Sep 17 00:00:00 2001 From: Nils Thenhausen Date: Tue, 9 Oct 2018 19:24:48 +0200 Subject: [PATCH 8/8] Add reloadAll and cssModules to typings (#115) * docs(readme): Tone of Voice clairification on what hot:true does (#113) * Add reloadAll and cssModules to typings --- README.md | 2 +- index.d.ts | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 4a649f3a..9d67323b 100644 --- a/README.md +++ b/README.md @@ -90,7 +90,7 @@ module.exports = { // both options are optional filename: "[name].css", chunkFilename: "[id].css", - hot: true, // optional as the plugin cannot automatically detect if you are using HOT, not for production use + hot: true, // if you want HMR - we try to automatically inject hot reloading but if it's not working, add it to the config orderWarning: true, // Disable to remove warnings about conflicting order between imports reloadAll: true, // when desperation kicks in - this is a brute force HMR flag cssModules: true // if you use cssModules, this can help. diff --git a/index.d.ts b/index.d.ts index 8b273f06..39ae31ce 100644 --- a/index.d.ts +++ b/index.d.ts @@ -18,6 +18,8 @@ declare module 'extract-css-chunks-webpack-plugin' { * we try to automatically inject hot reloading, but if it's not working, use this config */ hot?: boolean; + reloadAll?: boolean; + cssModules?: boolean; } }