Skip to content

Commit 1675a5a

Browse files
committed
new way to handle new ids, reuse old ids
adjusted HMR to handle id reusing
1 parent d826f99 commit 1675a5a

29 files changed

Lines changed: 274 additions & 206 deletions

lib/Chunk.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -342,14 +342,14 @@ Chunk.prototype.checkConstraints = function() {
342342
var chunk = this;
343343
chunk.chunks.forEach(function(child, idx) {
344344
if(chunk.chunks.indexOf(child) !== idx)
345-
console.log("checkConstraints: duplicate child in chunk", chunk.debugId, child.debugId);
345+
throw new Error("checkConstraints: duplicate child in chunk " + chunk.debugId + " " + child.debugId);
346346
if(child.parents.indexOf(chunk) < 0)
347-
console.log("checkConstraints: child missing parent", chunk.debugId, "->", child.debugId);
347+
throw new Error("checkConstraints: child missing parent " + chunk.debugId + " -> " + child.debugId);
348348
});
349349
chunk.parents.forEach(function(parent, idx) {
350350
if(chunk.parents.indexOf(parent) !== idx)
351-
console.log("checkConstraints: duplicate parent in chunk", chunk.debugId, parent.debugId);
351+
throw new Error("checkConstraints: duplicate parent in chunk " + chunk.debugId + " " + parent.debugId);
352352
if(parent.chunks.indexOf(chunk) < 0)
353-
console.log("checkConstraints: parent missing child", parent.debugId, "<-", chunk.debugId);
353+
throw new Error("checkConstraints: parent missing child " + parent.debugId + " <- " + chunk.debugId);
354354
});
355355
};

lib/Compilation.js

