added hot module replacement, fixes #26

This commit is contained in:
Tobias Koppers 2013-06-19 13:49:57 +02:00
parent f944553c77
commit 8b2301056d
24 changed files with 732 additions and 2 deletions

View File

@ -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)

View File

@ -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")

View File

@ -207,6 +207,7 @@ module.exports = function(optimist, argv, convertOptions) {
options.watchDelay = value;
});
mapArgToBoolean("hot", "hot");
mapArgToBoolean("debug", "debug");
ifBooleanArg("progress", function() {

22
hot/dev-server.js Normal file
View File

@ -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);
});
});
}
};
}

13
hot/poll.js Normal file
View File

@ -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);
}

View File

@ -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);

View File

@ -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, "");

View File

@ -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");
};

View File

@ -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 + "");
};

View File

@ -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";

View File

@ -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);
});
};

View File

@ -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;

View File

@ -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")

View File

@ -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");

View File

@ -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");

View File

@ -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",

3
test/hotPlayground/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
*bundle-update.js
bundle.js
records.json

View File

@ -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.";

View File

@ -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;

View File

@ -0,0 +1,3 @@
// This file can update, because 'index.js' accept it.
module.exports = "This text comes from <b>'html.js'</b>.";

View File

@ -0,0 +1,7 @@
<html>
<head>
<script src="bundle.js" type="text/javascript"></script>
</head>
<body>
</body>
</html>

View File

@ -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);
});
}
};

View File

@ -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);
});
}

View File

@ -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.
};