added hot module replacement, fixes #26
This commit is contained in:
parent
f944553c77
commit
8b2301056d
|
@ -35,6 +35,8 @@ Take a look at the [`examples`](https://github.com/webpack/webpack/tree/master/e
|
|||
* usable as [grunt plugin](https://github.com/webpack/grunt-webpack)
|
||||
* browser replacements
|
||||
* comes with browser replacements for some node.js modules
|
||||
* [Hot Module Replacement](https://github.com/webpack/docs/wiki/hot-code-replacement)
|
||||
* install updates without full page refresh
|
||||
* see also
|
||||
* [webpack-dev-middleware](https://github.com/webpack/webpack-dev-middleware)
|
||||
* [webpack-dev-server](https://github.com/webpack/webpack-dev-server)
|
||||
|
|
|
@ -43,6 +43,8 @@ module.exports = function(optimist) {
|
|||
|
||||
.boolean("watch").alias("watch", "w").describe("watch")
|
||||
|
||||
.boolean("hot").alias("hot", "h").describe("hot")
|
||||
|
||||
.boolean("debug").alias("debug", "d").describe("debug")
|
||||
|
||||
.string("devtool").describe("devtool")
|
||||
|
|
|
@ -207,6 +207,7 @@ module.exports = function(optimist, argv, convertOptions) {
|
|||
options.watchDelay = value;
|
||||
});
|
||||
|
||||
mapArgToBoolean("hot", "hot");
|
||||
mapArgToBoolean("debug", "debug");
|
||||
|
||||
ifBooleanArg("progress", function() {
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
if(module.hot) {
|
||||
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);
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
if(module.hot) {
|
||||
var hotPollInterval = +(__resourceQuery.substr(1)) || (10*60*1000);
|
||||
setInterval(function() {
|
||||
if(module.hot.status() === "idle") {
|
||||
module.hot.check(function(err, updatedModules) {
|
||||
if(err) {
|
||||
console.warn("Update failed: " + err);
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
}, hotPollInterval);
|
||||
}
|
|
@ -21,6 +21,7 @@ function Compilation(compiler) {
|
|||
this.compiler = compiler;
|
||||
this.mainTemplate = compiler.mainTemplate;
|
||||
this.chunkTemplate = compiler.chunkTemplate;
|
||||
this.hotUpdateChunkTemplate = compiler.hotUpdateChunkTemplate;
|
||||
this.moduleTemplate = compiler.moduleTemplate;
|
||||
this.resolvers = compiler.resolvers;
|
||||
this.inputFileSystem = compiler.inputFileSystem;
|
||||
|
@ -373,6 +374,7 @@ Compilation.prototype.seal = function seal(callback) {
|
|||
|
||||
this.sortItems();
|
||||
this.createChunkAssets();
|
||||
this.applyPlugins("after-chunk-assets", this.chunks);
|
||||
this.summarizeDependencies();
|
||||
this.applyPlugins("record", this, this.records);
|
||||
|
||||
|
|
|
@ -0,0 +1,426 @@
|
|||
/*
|
||||
MIT License http://www.opensource.org/licenses/mit-license.php
|
||||
Author Tobias Koppers @sokra
|
||||
*/
|
||||
var Template = require("./Template");
|
||||
var BasicEvaluatedExpression = require("./BasicEvaluatedExpression");
|
||||
var ModuleHotAcceptDependency = require("./dependencies/ModuleHotAcceptDependency");
|
||||
var ModuleHotDeclineDependency = require("./dependencies/ModuleHotDeclineDependency");
|
||||
|
||||
function HotModuleReplacementPlugin(outputOptions) {
|
||||
this.outputOptions = outputOptions;
|
||||
}
|
||||
module.exports = HotModuleReplacementPlugin;
|
||||
|
||||
HotModuleReplacementPlugin.prototype.apply = function(compiler) {
|
||||
var hotUpdateChunkFilename = this.outputOptions.hotUpdateChunkFilename || "[id].[hash].hot-update.js";
|
||||
var hotUpdateFunction = this.outputOptions.hotUpdateFunction || ("webpackHotUpdate" + (this.outputOptions.library || ""));
|
||||
compiler.plugin("compilation", function(compilation, params) {
|
||||
var normalModuleFactory = params.normalModuleFactory;
|
||||
var contextModuleFactory = params.contextModuleFactory;
|
||||
|
||||
compilation.dependencyFactories.set(ModuleHotAcceptDependency, normalModuleFactory);
|
||||
compilation.dependencyTemplates.set(ModuleHotAcceptDependency, new ModuleHotAcceptDependency.Template());
|
||||
|
||||
compilation.dependencyFactories.set(ModuleHotDeclineDependency, normalModuleFactory);
|
||||
compilation.dependencyTemplates.set(ModuleHotDeclineDependency, new ModuleHotDeclineDependency.Template());
|
||||
|
||||
var hotUpdateChunkTemplate = compiler.hotUpdateChunkTemplate;
|
||||
compilation.plugin("record", function(compilation, records) {
|
||||
if(records.hash === this.hash) return;
|
||||
records.hash = compilation.hash;
|
||||
records.moduleHashs = {};
|
||||
this.modules.forEach(function(module) {
|
||||
var identifier = module.identifier();
|
||||
var hash = new (require("crypto")).Hash("md5");
|
||||
module.updateHash(hash);
|
||||
records.moduleHashs[identifier] = hash.digest("hex");
|
||||
});
|
||||
records.chunkHashs = {};
|
||||
this.chunks.forEach(function(chunk) {
|
||||
records.chunkHashs[chunk.id] = chunk.hash;
|
||||
});
|
||||
records.chunkModuleIds = {};
|
||||
this.chunks.forEach(function(chunk) {
|
||||
records.chunkModuleIds[chunk.id] = chunk.modules.map(function(m) { return m.id; });
|
||||
});
|
||||
});
|
||||
compilation.plugin("after-chunk-assets", function() {
|
||||
var records = this.records;
|
||||
if(records.hash === this.hash) return;
|
||||
if(!records.moduleHashs || !records.chunkHashs || !records.chunkModuleIds) return;
|
||||
var moduleHashs = {};
|
||||
this.modules.forEach(function(module) {
|
||||
var identifier = module.identifier();
|
||||
var hash = new (require("crypto")).Hash("md5");
|
||||
module.updateHash(hash);
|
||||
hash = hash.digest("hex");
|
||||
module.hotUpdate = records.moduleHashs[identifier] !== hash;
|
||||
});
|
||||
Object.keys(records.chunkHashs).forEach(function(chunkId) {
|
||||
chunkId = +chunkId;
|
||||
var newModules = [];
|
||||
var removedModules = records.chunkModuleIds[chunkId].slice();
|
||||
var currentChunk = this.chunks.filter(function(chunk) {
|
||||
return chunk.id === chunkId;
|
||||
})[0];
|
||||
if(currentChunk) {
|
||||
currentChunk.modules.forEach(function(module) {
|
||||
var idx = removedModules.indexOf(module.id);
|
||||
if(idx >= 0)
|
||||
removedModules.splice(idx, 1);
|
||||
if(!module.hotUpdate) return;
|
||||
newModules.push(module);
|
||||
});
|
||||
}
|
||||
newModules = removedModules.concat(newModules);
|
||||
var source = hotUpdateChunkTemplate.render(chunkId, newModules, this.hash, this.moduleTemplate, this.dependencyTemplates);
|
||||
var filename = hotUpdateChunkFilename
|
||||
.replace(Template.REGEXP_HASH, records.hash)
|
||||
.replace(Template.REGEXP_CHUNKHASH, records.chunkHashs[chunkId])
|
||||
.replace(Template.REGEXP_ID, chunkId);
|
||||
this.assets[filename] = source;
|
||||
if(currentChunk) {
|
||||
currentChunk.files.push(filename);
|
||||
this.applyPlugins("chunk-asset", currentChunk, filename);
|
||||
} else if(this.chunks[0]) {
|
||||
// TODO: good place for this? (minimizing is only on chunk assets)
|
||||
this.chunks[0].files.push(filename);
|
||||
this.applyPlugins("chunk-asset", this.chunks[0], filename);
|
||||
}
|
||||
}, this);
|
||||
});
|
||||
|
||||
var mainTemplate = compilation.mainTemplate;
|
||||
compilation.mainTemplate = Object.create(mainTemplate);
|
||||
compilation.mainTemplate.renderRequireFunctionForModule = function(hash, chunk, varModuleId) {
|
||||
return "hotCreateRequire(" + varModuleId + ")";
|
||||
};
|
||||
|
||||
compilation.mainTemplate.renderInit = function(hash, chunk) {
|
||||
var buf = mainTemplate.renderInit(hash, chunk);
|
||||
var currentHotUpdateChunkFilename = JSON.stringify(hotUpdateChunkFilename)
|
||||
.replace(Template.REGEXP_HASH, "\" + " + this.renderCurrentHashCode(hash) + " + \"")
|
||||
.replace(Template.REGEXP_ID, "\" + chunkId + \"");
|
||||
buf.push("this[" + JSON.stringify(hotUpdateFunction) + "] = " +
|
||||
(hotInitCode
|
||||
.replace(/\$require\$/g, this.requireFn)
|
||||
.replace(/\$hotChunkFilename\$/g, currentHotUpdateChunkFilename)
|
||||
.replace(/\$hash\$/g, JSON.stringify(hash))
|
||||
.replace(/\/\*foreachInstalledChunks\*\//g, chunk.chunks.length > 0 ? "for(var chunkId in installedChunks)" : "var chunkId = 0;")));
|
||||
return buf;
|
||||
};
|
||||
|
||||
compilation.mainTemplate.renderCurrentHashCode = function(hash) {
|
||||
return "hotCurrentHash";
|
||||
};
|
||||
|
||||
compilation.mainTemplate.renderModule = function(hash, chunk, varModuleId) {
|
||||
var buf = mainTemplate.renderModule(hash, chunk, varModuleId);
|
||||
buf.push(buf.pop() + ",");
|
||||
buf.push("hot: hotCreateModule(" + varModuleId + "),");
|
||||
buf.push("parents: [hotCurrentParent],");
|
||||
buf.push("children: []");
|
||||
return buf;
|
||||
};
|
||||
|
||||
});
|
||||
compiler.parser.plugin("evaluate Identifier module.hot", function(expr) {
|
||||
return new BasicEvaluatedExpression().setBoolean(true).setRange(expr.range);
|
||||
});
|
||||
compiler.parser.plugin("call module.hot.accept", function(expr) {
|
||||
if(expr.arguments.length > 1) {
|
||||
var param = this.evaluateExpression(expr.arguments[0]);
|
||||
if(param.isString()) {
|
||||
var dep = new ModuleHotAcceptDependency(param.string, param.range);
|
||||
dep.optional = true;
|
||||
this.state.module.addDependency(dep);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
compiler.parser.plugin("call module.hot.decline", function(expr) {
|
||||
if(expr.arguments.length > 1) {
|
||||
var param = this.evaluateExpression(expr.arguments[0]);
|
||||
if(param.isString()) {
|
||||
var dep = new ModuleHotDeclineDependency(param.string, param.range);
|
||||
dep.optional = true;
|
||||
this.state.module.addDependency(dep);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
compiler.parser.plugin("expression module.hot", function() {
|
||||
return true;
|
||||
});
|
||||
};
|
||||
|
||||
var hotInitCode = function() {
|
||||
function webpackHotUpdateCallback(newHash, chunkId, moreModules) {
|
||||
for(var moduleId in moreModules) {
|
||||
if(moreModules[moduleId])
|
||||
hotUpdate[moduleId] = moreModules[moduleId];
|
||||
else if(!hotUpdate[moduleId])
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
hotSetStatus("ready");
|
||||
if(hotApplyOnUpdate) {
|
||||
hotApply(hotCallback);
|
||||
} else {
|
||||
hotCallback(null, outdatedModules);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var hotApplyOnUpdate = true;
|
||||
var hotCurrentHash = $hash$;
|
||||
var hotCurrentParent = 0;
|
||||
|
||||
function hotCreateRequire(moduleId) {
|
||||
var me = installedModules[moduleId];
|
||||
var fn = function(request) {
|
||||
if(installedModules[request] && installedModules[request].parents.indexOf(moduleId) < 0)
|
||||
installedModules[request].parents.push(moduleId);
|
||||
if(me && me.children.indexOf(request) < 0)
|
||||
me.children.push(request);
|
||||
hotCurrentParent = moduleId;
|
||||
return $require$(request);
|
||||
};
|
||||
fn.e = function(chunkId, callback) {
|
||||
if(hotStatus !== "idle") throw new Error("TODO: chunk loading while updating");
|
||||
$require$.e(chunkId, function() {
|
||||
callback(fn);
|
||||
});
|
||||
}
|
||||
fn.cache = $require$.cache;
|
||||
fn.modules = $require$.modules;
|
||||
return fn;
|
||||
}
|
||||
|
||||
function hotCreateModule(moduleId) {
|
||||
var hot = {
|
||||
// private stuff
|
||||
_acceptedDependencies: {},
|
||||
_declinedDependencies: {},
|
||||
_selfAccepted: false,
|
||||
_selfDeclined: false,
|
||||
_disposeHandlers: [],
|
||||
|
||||
// Module API
|
||||
accept: function(dep, callback) {
|
||||
if(typeof dep === "undefined")
|
||||
hot._selfAccepted = true;
|
||||
else if(typeof dep === "number")
|
||||
hot._acceptedDependencies[dep] = callback;
|
||||
else for(var i = 0; i < dep.length; i++)
|
||||
hot._acceptedDependencies[dep[i]] = callback;
|
||||
},
|
||||
decline: function(dep) {
|
||||
if(typeof dep === "undefined")
|
||||
hot._selfDeclined = true;
|
||||
else if(typeof dep === "number")
|
||||
hot._declinedDependencies[dep] = true;
|
||||
else for(var i = 0; i < dep.length; i++)
|
||||
hot._declinedDependencies[dep[i]] = 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);
|
||||
},
|
||||
|
||||
// Management API
|
||||
check: hotCheck,
|
||||
apply: hotApply,
|
||||
status: function(l) {
|
||||
if(!l) return hotStatus;
|
||||
hotStatusHandlers.push(l);
|
||||
},
|
||||
addStatusHandler: function(l) {
|
||||
hotStatusHandlers.push(l);
|
||||
},
|
||||
removeDisposeHandler: function(l) {
|
||||
var idx = hotStatusHandlers.indexOf(l);
|
||||
if(idx >= 0) hotStatusHandlers.splice(idx, 1);
|
||||
}
|
||||
};
|
||||
return hot;
|
||||
}
|
||||
|
||||
var hotStatusHandlers = [];
|
||||
var hotStatus = "idle";
|
||||
function hotSetStatus(newStatus) {
|
||||
hotStatus = newStatus;
|
||||
// TODO notify listeners
|
||||
}
|
||||
|
||||
var hotWaitingFiles = 0;
|
||||
var hotCallback;
|
||||
function hotCheck(callback) {
|
||||
if(hotStatus !== "idle") throw new Error("check() is only allowed in idle status");
|
||||
if(typeof XMLHttpRequest === "undefined" || !Array.prototype.forEach || !Object.keys)
|
||||
return callback(new Error("No browser support"));
|
||||
hotSetStatus("check");
|
||||
|
||||
try {
|
||||
var request = new XMLHttpRequest();
|
||||
var chunkId = 0;
|
||||
request.open("GET", modules.c + $hotChunkFilename$, true);
|
||||
request.send(null);
|
||||
} catch(err) {
|
||||
return callback(err);
|
||||
}
|
||||
request.onreadystatechange = function() {
|
||||
if(request.readyState !== 4) return;
|
||||
if(request.status !== 200 && request.status !== 304) {
|
||||
|
||||
hotSetStatus("idle");
|
||||
callback(null, []);
|
||||
|
||||
} else {
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
var hotUpdate, hotUpdateOutdatedDependencies, hotUpdateOutdatedModules, hotUpdateNewHash;
|
||||
|
||||
function hotApply(callback) {
|
||||
var outdatedModules = hotUpdateOutdatedModules;
|
||||
var outdatedDependencies = hotUpdateOutdatedDependencies;
|
||||
var outdatedSelfAcceptedModules = outdatedModules.filter(function(moduleId) {
|
||||
return installedModules[moduleId].hot._selfAccepted;
|
||||
});
|
||||
|
||||
hotSetStatus("dispose");
|
||||
var oldModulesData = {};
|
||||
outdatedModules.forEach(function(moduleId) {
|
||||
var data = {};
|
||||
var module = installedModules[moduleId];
|
||||
module.hot._disposeHandlers.forEach(function(cb) {
|
||||
cb(data);
|
||||
});
|
||||
oldModulesData[moduleId] = data;
|
||||
return
|
||||
}, this);
|
||||
outdatedModules.forEach(function(moduleId) {
|
||||
var module = installedModules[moduleId];
|
||||
delete installedModules[moduleId];
|
||||
module.children.forEach(function(child) {
|
||||
child = installedModules[child];
|
||||
if(!child) return;
|
||||
var idx = child.parents.indexOf(moduleId);
|
||||
if(idx >= 0) child.parents.splice(idx, 1);
|
||||
}, this);
|
||||
});
|
||||
Object.keys(outdatedDependencies).forEach(function(moduleId) {
|
||||
var module = installedModules[moduleId];
|
||||
var moduleOutdatedDependencies = outdatedDependencies[moduleId];
|
||||
moduleOutdatedDependencies.forEach(function(dependency) {
|
||||
var idx = module.children.indexOf(dependency);
|
||||
if(idx >= 0) module.children.splice(idx, 1);
|
||||
});
|
||||
});
|
||||
|
||||
hotSetStatus("apply");
|
||||
// insert new code
|
||||
Object.keys(hotUpdate).forEach(function(moduleId) {
|
||||
var fn = hotUpdate[moduleId];
|
||||
if(fn) modules[moduleId] = fn;
|
||||
else delete modules[moduleId];
|
||||
});
|
||||
|
||||
// call accept handlers
|
||||
var error = null;
|
||||
Object.keys(outdatedDependencies).forEach(function(moduleId) {
|
||||
var module = installedModules[moduleId];
|
||||
var moduleOutdatedDependencies = outdatedDependencies[moduleId];
|
||||
var callbacks = [];
|
||||
moduleOutdatedDependencies.forEach(function(dependency) {
|
||||
var cb = module.hot._acceptedDependencies[dependency];
|
||||
if(callbacks.indexOf(cb) >= 0) return;
|
||||
callbacks.push(cb);
|
||||
});
|
||||
callbacks.forEach(function(cb) {
|
||||
try {
|
||||
cb(outdatedDependencies);
|
||||
} catch(err) {
|
||||
if(!error)
|
||||
error = err;
|
||||
}
|
||||
});
|
||||
});
|
||||
if(error) {
|
||||
hotSetStatus("fail");
|
||||
return callback(error);
|
||||
}
|
||||
|
||||
// Load self accepted
|
||||
outdatedSelfAcceptedModules.forEach(function updateSelfAcceptedModules(moduleId) {
|
||||
hotCurrentParent = moduleId;
|
||||
$require$(moduleId);
|
||||
});
|
||||
|
||||
hotCurrentHash = hotUpdateNewHash;
|
||||
|
||||
hotSetStatus("idle");
|
||||
callback(null, outdatedModules);
|
||||
}
|
||||
}.toString().replace(/^function\s?\(\)\s?\{\n?|\n?\}$/g, "").replace(/^\t/mg, "");
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
MIT License http://www.opensource.org/licenses/mit-license.php
|
||||
Author Tobias Koppers @sokra
|
||||
*/
|
||||
var ConcatSource = require("webpack-core/lib/ConcatSource");
|
||||
var Template = require("./Template");
|
||||
|
||||
function HotUpdateChunkTemplate(outputOptions) {
|
||||
Template.call(this, outputOptions);
|
||||
}
|
||||
|
||||
module.exports = HotUpdateChunkTemplate;
|
||||
|
||||
HotUpdateChunkTemplate.prototype = Object.create(Template.prototype);
|
||||
HotUpdateChunkTemplate.prototype.render = function(id, modules, hash, moduleTemplate, dependencyTemplates) {
|
||||
var source = new ConcatSource();
|
||||
source.add(this.asString(this.renderHeader(id, modules, hash)));
|
||||
modules.forEach(function(module, idx) {
|
||||
if(idx != 0) source.add(",\n");
|
||||
if(typeof module === "number") {
|
||||
source.add("\n/***/ " + module + ": false");
|
||||
} else {
|
||||
source.add("\n/***/ " + module.id + ":\n");
|
||||
source.add(moduleTemplate.render(module, dependencyTemplates));
|
||||
}
|
||||
});
|
||||
source.add("\n\n");
|
||||
source.add(this.asString(this.renderFooter(id, modules, hash)));
|
||||
return source;
|
||||
};
|
||||
|
||||
HotUpdateChunkTemplate.prototype.renderHeader = function(id, modules, hash) {
|
||||
return ["{\n"];
|
||||
};
|
||||
|
||||
HotUpdateChunkTemplate.prototype.renderFooter = function(id, modules, hash) {
|
||||
return ["}"];
|
||||
};
|
||||
|
||||
HotUpdateChunkTemplate.prototype.updateHash = function(hash) {
|
||||
hash.update("HotUpdateChunkTemplate");
|
||||
hash.update("1");
|
||||
};
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
MIT License http://www.opensource.org/licenses/mit-license.php
|
||||
Author Tobias Koppers @sokra
|
||||
*/
|
||||
var HotUpdateChunkTemplate = require("./HotUpdateChunkTemplate");
|
||||
|
||||
function JsonpHotUpdateChunkTemplate(outputOptions) {
|
||||
HotUpdateChunkTemplate.call(this, outputOptions);
|
||||
}
|
||||
module.exports = JsonpHotUpdateChunkTemplate;
|
||||
|
||||
JsonpHotUpdateChunkTemplate.prototype = Object.create(HotUpdateChunkTemplate.prototype);
|
||||
JsonpHotUpdateChunkTemplate.prototype.renderHeader = function(id, modules, hash) {
|
||||
var buf = HotUpdateChunkTemplate.prototype.renderHeader.call(this, id, modules, hash);
|
||||
var jsonpFunction = this.outputOptions.hotUpdateFunction || ("webpackHotUpdate" + (this.outputOptions.library || ""));
|
||||
buf.unshift(jsonpFunction + "(" + JSON.stringify(hash) + "," + JSON.stringify(id) + ",");
|
||||
return buf;
|
||||
};
|
||||
|
||||
JsonpHotUpdateChunkTemplate.prototype.renderFooter = function(id, modules, hash) {
|
||||
var buf = HotUpdateChunkTemplate.prototype.renderFooter.call(this, id, modules, hash);
|
||||
buf.push(")");
|
||||
return buf;
|
||||
};
|
||||
|
||||
JsonpHotUpdateChunkTemplate.prototype.updateHash = function(hash) {
|
||||
HotUpdateChunkTemplate.prototype.updateHash.call(this, hash);
|
||||
hash.update("JsonpHotUpdateChunkTemplate");
|
||||
hash.update("3");
|
||||
hash.update(this.outputOptions.hotUpdateFunction + "");
|
||||
hash.update(this.outputOptions.library + "");
|
||||
};
|
|
@ -4,6 +4,7 @@
|
|||
*/
|
||||
var JsonpMainTemplate = require("./JsonpMainTemplate");
|
||||
var JsonpChunkTemplate = require("./JsonpChunkTemplate");
|
||||
var JsonpHotUpdateChunkTemplate = require("./JsonpHotUpdateChunkTemplate");
|
||||
|
||||
function JsonpTemplatePlugin(options) {
|
||||
this.options = options;
|
||||
|
@ -13,6 +14,7 @@ JsonpTemplatePlugin.prototype.apply = function(compiler) {
|
|||
var options = this.options;
|
||||
compiler.mainTemplate = new JsonpMainTemplate(options);
|
||||
compiler.chunkTemplate = new JsonpChunkTemplate(options);
|
||||
compiler.hotUpdateChunkTemplate = new JsonpHotUpdateChunkTemplate(options);
|
||||
compiler.plugin("compilation", function(compilation) {
|
||||
compilation.plugin("normal-module-loader", function(loaderContext) {
|
||||
loaderContext.target = "web";
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
MIT License http://www.opensource.org/licenses/mit-license.php
|
||||
Author Tobias Koppers @sokra
|
||||
*/
|
||||
var BasicEvaluatedExpression = require("./BasicEvaluatedExpression");
|
||||
|
||||
function NoHotModuleReplacementPlugin() {
|
||||
}
|
||||
module.exports = NoHotModuleReplacementPlugin;
|
||||
|
||||
NoHotModuleReplacementPlugin.prototype.apply = function(compiler) {
|
||||
compiler.parser.plugin("evaluate Identifier module.hot", function(expr) {
|
||||
return new BasicEvaluatedExpression().setBoolean(false).setRange(expr.range);
|
||||
});
|
||||
};
|
|
@ -129,6 +129,19 @@ Parser.prototype.initializeEvaluating = function() {
|
|||
this.plugin("evaluate Identifier", function(expr) {
|
||||
return this.applyPluginsBailResult("evaluate Identifier " + expr.name, expr);
|
||||
});
|
||||
this.plugin("evaluate MemberExpression", function(expression) {
|
||||
var expr = expression;
|
||||
var exprName = [];
|
||||
while(expr.type == "MemberExpression" && expr.property.type == "Identifier") {
|
||||
exprName.unshift(expr.property.name);
|
||||
expr = expr.object;
|
||||
}
|
||||
if(expr.type == "Identifier" && this.scope.definitions.indexOf(expr.name) == -1) {
|
||||
exprName.unshift(expr.name);
|
||||
exprName = exprName.join(".");
|
||||
return this.applyPluginsBailResult("evaluate Identifier " + exprName, expression);
|
||||
}
|
||||
});
|
||||
this.plugin("evaluate CallExpression", function(expr) {
|
||||
if(expr.callee.type != "MemberExpression") return;
|
||||
if(expr.callee.property.type != "Identifier") return;
|
||||
|
|
|
@ -11,6 +11,8 @@ var NodeTemplatePlugin = require("./node/NodeTemplatePlugin");
|
|||
var EvalDevToolModulePlugin = require("./EvalDevToolModulePlugin");
|
||||
var SourceMapDevToolPlugin = require("./SourceMapDevToolPlugin");
|
||||
var LibraryTemplatePlugin = require("./LibraryTemplatePlugin");
|
||||
var HotModuleReplacementPlugin = require("./HotModuleReplacementPlugin");
|
||||
var NoHotModuleReplacementPlugin = require("./NoHotModuleReplacementPlugin");
|
||||
|
||||
var PrefetchPlugin = require("./PrefetchPlugin");
|
||||
var SingleEntryPlugin = require("./SingleEntryPlugin");
|
||||
|
@ -96,6 +98,12 @@ WebpackOptionsApply.prototype.process = function(options, compiler) {
|
|||
compiler.apply(new LibraryTemplatePlugin(options.output.library, options.output.libraryTarget));
|
||||
}
|
||||
|
||||
if(options.hot) {
|
||||
compiler.apply(new HotModuleReplacementPlugin(options.output));
|
||||
} else {
|
||||
compiler.apply(new NoHotModuleReplacementPlugin());
|
||||
}
|
||||
|
||||
if(options.devtool == "eval")
|
||||
compiler.apply(new EvalDevToolModulePlugin());
|
||||
else if(options.devtool == "sourcemap" || options.devtool == "source-map")
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
/*
|
||||
MIT License http://www.opensource.org/licenses/mit-license.php
|
||||
Author Tobias Koppers @sokra
|
||||
*/
|
||||
var ModuleDependency = require("./ModuleDependency");
|
||||
|
||||
function ModuleHotAcceptDependency(request, range) {
|
||||
ModuleDependency.call(this, request);
|
||||
this.Class = ModuleHotAcceptDependency;
|
||||
this.range = range;
|
||||
}
|
||||
module.exports = ModuleHotAcceptDependency;
|
||||
|
||||
ModuleHotAcceptDependency.prototype = Object.create(ModuleDependency.prototype);
|
||||
ModuleHotAcceptDependency.prototype.type = "module.hot.accept";
|
||||
|
||||
ModuleHotAcceptDependency.Template = require("./ModuleDependencyTemplateAsId");
|
|
@ -0,0 +1,17 @@
|
|||
/*
|
||||
MIT License http://www.opensource.org/licenses/mit-license.php
|
||||
Author Tobias Koppers @sokra
|
||||
*/
|
||||
var ModuleDependency = require("./ModuleDependency");
|
||||
|
||||
function ModuleHotDeclineDependency(request, range) {
|
||||
ModuleDependency.call(this, request);
|
||||
this.Class = ModuleHotDeclineDependency;
|
||||
this.range = range;
|
||||
}
|
||||
module.exports = ModuleHotDeclineDependency;
|
||||
|
||||
ModuleHotDeclineDependency.prototype = Object.create(ModuleDependency.prototype);
|
||||
ModuleHotDeclineDependency.prototype.type = "module.hot.decline";
|
||||
|
||||
ModuleHotDeclineDependency.Template = require("./ModuleDependencyTemplateAsId");
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "webpack",
|
||||
"version": "0.10.0",
|
||||
"version": "0.11.0-beta1",
|
||||
"author": "Tobias Koppers @sokra",
|
||||
"description": "Packs CommonJs/AMD/Labeled Modules for the browser. Allows to split your codebase into multiple bundles, which can be loaded on demand. Support loaders to preprocess files, i.e. json, jade, coffee, css, less, ... and your custom stuff.",
|
||||
"dependencies": {
|
||||
|
@ -27,7 +27,7 @@
|
|||
"should": "1.2.x",
|
||||
"vm-browserify": "0.0.x",
|
||||
"express": "3.2.x",
|
||||
"webpack-dev-middleware": "0.10.x",
|
||||
"webpack-dev-middleware": "0.11.x",
|
||||
"worker-loader": "0.5.x",
|
||||
"raw-loader": "0.5.x",
|
||||
"json-loader": "0.5.x",
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
*bundle-update.js
|
||||
bundle.js
|
||||
records.json
|
|
@ -0,0 +1,6 @@
|
|||
// This file doesn't accept itself neither the parent accepts it.
|
||||
// On change it will bubble to all parents (which is 'element.js'),
|
||||
// this parent is accepted by 'index.js'.
|
||||
// So on change 'element-dependency.js' and 'element.js' will be reloaded.
|
||||
|
||||
module.exports = "This text comes from <b>'element-dependency.js'</b> version 1.";
|
|
@ -0,0 +1,8 @@
|
|||
// This file can update, because 'index.js' accept it.
|
||||
|
||||
var element = document.createElement("h4");
|
||||
element.innerText = "This is 'element.js'.";
|
||||
var x = document.createElement("b");
|
||||
x.innerHTML = require("./element-dependency");
|
||||
element.appendChild(x);
|
||||
module.exports = element;
|
|
@ -0,0 +1,3 @@
|
|||
// This file can update, because 'index.js' accept it.
|
||||
|
||||
module.exports = "This text comes from <b>'html.js'</b>.";
|
|
@ -0,0 +1,7 @@
|
|||
<html>
|
||||
<head>
|
||||
<script src="bundle.js" type="text/javascript"></script>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,56 @@
|
|||
// This module is not accepted nor declined.
|
||||
// Any change will make the update fail.
|
||||
// If running in webpack-dev-server, it will do a complete reload on abort or fail.
|
||||
|
||||
window.onload = function() {
|
||||
|
||||
if(module.hot) {
|
||||
|
||||
var checkButton = document.createElement("button");
|
||||
checkButton.innerText = "Update!";
|
||||
checkButton.onclick = function() {
|
||||
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);
|
||||
});
|
||||
});
|
||||
};
|
||||
document.body.appendChild(checkButton);
|
||||
}
|
||||
|
||||
var element1 = document.createElement("div");
|
||||
element1.innerHTML = require("./html.js");
|
||||
document.body.appendChild(element1);
|
||||
|
||||
var element2 = require("./element.js");
|
||||
document.body.appendChild(element2);
|
||||
|
||||
require("./style.js");
|
||||
|
||||
if(module.hot) {
|
||||
|
||||
module.hot.accept("./html.js", function() {
|
||||
console.log("Replacing 'html.js' in 'index.js'");
|
||||
element1.innerHTML = require("./html.js");
|
||||
});
|
||||
|
||||
module.hot.accept("./element.js", function() {
|
||||
document.body.removeChild(element2);
|
||||
console.log("Replacing 'element.js' in 'index.js'");
|
||||
element2 = require("./element.js");
|
||||
document.body.appendChild(element2);
|
||||
});
|
||||
|
||||
}
|
||||
};
|
|
@ -0,0 +1,22 @@
|
|||
// This file can update, because it accept itself.
|
||||
// A dispose handler removes the old <style> element.
|
||||
|
||||
var cssCode = "body { background: green; }";
|
||||
|
||||
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);
|
||||
|
||||
if(module.hot) {
|
||||
module.hot.accept();
|
||||
module.hot.dispose(function() {
|
||||
head.removeChild(styleElement);
|
||||
});
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
module.exports = {
|
||||
entry: ["../../hot/dev-server", "./index.js"],
|
||||
output: {
|
||||
filename: "bundle.js",
|
||||
hotUpdateChunkFilename: "[id].[hash].bundle-update.js",
|
||||
hashDigestLength: 4
|
||||
},
|
||||
hot: true, // enable hot module replacement
|
||||
recordsPath: __dirname + "/records.json" // this is not required for the webpack-dev-server, but when compiled.
|
||||
};
|
Loading…
Reference in New Issue