hot module replacement with code splitting #26

This commit is contained in:
Tobias Koppers 2013-06-19 16:09:46 +02:00
parent 8b2301056d
commit d8fc8472f6
6 changed files with 136 additions and 80 deletions

View File

@ -1,22 +1,31 @@
if(module.hot) {
function check() {
module.hot.check(function(err, updatedModules) {
if(err) {
if(module.hot.status() in {abort:1,fail:1})
window.location.reload();
else
console.warn("Update failed: " + err);
return;
}
if(!updatedModules)
return console.log("No Update found.");
check();
if(!updatedModules || updatedModules.length === 0)
return console.log("Update is empty.");
console.log("Updated modules:");
updatedModules.forEach(function(moduleId) {
console.log(" - " + moduleId);
});
});
}
window.onmessage = function(event) {
if(event.data === "webpackHotUpdate" && module.hot.status() === "idle") {
module.hot.check(function(err, updatedModules) {
if(err) {
if(module.hot.status() in {abort:1,fail:1})
window.location.reload();
else
console.warn("Update failed: " + err);
return;
}
if(!updatedModules || updatedModules.length === 0)
return console.log("Update is empty.");
console.log("Updated modules:");
updatedModules.forEach(function(moduleId) {
console.log(" - " + moduleId);
});
});
check();
}
};
}

View File

