Skip to content

Commit 81203a3

Browse files
committed
added node.js HMR,
fixed webpack#137: exports correct require properties on hotRequire too added more test combinations
1 parent a395c07 commit 81203a3

File tree

12 files changed

+224
-96
lines changed

12 files changed

+224
-96
lines changed

lib/HotModuleReplacementPlugin.js

Lines changed: 48 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,11 @@ function HotModuleReplacementPlugin(outputOptions) {
1414
module.exports = HotModuleReplacementPlugin;
1515

1616
HotModuleReplacementPlugin.prototype.apply = function(compiler) {
17-
var hotUpdateChunkFilename = this.outputOptions.hotUpdateChunkFilename || "[id].[hash].hot-update.js";
18-
var hotUpdateMainFilename = this.outputOptions.hotUpdateMainFilename || "[hash].hot-update.json";
19-
var hotUpdateFunction = this.outputOptions.hotUpdateFunction || ("webpackHotUpdate" + (this.outputOptions.library || ""));
17+
var hotUpdateChunkFilename = this.outputOptions.hotUpdateChunkFilename;
18+
var hotUpdateMainFilename = this.outputOptions.hotUpdateMainFilename;
2019
compiler.plugin("compilation", function(compilation, params) {
2120
var hotUpdateChunkTemplate = compilation.compiler.hotUpdateChunkTemplate;
22-
if(!hotUpdateChunkTemplate) return;
21+
if(!hotUpdateChunkTemplate && !compilation.mainTemplate.renderHotModuleReplacementInit) return;
2322

2423
var normalModuleFactory = params.normalModuleFactory;
2524
var contextModuleFactory = params.contextModuleFactory;
@@ -120,18 +119,12 @@ HotModuleReplacementPlugin.prototype.apply = function(compiler) {
120119

121120
compilation.mainTemplate.renderInit = function(hash, chunk) {
122121
var buf = mainTemplate.renderInit(hash, chunk);
123-
var currentHotUpdateChunkFilename = JSON.stringify(hotUpdateChunkFilename)
124-
.replace(Template.REGEXP_HASH, "\" + " + this.renderCurrentHashCode(hash) + " + \"")
125-
.replace(Template.REGEXP_ID, "\" + chunkId + \"");
126-
var currentHotUpdateMainFilename = JSON.stringify(hotUpdateMainFilename)
127-
.replace(Template.REGEXP_HASH, "\" + " + this.renderCurrentHashCode(hash) + " + \"");
128-
buf.push("this[" + JSON.stringify(hotUpdateFunction) + "] = " +
129-
(hotInitCode
130-
.replace(/\$require\$/g, this.requireFn)
131-
.replace(/\$hotMainFilename\$/g, currentHotUpdateMainFilename)
132-
.replace(/\$hotChunkFilename\$/g, currentHotUpdateChunkFilename)
133-
.replace(/\$hash\$/g, JSON.stringify(hash))
134-
.replace(/\/\*foreachInstalledChunks\*\//g, chunk.chunks.length > 0 ? "for(var chunkId in installedChunks)" : "var chunkId = 0;")));
122+
buf = buf.concat(this.renderHotModuleReplacementInit(hash, chunk));
123+
buf.push("\n\n");
124+
buf.push(hotInitCode
125+
.replace(/\$require\$/g, this.requireFn)
126+
.replace(/\$hash\$/g, JSON.stringify(hash))
127+
.replace(/\/\*foreachInstalledChunks\*\//g, chunk.chunks.length > 0 ? "for(var chunkId in installedChunks)" : "var chunkId = 0;"));
135128
return buf;
136129
};
137130

@@ -182,15 +175,7 @@ HotModuleReplacementPlugin.prototype.apply = function(compiler) {
182175
});
183176
};
184177

185-
var hotInitCode = function() {
186-
function webpackHotUpdateCallback(chunkId, moreModules) {
187-
for(var moduleId in moreModules) {
188-
hotUpdate[moduleId] = moreModules[moduleId];
189-
}
190-
if(--hotWaitingFiles === 0 && hotChunksLoading === 0) {
191-
hotUpdateDownloaded();
192-
}
193-
}
178+
var hotInitCode = Template.getFunctionContent(function() {
194179

195180
var hotApplyOnUpdate = true;
196181
var hotCurrentHash = $hash$;
@@ -221,7 +206,7 @@ var hotInitCode = function() {
221206
hotChunksLoading--;
222207
if(hotStatus === "prepare") {
223208
if(!hotWaitingFilesMap[chunkId]) {
224-
hotDownloadUpdateChunk(chunkId);
209+
hotEnsureUpdateChunk(chunkId);
225210
}
226211
if(hotChunksLoading === 0 && hotWaitingFiles === 0) {
227212
hotUpdateDownloaded();
@@ -230,8 +215,8 @@ var hotInitCode = function() {
230215
}
231216
});
232217
}
233-
fn.cache = $require$.cache;
234-
fn.modules = $require$.modules;
218+
for(var name in $require$)
219+
fn[name] = $require$[name];
235220
return fn;
236221
}
237222

@@ -316,64 +301,49 @@ var hotInitCode = function() {
316301
function hotCheck(callback) {
317302
callback = callback || function(err) { if(err) throw err };
318303
if(hotStatus !== "idle") throw new Error("check() is only allowed in idle status");
319-
if(typeof XMLHttpRequest === "undefined")
320-
return callback(new Error("No browser support"));
321304
hotSetStatus("check");
322-
323-
try {
324-
var request = new XMLHttpRequest();
325-
request.open("GET", $require$.p + $hotMainFilename$, true);
326-
request.send(null);
327-
} catch(err) {
328-
return callback(err);
329-
}
330-
request.onreadystatechange = function() {
331-
if(request.readyState !== 4) return;
332-
if(request.status !== 200 && request.status !== 304) {
333-
305+
hotDownloadManifest(function(err, update) {
306+
if(err) return callback(err);
307+
if(!update) {
334308
hotSetStatus("idle");
335309
callback(null, null);
310+
return;
311+
}
336312

337-
} else {
338-
339-
try {
340-
var update = JSON.parse(request.responseText);
341-
} catch(e) {
342-
hotSetStatus("idle");
343-
callback(null, null);
344-
return;
345-
}
346-
347-
hotAvailibleFilesMap = {};
348-
hotWaitingFilesMap = {};
349-
for(var i = 0; i < update.c.length; i++)
350-
hotAvailibleFilesMap[update.c[i]] = true;
351-
hotUpdateNewHash = update.h;
352-
353-
hotSetStatus("prepare");
354-
hotCallback = callback;
355-
hotUpdate = {};
356-
/*foreachInstalledChunks*/ {
357-
hotDownloadUpdateChunk(chunkId);
358-
}
359-
if(hotChunksLoading === 0 && hotWaitingFiles === 0) {
360-
hotUpdateDownloaded();
361-
}
313+
hotAvailibleFilesMap = {};
314+
hotWaitingFilesMap = {};
315+
for(var i = 0; i < update.c.length; i++)
316+
hotAvailibleFilesMap[update.c[i]] = true;
317+
hotUpdateNewHash = update.h;
318+
319+
hotSetStatus("prepare");
320+
hotCallback = callback;
321+
hotUpdate = {};
322+
/*foreachInstalledChunks*/ {
323+
hotEnsureUpdateChunk(chunkId);
362324
}
363-
};
325+
if(hotChunksLoading === 0 && hotWaitingFiles === 0) {
326+
hotUpdateDownloaded();
327+
}
328+
});
329+
}
330+
331+
function hotAddUpdateChunk(chunkId, moreModules) {
332+
for(var moduleId in moreModules) {
333+
hotUpdate[moduleId] = moreModules[moduleId];
334+
}
335+
if(--hotWaitingFiles === 0 && hotChunksLoading === 0) {
336+
hotUpdateDownloaded();
337+
}
364338
}
365339

366-
function hotDownloadUpdateChunk(chunkId) {
367-
if(hotAvailibleFilesMap[chunkId]) {
340+
function hotEnsureUpdateChunk(chunkId) {
341+
if(!hotAvailibleFilesMap[chunkId]) {
342+
hotWaitingFilesMap[chunkId] = true;
343+
} else {
368344
hotWaitingFiles++;
369-
var head = document.getElementsByTagName('head')[0];
370-
var script = document.createElement('script');
371-
script.type = 'text/javascript';
372-
script.charset = 'utf-8';
373-
script.src = $require$.p + $hotChunkFilename$;
374-
head.appendChild(script);
345+
hotDownloadUpdateChunk(chunkId);
375346
}
376-
hotWaitingFilesMap[chunkId] = true;
377347
}
378348

379349
function hotUpdateDownloaded() {
@@ -539,4 +509,4 @@ var hotInitCode = function() {
539509
hotSetStatus("idle");
540510
callback(null, outdatedModules);
541511
}
542-
}.toString().replace(/^function\s?\(\)\s?\{\n?|\n?\}$/g, "").replace(/^\t/mg, "");
512+
});

lib/HotUpdateChunkTemplate.js

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,26 +15,20 @@ HotUpdateChunkTemplate.prototype = Object.create(Template.prototype);
1515
HotUpdateChunkTemplate.prototype.render = function(id, modules, hash, moduleTemplate, dependencyTemplates) {
1616
var source = new ConcatSource();
1717
source.add(this.asString(this.renderHeader(id, modules, hash)));
18-
modules.forEach(function(module, idx) {
19-
if(idx != 0) source.add(",\n");
20-
if(typeof module === "number") {
21-
source.add("\n/***/ " + module + ": false");
22-
} else {
23-
source.add("\n/***/ " + module.id + ":\n");
24-
source.add(moduleTemplate.render(module, dependencyTemplates));
25-
}
26-
});
27-
source.add("\n\n");
18+
source.add(this.renderChunkModules({
19+
id: id,
20+
modules: modules
21+
}, moduleTemplate, dependencyTemplates));
2822
source.add(this.asString(this.renderFooter(id, modules, hash)));
2923
return source;
3024
};
3125

3226
HotUpdateChunkTemplate.prototype.renderHeader = function(id, modules, hash) {
33-
return ["{\n"];
27+
return [];
3428
};
3529

3630
HotUpdateChunkTemplate.prototype.renderFooter = function(id, modules, hash) {
37-
return ["}"];
31+
return [];
3832
};
3933

4034
HotUpdateChunkTemplate.prototype.updateHash = function(hash) {

lib/JsonpMainTemplate.js

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ function JsonpMainTemplate(outputOptions) {
1111
module.exports = JsonpMainTemplate;
1212

1313
JsonpMainTemplate.prototype = Object.create(MainTemplate.prototype);
14+
JsonpMainTemplate.prototype.constructor = JsonpMainTemplate;
1415

1516
JsonpMainTemplate.prototype.renderLocalVars = function(hash, chunk) {
1617
var buf = MainTemplate.prototype.renderLocalVars.call(this, hash, chunk);
@@ -103,8 +104,59 @@ JsonpMainTemplate.prototype.renderInit = function(hash, chunk) {
103104
return buf;
104105
};
105106

106-
JsonpMainTemplate.prototype.renderCurrentHashCode = function(hash) {
107-
return JSON.stringify(hash);
107+
JsonpMainTemplate.prototype.renderHotModuleReplacementInit = function(hash, chunk) {
108+
var hotUpdateChunkFilename = this.outputOptions.hotUpdateChunkFilename;
109+
var hotUpdateMainFilename = this.outputOptions.hotUpdateMainFilename;
110+
var hotUpdateFunction = this.outputOptions.hotUpdateFunction || ("webpackHotUpdate" + (this.outputOptions.library || ""));
111+
var currentHotUpdateChunkFilename = JSON.stringify(hotUpdateChunkFilename)
112+
.replace(Template.REGEXP_HASH, "\" + " + this.renderCurrentHashCode(hash) + " + \"")
113+
.replace(Template.REGEXP_ID, "\" + chunkId + \"");
114+
var currentHotUpdateMainFilename = JSON.stringify(hotUpdateMainFilename)
115+
.replace(Template.REGEXP_HASH, "\" + " + this.renderCurrentHashCode(hash) + " + \"");
116+
return "this[" + JSON.stringify(hotUpdateFunction) + "] = " + Template.getFunctionContent(function() {
117+
function webpackHotUpdateCallback(chunkId, moreModules) {
118+
hotAddUpdateChunk(chunkId, moreModules);
119+
}
120+
121+
function hotDownloadUpdateChunk(chunkId) {
122+
var head = document.getElementsByTagName('head')[0];
123+
var script = document.createElement('script');
124+
script.type = 'text/javascript';
125+
script.charset = 'utf-8';
126+
script.src = $require$.p + $hotChunkFilename$;
127+
head.appendChild(script);
128+
}
129+
130+
function hotDownloadManifest(callback) {
131+
if(typeof XMLHttpRequest === "undefined")
132+
return callback(new Error("No browser support"));
133+
try {
134+
var request = new XMLHttpRequest();
135+
request.open("GET", $require$.p + $hotMainFilename$, true);
136+
request.send(null);
137+
} catch(err) {
138+
return callback(err);
139+
}
140+
request.onreadystatechange = function() {
141+
if(request.readyState !== 4) return;
142+
if(request.status !== 200 && request.status !== 304) {
143+
callback();
144+
} else {
145+
try {
146+
var update = JSON.parse(request.responseText);
147+
} catch(e) {
148+
callback();
149+
return;
150+
}
151+
callback(null, update);
152+
}
153+
};
154+
}
155+
})
156+
.replace(/\$require\$/g, this.requireFn)
157+
.replace(/\$hotMainFilename\$/g, currentHotUpdateMainFilename)
158+
.replace(/\$hotChunkFilename\$/g, currentHotUpdateChunkFilename)
159+
.replace(/\$hash\$/g, JSON.stringify(hash))
108160
};
109161

110162
JsonpMainTemplate.prototype.updateHash = function(hash) {

lib/MainTemplate.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,10 @@ MainTemplate.prototype.renderAddModule = function(hash, chunk, varModuleId, varM
154154
return ["modules[" + varModuleId + "] = " + varModule + ";"];
155155
};
156156

157+
MainTemplate.prototype.renderCurrentHashCode = function(hash) {
158+
return JSON.stringify(hash);
159+
};
160+
157161
MainTemplate.prototype.updateHash = function(hash) {
158162
hash.update("maintemplate");
159163
hash.update("2");

lib/Template.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,3 +81,7 @@ Template.prototype.renderChunkModules = function(chunk, moduleTemplate, dependen
8181
}
8282
return source;
8383
};
84+
85+
Template.getFunctionContent = function(fn) {
86+
return fn.toString().replace(/^function\s?\(\)\s?\{\n?|\n?\}$/g, "").replace(/^\t/mg, "");
87+
};

lib/WebpackOptionsDefaulter.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ function WebpackOptionsDefaulter() {
2020
this.set("output.libraryTarget", "var");
2121
this.set("output.path", "");
2222
this.set("output.sourceMapFilename", "[file].map");
23+
this.set("output.hotUpdateChunkFilename", "[id].[hash].hot-update.js");
24+
this.set("output.hotUpdateMainFilename", "[hash].hot-update.json");
2325
this.set("output.hashFunction", "md5");
2426
this.set("output.hashDigest", "hex");
2527
this.set("output.hashDigestLength", 20);
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
MIT License http://www.opensource.org/licenses/mit-license.php
3+
Author Tobias Koppers @sokra
4+
*/
5+
var HotUpdateChunkTemplate = require("../HotUpdateChunkTemplate");
6+
7+
function NodeHotUpdateChunkTemplate(outputOptions) {
8+
HotUpdateChunkTemplate.call(this, outputOptions);
9+
}
10+
module.exports = NodeHotUpdateChunkTemplate;
11+
12+
NodeHotUpdateChunkTemplate.prototype = Object.create(HotUpdateChunkTemplate.prototype);
13+
NodeHotUpdateChunkTemplate.prototype.renderHeader = function(id, modules, hash) {
14+
var buf = HotUpdateChunkTemplate.prototype.renderHeader.call(this, id, modules, hash);
15+
buf.unshift(
16+
"exports.id = " + JSON.stringify(id) + ";\n",
17+
"exports.modules = "
18+
);
19+
return buf;
20+
};
21+
22+
NodeHotUpdateChunkTemplate.prototype.updateHash = function(hash) {
23+
HotUpdateChunkTemplate.prototype.updateHash.call(this, hash);
24+
hash.update("NodeHotUpdateChunkTemplate");
25+
hash.update("3");
26+
hash.update(this.outputOptions.hotUpdateFunction + "");
27+
hash.update(this.outputOptions.library + "");
28+
};

0 commit comments

Comments
 (0)