From 0a3362d46cd741a154f944bcf1b2e446aaa1d6c2 Mon Sep 17 00:00:00 2001 From: Tobias Koppers Date: Thu, 24 Sep 2020 10:14:26 +0200 Subject: [PATCH 1/5] chore(webpack-5): update for webpack 5 update manual test cases update CssModule for webpack 5 (to avoid deprecation) clean up inconsistency in loader --- src/CssModule.js | 25 ++++++++++++++++++++++++- src/loader.js | 11 +++++++---- test/manual/index.html | 11 +++++++---- test/manual/src/index.js | 23 ++++++++++++++++++++++- test/manual/src/lazy.module.css | 3 +++ test/manual/webpack.config.js | 4 ++-- 6 files changed, 65 insertions(+), 12 deletions(-) create mode 100644 test/manual/src/lazy.module.css diff --git a/src/CssModule.js b/src/CssModule.js index 80839d62..3bdac2d9 100644 --- a/src/CssModule.js +++ b/src/CssModule.js @@ -2,6 +2,12 @@ import webpack from 'webpack'; import { MODULE_TYPE } from './utils'; +const TYPES = new Set([MODULE_TYPE]); +const CODE_GENERATION_RESULT = { + sources: new Map(), + runtimeRequirements: new Set(), +}; + class CssModule extends webpack.Module { constructor({ context, @@ -20,9 +26,11 @@ class CssModule extends webpack.Module { this.content = content; this.media = media; this.sourceMap = sourceMap; + this.buildInfo = {}; + this.buildMeta = {}; } - // no source() so webpack doesn't do add stuff to the bundle + // no source() so webpack 4 doesn't do add stuff to the bundle size() { return this.content.length; @@ -38,6 +46,16 @@ class CssModule extends webpack.Module { }`; } + // eslint-disable-next-line class-methods-use-this + getSourceTypes() { + return TYPES; + } + + // eslint-disable-next-line class-methods-use-this + codeGeneration() { + return CODE_GENERATION_RESULT; + } + nameForCondition() { const resource = this._identifier.split('!').pop(); const idx = resource.indexOf('?'); @@ -60,6 +78,11 @@ class CssModule extends webpack.Module { return true; } + // eslint-disable-next-line class-methods-use-this + needBuild(context, callback) { + callback(null, false); + } + build(options, compilation, resolver, fileSystem, callback) { this.buildInfo = {}; this.buildMeta = {}; diff --git a/src/loader.js b/src/loader.js index ff9e7f84..d1a4c373 100644 --- a/src/loader.js +++ b/src/loader.js @@ -192,11 +192,10 @@ export function pitch(request) { : originalExports; if (namedExport) { - locals = ''; - Object.keys(originalExports).forEach((key) => { if (key !== 'default') { - locals += `\nexport const ${key} = "${originalExports[key]}";`; + if (!locals) locals = {}; + locals[key] = originalExports[key]; } }); } else { @@ -228,7 +227,11 @@ export function pitch(request) { const result = locals ? namedExport - ? locals + ? Object.keys(locals) + .map( + (key) => `\nexport const ${key} = ${JSON.stringify(locals[key])};` + ) + .join('') : `\n${ esModule ? 'export default' : 'module.exports =' } ${JSON.stringify(locals)};` diff --git a/test/manual/index.html b/test/manual/index.html index c95108db..26b9e7c9 100644 --- a/test/manual/index.html +++ b/test/manual/index.html @@ -5,7 +5,7 @@ mini-css-extract-plugin testcase - + - + .preloaded-css2 { background: lightgreen; } @@ -46,6 +46,9 @@

But turn orange, when . Additional clicks have no effect.

Refresh and press buttons in reverse order: This should turn green instead.

+
+

Lazy CSS Module: Must be red, but turn green when .

+

Lazy CSS: Turn off the network and .

An error should have appeared.

@@ -53,11 +56,11 @@

Preloaded CSS: Must be green.

-

displays an alert and should turn red.

+

displays an alert but has no styling effect.

Preloaded inlined CSS: Must be green.

-

displays an alert and should turn red.

+

displays an alert but has no styling effect.

CrossOriginLoading Option: Must be red.

diff --git a/test/manual/src/index.js b/test/manual/src/index.js index 92761702..9525284b 100644 --- a/test/manual/src/index.js +++ b/test/manual/src/index.js @@ -23,6 +23,21 @@ Object.keys(classes).forEach((localClass) => { replaceClass(localClass, classes[localClass]); }); +let oldClasses = classes; + +if (module.hot) { + module.hot.accept('./simple.module.css', () => { + Object.keys(oldClasses).forEach((localClass) => { + replaceClass(oldClasses[localClass], localClass); + }); + Object.keys(classes).forEach((localClass) => { + replaceClass(localClass, classes[localClass]); + }); + oldClasses = classes; + alert('HMR updated CSS module'); + }); +} + const handleError = (err) => { document.querySelector('.errors').textContent += `\n${err.toString()}`; console.error(err); @@ -44,6 +59,12 @@ const makeButton = (className, fn, shouldDisable = true) => { makeButton('.lazy-button', () => import('./lazy.js')); makeButton('.lazy-button2', () => import('./lazy2.css')); +makeButton('.lazy-module-button', () => + import('./lazy.module.css').then((module) => { + console.log(module); + document.querySelector('.lazy-css-module').classList.add(module.style); + }) +); makeButton('.preloaded-button1', () => import(/* webpackChunkName: "preloaded1" */ './preloaded1') @@ -56,7 +77,7 @@ makeButton('.lazy-failure-button', () => import('./lazy-failure.js'), false); makeButton('.crossorigin', () => { const originalPublicPath = __webpack_public_path__; - __webpack_public_path__ = 'http://0.0.0.0:8080/dist/'; + __webpack_public_path__ = 'http://127.0.0.1:8080/dist/'; const promise = import('./crossorigin').then(() => { const lastTwoElements = Array.from(document.head.children).slice(-2); const hasCrossorigin = lastTwoElements.every( diff --git a/test/manual/src/lazy.module.css b/test/manual/src/lazy.module.css new file mode 100644 index 00000000..e9c49ebd --- /dev/null +++ b/test/manual/src/lazy.module.css @@ -0,0 +1,3 @@ +.style { + background: lightgreen; +} diff --git a/test/manual/webpack.config.js b/test/manual/webpack.config.js index 3d308a6c..27543cb2 100644 --- a/test/manual/webpack.config.js +++ b/test/manual/webpack.config.js @@ -13,7 +13,7 @@ const ENABLE_ES_MODULE = module.exports = { mode: 'development', output: { - chunkFilename: '[contenthash].js', + chunkFilename: '[name].chunk.js', publicPath: '/dist/', crossOriginLoading: 'anonymous', }, @@ -60,7 +60,7 @@ module.exports = { plugins: [ new Self({ filename: '[name].css', - chunkFilename: '[contenthash].css', + chunkFilename: '[name].chunk.css', }), ], devServer: { From 9465a4cd63fdfe4d04b66cb04c5679fdb558f586 Mon Sep 17 00:00:00 2001 From: Tobias Koppers Date: Thu, 24 Sep 2020 10:21:51 +0200 Subject: [PATCH 2/5] fix(filename): fix usage of filenameTemplate using filenameTemplate is incorrect It would point to a .js filename. --- src/index.js | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/index.js b/src/index.js index 9a2cbc68..6d71668d 100644 --- a/src/index.js +++ b/src/index.js @@ -59,6 +59,7 @@ class MiniCssExtractPlugin { } } + /** @param {import("webpack").Compiler} compiler */ apply(compiler) { compiler.hooks.thisCompilation.tap(pluginName, (compilation) => { compilation.dependencyFactories.set( @@ -149,13 +150,9 @@ class MiniCssExtractPlugin { this.getChunkModules(chunk, chunkGraph) ).filter((module) => module.type === MODULE_TYPE); - const filenameTemplate = - chunk.filenameTemplate || - chunk.hasRuntime() || - chunk.isOnlyInitial() - ? ({ chunk: chunkData }) => - this.options.moduleFilename(chunkData) - : this.options.chunkFilename; + const filenameTemplate = chunk.canBeInitial() + ? ({ chunk: chunkData }) => this.options.moduleFilename(chunkData) + : this.options.chunkFilename; if (renderedModules.length > 0) { result.push({ From dcdd09442ebacfbf226d9a67a1f8221c5143ae78 Mon Sep 17 00:00:00 2001 From: Tobias Koppers Date: Thu, 24 Sep 2020 11:20:45 +0200 Subject: [PATCH 3/5] refactor(runtime): move runtime logic for webpack 5 into runtime module --- src/CssLoadingRuntimeModule.js | 126 ++++++++++++++ src/index.js | 306 ++++++++++++++++++--------------- 2 files changed, 289 insertions(+), 143 deletions(-) create mode 100644 src/CssLoadingRuntimeModule.js diff --git a/src/CssLoadingRuntimeModule.js b/src/CssLoadingRuntimeModule.js new file mode 100644 index 00000000..1b1ea244 --- /dev/null +++ b/src/CssLoadingRuntimeModule.js @@ -0,0 +1,126 @@ +import { RuntimeGlobals, RuntimeModule, Template, util } from 'webpack'; + +import { MODULE_TYPE } from './utils'; + +const { + comparators: { compareModulesByIdentifier }, +} = util; + +const getCssChunkObject = (mainChunk, compilation) => { + const obj = {}; + const { chunkGraph } = compilation; + + for (const chunk of mainChunk.getAllAsyncChunks()) { + const modules = chunkGraph.getOrderedChunkModulesIterable( + chunk, + compareModulesByIdentifier + ); + for (const module of modules) { + if (module.type === MODULE_TYPE) { + obj[chunk.id] = 1; + break; + } + } + } + + return obj; +}; + +module.exports = class CssLoadingRuntimeModule extends RuntimeModule { + constructor(runtimeRequirements) { + super('css loading', 10); + this.runtimeRequirements = runtimeRequirements; + } + + generate() { + const { chunk, compilation, runtimeRequirements } = this; + const { + runtimeTemplate, + outputOptions: { crossOriginLoading }, + } = compilation; + const chunkMap = getCssChunkObject(chunk, compilation); + + if (Object.keys(chunkMap).length === 0) return null; + + const withLoading = runtimeRequirements.has( + RuntimeGlobals.ensureChunkHandlers + ); + + return Template.asString([ + '// object to store loaded CSS chunks', + 'var installedCssChunks = {', + Template.indent( + chunk.ids.map((id) => `${JSON.stringify(id)}: 0`).join(',\n') + ), + '};', + '', + withLoading + ? Template.asString([ + `${ + RuntimeGlobals.ensureChunkHandlers + }.miniCss = ${runtimeTemplate.basicFunction('chunkId, promises', [ + `var cssChunks = ${JSON.stringify(chunkMap)};`, + 'if(installedCssChunks[chunkId]) promises.push(installedCssChunks[chunkId]);', + 'else if(installedCssChunks[chunkId] !== 0 && cssChunks[chunkId]) {', + Template.indent([ + 'promises.push(installedCssChunks[chunkId] = new Promise(function(resolve, reject) {', + Template.indent([ + `var href = ${RuntimeGlobals.require}.miniCssF(chunkId);`, + `var fullhref = ${RuntimeGlobals.publicPath} + href;`, + 'var existingLinkTags = document.getElementsByTagName("link");', + 'for(var i = 0; i < existingLinkTags.length; i++) {', + Template.indent([ + 'var tag = existingLinkTags[i];', + 'var dataHref = tag.getAttribute("data-href") || tag.getAttribute("href");', + 'if(tag.rel === "stylesheet" && (dataHref === href || dataHref === fullhref)) return resolve();', + ]), + '}', + 'var existingStyleTags = document.getElementsByTagName("style");', + 'for(var i = 0; i < existingStyleTags.length; i++) {', + Template.indent([ + 'var tag = existingStyleTags[i];', + 'var dataHref = tag.getAttribute("data-href");', + 'if(dataHref === href || dataHref === fullhref) return resolve();', + ]), + '}', + 'var linkTag = document.createElement("link");', + 'linkTag.rel = "stylesheet";', + 'linkTag.type = "text/css";', + 'linkTag.onload = resolve;', + 'linkTag.onerror = function(event) {', + Template.indent([ + 'var request = event && event.target && event.target.src || fullhref;', + 'var err = new Error("Loading CSS chunk " + chunkId + " failed.\\n(" + request + ")");', + 'err.code = "CSS_CHUNK_LOAD_FAILED";', + 'err.request = request;', + 'delete installedCssChunks[chunkId]', + 'linkTag.parentNode.removeChild(linkTag)', + 'reject(err);', + ]), + '};', + 'linkTag.href = fullhref;', + crossOriginLoading + ? Template.asString([ + `if (linkTag.href.indexOf(window.location.origin + '/') !== 0) {`, + Template.indent( + `linkTag.crossOrigin = ${JSON.stringify( + crossOriginLoading + )};` + ), + '}', + ]) + : '', + 'var head = document.getElementsByTagName("head")[0];', + 'head.appendChild(linkTag);', + ]), + '}).then(function() {', + Template.indent(['installedCssChunks[chunkId] = 0;']), + '}));', + ]), + '}', + ])};`, + ]) + : '// no chunk loading', + ]); + } +}; diff --git a/src/index.js b/src/index.js index 6d71668d..117bb087 100644 --- a/src/index.js +++ b/src/index.js @@ -225,169 +225,189 @@ class MiniCssExtractPlugin { const { mainTemplate } = compilation; - mainTemplate.hooks.localVars.tap(pluginName, (source, chunk) => { - const chunkMap = this.getCssChunkObject(chunk, compilation); - - if (Object.keys(chunkMap).length > 0) { - return Template.asString([ - source, - '', - '// object to store loaded CSS chunks', - 'var installedCssChunks = {', - Template.indent( - chunk.ids.map((id) => `${JSON.stringify(id)}: 0`).join(',\n') - ), - '};', - ]); - } - - return source; - }); - - mainTemplate.hooks.requireEnsure.tap( - pluginName, - (source, chunk, hash) => { + if (isWebpack4) { + mainTemplate.hooks.localVars.tap(pluginName, (source, chunk) => { const chunkMap = this.getCssChunkObject(chunk, compilation); if (Object.keys(chunkMap).length > 0) { - const maintemplateObject = isWebpack4 ? mainTemplate : compilation; - const chunkMaps = chunk.getChunkMaps(); - const { crossOriginLoading } = maintemplateObject.outputOptions; - const linkHrefPath = maintemplateObject.getAssetPath( - JSON.stringify(this.options.chunkFilename), - { - hash: isWebpack4 - ? `" + ${mainTemplate.renderCurrentHashCode(hash)} + "` - : `" + ${webpack.RuntimeGlobals.getFullHash} + "`, - hashWithLength: (length) => - isWebpack4 - ? `" + ${mainTemplate.renderCurrentHashCode( - hash, - length - )} + "` - : `" + ${webpack.RuntimeGlobals.getFullHash} + "`, - chunk: { - id: '" + chunkId + "', - hash: `" + ${JSON.stringify(chunkMaps.hash)}[chunkId] + "`, - hashWithLength(length) { - const shortChunkHashMap = Object.create(null); - - for (const chunkId of Object.keys(chunkMaps.hash)) { - if (typeof chunkMaps.hash[chunkId] === 'string') { - shortChunkHashMap[chunkId] = chunkMaps.hash[ - chunkId - ].substring(0, length); - } - } + return Template.asString([ + source, + '', + '// object to store loaded CSS chunks', + 'var installedCssChunks = {', + Template.indent( + chunk.ids.map((id) => `${JSON.stringify(id)}: 0`).join(',\n') + ), + '};', + ]); + } - return `" + ${JSON.stringify( - shortChunkHashMap - )}[chunkId] + "`; - }, - contentHash: { - [MODULE_TYPE]: `" + ${JSON.stringify( - chunkMaps.contentHash[MODULE_TYPE] - )}[chunkId] + "`, - }, - contentHashWithLength: { - [MODULE_TYPE]: (length) => { - const shortContentHashMap = {}; - const contentHash = chunkMaps.contentHash[MODULE_TYPE]; - - for (const chunkId of Object.keys(contentHash)) { - if (typeof contentHash[chunkId] === 'string') { - shortContentHashMap[chunkId] = contentHash[ + return source; + }); + + mainTemplate.hooks.requireEnsure.tap( + pluginName, + (source, chunk, hash) => { + const chunkMap = this.getCssChunkObject(chunk, compilation); + + if (Object.keys(chunkMap).length > 0) { + const chunkMaps = chunk.getChunkMaps(); + const { crossOriginLoading } = mainTemplate.outputOptions; + const linkHrefPath = mainTemplate.getAssetPath( + JSON.stringify(this.options.chunkFilename), + { + hash: `" + ${mainTemplate.renderCurrentHashCode(hash)} + "`, + hashWithLength: (length) => + `" + ${mainTemplate.renderCurrentHashCode( + hash, + length + )} + "`, + chunk: { + id: '" + chunkId + "', + hash: `" + ${JSON.stringify(chunkMaps.hash)}[chunkId] + "`, + hashWithLength(length) { + const shortChunkHashMap = Object.create(null); + + for (const chunkId of Object.keys(chunkMaps.hash)) { + if (typeof chunkMaps.hash[chunkId] === 'string') { + shortChunkHashMap[chunkId] = chunkMaps.hash[ chunkId ].substring(0, length); } } return `" + ${JSON.stringify( - shortContentHashMap + shortChunkHashMap )}[chunkId] + "`; }, + contentHash: { + [MODULE_TYPE]: `" + ${JSON.stringify( + chunkMaps.contentHash[MODULE_TYPE] + )}[chunkId] + "`, + }, + contentHashWithLength: { + [MODULE_TYPE]: (length) => { + const shortContentHashMap = {}; + const contentHash = chunkMaps.contentHash[MODULE_TYPE]; + + for (const chunkId of Object.keys(contentHash)) { + if (typeof contentHash[chunkId] === 'string') { + shortContentHashMap[chunkId] = contentHash[ + chunkId + ].substring(0, length); + } + } + + return `" + ${JSON.stringify( + shortContentHashMap + )}[chunkId] + "`; + }, + }, + name: `" + (${JSON.stringify( + chunkMaps.name + )}[chunkId]||chunkId) + "`, }, - name: `" + (${JSON.stringify( - chunkMaps.name - )}[chunkId]||chunkId) + "`, - }, - contentHashType: MODULE_TYPE, - } - ); + contentHashType: MODULE_TYPE, + } + ); - return Template.asString([ - source, - '', - `// ${pluginName} CSS loading`, - `var cssChunks = ${JSON.stringify(chunkMap)};`, - 'if(installedCssChunks[chunkId]) promises.push(installedCssChunks[chunkId]);', - 'else if(installedCssChunks[chunkId] !== 0 && cssChunks[chunkId]) {', - Template.indent([ - 'promises.push(installedCssChunks[chunkId] = new Promise(function(resolve, reject) {', + return Template.asString([ + source, + '', + `// ${pluginName} CSS loading`, + `var cssChunks = ${JSON.stringify(chunkMap)};`, + 'if(installedCssChunks[chunkId]) promises.push(installedCssChunks[chunkId]);', + 'else if(installedCssChunks[chunkId] !== 0 && cssChunks[chunkId]) {', Template.indent([ - `var href = ${linkHrefPath};`, - `var fullhref = ${ - isWebpack4 - ? mainTemplate.requireFn - : webpack.RuntimeGlobals.require - }.p + href;`, - 'var existingLinkTags = document.getElementsByTagName("link");', - 'for(var i = 0; i < existingLinkTags.length; i++) {', - Template.indent([ - 'var tag = existingLinkTags[i];', - 'var dataHref = tag.getAttribute("data-href") || tag.getAttribute("href");', - 'if(tag.rel === "stylesheet" && (dataHref === href || dataHref === fullhref)) return resolve();', - ]), - '}', - 'var existingStyleTags = document.getElementsByTagName("style");', - 'for(var i = 0; i < existingStyleTags.length; i++) {', + 'promises.push(installedCssChunks[chunkId] = new Promise(function(resolve, reject) {', Template.indent([ - 'var tag = existingStyleTags[i];', - 'var dataHref = tag.getAttribute("data-href");', - 'if(dataHref === href || dataHref === fullhref) return resolve();', + `var href = ${linkHrefPath};`, + `var fullhref = ${mainTemplate.requireFn}.p + href;`, + 'var existingLinkTags = document.getElementsByTagName("link");', + 'for(var i = 0; i < existingLinkTags.length; i++) {', + Template.indent([ + 'var tag = existingLinkTags[i];', + 'var dataHref = tag.getAttribute("data-href") || tag.getAttribute("href");', + 'if(tag.rel === "stylesheet" && (dataHref === href || dataHref === fullhref)) return resolve();', + ]), + '}', + 'var existingStyleTags = document.getElementsByTagName("style");', + 'for(var i = 0; i < existingStyleTags.length; i++) {', + Template.indent([ + 'var tag = existingStyleTags[i];', + 'var dataHref = tag.getAttribute("data-href");', + 'if(dataHref === href || dataHref === fullhref) return resolve();', + ]), + '}', + 'var linkTag = document.createElement("link");', + 'linkTag.rel = "stylesheet";', + 'linkTag.type = "text/css";', + 'linkTag.onload = resolve;', + 'linkTag.onerror = function(event) {', + Template.indent([ + 'var request = event && event.target && event.target.src || fullhref;', + 'var err = new Error("Loading CSS chunk " + chunkId + " failed.\\n(" + request + ")");', + 'err.code = "CSS_CHUNK_LOAD_FAILED";', + 'err.request = request;', + 'delete installedCssChunks[chunkId]', + 'linkTag.parentNode.removeChild(linkTag)', + 'reject(err);', + ]), + '};', + 'linkTag.href = fullhref;', + crossOriginLoading + ? Template.asString([ + `if (linkTag.href.indexOf(window.location.origin + '/') !== 0) {`, + Template.indent( + `linkTag.crossOrigin = ${JSON.stringify( + crossOriginLoading + )};` + ), + '}', + ]) + : '', + 'var head = document.getElementsByTagName("head")[0];', + 'head.appendChild(linkTag);', ]), - '}', - 'var linkTag = document.createElement("link");', - 'linkTag.rel = "stylesheet";', - 'linkTag.type = "text/css";', - 'linkTag.onload = resolve;', - 'linkTag.onerror = function(event) {', - Template.indent([ - 'var request = event && event.target && event.target.src || fullhref;', - 'var err = new Error("Loading CSS chunk " + chunkId + " failed.\\n(" + request + ")");', - 'err.code = "CSS_CHUNK_LOAD_FAILED";', - 'err.request = request;', - 'delete installedCssChunks[chunkId]', - 'linkTag.parentNode.removeChild(linkTag)', - 'reject(err);', - ]), - '};', - 'linkTag.href = fullhref;', - crossOriginLoading - ? Template.asString([ - `if (linkTag.href.indexOf(window.location.origin + '/') !== 0) {`, - Template.indent( - `linkTag.crossOrigin = ${JSON.stringify( - crossOriginLoading - )};` - ), - '}', - ]) - : '', - 'var head = document.getElementsByTagName("head")[0];', - 'head.appendChild(linkTag);', + '}).then(function() {', + Template.indent(['installedCssChunks[chunkId] = 0;']), + '}));', ]), - '}).then(function() {', - Template.indent(['installedCssChunks[chunkId] = 0;']), - '}));', - ]), - '}', - ]); + '}', + ]); + } + + return source; } + ); + } else { + // eslint-disable-next-line global-require + const CssLoadingRuntimeModule = require('./CssLoadingRuntimeModule'); - return source; - } - ); + compilation.hooks.additionalTreeRuntimeRequirements.tap( + pluginName, + (chunk, set) => { + set.add(webpack.RuntimeGlobals.publicPath); + compilation.addRuntimeModule( + chunk, + new webpack.runtime.GetChunkFilenameRuntimeModule( + MODULE_TYPE, + 'mini-css', + `${webpack.RuntimeGlobals.require}.miniCssF`, + (referencedChunk) => + referencedChunk.canBeInitial() + ? ({ chunk: chunkData }) => + this.options.moduleFilename(chunkData) + : this.options.chunkFilename + ) + ); + compilation.addRuntimeModule( + chunk, + new CssLoadingRuntimeModule(set) + ); + } + ); + } }); } From eb18378c9a73c451de7415dd22eedcdc973fb643 Mon Sep 17 00:00:00 2001 From: Tobias Koppers Date: Thu, 24 Sep 2020 20:27:10 +0200 Subject: [PATCH 4/5] feat(hmr): add hmr remove hmr option for webpack 5 (now automatically) fix/update test cases --- README.md | 8 +- src/CssLoadingRuntimeModule.js | 194 ++- src/index.js | 73 +- .../expected/webpack-5/common.js | 8 +- .../dependOn/expected/webpack-5/common.js | 8 +- test/cases/hmr/expected/webpack-5/main.js | 1069 ++++++++++++++++- test/cases/hmr/webpack.config.js | 35 +- test/enforce-esm.test.js | 14 +- 8 files changed, 1280 insertions(+), 129 deletions(-) diff --git a/README.md b/README.md index 825554ab..52ba3ae4 100644 --- a/README.md +++ b/README.md @@ -297,7 +297,7 @@ module.exports = { // you can specify a publicPath here // by default it uses publicPath in webpackOptions.output publicPath: '../', - hmr: process.env.NODE_ENV === 'development', + hmr: process.env.NODE_ENV === 'development', // webpack 4 only }, }, 'css-loader', @@ -379,7 +379,7 @@ module.exports = { { loader: MiniCssExtractPlugin.loader, options: { - hmr: process.env.NODE_ENV === 'development', + hmr: process.env.NODE_ENV === 'development', // webpack 4 only }, }, 'css-loader', @@ -394,6 +394,8 @@ module.exports = { ### Hot Module Reloading (HMR) +Note: HMR is automatically supported in webpack 5. No need to configure it. Skip the following: + The `mini-css-extract-plugin` supports hot reloading of actual css files in development. Some options are provided to enable HMR of both standard stylesheets and locally scoped CSS or CSS modules. Below is an example configuration of mini-css for HMR use with CSS modules. @@ -424,7 +426,7 @@ module.exports = { { loader: MiniCssExtractPlugin.loader, options: { - // only enable hot in development + // only enable hot in development (webpack 4 only) hmr: process.env.NODE_ENV === 'development', // if hmr does not work, this is a forceful method. reloadAll: true, diff --git a/src/CssLoadingRuntimeModule.js b/src/CssLoadingRuntimeModule.js index 1b1ea244..e3b7d991 100644 --- a/src/CssLoadingRuntimeModule.js +++ b/src/CssLoadingRuntimeModule.js @@ -40,22 +40,84 @@ module.exports = class CssLoadingRuntimeModule extends RuntimeModule { } = compilation; const chunkMap = getCssChunkObject(chunk, compilation); - if (Object.keys(chunkMap).length === 0) return null; - - const withLoading = runtimeRequirements.has( - RuntimeGlobals.ensureChunkHandlers + const withLoading = + runtimeRequirements.has(RuntimeGlobals.ensureChunkHandlers) && + Object.keys(chunkMap).length > 0; + const withHmr = runtimeRequirements.has( + RuntimeGlobals.hmrDownloadUpdateHandlers ); + if (!withLoading && !withHmr) return null; + return Template.asString([ - '// object to store loaded CSS chunks', - 'var installedCssChunks = {', - Template.indent( - chunk.ids.map((id) => `${JSON.stringify(id)}: 0`).join(',\n') - ), - '};', - '', + `var createStylesheet = ${runtimeTemplate.basicFunction( + 'fullhref, resolve, reject', + [ + 'var linkTag = document.createElement("link");', + 'linkTag.rel = "stylesheet";', + 'linkTag.type = "text/css";', + 'linkTag.onload = resolve;', + 'linkTag.onerror = function(event) {', + Template.indent([ + 'var request = event && event.target && event.target.src || fullhref;', + 'var err = new Error("Loading CSS chunk " + chunkId + " failed.\\n(" + request + ")");', + 'err.code = "CSS_CHUNK_LOAD_FAILED";', + 'err.request = request;', + 'linkTag.parentNode.removeChild(linkTag)', + 'reject(err);', + ]), + '};', + 'linkTag.href = fullhref;', + crossOriginLoading + ? Template.asString([ + `if (linkTag.href.indexOf(window.location.origin + '/') !== 0) {`, + Template.indent( + `linkTag.crossOrigin = ${JSON.stringify(crossOriginLoading)};` + ), + '}', + ]) + : '', + 'var head = document.getElementsByTagName("head")[0];', + 'head.appendChild(linkTag);', + 'return linkTag;', + ] + )};`, + `var findStylesheet = ${runtimeTemplate.basicFunction('href, fullhref', [ + 'var existingLinkTags = document.getElementsByTagName("link");', + 'for(var i = 0; i < existingLinkTags.length; i++) {', + Template.indent([ + 'var tag = existingLinkTags[i];', + 'var dataHref = tag.getAttribute("data-href") || tag.getAttribute("href");', + 'if(tag.rel === "stylesheet" && (dataHref === href || dataHref === fullhref)) return tag;', + ]), + '}', + 'var existingStyleTags = document.getElementsByTagName("style");', + 'for(var i = 0; i < existingStyleTags.length; i++) {', + Template.indent([ + 'var tag = existingStyleTags[i];', + 'var dataHref = tag.getAttribute("data-href");', + 'if(dataHref === href || dataHref === fullhref) return tag;', + ]), + '}', + ])};`, + `var loadStylesheet = ${runtimeTemplate.basicFunction( + 'chunkId', + `return new Promise(${runtimeTemplate.basicFunction('resolve, reject', [ + `var href = ${RuntimeGlobals.require}.miniCssF(chunkId);`, + `var fullhref = ${RuntimeGlobals.publicPath} + href;`, + 'if(findStylesheet(href, fullhref)) return resolve();', + 'createStylesheet(fullhref, resolve, reject);', + ])});` + )}`, withLoading ? Template.asString([ + '// object to store loaded CSS chunks', + 'var installedCssChunks = {', + Template.indent( + chunk.ids.map((id) => `${JSON.stringify(id)}: 0`).join(',\n') + ), + '};', + '', `${ RuntimeGlobals.ensureChunkHandlers }.miniCss = ${runtimeTemplate.basicFunction('chunkId, promises', [ @@ -63,64 +125,68 @@ module.exports = class CssLoadingRuntimeModule extends RuntimeModule { 'if(installedCssChunks[chunkId]) promises.push(installedCssChunks[chunkId]);', 'else if(installedCssChunks[chunkId] !== 0 && cssChunks[chunkId]) {', Template.indent([ - 'promises.push(installedCssChunks[chunkId] = new Promise(function(resolve, reject) {', - Template.indent([ - `var href = ${RuntimeGlobals.require}.miniCssF(chunkId);`, - `var fullhref = ${RuntimeGlobals.publicPath} + href;`, - 'var existingLinkTags = document.getElementsByTagName("link");', - 'for(var i = 0; i < existingLinkTags.length; i++) {', - Template.indent([ - 'var tag = existingLinkTags[i];', - 'var dataHref = tag.getAttribute("data-href") || tag.getAttribute("href");', - 'if(tag.rel === "stylesheet" && (dataHref === href || dataHref === fullhref)) return resolve();', - ]), - '}', - 'var existingStyleTags = document.getElementsByTagName("style");', - 'for(var i = 0; i < existingStyleTags.length; i++) {', - Template.indent([ - 'var tag = existingStyleTags[i];', - 'var dataHref = tag.getAttribute("data-href");', - 'if(dataHref === href || dataHref === fullhref) return resolve();', - ]), - '}', - 'var linkTag = document.createElement("link");', - 'linkTag.rel = "stylesheet";', - 'linkTag.type = "text/css";', - 'linkTag.onload = resolve;', - 'linkTag.onerror = function(event) {', - Template.indent([ - 'var request = event && event.target && event.target.src || fullhref;', - 'var err = new Error("Loading CSS chunk " + chunkId + " failed.\\n(" + request + ")");', - 'err.code = "CSS_CHUNK_LOAD_FAILED";', - 'err.request = request;', - 'delete installedCssChunks[chunkId]', - 'linkTag.parentNode.removeChild(linkTag)', - 'reject(err);', - ]), - '};', - 'linkTag.href = fullhref;', - crossOriginLoading - ? Template.asString([ - `if (linkTag.href.indexOf(window.location.origin + '/') !== 0) {`, - Template.indent( - `linkTag.crossOrigin = ${JSON.stringify( - crossOriginLoading - )};` - ), - '}', - ]) - : '', - 'var head = document.getElementsByTagName("head")[0];', - 'head.appendChild(linkTag);', - ]), - '}).then(function() {', - Template.indent(['installedCssChunks[chunkId] = 0;']), - '}));', + `promises.push(installedCssChunks[chunkId] = loadStylesheet(chunkId).then(${runtimeTemplate.basicFunction( + '', + 'installedCssChunks[chunkId] = 0;' + )}, ${runtimeTemplate.basicFunction('e', [ + 'delete installedCssChunks[chunkId];', + 'throw e;', + ])}));`, ]), '}', ])};`, ]) : '// no chunk loading', + '', + withHmr + ? Template.asString([ + 'var oldTags = [];', + 'var newTags = [];', + `var applyHandler = ${runtimeTemplate.basicFunction('options', [ + `return { dispose: ${runtimeTemplate.basicFunction('', [ + 'for(var i = 0; i < oldTags.length; i++) {', + Template.indent([ + 'var oldTag = oldTags[i];', + 'if(oldTag.parentNode) oldTag.parentNode.removeChild(oldTag);', + ]), + '}', + 'oldTags.length = 0;', + ])}, apply: ${runtimeTemplate.basicFunction('', [ + 'for(var i = 0; i < newTags.length; i++) newTags[i].rel = "stylesheet";', + 'newTags.length = 0;', + ])} };`, + ])}`, + `${ + RuntimeGlobals.hmrDownloadUpdateHandlers + }.miniCss = ${runtimeTemplate.basicFunction( + 'chunkIds, removedChunks, removedModules, promises, applyHandlers, updatedModulesList', + [ + 'applyHandlers.push(applyHandler);', + `chunkIds.forEach(${runtimeTemplate.basicFunction('chunkId', [ + `var href = ${RuntimeGlobals.require}.miniCssF(chunkId);`, + `var fullhref = ${RuntimeGlobals.publicPath} + href;`, + 'const oldTag = findStylesheet(href, fullhref);', + 'if(!oldTag) return;', + `promises.push(new Promise(${runtimeTemplate.basicFunction( + 'resolve, reject', + [ + `var tag = createStylesheet(fullhref, ${runtimeTemplate.basicFunction( + '', + [ + 'tag.as = "style";', + 'tag.rel = "preload";', + 'resolve();', + ] + )}, reject);`, + 'oldTags.push(oldTag);', + 'newTags.push(tag);', + ] + )}));`, + ])});`, + ] + )}`, + ]) + : '// no hmr', ]); } }; diff --git a/src/index.js b/src/index.js index 117bb087..86a16ead 100644 --- a/src/index.js +++ b/src/index.js @@ -57,10 +57,22 @@ class MiniCssExtractPlugin { ); } } + + if (!isWebpack4 && 'hmr' in this.options) { + throw new Error( + "The 'hmr' option doesn't exist for the mini-css-extract-plugin when using webpack 5 (it's automatically determined)" + ); + } } /** @param {import("webpack").Compiler} compiler */ apply(compiler) { + const { splitChunks } = compiler.options.optimization; + if (splitChunks) { + if (splitChunks.defaultSizeTypes.includes('...')) { + splitChunks.defaultSizeTypes.push(MODULE_TYPE); + } + } compiler.hooks.thisCompilation.tap(pluginName, (compilation) => { compilation.dependencyFactories.set( CssDependency, @@ -146,6 +158,10 @@ class MiniCssExtractPlugin { (result, { chunk }) => { const { chunkGraph } = compilation; + // We don't need hot update chunks for css + // We will use the real asset instead to update + if (chunk instanceof webpack.HotUpdateChunk) return; + const renderedModules = Array.from( this.getChunkModules(chunk, chunkGraph) ).filter((module) => module.type === MODULE_TYPE); @@ -381,32 +397,37 @@ class MiniCssExtractPlugin { } ); } else { - // eslint-disable-next-line global-require - const CssLoadingRuntimeModule = require('./CssLoadingRuntimeModule'); - - compilation.hooks.additionalTreeRuntimeRequirements.tap( - pluginName, - (chunk, set) => { - set.add(webpack.RuntimeGlobals.publicPath); - compilation.addRuntimeModule( - chunk, - new webpack.runtime.GetChunkFilenameRuntimeModule( - MODULE_TYPE, - 'mini-css', - `${webpack.RuntimeGlobals.require}.miniCssF`, - (referencedChunk) => - referencedChunk.canBeInitial() - ? ({ chunk: chunkData }) => - this.options.moduleFilename(chunkData) - : this.options.chunkFilename - ) - ); - compilation.addRuntimeModule( - chunk, - new CssLoadingRuntimeModule(set) - ); - } - ); + const enabledChunks = new WeakSet(); + const handler = (chunk, set) => { + if (enabledChunks.has(chunk)) return; + enabledChunks.add(chunk); + + // eslint-disable-next-line global-require + const CssLoadingRuntimeModule = require('./CssLoadingRuntimeModule'); + + set.add(webpack.RuntimeGlobals.publicPath); + compilation.addRuntimeModule( + chunk, + new webpack.runtime.GetChunkFilenameRuntimeModule( + MODULE_TYPE, + 'mini-css', + `${webpack.RuntimeGlobals.require}.miniCssF`, + (referencedChunk) => + referencedChunk.canBeInitial() + ? ({ chunk: chunkData }) => + this.options.moduleFilename(chunkData) + : this.options.chunkFilename, + true + ) + ); + compilation.addRuntimeModule(chunk, new CssLoadingRuntimeModule(set)); + }; + compilation.hooks.runtimeRequirementInTree + .for(webpack.RuntimeGlobals.ensureChunkHandlers) + .tap(pluginName, handler); + compilation.hooks.runtimeRequirementInTree + .for(webpack.RuntimeGlobals.hmrDownloadUpdateHandlers) + .tap(pluginName, handler); } }); } diff --git a/test/cases/dependOn-multiple-files-per-entry/expected/webpack-5/common.js b/test/cases/dependOn-multiple-files-per-entry/expected/webpack-5/common.js index 589cd38f..3e43d6d5 100644 --- a/test/cases/dependOn-multiple-files-per-entry/expected/webpack-5/common.js +++ b/test/cases/dependOn-multiple-files-per-entry/expected/webpack-5/common.js @@ -103,13 +103,7 @@ __webpack_require__.r(__webpack_exports__); /******/ }; /******/ })(); /******/ -/******/ /* webpack/runtime/compat */ -/******/ -/******/ -/******/ // object to store loaded CSS chunks -/******/ var installedCssChunks = { -/******/ 0: 0 -/******/ };/* webpack/runtime/jsonp chunk loading */ +/******/ /* webpack/runtime/jsonp chunk loading */ /******/ (() => { /******/ // no baseURI /******/ diff --git a/test/cases/dependOn/expected/webpack-5/common.js b/test/cases/dependOn/expected/webpack-5/common.js index a1926b90..0b8c0134 100644 --- a/test/cases/dependOn/expected/webpack-5/common.js +++ b/test/cases/dependOn/expected/webpack-5/common.js @@ -86,13 +86,7 @@ __webpack_require__.r(__webpack_exports__); /******/ }; /******/ })(); /******/ -/******/ /* webpack/runtime/compat */ -/******/ -/******/ -/******/ // object to store loaded CSS chunks -/******/ var installedCssChunks = { -/******/ 0: 0 -/******/ };/* webpack/runtime/jsonp chunk loading */ +/******/ /* webpack/runtime/jsonp chunk loading */ /******/ (() => { /******/ // no baseURI /******/ diff --git a/test/cases/hmr/expected/webpack-5/main.js b/test/cases/hmr/expected/webpack-5/main.js index d45ee120..48dad557 100644 --- a/test/cases/hmr/expected/webpack-5/main.js +++ b/test/cases/hmr/expected/webpack-5/main.js @@ -1,4 +1,13 @@ /******/ (() => { // webpackBootstrap +/******/ var __webpack_modules__ = ([ +/* 0 */ +/***/ (() => { + +// extracted by mini-css-extract-plugin + +/***/ }) +/******/ ]); +/************************************************************************/ /******/ // The module cache /******/ var __webpack_module_cache__ = {}; /******/ @@ -16,16 +25,1066 @@ /******/ }; /******/ /******/ // Execute the module function -/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); +/******/ var execOptions = { id: moduleId, module: module, factory: __webpack_modules__[moduleId], require: __webpack_require__ }; +/******/ __webpack_require__.i.forEach(function(handler) { handler(execOptions); }); +/******/ module = execOptions.module; +/******/ execOptions.factory.call(module.exports, module, module.exports, execOptions.require); /******/ /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = __webpack_modules__; +/******/ +/******/ // expose the module cache +/******/ __webpack_require__.c = __webpack_module_cache__; +/******/ +/******/ // expose the module execution interceptor +/******/ __webpack_require__.i = []; +/******/ /************************************************************************/ -/******/ /************************************************************************/ -// extracted by mini-css-extract-plugin - if(false) { var cssReload; } - +/******/ /* webpack/runtime/get javascript update chunk filename */ +/******/ (() => { +/******/ // This function allow to reference all chunks +/******/ __webpack_require__.hu = (chunkId) => { +/******/ // return url for filenames based on template +/******/ return "" + chunkId + "." + __webpack_require__.h() + ".hot-update.js"; +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/get mini-css chunk filename */ +/******/ (() => { +/******/ // This function allow to reference all chunks +/******/ __webpack_require__.miniCssF = (chunkId) => { +/******/ // return url for filenames not based on template +/******/ if (chunkId === 0) return "main.css"; +/******/ // return url for filenames based on template +/******/ return undefined; +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/get update manifest filename */ +/******/ (() => { +/******/ __webpack_require__.hmrF = () => "" + __webpack_require__.h() + ".hot-update.json"; +/******/ })(); +/******/ +/******/ /* webpack/runtime/getFullHash */ +/******/ (() => { +/******/ __webpack_require__.h = () => "56250388fc166692d434" +/******/ })(); +/******/ +/******/ /* webpack/runtime/global */ +/******/ (() => { +/******/ __webpack_require__.g = (function() { +/******/ if (typeof globalThis === 'object') return globalThis; +/******/ try { +/******/ return this || new Function('return this')(); +/******/ } catch (e) { +/******/ if (typeof window === 'object') return window; +/******/ } +/******/ })(); +/******/ })(); +/******/ +/******/ /* webpack/runtime/hasOwnProperty shorthand */ +/******/ (() => { +/******/ __webpack_require__.o = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop) +/******/ })(); +/******/ +/******/ /* webpack/runtime/load script */ +/******/ (() => { +/******/ var inProgress = {}; +/******/ // data-webpack is not used as build has no uniqueName +/******/ // loadScript function to load a script via script tag +/******/ __webpack_require__.l = (url, done, key) => { +/******/ if(inProgress[url]) { inProgress[url].push(done); return; } +/******/ var script, needAttach; +/******/ if(key !== undefined) { +/******/ var scripts = document.getElementsByTagName("script"); +/******/ for(var i = 0; i < scripts.length; i++) { +/******/ var s = scripts[i]; +/******/ if(s.getAttribute("src") == url) { script = s; break; } +/******/ } +/******/ } +/******/ if(!script) { +/******/ needAttach = true; +/******/ script = document.createElement('script'); +/******/ +/******/ script.charset = 'utf-8'; +/******/ script.timeout = 120; +/******/ if (__webpack_require__.nc) { +/******/ script.setAttribute("nonce", __webpack_require__.nc); +/******/ } +/******/ +/******/ script.src = url; +/******/ } +/******/ inProgress[url] = [done]; +/******/ var onScriptComplete = (prev, event) => { +/******/ // avoid mem leaks in IE. +/******/ script.onerror = script.onload = null; +/******/ clearTimeout(timeout); +/******/ var doneFns = inProgress[url]; +/******/ delete inProgress[url]; +/******/ script.parentNode && script.parentNode.removeChild(script); +/******/ doneFns && doneFns.forEach((fn) => fn(event)); +/******/ if(prev) return prev(event); +/******/ } +/******/ ; +/******/ var timeout = setTimeout(onScriptComplete.bind(null, undefined, { type: 'timeout', target: script }), 120000); +/******/ script.onerror = onScriptComplete.bind(null, script.onerror); +/******/ script.onload = onScriptComplete.bind(null, script.onload); +/******/ needAttach && document.head.appendChild(script); +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/hot module replacement */ +/******/ (() => { +/******/ var currentModuleData = {}; +/******/ var installedModules = __webpack_require__.c; +/******/ +/******/ // module and require creation +/******/ var currentChildModule; +/******/ var currentParents = []; +/******/ +/******/ // status +/******/ var registeredStatusHandlers = []; +/******/ var currentStatus = "idle"; +/******/ +/******/ // while downloading +/******/ var blockingPromises; +/******/ +/******/ // The update info +/******/ var currentUpdateApplyHandlers; +/******/ var queuedInvalidatedModules; +/******/ +/******/ __webpack_require__.hmrD = currentModuleData; +/******/ +/******/ __webpack_require__.i.push(function (options) { +/******/ var module = options.module; +/******/ var require = createRequire(options.require, options.id); +/******/ module.hot = createModuleHotObject(options.id, module); +/******/ module.parents = currentParents; +/******/ module.children = []; +/******/ currentParents = []; +/******/ options.require = require; +/******/ }); +/******/ +/******/ __webpack_require__.hmrC = {}; +/******/ __webpack_require__.hmrI = {}; +/******/ +/******/ function createRequire(require, moduleId) { +/******/ var me = installedModules[moduleId]; +/******/ if (!me) return require; +/******/ var fn = function (request) { +/******/ if (me.hot.active) { +/******/ if (installedModules[request]) { +/******/ var parents = installedModules[request].parents; +/******/ if (parents.indexOf(moduleId) === -1) { +/******/ parents.push(moduleId); +/******/ } +/******/ } else { +/******/ currentParents = [moduleId]; +/******/ currentChildModule = request; +/******/ } +/******/ if (me.children.indexOf(request) === -1) { +/******/ me.children.push(request); +/******/ } +/******/ } else { +/******/ console.warn( +/******/ "[HMR] unexpected require(" + +/******/ request + +/******/ ") from disposed module " + +/******/ moduleId +/******/ ); +/******/ currentParents = []; +/******/ } +/******/ return require(request); +/******/ }; +/******/ var createPropertyDescriptor = function (name) { +/******/ return { +/******/ configurable: true, +/******/ enumerable: true, +/******/ get: function () { +/******/ return require[name]; +/******/ }, +/******/ set: function (value) { +/******/ require[name] = value; +/******/ } +/******/ }; +/******/ }; +/******/ for (var name in require) { +/******/ if (Object.prototype.hasOwnProperty.call(require, name) && name !== "e") { +/******/ Object.defineProperty(fn, name, createPropertyDescriptor(name)); +/******/ } +/******/ } +/******/ fn.e = function (chunkId) { +/******/ return trackBlockingPromise(require.e(chunkId)); +/******/ }; +/******/ return fn; +/******/ } +/******/ +/******/ function createModuleHotObject(moduleId, me) { +/******/ var hot = { +/******/ // private stuff +/******/ _acceptedDependencies: {}, +/******/ _declinedDependencies: {}, +/******/ _selfAccepted: false, +/******/ _selfDeclined: false, +/******/ _selfInvalidated: false, +/******/ _disposeHandlers: [], +/******/ _main: currentChildModule !== moduleId, +/******/ _requireSelf: function () { +/******/ currentParents = me.parents.slice(); +/******/ currentChildModule = moduleId; +/******/ __webpack_require__(moduleId); +/******/ }, +/******/ +/******/ // Module API +/******/ active: true, +/******/ accept: function (dep, callback) { +/******/ if (dep === undefined) hot._selfAccepted = true; +/******/ else if (typeof dep === "function") hot._selfAccepted = dep; +/******/ else if (typeof dep === "object" && dep !== null) +/******/ for (var i = 0; i < dep.length; i++) +/******/ hot._acceptedDependencies[dep[i]] = callback || function () {}; +/******/ else hot._acceptedDependencies[dep] = callback || function () {}; +/******/ }, +/******/ decline: function (dep) { +/******/ if (dep === undefined) hot._selfDeclined = true; +/******/ else if (typeof dep === "object" && dep !== null) +/******/ for (var i = 0; i < dep.length; i++) +/******/ hot._declinedDependencies[dep[i]] = true; +/******/ else hot._declinedDependencies[dep] = true; +/******/ }, +/******/ dispose: function (callback) { +/******/ hot._disposeHandlers.push(callback); +/******/ }, +/******/ addDisposeHandler: function (callback) { +/******/ hot._disposeHandlers.push(callback); +/******/ }, +/******/ removeDisposeHandler: function (callback) { +/******/ var idx = hot._disposeHandlers.indexOf(callback); +/******/ if (idx >= 0) hot._disposeHandlers.splice(idx, 1); +/******/ }, +/******/ invalidate: function () { +/******/ this._selfInvalidated = true; +/******/ switch (currentStatus) { +/******/ case "idle": +/******/ currentUpdateApplyHandlers = []; +/******/ Object.keys(__webpack_require__.hmrI).forEach(function (key) { +/******/ __webpack_require__.hmrI[key]( +/******/ moduleId, +/******/ currentUpdateApplyHandlers +/******/ ); +/******/ }); +/******/ setStatus("ready"); +/******/ break; +/******/ case "ready": +/******/ Object.keys(__webpack_require__.hmrI).forEach(function (key) { +/******/ __webpack_require__.hmrI[key]( +/******/ moduleId, +/******/ currentUpdateApplyHandlers +/******/ ); +/******/ }); +/******/ break; +/******/ case "prepare": +/******/ case "check": +/******/ case "dispose": +/******/ case "apply": +/******/ (queuedInvalidatedModules = queuedInvalidatedModules || []).push( +/******/ moduleId +/******/ ); +/******/ break; +/******/ default: +/******/ // ignore requests in error states +/******/ break; +/******/ } +/******/ }, +/******/ +/******/ // Management API +/******/ check: hotCheck, +/******/ apply: hotApply, +/******/ status: function (l) { +/******/ if (!l) return currentStatus; +/******/ registeredStatusHandlers.push(l); +/******/ }, +/******/ addStatusHandler: function (l) { +/******/ registeredStatusHandlers.push(l); +/******/ }, +/******/ removeStatusHandler: function (l) { +/******/ var idx = registeredStatusHandlers.indexOf(l); +/******/ if (idx >= 0) registeredStatusHandlers.splice(idx, 1); +/******/ }, +/******/ +/******/ //inherit from previous dispose call +/******/ data: currentModuleData[moduleId] +/******/ }; +/******/ currentChildModule = undefined; +/******/ return hot; +/******/ } +/******/ +/******/ function setStatus(newStatus) { +/******/ currentStatus = newStatus; +/******/ for (var i = 0; i < registeredStatusHandlers.length; i++) +/******/ registeredStatusHandlers[i].call(null, newStatus); +/******/ } +/******/ +/******/ function trackBlockingPromise(promise) { +/******/ switch (currentStatus) { +/******/ case "ready": +/******/ setStatus("prepare"); +/******/ blockingPromises.push(promise); +/******/ waitForBlockingPromises(function () { +/******/ setStatus("ready"); +/******/ }); +/******/ return promise; +/******/ case "prepare": +/******/ blockingPromises.push(promise); +/******/ return promise; +/******/ default: +/******/ return promise; +/******/ } +/******/ } +/******/ +/******/ function waitForBlockingPromises(fn) { +/******/ if (blockingPromises.length === 0) return fn(); +/******/ var blocker = blockingPromises; +/******/ blockingPromises = []; +/******/ return Promise.all(blocker).then(function () { +/******/ return waitForBlockingPromises(fn); +/******/ }); +/******/ } +/******/ +/******/ function hotCheck(applyOnUpdate) { +/******/ if (currentStatus !== "idle") { +/******/ throw new Error("check() is only allowed in idle status"); +/******/ } +/******/ setStatus("check"); +/******/ return __webpack_require__.hmrM().then(function (update) { +/******/ if (!update) { +/******/ setStatus(applyInvalidatedModules() ? "ready" : "idle"); +/******/ return null; +/******/ } +/******/ +/******/ setStatus("prepare"); +/******/ +/******/ var updatedModules = []; +/******/ blockingPromises = []; +/******/ currentUpdateApplyHandlers = []; +/******/ +/******/ return Promise.all( +/******/ Object.keys(__webpack_require__.hmrC).reduce(function ( +/******/ promises, +/******/ key +/******/ ) { +/******/ __webpack_require__.hmrC[key]( +/******/ update.c, +/******/ update.r, +/******/ update.m, +/******/ promises, +/******/ currentUpdateApplyHandlers, +/******/ updatedModules +/******/ ); +/******/ return promises; +/******/ }, +/******/ []) +/******/ ).then(function () { +/******/ return waitForBlockingPromises(function () { +/******/ if (applyOnUpdate) { +/******/ return internalApply(applyOnUpdate); +/******/ } else { +/******/ setStatus("ready"); +/******/ +/******/ return updatedModules; +/******/ } +/******/ }); +/******/ }); +/******/ }); +/******/ } +/******/ +/******/ function hotApply(options) { +/******/ if (currentStatus !== "ready") { +/******/ return Promise.resolve().then(function () { +/******/ throw new Error("apply() is only allowed in ready status"); +/******/ }); +/******/ } +/******/ return internalApply(options); +/******/ } +/******/ +/******/ function internalApply(options) { +/******/ options = options || {}; +/******/ +/******/ applyInvalidatedModules(); +/******/ +/******/ var results = currentUpdateApplyHandlers.map(function (handler) { +/******/ return handler(options); +/******/ }); +/******/ currentUpdateApplyHandlers = undefined; +/******/ +/******/ var errors = results +/******/ .map(function (r) { +/******/ return r.error; +/******/ }) +/******/ .filter(Boolean); +/******/ +/******/ if (errors.length > 0) { +/******/ setStatus("abort"); +/******/ return Promise.resolve().then(function () { +/******/ throw errors[0]; +/******/ }); +/******/ } +/******/ +/******/ // Now in "dispose" phase +/******/ setStatus("dispose"); +/******/ +/******/ results.forEach(function (result) { +/******/ if (result.dispose) result.dispose(); +/******/ }); +/******/ +/******/ // Now in "apply" phase +/******/ setStatus("apply"); +/******/ +/******/ var error; +/******/ var reportError = function (err) { +/******/ if (!error) error = err; +/******/ }; +/******/ +/******/ var outdatedModules = []; +/******/ results.forEach(function (result) { +/******/ if (result.apply) { +/******/ var modules = result.apply(reportError); +/******/ if (modules) { +/******/ for (var i = 0; i < modules.length; i++) { +/******/ outdatedModules.push(modules[i]); +/******/ } +/******/ } +/******/ } +/******/ }); +/******/ +/******/ // handle errors in accept handlers and self accepted module load +/******/ if (error) { +/******/ setStatus("fail"); +/******/ return Promise.resolve().then(function () { +/******/ throw error; +/******/ }); +/******/ } +/******/ +/******/ if (queuedInvalidatedModules) { +/******/ return internalApply(options).then(function (list) { +/******/ outdatedModules.forEach(function (moduleId) { +/******/ if (list.indexOf(moduleId) < 0) list.push(moduleId); +/******/ }); +/******/ return list; +/******/ }); +/******/ } +/******/ +/******/ setStatus("idle"); +/******/ return Promise.resolve(outdatedModules); +/******/ } +/******/ +/******/ function applyInvalidatedModules() { +/******/ if (queuedInvalidatedModules) { +/******/ if (!currentUpdateApplyHandlers) currentUpdateApplyHandlers = []; +/******/ Object.keys(__webpack_require__.hmrI).forEach(function (key) { +/******/ queuedInvalidatedModules.forEach(function (moduleId) { +/******/ __webpack_require__.hmrI[key]( +/******/ moduleId, +/******/ currentUpdateApplyHandlers +/******/ ); +/******/ }); +/******/ }); +/******/ queuedInvalidatedModules = undefined; +/******/ return true; +/******/ } +/******/ } +/******/ })(); +/******/ +/******/ /* webpack/runtime/publicPath */ +/******/ (() => { +/******/ var scriptUrl; +/******/ if (__webpack_require__.g.importScripts) scriptUrl = __webpack_require__.g.location + ""; +/******/ var document = __webpack_require__.g.document; +/******/ if (!scriptUrl && document) { +/******/ if (document.currentScript) +/******/ scriptUrl = document.currentScript.src +/******/ if (!scriptUrl) { +/******/ var scripts = document.getElementsByTagName("script"); +/******/ if(scripts.length) scriptUrl = scripts[scripts.length - 1].src +/******/ } +/******/ } +/******/ // When supporting browsers where an automatic publicPath is not supported you must specify an output.publicPath manually via configuration +/******/ // or pass an empty string ("") and set the __webpack_public_path__ variable from your code to use your own logic. +/******/ if (!scriptUrl) throw new Error("Automatic publicPath is not supported in this browser"); +/******/ scriptUrl = scriptUrl.replace(/#.*$/, "").replace(/\?.*$/, "").replace(/\/[^\/]+$/, "/"); +/******/ __webpack_require__.p = scriptUrl; +/******/ })(); +/******/ +/******/ /* webpack/runtime/css loading */ +/******/ (() => { +/******/ var createStylesheet = (fullhref, resolve, reject) => { +/******/ var linkTag = document.createElement("link"); +/******/ linkTag.rel = "stylesheet"; +/******/ linkTag.type = "text/css"; +/******/ linkTag.onload = resolve; +/******/ linkTag.onerror = function(event) { +/******/ var request = event && event.target && event.target.src || fullhref; +/******/ var err = new Error("Loading CSS chunk " + chunkId + " failed.\n(" + request + ")"); +/******/ err.code = "CSS_CHUNK_LOAD_FAILED"; +/******/ err.request = request; +/******/ linkTag.parentNode.removeChild(linkTag) +/******/ reject(err); +/******/ }; +/******/ linkTag.href = fullhref; +/******/ +/******/ var head = document.getElementsByTagName("head")[0]; +/******/ head.appendChild(linkTag); +/******/ return linkTag; +/******/ }; +/******/ var findStylesheet = (href, fullhref) => { +/******/ var existingLinkTags = document.getElementsByTagName("link"); +/******/ for(var i = 0; i < existingLinkTags.length; i++) { +/******/ var tag = existingLinkTags[i]; +/******/ var dataHref = tag.getAttribute("data-href") || tag.getAttribute("href"); +/******/ if(tag.rel === "stylesheet" && (dataHref === href || dataHref === fullhref)) return tag; +/******/ } +/******/ var existingStyleTags = document.getElementsByTagName("style"); +/******/ for(var i = 0; i < existingStyleTags.length; i++) { +/******/ var tag = existingStyleTags[i]; +/******/ var dataHref = tag.getAttribute("data-href"); +/******/ if(dataHref === href || dataHref === fullhref) return tag; +/******/ } +/******/ }; +/******/ var loadStylesheet = (chunkId) => { +/******/ return new Promise((resolve, reject) => { +/******/ var href = __webpack_require__.miniCssF(chunkId); +/******/ var fullhref = __webpack_require__.p + href; +/******/ if(findStylesheet(href, fullhref)) return resolve(); +/******/ createStylesheet(fullhref, resolve, reject); +/******/ }); +/******/ } +/******/ // no chunk loading +/******/ +/******/ var oldTags = []; +/******/ var newTags = []; +/******/ var applyHandler = (options) => { +/******/ return { dispose: () => { +/******/ for(var i = 0; i < oldTags.length; i++) { +/******/ var oldTag = oldTags[i]; +/******/ if(oldTag.parentNode) oldTag.parentNode.removeChild(oldTag); +/******/ } +/******/ oldTags.length = 0; +/******/ }, apply: () => { +/******/ for(var i = 0; i < newTags.length; i++) newTags[i].rel = "stylesheet"; +/******/ newTags.length = 0; +/******/ } }; +/******/ } +/******/ __webpack_require__.hmrC.miniCss = (chunkIds, removedChunks, removedModules, promises, applyHandlers, updatedModulesList) => { +/******/ applyHandlers.push(applyHandler); +/******/ chunkIds.forEach((chunkId) => { +/******/ var href = __webpack_require__.miniCssF(chunkId); +/******/ var fullhref = __webpack_require__.p + href; +/******/ const oldTag = findStylesheet(href, fullhref); +/******/ if(!oldTag) return; +/******/ promises.push(new Promise((resolve, reject) => { +/******/ var tag = createStylesheet(fullhref, () => { +/******/ tag.as = "style"; +/******/ tag.rel = "preload"; +/******/ resolve(); +/******/ }, reject); +/******/ oldTags.push(oldTag); +/******/ newTags.push(tag); +/******/ })); +/******/ }); +/******/ } +/******/ })(); +/******/ +/******/ /* webpack/runtime/jsonp chunk loading */ +/******/ (() => { +/******/ // no baseURI +/******/ +/******/ // object to store loaded and loading chunks +/******/ // undefined = chunk not loaded, null = chunk preloaded/prefetched +/******/ // Promise = chunk loading, 0 = chunk loaded +/******/ var installedChunks = { +/******/ 0: 0 +/******/ }; +/******/ +/******/ +/******/ // no chunk on demand loading +/******/ +/******/ // no prefetching +/******/ +/******/ // no preloaded +/******/ +/******/ var currentUpdatedModulesList; +/******/ var waitingUpdateResolves = {}; +/******/ function loadUpdateChunk(chunkId) { +/******/ return new Promise((resolve, reject) => { +/******/ waitingUpdateResolves[chunkId] = resolve; +/******/ // start update chunk loading +/******/ var url = __webpack_require__.p + __webpack_require__.hu(chunkId); +/******/ // create error before stack unwound to get useful stacktrace later +/******/ var error = new Error(); +/******/ var loadingEnded = (event) => { +/******/ if(waitingUpdateResolves[chunkId]) { +/******/ waitingUpdateResolves[chunkId] = undefined +/******/ var errorType = event && (event.type === 'load' ? 'missing' : event.type); +/******/ var realSrc = event && event.target && event.target.src; +/******/ error.message = 'Loading hot update chunk ' + chunkId + ' failed.\n(' + errorType + ': ' + realSrc + ')'; +/******/ error.name = 'ChunkLoadError'; +/******/ error.type = errorType; +/******/ error.request = realSrc; +/******/ reject(error); +/******/ } +/******/ }; +/******/ __webpack_require__.l(url, loadingEnded); +/******/ }); +/******/ } +/******/ +/******/ self["webpackHotUpdate"] = (chunkId, moreModules, runtime) => { +/******/ for(var moduleId in moreModules) { +/******/ if(__webpack_require__.o(moreModules, moduleId)) { +/******/ currentUpdate[moduleId] = moreModules[moduleId]; +/******/ if(currentUpdatedModulesList) currentUpdatedModulesList.push(moduleId); +/******/ } +/******/ } +/******/ if(runtime) currentUpdateRuntime.push(runtime); +/******/ if(waitingUpdateResolves[chunkId]) { +/******/ waitingUpdateResolves[chunkId](); +/******/ waitingUpdateResolves[chunkId] = undefined; +/******/ } +/******/ }; +/******/ +/******/ var currentUpdateChunks; +/******/ var currentUpdate; +/******/ var currentUpdateRemovedChunks; +/******/ var currentUpdateRuntime; +/******/ function applyHandler(options) { +/******/ if (__webpack_require__.f) delete __webpack_require__.f.jsonpHmr; +/******/ currentUpdateChunks = undefined; +/******/ function getAffectedModuleEffects(updateModuleId) { +/******/ var outdatedModules = [updateModuleId]; +/******/ var outdatedDependencies = {}; +/******/ +/******/ var queue = outdatedModules.map(function (id) { +/******/ return { +/******/ chain: [id], +/******/ id: id +/******/ }; +/******/ }); +/******/ while (queue.length > 0) { +/******/ var queueItem = queue.pop(); +/******/ var moduleId = queueItem.id; +/******/ var chain = queueItem.chain; +/******/ var module = __webpack_require__.c[moduleId]; +/******/ if ( +/******/ !module || +/******/ (module.hot._selfAccepted && !module.hot._selfInvalidated) +/******/ ) +/******/ continue; +/******/ if (module.hot._selfDeclined) { +/******/ return { +/******/ type: "self-declined", +/******/ chain: chain, +/******/ moduleId: moduleId +/******/ }; +/******/ } +/******/ if (module.hot._main) { +/******/ return { +/******/ type: "unaccepted", +/******/ chain: chain, +/******/ moduleId: moduleId +/******/ }; +/******/ } +/******/ for (var i = 0; i < module.parents.length; i++) { +/******/ var parentId = module.parents[i]; +/******/ var parent = __webpack_require__.c[parentId]; +/******/ if (!parent) continue; +/******/ if (parent.hot._declinedDependencies[moduleId]) { +/******/ return { +/******/ type: "declined", +/******/ chain: chain.concat([parentId]), +/******/ moduleId: moduleId, +/******/ parentId: parentId +/******/ }; +/******/ } +/******/ if (outdatedModules.indexOf(parentId) !== -1) continue; +/******/ if (parent.hot._acceptedDependencies[moduleId]) { +/******/ if (!outdatedDependencies[parentId]) +/******/ outdatedDependencies[parentId] = []; +/******/ addAllToSet(outdatedDependencies[parentId], [moduleId]); +/******/ continue; +/******/ } +/******/ delete outdatedDependencies[parentId]; +/******/ outdatedModules.push(parentId); +/******/ queue.push({ +/******/ chain: chain.concat([parentId]), +/******/ id: parentId +/******/ }); +/******/ } +/******/ } +/******/ +/******/ return { +/******/ type: "accepted", +/******/ moduleId: updateModuleId, +/******/ outdatedModules: outdatedModules, +/******/ outdatedDependencies: outdatedDependencies +/******/ }; +/******/ } +/******/ +/******/ function addAllToSet(a, b) { +/******/ for (var i = 0; i < b.length; i++) { +/******/ var item = b[i]; +/******/ if (a.indexOf(item) === -1) a.push(item); +/******/ } +/******/ } +/******/ +/******/ // at begin all updates modules are outdated +/******/ // the "outdated" status can propagate to parents if they don't accept the children +/******/ var outdatedDependencies = {}; +/******/ var outdatedModules = []; +/******/ var appliedUpdate = {}; +/******/ +/******/ var warnUnexpectedRequire = function warnUnexpectedRequire(module) { +/******/ console.warn( +/******/ "[HMR] unexpected require(" + module.id + ") to disposed module" +/******/ ); +/******/ }; +/******/ +/******/ for (var moduleId in currentUpdate) { +/******/ if (__webpack_require__.o(currentUpdate, moduleId)) { +/******/ var newModuleFactory = currentUpdate[moduleId]; +/******/ /** @type {TODO} */ +/******/ var result; +/******/ if (newModuleFactory) { +/******/ result = getAffectedModuleEffects(moduleId); +/******/ } else { +/******/ result = { +/******/ type: "disposed", +/******/ moduleId: moduleId +/******/ }; +/******/ } +/******/ /** @type {Error|false} */ +/******/ var abortError = false; +/******/ var doApply = false; +/******/ var doDispose = false; +/******/ var chainInfo = ""; +/******/ if (result.chain) { +/******/ chainInfo = "\nUpdate propagation: " + result.chain.join(" -> "); +/******/ } +/******/ switch (result.type) { +/******/ case "self-declined": +/******/ if (options.onDeclined) options.onDeclined(result); +/******/ if (!options.ignoreDeclined) +/******/ abortError = new Error( +/******/ "Aborted because of self decline: " + +/******/ result.moduleId + +/******/ chainInfo +/******/ ); +/******/ break; +/******/ case "declined": +/******/ if (options.onDeclined) options.onDeclined(result); +/******/ if (!options.ignoreDeclined) +/******/ abortError = new Error( +/******/ "Aborted because of declined dependency: " + +/******/ result.moduleId + +/******/ " in " + +/******/ result.parentId + +/******/ chainInfo +/******/ ); +/******/ break; +/******/ case "unaccepted": +/******/ if (options.onUnaccepted) options.onUnaccepted(result); +/******/ if (!options.ignoreUnaccepted) +/******/ abortError = new Error( +/******/ "Aborted because " + moduleId + " is not accepted" + chainInfo +/******/ ); +/******/ break; +/******/ case "accepted": +/******/ if (options.onAccepted) options.onAccepted(result); +/******/ doApply = true; +/******/ break; +/******/ case "disposed": +/******/ if (options.onDisposed) options.onDisposed(result); +/******/ doDispose = true; +/******/ break; +/******/ default: +/******/ throw new Error("Unexception type " + result.type); +/******/ } +/******/ if (abortError) { +/******/ return { +/******/ error: abortError +/******/ }; +/******/ } +/******/ if (doApply) { +/******/ appliedUpdate[moduleId] = newModuleFactory; +/******/ addAllToSet(outdatedModules, result.outdatedModules); +/******/ for (moduleId in result.outdatedDependencies) { +/******/ if (__webpack_require__.o(result.outdatedDependencies, moduleId)) { +/******/ if (!outdatedDependencies[moduleId]) +/******/ outdatedDependencies[moduleId] = []; +/******/ addAllToSet( +/******/ outdatedDependencies[moduleId], +/******/ result.outdatedDependencies[moduleId] +/******/ ); +/******/ } +/******/ } +/******/ } +/******/ if (doDispose) { +/******/ addAllToSet(outdatedModules, [result.moduleId]); +/******/ appliedUpdate[moduleId] = warnUnexpectedRequire; +/******/ } +/******/ } +/******/ } +/******/ currentUpdate = undefined; +/******/ +/******/ // Store self accepted outdated modules to require them later by the module system +/******/ var outdatedSelfAcceptedModules = []; +/******/ for (var j = 0; j < outdatedModules.length; j++) { +/******/ var outdatedModuleId = outdatedModules[j]; +/******/ if ( +/******/ __webpack_require__.c[outdatedModuleId] && +/******/ __webpack_require__.c[outdatedModuleId].hot._selfAccepted && +/******/ // removed self-accepted modules should not be required +/******/ appliedUpdate[outdatedModuleId] !== warnUnexpectedRequire && +/******/ // when called invalidate self-accepting is not possible +/******/ !__webpack_require__.c[outdatedModuleId].hot._selfInvalidated +/******/ ) { +/******/ outdatedSelfAcceptedModules.push({ +/******/ module: outdatedModuleId, +/******/ require: __webpack_require__.c[outdatedModuleId].hot._requireSelf, +/******/ errorHandler: __webpack_require__.c[outdatedModuleId].hot._selfAccepted +/******/ }); +/******/ } +/******/ } +/******/ +/******/ var moduleOutdatedDependencies; +/******/ +/******/ return { +/******/ dispose: function () { +/******/ currentUpdateRemovedChunks.forEach(function (chunkId) { +/******/ delete installedChunks[chunkId]; +/******/ }); +/******/ currentUpdateRemovedChunks = undefined; +/******/ +/******/ var idx; +/******/ var queue = outdatedModules.slice(); +/******/ while (queue.length > 0) { +/******/ var moduleId = queue.pop(); +/******/ var module = __webpack_require__.c[moduleId]; +/******/ if (!module) continue; +/******/ +/******/ var data = {}; +/******/ +/******/ // Call dispose handlers +/******/ var disposeHandlers = module.hot._disposeHandlers; +/******/ for (j = 0; j < disposeHandlers.length; j++) { +/******/ disposeHandlers[j].call(null, data); +/******/ } +/******/ __webpack_require__.hmrD[moduleId] = data; +/******/ +/******/ // disable module (this disables requires from this module) +/******/ module.hot.active = false; +/******/ +/******/ // remove module from cache +/******/ delete __webpack_require__.c[moduleId]; +/******/ +/******/ // when disposing there is no need to call dispose handler +/******/ delete outdatedDependencies[moduleId]; +/******/ +/******/ // remove "parents" references from all children +/******/ for (j = 0; j < module.children.length; j++) { +/******/ var child = __webpack_require__.c[module.children[j]]; +/******/ if (!child) continue; +/******/ idx = child.parents.indexOf(moduleId); +/******/ if (idx >= 0) { +/******/ child.parents.splice(idx, 1); +/******/ } +/******/ } +/******/ } +/******/ +/******/ // remove outdated dependency from module children +/******/ var dependency; +/******/ for (var outdatedModuleId in outdatedDependencies) { +/******/ if (__webpack_require__.o(outdatedDependencies, outdatedModuleId)) { +/******/ module = __webpack_require__.c[outdatedModuleId]; +/******/ if (module) { +/******/ moduleOutdatedDependencies = +/******/ outdatedDependencies[outdatedModuleId]; +/******/ for (j = 0; j < moduleOutdatedDependencies.length; j++) { +/******/ dependency = moduleOutdatedDependencies[j]; +/******/ idx = module.children.indexOf(dependency); +/******/ if (idx >= 0) module.children.splice(idx, 1); +/******/ } +/******/ } +/******/ } +/******/ } +/******/ }, +/******/ apply: function (reportError) { +/******/ // insert new code +/******/ for (var updateModuleId in appliedUpdate) { +/******/ if (__webpack_require__.o(appliedUpdate, updateModuleId)) { +/******/ __webpack_require__.m[updateModuleId] = appliedUpdate[updateModuleId]; +/******/ } +/******/ } +/******/ +/******/ // run new runtime modules +/******/ for (var i = 0; i < currentUpdateRuntime.length; i++) { +/******/ currentUpdateRuntime[i](__webpack_require__); +/******/ } +/******/ +/******/ // call accept handlers +/******/ for (var outdatedModuleId in outdatedDependencies) { +/******/ if (__webpack_require__.o(outdatedDependencies, outdatedModuleId)) { +/******/ var module = __webpack_require__.c[outdatedModuleId]; +/******/ if (module) { +/******/ moduleOutdatedDependencies = +/******/ outdatedDependencies[outdatedModuleId]; +/******/ var callbacks = []; +/******/ var dependenciesForCallbacks = []; +/******/ for (var j = 0; j < moduleOutdatedDependencies.length; j++) { +/******/ var dependency = moduleOutdatedDependencies[j]; +/******/ var acceptCallback = +/******/ module.hot._acceptedDependencies[dependency]; +/******/ if (acceptCallback) { +/******/ if (callbacks.indexOf(acceptCallback) !== -1) continue; +/******/ callbacks.push(acceptCallback); +/******/ dependenciesForCallbacks.push(dependency); +/******/ } +/******/ } +/******/ for (var k = 0; k < callbacks.length; k++) { +/******/ try { +/******/ callbacks[k].call(null, moduleOutdatedDependencies); +/******/ } catch (err) { +/******/ if (options.onErrored) { +/******/ options.onErrored({ +/******/ type: "accept-errored", +/******/ moduleId: outdatedModuleId, +/******/ dependencyId: dependenciesForCallbacks[k], +/******/ error: err +/******/ }); +/******/ } +/******/ if (!options.ignoreErrored) { +/******/ reportError(err); +/******/ } +/******/ } +/******/ } +/******/ } +/******/ } +/******/ } +/******/ +/******/ // Load self accepted modules +/******/ for (var o = 0; o < outdatedSelfAcceptedModules.length; o++) { +/******/ var item = outdatedSelfAcceptedModules[o]; +/******/ var moduleId = item.module; +/******/ try { +/******/ item.require(moduleId); +/******/ } catch (err) { +/******/ if (typeof item.errorHandler === "function") { +/******/ try { +/******/ item.errorHandler(err); +/******/ } catch (err2) { +/******/ if (options.onErrored) { +/******/ options.onErrored({ +/******/ type: "self-accept-error-handler-errored", +/******/ moduleId: moduleId, +/******/ error: err2, +/******/ originalError: err +/******/ }); +/******/ } +/******/ if (!options.ignoreErrored) { +/******/ reportError(err2); +/******/ } +/******/ reportError(err); +/******/ } +/******/ } else { +/******/ if (options.onErrored) { +/******/ options.onErrored({ +/******/ type: "self-accept-errored", +/******/ moduleId: moduleId, +/******/ error: err +/******/ }); +/******/ } +/******/ if (!options.ignoreErrored) { +/******/ reportError(err); +/******/ } +/******/ } +/******/ } +/******/ } +/******/ +/******/ return outdatedModules; +/******/ } +/******/ }; +/******/ } +/******/ __webpack_require__.hmrI.jsonp = function (moduleId, applyHandlers) { +/******/ if (!currentUpdate) { +/******/ currentUpdate = {}; +/******/ currentUpdateRuntime = []; +/******/ currentUpdateRemovedChunks = []; +/******/ applyHandlers.push(applyHandler); +/******/ } +/******/ if (!__webpack_require__.o(currentUpdate, moduleId)) { +/******/ currentUpdate[moduleId] = __webpack_require__.m[moduleId]; +/******/ } +/******/ }; +/******/ __webpack_require__.hmrC.jsonp = function ( +/******/ chunkIds, +/******/ removedChunks, +/******/ removedModules, +/******/ promises, +/******/ applyHandlers, +/******/ updatedModulesList +/******/ ) { +/******/ applyHandlers.push(applyHandler); +/******/ currentUpdateChunks = {}; +/******/ currentUpdateRemovedChunks = removedChunks; +/******/ currentUpdate = removedModules.reduce(function (obj, key) { +/******/ obj[key] = false; +/******/ return obj; +/******/ }, {}); +/******/ currentUpdateRuntime = []; +/******/ chunkIds.forEach(function (chunkId) { +/******/ if ( +/******/ __webpack_require__.o(installedChunks, chunkId) && +/******/ installedChunks[chunkId] !== undefined +/******/ ) { +/******/ promises.push(loadUpdateChunk(chunkId, updatedModulesList)); +/******/ currentUpdateChunks[chunkId] = true; +/******/ } +/******/ }); +/******/ if (__webpack_require__.f) { +/******/ __webpack_require__.f.jsonpHmr = function (chunkId, promises) { +/******/ if ( +/******/ currentUpdateChunks && +/******/ !__webpack_require__.o(currentUpdateChunks, chunkId) && +/******/ __webpack_require__.o(installedChunks, chunkId) && +/******/ installedChunks[chunkId] !== undefined +/******/ ) { +/******/ promises.push(loadUpdateChunk(chunkId)); +/******/ currentUpdateChunks[chunkId] = true; +/******/ } +/******/ }; +/******/ } +/******/ }; +/******/ +/******/ __webpack_require__.hmrM = () => { +/******/ if (typeof fetch === "undefined") throw new Error("No browser support: need fetch API"); +/******/ return fetch(__webpack_require__.p + __webpack_require__.hmrF()).then((response) => { +/******/ if(response.status === 404) return; // no update available +/******/ if(!response.ok) throw new Error("Failed to fetch update manifest " + response.statusText); +/******/ return response.json(); +/******/ }); +/******/ }; +/******/ +/******/ // no deferred startup +/******/ +/******/ // no jsonp function +/******/ })(); +/******/ +/************************************************************************/ +/******/ // module cache are used so entry inlining is disabled +/******/ // startup +/******/ // Load entry module +/******/ __webpack_require__(0); /******/ })() ; \ No newline at end of file diff --git a/test/cases/hmr/webpack.config.js b/test/cases/hmr/webpack.config.js index 0bd33f7d..59546433 100644 --- a/test/cases/hmr/webpack.config.js +++ b/test/cases/hmr/webpack.config.js @@ -1,3 +1,5 @@ +import { HotModuleReplacementPlugin, version } from 'webpack'; + import Self from '../../../src'; module.exports = { @@ -7,20 +9,31 @@ module.exports = { { test: /\.css$/, use: [ - { - loader: Self.loader, - options: { - hmr: true, - }, - }, + version.startsWith('4') + ? { + loader: Self.loader, + options: { + hmr: true, + }, + } + : { + loader: Self.loader, + }, 'css-loader', ], }, ], }, - plugins: [ - new Self({ - filename: '[name].css', - }), - ], + plugins: version.startsWith('4') + ? [ + new Self({ + filename: '[name].css', + }), + ] + : [ + new HotModuleReplacementPlugin(), + new Self({ + filename: '[name].css', + }), + ], }; diff --git a/test/enforce-esm.test.js b/test/enforce-esm.test.js index cc4afe51..18fa93a0 100644 --- a/test/enforce-esm.test.js +++ b/test/enforce-esm.test.js @@ -1,9 +1,5 @@ -import webpack from 'webpack'; - import { getCompiler, source, compile } from './helpers'; -const isWebpack5 = webpack.version[0] === '5'; - it('should enforce esm for empty module with options.esModule', async (done) => { const compiler = getCompiler( './esm.js', @@ -15,7 +11,10 @@ it('should enforce esm for empty module with options.esModule', async (done) => ); const stats = await compile(compiler); expect(stats.hasErrors()).toBe(false); - expect(stats.compilation.modules.length).toBe(isWebpack5 ? 4 : 2); + const { modules } = stats.toJson({ all: false, modules: true }); + expect( + modules.filter((m) => m.moduleType !== 'runtime' && !m.orphan).length + ).toBe(2); expect(source('./simple.css', stats)).toMatchInlineSnapshot(` "// extracted by mini-css-extract-plugin export {};" @@ -34,7 +33,10 @@ it('should keep empty module without options.esModule', async (done) => { ); const stats = await compile(compiler); expect(stats.hasErrors()).toBe(false); - expect(stats.compilation.modules.length).toBe(isWebpack5 ? 7 : 3); + const { modules } = stats.toJson({ all: false, modules: true }); + expect( + modules.filter((m) => m.moduleType !== 'runtime' && !m.orphan).length + ).toBe(3); expect(source('./simple.css', stats)).toMatchInlineSnapshot( `"// extracted by mini-css-extract-plugin"` ); From ec23c39f7a1fa415188b8d5dfd93689067102614 Mon Sep 17 00:00:00 2001 From: Tobias Koppers Date: Tue, 29 Sep 2020 21:41:42 +0200 Subject: [PATCH 5/5] fix(webpack4): restore webpack 4 support --- src/index.js | 10 ++++++---- test/enforce-esm.test.js | 8 ++++++-- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/index.js b/src/index.js index 86a16ead..7713cdcc 100644 --- a/src/index.js +++ b/src/index.js @@ -67,10 +67,12 @@ class MiniCssExtractPlugin { /** @param {import("webpack").Compiler} compiler */ apply(compiler) { - const { splitChunks } = compiler.options.optimization; - if (splitChunks) { - if (splitChunks.defaultSizeTypes.includes('...')) { - splitChunks.defaultSizeTypes.push(MODULE_TYPE); + if (!isWebpack4) { + const { splitChunks } = compiler.options.optimization; + if (splitChunks) { + if (splitChunks.defaultSizeTypes.includes('...')) { + splitChunks.defaultSizeTypes.push(MODULE_TYPE); + } } } compiler.hooks.thisCompilation.tap(pluginName, (compilation) => { diff --git a/test/enforce-esm.test.js b/test/enforce-esm.test.js index 18fa93a0..3587ffea 100644 --- a/test/enforce-esm.test.js +++ b/test/enforce-esm.test.js @@ -1,5 +1,9 @@ +import webpack from 'webpack'; + import { getCompiler, source, compile } from './helpers'; +const isWebpack4 = webpack.version[0] === '4'; + it('should enforce esm for empty module with options.esModule', async (done) => { const compiler = getCompiler( './esm.js', @@ -14,7 +18,7 @@ it('should enforce esm for empty module with options.esModule', async (done) => const { modules } = stats.toJson({ all: false, modules: true }); expect( modules.filter((m) => m.moduleType !== 'runtime' && !m.orphan).length - ).toBe(2); + ).toBe(isWebpack4 ? 1 : 2); expect(source('./simple.css', stats)).toMatchInlineSnapshot(` "// extracted by mini-css-extract-plugin export {};" @@ -36,7 +40,7 @@ it('should keep empty module without options.esModule', async (done) => { const { modules } = stats.toJson({ all: false, modules: true }); expect( modules.filter((m) => m.moduleType !== 'runtime' && !m.orphan).length - ).toBe(3); + ).toBe(isWebpack4 ? 2 : 3); expect(source('./simple.css', stats)).toMatchInlineSnapshot( `"// extracted by mini-css-extract-plugin"` );