From 360c155567b5e5f61ee0ecdab6d46c01a277b8c4 Mon Sep 17 00:00:00 2001 From: Clayton Watts Date: Wed, 8 Feb 2017 23:33:23 -0700 Subject: [PATCH] Support hot module replacement --- hotModuleReplacement.js | 19 +++++++++++++++++++ index.js | 18 ++++++++++++++++-- loader.js | 18 +++++++++++++++--- 3 files changed, 50 insertions(+), 5 deletions(-) create mode 100644 hotModuleReplacement.js diff --git a/hotModuleReplacement.js b/hotModuleReplacement.js new file mode 100644 index 00000000..4efff29d --- /dev/null +++ b/hotModuleReplacement.js @@ -0,0 +1,19 @@ +module.exports = function(compilationHash, publicPath, outputFilename) { + if (document) { + var origin = document.location.protocol + '//' + document.location.hostname + (document.location.port ? ':' + document.location.port: ''); + var styleSheets = document.getElementsByTagName('link'); + for (var i = 0; i < styleSheets.length; i++) { + if (styleSheets[i].href) { + var hrefUrl = styleSheets[i].href.split('?'); + var href = hrefUrl[0]; + var hash = hrefUrl[1]; + if (hash !== compilationHash && href === origin + publicPath + outputFilename) { + var url = href + '?' + compilationHash; + styleSheets[i].href = url; + console.log('[HMR]', 'Reload css: ', url); + break; + } + } + } + } +} diff --git a/index.js b/index.js index e6e12b23..c6d9d927 100644 --- a/index.js +++ b/index.js @@ -22,6 +22,13 @@ function ExtractTextPluginCompilation() { } ExtractTextPlugin.prototype.mergeNonInitialChunks = function(chunk, intoChunk, checkedChunks) { + if (chunk.chunks) { + // Fix error when hot module replacement used with CommonsChunkPlugin + chunk.chunks = chunk.chunks.filter(function(c) { + return typeof c !== 'undefined'; + }) + } + if(!intoChunk) { checkedChunks = []; chunk.chunks.forEach(function(c) { @@ -313,7 +320,8 @@ ExtractTextPlugin.prototype.apply = function(compiler) { callback(); }.bind(this)); }.bind(this)); - compilation.plugin("additional-assets", function(callback) { + compilation.plugin("before-chunk-assets", function() { + // This appears to be the latest hook where the %%extracted-file%% and hash replacements work on initial load. Any later and the contents of modules appears to be sealed and changes don't have any effect until the next hot update. extractedChunks.forEach(function(extractedChunk) { if(extractedChunk.modules.length) { extractedChunk.modules.sort(function(a, b) { @@ -336,9 +344,15 @@ ExtractTextPlugin.prototype.apply = function(compiler) { compilation.assets[file] = source; chunk.files.push(file); + + // Hot module replacement + extractedChunk.modules.forEach(function(module){ + var originalModule = module.getOriginalModule(); + originalModule._source._value = originalModule._source._value.replace('%%extracted-file%%', file); + originalModule._source._value = originalModule._source._value.replace('%%extracted-hash%%', compilation.hash); + }); } }, this); - callback(); }.bind(this)); }.bind(this)); }; diff --git a/loader.js b/loader.js index 057763c2..68497f42 100644 --- a/loader.js +++ b/loader.js @@ -10,11 +10,13 @@ var LibraryTemplatePlugin = require("webpack/lib/LibraryTemplatePlugin"); var SingleEntryPlugin = require("webpack/lib/SingleEntryPlugin"); var LimitChunkCountPlugin = require("webpack/lib/optimize/LimitChunkCountPlugin"); +var extractTextPluginHmrRuntime = require.resolve("./hotModuleReplacement.js"); var NS = fs.realpathSync(__dirname); module.exports = function(source) { if(this.cacheable) this.cacheable(); - return source; + // Even though this gets overwritten if extract+remove are true, without it, the runtime doesn't get added to the chunk + return `if (module.hot) { require('${extractTextPluginHmrRuntime}'); }\n${source}`; }; module.exports.pitch = function(request) { @@ -120,8 +122,18 @@ module.exports.pitch = function(request) { }); }); this[NS](text, query); - if(text.locals && typeof resultSource !== "undefined") { - resultSource += "\nmodule.exports = " + JSON.stringify(text.locals) + ";"; + if(typeof resultSource !== "undefined") { + if (text.locals) { + resultSource += "\nmodule.exports = " + JSON.stringify(text.locals) + ";"; + } + // module.hot.data is undefined on initial load, and an object in hot updates + resultSource += ` +if (module.hot) { + module.hot.accept(); + if (module.hot.data) { + require('${extractTextPluginHmrRuntime}')('%%extracted-hash%%','${publicPath}','%%extracted-file%%'); + } +}`; } } catch(e) { return callback(e);