@ -93,6 +93,12 @@ HotModuleReplacementPlugin.prototype.apply = function(compiler) {
var mainTemplate = compilation.mainTemplate;
compilation.mainTemplate = Object.create(mainTemplate);
compilation.mainTemplate.updateHash = function(hash) {
hash.update(compilation.records.hash + "");
mainTemplate.updateHash(hash);
};
compilation.mainTemplate.renderRequireFunctionForModule = function(hash, chunk, varModuleId) {
return "hotCreateRequire(" + varModuleId + ")";
};
@ -164,49 +170,53 @@ var hotInitCode = function() {
hotUpdate[moduleId] = false;
}
hotUpdateNewHash = newHash;
if(--hotWaitingFiles === 0) {
var outdatedDependencies = hotUpdateOutdatedDependencies = {};
var outdatedModules = hotUpdateOutdatedModules = Object.keys(hotUpdate).slice();
var queue = outdatedModules.slice();
while(queue.length > 0) {
var moduleId = queue.pop();
var module = installedModules[moduleId];
if(module.hot._selfAccepted)
continue;
if(module.hot._selfDeclined) {
hotSetStatus("abort");
return hotCallback(new Error("Aborted because of self decline: " + moduleId));
}
if(moduleId === 0) {
hotSetStatus("abort");
return hotCallback(new Error("Aborted because of bubbling"));
}
for(var i = 0; i < module.parents.length; i++) {
var parentId = module.parents[i];
var parent = installedModules[parentId];
if(parent.hot._declinedDependencies[moduleId]) {
hotSetStatus("abort");
return hotCallback(new Error("Aborted because of declined dependency: " + moduleId + " in " + parentId));
}
if(outdatedModules.indexOf(parentId) >= 0) continue;
if(parent.hot._acceptedDependencies[moduleId]) {
if(!outdatedDependencies[parentId]) outdatedDependencies[parentId] = [];
if(outdatedDependencies[parentId].indexOf(moduleId) >= 0) continue;
outdatedDependencies[parentId].push(moduleId);
continue;
}
delete outdatedDependencies[parentId];
outdatedModules.push(parentId);
queue.push(parentId);
}
}
if(--hotWaitingFiles === 0 && hotChunksLoading === 0) {
hotUpdateDownloaded();
}
}
hotSetStatus("ready");
if(hotApplyOnUpdate) {
hotApply(hotCallback);
} else {
hotCallback(null, outdatedModules);
function hotUpdateDownloaded() {
var outdatedDependencies = hotUpdateOutdatedDependencies = {};
var outdatedModules = hotUpdateOutdatedModules = Object.keys(hotUpdate).slice();
var queue = outdatedModules.slice();
while(queue.length > 0) {
var moduleId = queue.pop();
var module = installedModules[moduleId];
if(module.hot._selfAccepted)
continue;
if(module.hot._selfDeclined) {
hotSetStatus("abort");
return hotCallback(new Error("Aborted because of self decline: " + moduleId));
}
if(moduleId === 0) {
hotSetStatus("abort");
return hotCallback(new Error("Aborted because of bubbling"));
}
for(var i = 0; i < module.parents.length; i++) {
var parentId = module.parents[i];
var parent = installedModules[parentId];
if(parent.hot._declinedDependencies[moduleId]) {
hotSetStatus("abort");
return hotCallback(new Error("Aborted because of declined dependency: " + moduleId + " in " + parentId));
}
if(outdatedModules.indexOf(parentId) >= 0) continue;
if(parent.hot._acceptedDependencies[moduleId]) {
if(!outdatedDependencies[parentId]) outdatedDependencies[parentId] = [];
if(outdatedDependencies[parentId].indexOf(moduleId) >= 0) continue;
outdatedDependencies[parentId].push(moduleId);
continue;
}
delete outdatedDependencies[parentId];
outdatedModules.push(parentId);
queue.push(parentId);
}
}
hotSetStatus("ready");
if(hotApplyOnUpdate) {
hotApply(hotCallback);
} else {
hotCallback(null, outdatedModules);
}
}
@ -225,9 +235,19 @@ var hotInitCode = function() {
return $require$(request);
};
fn.e = function(chunkId, callback) {
if(hotStatus !== "idle") throw new Error("TODO: chunk loading while updating");
if(hotStatus === "ready") throw new Error("Cannot load chunks when update is ready");
hotChunksLoading++;
$require$.e(chunkId, function() {
callback(fn);
hotChunksLoading--;
if(hotStatus === "prepare") {
if(!hotWaitingFilesMap[chunkId]) {
hotDownloadUpdateChunk(chunkId);
}
if(hotChunksLoading === 0 && hotWaitingFiles === 0) {
hotUpdateDownloaded();
}
}
});
}
fn.cache = $require$.cache;
@ -298,6 +318,8 @@ var hotInitCode = function() {
}
var hotWaitingFiles = 0;
var hotChunksLoading = 0;
var hotWaitingFilesMap = {};
var hotCallback;
function hotCheck(callback) {
if(hotStatus !== "idle") throw new Error("check() is only allowed in idle status");
@ -318,27 +340,32 @@ var hotInitCode = function() {
if(request.status !== 200 && request.status !== 304) {
hotSetStatus("idle");
callback(null, []);
callback(null, null);
} else {
hotWaitingFilesMap = {};
hotSetStatus("prepare");
hotCallback = callback || function(err) { if(err) throw err };
hotUpdate = {};
var hash = hotCurrentHash;
/*foreachInstalledChunks*/ {
hotWaitingFiles++;
var head = document.getElementsByTagName('head')[0];
var script = document.createElement('script');
script.type = 'text/javascript';
script.charset = 'utf-8';
script.src = modules.c + $hotChunkFilename$;
head.appendChild(script);
hotDownloadUpdateChunk(chunkId);
}
}
};
}
function hotDownloadUpdateChunk(chunkId) {
hotWaitingFiles++;
var head = document.getElementsByTagName('head')[0];
var script = document.createElement('script');
script.type = 'text/javascript';
script.charset = 'utf-8';
script.src = modules.c + $hotChunkFilename$;
head.appendChild(script);
hotWaitingFilesMap[chunkId] = true;
}
var hotUpdate, hotUpdateOutdatedDependencies, hotUpdateOutdatedModules, hotUpdateNewHash;

View File

@ -0,0 +1,18 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
module.exports = function(cssCode) {
var styleElement = document.createElement("style");
styleElement.type = "text/css";
if (styleElement.styleSheet) {
styleElement.styleSheet.cssText = cssCode;
} else {
styleElement.appendChild(document.createTextNode(cssCode));
}
var head = document.getElementsByTagName("head")[0];
head.appendChild(styleElement);
return function() {
head.removeChild(styleElement);
};
}

View File

@ -38,6 +38,8 @@ window.onload = function() {
require("./style.js");
require("bundle!./style2.js");
if(module.hot) {
module.hot.accept("./html.js", function() {

View File

@ -1,22 +1,11 @@
// This file can update, because it accept itself.
// A dispose handler removes the old <style> element.
var cssCode = "body { background: green; }";
var addStyle = require("./addStyle");
var head = document.getElementsByTagName("head")[0];
var styleElement = document.createElement("style");
styleElement.type = "text/css";
if (styleElement.styleSheet) {
styleElement.styleSheet.cssText = cssCode;
} else {
styleElement.appendChild(document.createTextNode(cssCode));
}
head.appendChild(styleElement);
var dispose = addStyle("body { background: green; }");
if(module.hot) {
module.hot.accept();
module.hot.dispose(function() {
head.removeChild(styleElement);
});
module.hot.dispose(dispose);
}

View File

@ -0,0 +1,11 @@
// This file can update, because it accept itself.
// A dispose handler removes the old <style> element.
var addStyle = require("./addStyle");
var dispose = addStyle("body { color: blue; }");
if(module.hot) {
module.hot.accept();
module.hot.dispose(dispose);
}