Lines changed: 54 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,8 @@ function Compilation(compiler) {
4646
this._modules = {};
4747
this.cache = null;
4848
this.records = null;
49-
this.nextFreeModuleId = 0;
50-
this.nextFreeChunkId = 0;
51-
this.nextFreeModuleIndex = 0;
52-
this.nextFreeModuleIndex2 = 0;
49+
this.nextFreeModuleIndex = undefined;
50+
this.nextFreeModuleIndex2 = undefined;
5351
this.additionalChunkAssets = [];
5452
this.assets = {};
5553
this.errors = [];
@@ -492,10 +490,6 @@ Compilation.prototype.unseal = function unseal() {
492490
this.applyPlugins("unseal");
493491
this.chunks.length = 0;
494492
this.namedChunks = {};
495-
this.nextFreeChunkId = this.nextFreeChunkIdBeforeSeal;
496-
this.nextFreeModuleId = this.nextFreeModuleIdBeforeSeal;
497-
this.nextFreeModuleIndex = this.nextFreeModuleIndexBeforeSeal;
498-
this.nextFreeModuleIndex2 = this.nextFreeModuleIndex2BeforeSeal;
499493
this.additionalChunkAssets.length = 0;
500494
this.assets = {};
501495
this.modules.forEach(function(module) {
@@ -510,10 +504,8 @@ Compilation.prototype.seal = function seal(callback) {
510504
if(a.name > b.name) return 1;
511505
return 0;
512506
});
513-
this.nextFreeChunkIdBeforeSeal = this.nextFreeChunkId;
514-
this.nextFreeModuleIdBeforeSeal = this.nextFreeModuleId;
515-
this.nextFreeModuleIndexBeforeSeal = this.nextFreeModuleIndex;
516-
this.nextFreeModuleIndex2BeforeSeal = this.nextFreeModuleIndex2;
507+
this.nextFreeModuleIndex = 0;
508+
this.nextFreeModuleIndex2 = 0;
517509
this.preparedChunks.forEach(function(preparedChunk) {
518510
var module = preparedChunk.module;
519511
var chunk = this.addChunk(preparedChunk.name, module);
@@ -737,17 +729,59 @@ Compilation.prototype.removeChunkFromDependencies = function removeChunkFromDepe
737729
};
738730

739731
Compilation.prototype.applyModuleIds = function applyModuleIds() {
732+
var unusedIds = [];
733+
var nextFreeModuleId = 0;
734+
if(this.usedModuleIds) {
735+
var usedIds = Object.keys(this.usedModuleIds).map(function(key) {
736+
return this.usedModuleIds[key];
737+
}, this).sort();
738+
var usedNumberIds = usedIds.filter(function(id) {
739+
return typeof id === "number";
740+
});
741+
nextFreeModuleId = usedNumberIds.reduce(function(a, b) {
742+
return Math.max(a, b);
743+
}, -1) + 1;
744+
for(var i = 0; i < nextFreeModuleId; i++) {
745+
if(this.usedModuleIds[i] !== i)
746+
unusedIds.push(i);
747+
}
748+
unusedIds.reverse();
749+
}
740750
this.modules.forEach(function(module) {
741751
if(module.id === null) {
742-
module.id = this.nextFreeModuleId++;
752+
if(unusedIds.length > 0)
753+
module.id = unusedIds.pop();
754+
else
755+
module.id = nextFreeModuleId++;
743756
}
744757
}, this);
745758
};
746759

747760
Compilation.prototype.applyChunkIds = function applyChunkIds() {
761+
var unusedIds = [];
762+
var nextFreeChunkId = 0;
763+
if(this.usedChunkIds) {
764+
var usedIds = Object.keys(this.usedChunkIds).map(function(key) {
765+
return this.usedChunkIds[key];
766+
}, this).sort();
767+
var usedNumberIds = usedIds.filter(function(id) {
768+
return typeof id === "number";
769+
});
770+
nextFreeChunkId = usedNumberIds.reduce(function(a, b) {
771+
return Math.max(a, b);
772+
}, -1) + 1;
773+
for(var i = 0; i < nextFreeChunkId; i++) {
774+
if(this.usedChunkIds[i] !== i)
775+
unusedIds.push(i);
776+
}
777+
unusedIds.reverse();
778+
}
748779
this.chunks.forEach(function(chunk) {
749780
if(chunk.id === null) {
750-
chunk.id = this.nextFreeChunkId++;
781+
if(unusedIds.length > 0)
782+
chunk.id = unusedIds.pop();
783+
else
784+
chunk.id = nextFreeChunkId++;
751785
}
752786
if(!chunk.ids) {
753787
chunk.ids = [chunk.id];
@@ -950,9 +984,14 @@ Compilation.prototype.createChildCompiler = function(name, outputOptions) {
950984
};
951985

952986
Compilation.prototype.checkConstraints = function() {
987+
var usedIds = {};
988+
this.modules.forEach(function(module) {
989+
if(usedIds[module.id])
990+
throw new Error("checkConstraints: duplicate module id " + module.id);
991+
});
953992
this.chunks.forEach(function(chunk, idx) {
954993
if(this.chunks.indexOf(chunk) !== idx)
955-
console.log("checkConstraints: duplicate chunk in compilation", chunk.debugId);
994+
throw new Error("checkConstraints: duplicate chunk in compilation " + chunk.debugId);
956995
chunk.checkConstraints();
957996
}.bind(this));
958997
};

lib/HotModuleReplacement.runtime.js

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -92,9 +92,9 @@ module.exports = function() {
9292
hot._selfAccepted = dep;
9393
else if(typeof dep === "object")
9494
for(var i = 0; i < dep.length; i++)
95-
hot._acceptedDependencies[dep[i]] = callback;
95+
hot._acceptedDependencies[dep[i]] = callback || function() {};
9696
else
97-
hot._acceptedDependencies[dep] = callback;
97+
hot._acceptedDependencies[dep] = callback || function() {};
9898
},
9999
decline: function(dep) {
100100
if(typeof dep === "undefined")
@@ -174,10 +174,8 @@ module.exports = function() {
174174
}
175175

176176
hotRequestedFilesMap = {};
177-
hotAvailableFilesMap = {};
178177
hotWaitingFilesMap = {};
179-
for(var i = 0; i < update.c.length; i++)
180-
hotAvailableFilesMap[update.c[i]] = true;
178+
hotAvailableFilesMap = update.c;
181179
hotUpdateNewHash = update.h;
182180

183181
hotSetStatus("prepare");
@@ -339,9 +337,18 @@ module.exports = function() {
339337
for(var id in hotUpdate) {
340338
if(Object.prototype.hasOwnProperty.call(hotUpdate, id)) {
341339
moduleId = toModuleId(id);
342-
var result = getAffectedStuff(moduleId);
340+
var result;
341+
if(hotUpdate[id]) {
342+
result = getAffectedStuff(moduleId);
343+
} else {
344+
result = {
345+
type: "disposed",
346+
moduleId: id
347+
};
348+
}
343349
var abortError = false;
344350
var doApply = false;
351+
var doDispose = false;
345352
var chainInfo = "";
346353
if(result.chain) {
347354
chainInfo = "\nUpdate propagation: " + result.chain.join(" -> ");
@@ -370,6 +377,11 @@ module.exports = function() {
370377
options.onAccepted(result);
371378
doApply = true;
372379
break;
380+
case "disposed":
381+
if(options.onDisposed)
382+
options.onDisposed(result);
383+
doDispose = true;
384+
break;
373385
default:
374386
throw new Error("Unexception type " + result.type);
375387
}
@@ -388,6 +400,12 @@ module.exports = function() {
388400
}
389401
}
390402
}
403+
if(doDispose) {
404+
addAllToSet(outdatedModules, [result.moduleId]);
405+
appliedUpdate[moduleId] = function() {
406+
console.warn("[HMR] unexpected require(" + result.moduleId + ") to disposed module");
407+
};
408+
}
391409
}
392410
}
393411

@@ -404,6 +422,12 @@ module.exports = function() {
404422

405423
// Now in "dispose" phase
406424
hotSetStatus("dispose");
425+
Object.keys(hotAvailableFilesMap).forEach(function(chunkId) {
426+
if(hotAvailableFilesMap[chunkId] === false) {
427+
hotDisposeChunk(chunkId);
428+
}
429+
});
430+
407431
var idx;
408432
var queue = outdatedModules.slice();
409433
while(queue.length > 0) {

lib/HotModuleReplacementPlugin.js

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ HotModuleReplacementPlugin.prototype.apply = function(compiler) {
105105
});
106106
var hotUpdateMainContent = {
107107
h: this.hash,
108-
c: []
108+
c: {}
109109
};
110110
Object.keys(records.chunkHashs).forEach(function(chunkId) {
111111
chunkId = +chunkId;
@@ -116,18 +116,27 @@ HotModuleReplacementPlugin.prototype.apply = function(compiler) {
116116
var newModules = currentChunk.modules.filter(function(module) {
117117
return module.hotUpdate;
118118
});
119-
if(newModules.length > 0) {
120-
var source = hotUpdateChunkTemplate.render(chunkId, newModules, this.hash, this.moduleTemplate, this.dependencyTemplates);
119+
var allModules = {};
120+
currentChunk.modules.forEach(function(module) {
121+
allModules[module.id] = true;
122+
});
123+
var removedModules = records.chunkModuleIds[chunkId].filter(function(id) {
124+
return !allModules[id];
125+
});
126+
if(newModules.length > 0 || removedModules.length > 0) {
127+
var source = hotUpdateChunkTemplate.render(chunkId, newModules, removedModules, this.hash, this.moduleTemplate, this.dependencyTemplates);
121128
var filename = this.getPath(hotUpdateChunkFilename, {
122129
hash: records.hash,
123130
chunk: currentChunk
124131
});
125132
this.additionalChunkAssets.push(filename);
126133
this.assets[filename] = source;
127-
hotUpdateMainContent.c.push(chunkId);
134+
hotUpdateMainContent.c[chunkId] = true;
128135
currentChunk.files.push(filename);
129136
this.applyPlugins("chunk-asset", currentChunk, filename);
130137
}
138+
} else {
139+
hotUpdateMainContent.c[chunkId] = false;
131140
}
132141
}, this);
133142
var source = new RawSource(JSON.stringify(hotUpdateMainContent));

lib/HotUpdateChunkTemplate.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,14 @@ module.exports = HotUpdateChunkTemplate;
1313
HotUpdateChunkTemplate.prototype = Object.create(Template.prototype);
1414
HotUpdateChunkTemplate.prototype.constructor = HotUpdateChunkTemplate;
1515

16-
HotUpdateChunkTemplate.prototype.render = function(id, modules, hash, moduleTemplate, dependencyTemplates) {
16+
HotUpdateChunkTemplate.prototype.render = function(id, modules, removedModules, hash, moduleTemplate, dependencyTemplates) {
1717
var modulesSource = this.renderChunkModules({
1818
id: id,
19-
modules: modules
19+
modules: modules,
20+
removedModules: removedModules
2021
}, moduleTemplate, dependencyTemplates);
21-
var core = this.applyPluginsWaterfall("modules", modulesSource, modules, moduleTemplate, dependencyTemplates);
22-
var source = this.applyPluginsWaterfall("render", core, modules, hash, id, moduleTemplate, dependencyTemplates);
22+
var core = this.applyPluginsWaterfall("modules", modulesSource, modules, removedModules, moduleTemplate, dependencyTemplates);
23+
var source = this.applyPluginsWaterfall("render", core, modules, removedModules, hash, id, moduleTemplate, dependencyTemplates);
2324
return source;
2425
};
2526

lib/JsonpHotUpdateChunkTemplatePlugin.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ function JsonpHotUpdateChunkTemplatePlugin() {}
99
module.exports = JsonpHotUpdateChunkTemplatePlugin;
1010

1111
JsonpHotUpdateChunkTemplatePlugin.prototype.apply = function(hotUpdateChunkTemplate) {
12-
hotUpdateChunkTemplate.plugin("render", function(modulesSource, modules, hash, id) {
12+
hotUpdateChunkTemplate.plugin("render", function(modulesSource, modules, removedModules, hash, id) {
1313
var jsonpFunction = this.outputOptions.hotUpdateFunction;
1414
var source = new ConcatSource();
1515
source.add(jsonpFunction + "(" + JSON.stringify(id) + ",");

lib/JsonpMainTemplatePlugin.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,9 @@ JsonpMainTemplatePlugin.prototype.apply = function(mainTemplate) {
181181
});
182182

183183
return source + "\n" +
184+
"function hotDisposeChunk(chunkId) {\n" +
185+
"\tdelete installedChunks[chunkId];\n" +
186+
"}\n" +
184187
"var parentHotUpdateCallback = this[" + JSON.stringify(hotUpdateFunction) + "];\n" +
185188
"this[" + JSON.stringify(hotUpdateFunction) + "] = " + Template.getFunctionContent(require("./JsonpMainTemplate.runtime.js"))
186189
.replace(/\/\/\$semicolon/g, ";")

lib/RecordIdsPlugin.js

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,28 +19,30 @@ function makeRelative(compiler, identifier) {
1919
RecordIdsPlugin.prototype.apply = function(compiler) {
2020
compiler.plugin("compilation", function(compilation) {
2121
compilation.plugin("record-modules", function(modules, records) {
22-
records.nextFreeModuleId = compilation.nextFreeModuleId;
2322
if(!records.modules) records.modules = {};
2423
if(!records.modules.byIdentifier) records.modules.byIdentifier = {};
24+
if(!records.modules.usedIds) records.modules.usedIds = {};
2525
modules.forEach(function(module) {
2626
var identifier = makeRelative(compiler, module.identifier());
2727
records.modules.byIdentifier[identifier] = module.id;
28+
records.modules.usedIds[module.id] = module.id;
2829
});
2930
});
3031
compilation.plugin("revive-modules", function(modules, records) {
31-
if(typeof records.nextFreeModuleId === "number")
32-
compilation.nextFreeModuleId = records.nextFreeModuleId;
33-
if(!records.modules || !records.modules.byIdentifier) return;
34-
var usedIds = {};
35-
modules.forEach(function(module) {
36-
if(module.id !== null) return;
37-
var identifier = makeRelative(compiler, module.identifier());
38-
var id = records.modules.byIdentifier[identifier];
39-
if(id === undefined) return;
40-
if(usedIds[id]) return;
41-
usedIds[id] = true;
42-
module.id = id;
43-
});
32+
if(!records.modules) return;
33+
if(records.modules.byIdentifier) {
34+
var usedIds = {};
35+
modules.forEach(function(module) {
36+
if(module.id !== null) return;
37+
var identifier = makeRelative(compiler, module.identifier());
38+
var id = records.modules.byIdentifier[identifier];
39+
if(id === undefined) return;
40+
if(usedIds[id]) return;
41+
usedIds[id] = true;
42+
module.id = id;
43+
});
44+
}
45+
compilation.usedModuleIds = records.modules.usedIds;
4446
});
4547

4648
function getDepBlockIdent(chunk, block) {
@@ -63,18 +65,18 @@ RecordIdsPlugin.prototype.apply = function(compiler) {
6365
if(!records.chunks) records.chunks = {};
6466
if(!records.chunks.byName) records.chunks.byName = {};
6567
if(!records.chunks.byBlocks) records.chunks.byBlocks = {};
68+
records.chunks.usedIds = {};
6669
chunks.forEach(function(chunk) {
6770
var name = chunk.name;
6871
var blockIdents = chunk.blocks.map(getDepBlockIdent.bind(null, chunk)).filter(Boolean);
6972
if(name) records.chunks.byName[name] = chunk.id;
7073
blockIdents.forEach(function(blockIdent) {
7174
records.chunks.byBlocks[blockIdent] = chunk.id;
7275
});
76+
records.chunks.usedIds[chunk.id] = chunk.id;
7377
});
7478
});
7579
compilation.plugin("revive-chunks", function(chunks, records) {
76-
if(typeof records.nextFreeChunkId === "number")
77-
compilation.nextFreeChunkId = records.nextFreeChunkId;
7880
if(!records.chunks) return;
7981
var usedIds = {};
8082
if(records.chunks.byName) {
@@ -123,6 +125,7 @@ RecordIdsPlugin.prototype.apply = function(compiler) {
123125
chunk.id = id;
124126
});
125127
}
128+
compilation.usedChunkIds = records.chunks.usedIds;
126129
});
127130
});
128131
};

0 commit comments

Comments
 (0)