Skip to content

Commit

Permalink
optimize OccurenceOrderPlugin
Browse files Browse the repository at this point in the history
  • Loading branch information
sokra committed Jun 1, 2017
1 parent f2112f1 commit 890a507
Show file tree
Hide file tree
Showing 28 changed files with 369 additions and 373 deletions.
102 changes: 49 additions & 53 deletions lib/optimize/OccurrenceOrderPlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,77 +15,77 @@ class OccurrenceOrderPlugin {
const preferEntry = this.preferEntry;
compiler.plugin("compilation", (compilation) => {
compilation.plugin("optimize-module-order", (modules) => {
function entryChunks(m) {
let total = 0;
const occursInInitialChunksMap = new Map();
const occursInAllChunksMap = new Map();

const initialChunkChunkMap = new Map();
const entryCountMap = new Map();
modules.forEach(m => {
let initial = 0;
let entry = 0;
m.forEachChunk(c => {
const sum = (c.isInitial() ? 1 : 0) + (c.entryModule === m ? 1 : 0);
total += sum;
if(c.isInitial()) initial++;
if(c.entryModule === m) entry++;
});
return total;
}
initialChunkChunkMap.set(m, initial);
entryCountMap.set(m, entry);
});

function occursInEntry(m) {
if(typeof m.__OccurenceOrderPlugin_occursInEntry === "number") return m.__OccurenceOrderPlugin_occursInEntry;
const result = m.reasons.map((r) => {
if(!r.module) return 0;
return entryChunks(r.module);
}).reduce((a, b) => {
return a + b;
}, 0) + entryChunks(m);
return m.__OccurenceOrderPlugin_occursInEntry = result;
}
const countOccursInEntry = (sum, r) => {
if(!r.module) return sum;
return sum + initialChunkChunkMap.get(r.module);
};
const countOccurs = (sum, r) => {
if(!r.module) return sum;
return sum + r.module.getNumberOfChunks();
};

function occurs(m) {
if(typeof m.__OccurenceOrderPlugin_occurs === "number") return m.__OccurenceOrderPlugin_occurs;
let numberEntry = 0;
m.forEachChunk(c => {
if(c.entryModule === m)
numberEntry++;
if(preferEntry) {
modules.forEach(m => {
const result = m.reasons.reduce(countOccursInEntry, 0) + initialChunkChunkMap.get(m) + entryCountMap.get(m);
occursInInitialChunksMap.set(m, result);
});
const result = m.reasons.map((r) => {
if(!r.module) return 0;
return r.module.getNumberOfChunks();
}).reduce((a, b) => {
return a + b;
}, 0) + m.getNumberOfChunks() + numberEntry;
return m.__OccurenceOrderPlugin_occurs = result;
}

modules.forEach(m => {
const result = m.reasons.reduce(countOccurs, 0) + m.getNumberOfChunks() + entryCountMap.get(m);
occursInAllChunksMap.set(m, result);
});

modules.sort((a, b) => {
if(preferEntry) {
const aEntryOccurs = occursInEntry(a);
const bEntryOccurs = occursInEntry(b);
const aEntryOccurs = occursInInitialChunksMap.get(a);
const bEntryOccurs = occursInInitialChunksMap.get(b);
if(aEntryOccurs > bEntryOccurs) return -1;
if(aEntryOccurs < bEntryOccurs) return 1;
}
const aOccurs = occurs(a);
const bOccurs = occurs(b);
const aOccurs = occursInAllChunksMap.get(a);
const bOccurs = occursInAllChunksMap.get(b);
if(aOccurs > bOccurs) return -1;
if(aOccurs < bOccurs) return 1;
if(a.identifier() > b.identifier()) return 1;
if(a.identifier() < b.identifier()) return -1;
if(a.index > b.index) return 1;
if(a.index < b.index) return -1;
return 0;
});
// TODO refactor to Map
modules.forEach((m) => {
m.__OccurenceOrderPlugin_occursInEntry = undefined;
m.__OccurenceOrderPlugin_occurs = undefined;
});
});
compilation.plugin("optimize-chunk-order", (chunks) => {
function occursInEntry(c) {
if(typeof c.__OccurenceOrderPlugin_occursInEntry === "number") return c.__OccurenceOrderPlugin_occursInEntry;
const result = c.parents.filter((p) => {
return p.isInitial();
}).length;
return c.__OccurenceOrderPlugin_occursInEntry = result;
}
const occursInInitialChunksMap = new Map();

chunks.forEach(c => {
const result = c.parents.reduce((sum, p) => {
if(p.isInitial()) return sum + 1;
return sum;
}, 0);
return occursInInitialChunksMap.set(c, result);
});

function occurs(c) {
return c.blocks.length;
}

chunks.sort((a, b) => {
const aEntryOccurs = occursInEntry(a);
const bEntryOccurs = occursInEntry(b);
const aEntryOccurs = occursInInitialChunksMap.get(a);
const bEntryOccurs = occursInInitialChunksMap.get(b);
if(aEntryOccurs > bEntryOccurs) return -1;
if(aEntryOccurs < bEntryOccurs) return 1;
const aOccurs = occurs(a);
Expand All @@ -94,10 +94,6 @@ class OccurrenceOrderPlugin {
if(aOccurs < bOccurs) return 1;
return a.compareTo(b);
});
// TODO refactor to Map
chunks.forEach((c) => {
c.__OccurenceOrderPlugin_occursInEntry = undefined;
});
});
});
}
Expand Down
48 changes: 24 additions & 24 deletions test/Compiler-caching.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -223,21 +223,21 @@ describe("Compiler (caching)", function() {
const helper = compile("./temp-cache-fixture/c", options, (stats, files) => {

// Built the first time
stats.modules[0].name.should.containEql("a.js");
stats.modules[0].built.should.be.exactly(true, "a.js should have been built");
stats.modules[0].name.should.containEql("c.js");
stats.modules[0].built.should.be.exactly(true, "c.js should have been built");

stats.modules[1].name.should.containEql("c.js");
stats.modules[1].built.should.be.exactly(true, "c.js should have been built");
stats.modules[1].name.should.containEql("a.js");
stats.modules[1].built.should.be.exactly(true, "a.js should have been built");

setTimeout(() => {
helper.runAgain((stats, files, iteration) => {

// Not built when cached the second run
stats.modules[0].name.should.containEql("a.js");
//stats.modules[0].built.should.be.exactly(false, "a.js should not have built");
stats.modules[0].name.should.containEql("c.js");
//stats.modules[0].built.should.be.exactly(false, "c.js should not have built");

stats.modules[1].name.should.containEql("c.js");
//stats.modules[1].built.should.be.exactly(false, "c.js should not have built");
stats.modules[1].name.should.containEql("a.js");
//stats.modules[1].built.should.be.exactly(false, "a.js should not have built");

const aContent = fs.readFileSync(tempFixture.aFilepath).toString().replace("This is a", "This is a MODIFIED");

Expand All @@ -247,11 +247,11 @@ describe("Compiler (caching)", function() {
helper.runAgain((stats, files, iteration) => {

// And only a.js built after it was modified
stats.modules[0].name.should.containEql("a.js");
stats.modules[0].built.should.be.exactly(true, "a.js should have been built");
stats.modules[0].name.should.containEql("c.js");
stats.modules[0].built.should.be.exactly(false, "c.js should not have built");

stats.modules[1].name.should.containEql("c.js");
stats.modules[1].built.should.be.exactly(false, "c.js should not have built");
stats.modules[1].name.should.containEql("a.js");
stats.modules[1].built.should.be.exactly(true, "a.js should have been built");

done();
});
Expand All @@ -269,20 +269,20 @@ describe("Compiler (caching)", function() {
const helper = compile("./temp-cache-fixture/c", options, (stats, files) => {

// Built the first time
stats.modules[0].name.should.containEql("a.js");
stats.modules[0].built.should.be.exactly(true, "a.js should have been built");
stats.modules[0].name.should.containEql("c.js");
stats.modules[0].built.should.be.exactly(true, "c.js should have been built");

stats.modules[1].name.should.containEql("c.js");
stats.modules[1].built.should.be.exactly(true, "c.js should have been built");
stats.modules[1].name.should.containEql("a.js");
stats.modules[1].built.should.be.exactly(true, "a.js should have been built");

helper.runAgain((stats, files, iteration) => {

// Not built when cached the second run
stats.modules[0].name.should.containEql("a.js");
//stats.modules[0].built.should.be.exactly(false, "a.js should not have built");
stats.modules[0].name.should.containEql("c.js");
//stats.modules[0].built.should.be.exactly(false, "c.js should not have built");

stats.modules[1].name.should.containEql("c.js");
//stats.modules[1].built.should.be.exactly(false, "c.js should not have built");
stats.modules[1].name.should.containEql("a.js");
//stats.modules[1].built.should.be.exactly(false, "a.js should not have built");

const aContent = fs.readFileSync(tempFixture.aFilepath).toString().replace("This is a", "This is a MODIFIED");

Expand All @@ -291,11 +291,11 @@ describe("Compiler (caching)", function() {
helper.runAgain((stats, files, iteration) => {

// And only a.js built after it was modified
stats.modules[0].name.should.containEql("a.js");
stats.modules[0].built.should.be.exactly(true, "a.js should have been built");
stats.modules[0].name.should.containEql("c.js");
//stats.modules[0].built.should.be.exactly(false, "c.js should not have built");

stats.modules[1].name.should.containEql("c.js");
//stats.modules[1].built.should.be.exactly(false, "c.js should not have built");
stats.modules[1].name.should.containEql("a.js");
stats.modules[1].built.should.be.exactly(true, "a.js should have been built");

done();
});
Expand Down
2 changes: 1 addition & 1 deletion test/Compiler.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ describe("Compiler", () => {
Object.keys(files).should.be.eql(["/main.js"]);
const bundle = files["/main.js"];
bundle.should.containEql("function __webpack_require__(");
bundle.should.containEql("__webpack_require__(/*! ./a */ 0);");
bundle.should.containEql("__webpack_require__(/*! ./a */ 1);");
bundle.should.containEql("./c.js");
bundle.should.containEql("./a.js");
bundle.should.containEql("This is a");
Expand Down
4 changes: 2 additions & 2 deletions test/binCases/entry/multi-file/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ module.exports = function testAssertions(code, stdout, stderr) {

stdout.should.be.ok();
stdout[4].should.containEql("null.js");
stdout[5].should.match(/a\.js.*\{0\}/);
stdout[5].should.match(/multi.*index\.js.*a\.js/); // should have multi-file entry
stdout[6].should.match(/index\.js.*\{0\}/);
stdout[7].should.match(/multi.*index\.js.*a\.js/); // should have multi-file entry
stdout[7].should.match(/a\.js.*\{0\}/);
stderr.should.be.empty();
};

4 changes: 2 additions & 2 deletions test/binCases/entry/non-hyphenated-args/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ module.exports = function testAssertions(code, stdout, stderr) {
stdout.should.be.ok();
stdout[4].should.containEql("null.js");
stdout[5].should.containEql("main.js"); // non-hyphenated arg ./a.js should create chunk "main"
stdout[6].should.match(/a\.js.*\{1\}/); // a.js should be in chunk 1
stdout[7].should.match(/index\.js.*\{0\}/); // index.js should be in chunk 0
stdout[6].should.match(/index\.js.*\{0\}/); // index.js should be in chunk 0
stdout[7].should.match(/a\.js.*\{1\}/); // a.js should be in chunk 1
stderr.should.be.empty();
};

4 changes: 2 additions & 2 deletions test/configCases/code-generation/use-strict/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ it("should include only one use strict per module", function() {
}

matches.should.be.eql([
"it(\"should include only one use strict per module\", function() {",
"Object.defineProperty(__webpack_exports__, \"__esModule\", { value: true });",
"Object.defineProperty(__webpack_exports__, \"__esModule\", { value: true });",
"Object.defineProperty(__webpack_exports__, \"__esModule\", { value: true });",
"/* unused harmony default export */ var _unused_webpack_default_export = (\"a\");",
"Object.defineProperty(__webpack_exports__, \"__esModule\", { value: true });",
"it(\"should include only one use strict per module\", function() {"
"/* unused harmony default export */ var _unused_webpack_default_export = (\"a\");",
]);
});
8 changes: 4 additions & 4 deletions test/configCases/records/issue-2991/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ it("should write relative paths to records", function() {
content.should.eql(`{
"modules": {
"byIdentifier": {
"external \\"fs\\"": 0,
"external \\"path\\"": 1,
"ignored pkgs/somepackage/foo": 2,
"test.js": 3
"test.js": 0,
"ignored pkgs/somepackage/foo": 1,
"external \\"fs\\"": 2,
"external \\"path\\"": 3
},
"usedIds": {
"0": 0,
Expand Down
2 changes: 1 addition & 1 deletion test/configCases/records/issue-2991/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ var path = require("path");

module.exports = {
entry: "./test",
recordsPath: path.resolve(__dirname, "../../../js/config/records/issue-2991/records.json"),
recordsOutputPath: path.resolve(__dirname, "../../../js/config/records/issue-2991/records.json"),
target: "node",
node: {
__dirname: false
Expand Down
42 changes: 21 additions & 21 deletions test/statsCases/chunks/expected.txt
Original file line number Diff line number Diff line change
@@ -1,32 +1,32 @@
Hash: 6ab76347dbbefb99c3c5
Hash: 458904e7e19c8ce28066
Time: Xms
Asset Size Chunks Chunk Names
0.bundle.js 238 bytes 0 [emitted]
1.bundle.js 108 bytes 1 [emitted]
2.bundle.js 204 bytes 2 [emitted]
bundle.js 6.11 kB 3 [emitted] main
1.bundle.js 102 bytes 1 [emitted]
2.bundle.js 182 bytes 2 [emitted]
bundle.js 6.1 kB 3 [emitted] main
chunk {0} 0.bundle.js 54 bytes {3} [rendered]
> [5] (webpack)/test/statsCases/chunks/index.js 3:0-16
[2] (webpack)/test/statsCases/chunks/c.js 54 bytes {0} [built]
amd require ./c [5] (webpack)/test/statsCases/chunks/index.js 3:0-16
> [0] (webpack)/test/statsCases/chunks/index.js 3:0-16
[3] (webpack)/test/statsCases/chunks/c.js 54 bytes {0} [built]
amd require ./c [0] (webpack)/test/statsCases/chunks/index.js 3:0-16
[] -> factory:Xms building:Xms = Xms
chunk {1} 1.bundle.js 22 bytes {3} [rendered]
> [5] (webpack)/test/statsCases/chunks/index.js 2:0-16
[1] (webpack)/test/statsCases/chunks/b.js 22 bytes {1} [built]
amd require ./b [5] (webpack)/test/statsCases/chunks/index.js 2:0-16
> [0] (webpack)/test/statsCases/chunks/index.js 2:0-16
[2] (webpack)/test/statsCases/chunks/b.js 22 bytes {1} [built]
amd require ./b [0] (webpack)/test/statsCases/chunks/index.js 2:0-16
[] -> factory:Xms building:Xms = Xms
chunk {2} 2.bundle.js 44 bytes {0} [rendered]
> [2] (webpack)/test/statsCases/chunks/c.js 1:0-52
[3] (webpack)/test/statsCases/chunks/d.js 22 bytes {2} [built]
require.ensure item ./d [2] (webpack)/test/statsCases/chunks/c.js 1:0-52
> [3] (webpack)/test/statsCases/chunks/c.js 1:0-52
[4] (webpack)/test/statsCases/chunks/d.js 22 bytes {2} [built]
require.ensure item ./d [3] (webpack)/test/statsCases/chunks/c.js 1:0-52
[] -> factory:Xms building:Xms = Xms
[4] (webpack)/test/statsCases/chunks/e.js 22 bytes {2} [built]
require.ensure item ./e [2] (webpack)/test/statsCases/chunks/c.js 1:0-52
[5] (webpack)/test/statsCases/chunks/e.js 22 bytes {2} [built]
require.ensure item ./e [3] (webpack)/test/statsCases/chunks/c.js 1:0-52
[] -> factory:Xms building:Xms = Xms
chunk {3} bundle.js (main) 73 bytes [entry] [rendered]
> main [5] (webpack)/test/statsCases/chunks/index.js
[0] (webpack)/test/statsCases/chunks/a.js 22 bytes {3} [built]
cjs require ./a [5] (webpack)/test/statsCases/chunks/index.js 1:0-14
[] -> factory:Xms building:Xms = Xms
[5] (webpack)/test/statsCases/chunks/index.js 51 bytes {3} [built]
factory:Xms building:Xms = Xms
> main [0] (webpack)/test/statsCases/chunks/index.js
[0] (webpack)/test/statsCases/chunks/index.js 51 bytes {3} [built]
factory:Xms building:Xms = Xms
[1] (webpack)/test/statsCases/chunks/a.js 22 bytes {3} [built]
cjs require ./a [0] (webpack)/test/statsCases/chunks/index.js 1:0-14
[] -> factory:Xms building:Xms = Xms
6 changes: 3 additions & 3 deletions test/statsCases/exclude-with-loader/expected.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Hash: 7ab067a6a9fc61623ae0
Hash: 9a949e8727d0583cb1c4
Time: Xms
Asset Size Chunks Chunk Names
bundle.js 2.74 kB 0 [emitted] main
[0] (webpack)/test/statsCases/exclude-with-loader/a.txt 43 bytes {0} [built]
[2] (webpack)/test/statsCases/exclude-with-loader/index.js 46 bytes {0} [built]
[0] (webpack)/test/statsCases/exclude-with-loader/index.js 46 bytes {0} [built]
[1] (webpack)/test/statsCases/exclude-with-loader/a.txt 43 bytes {0} [built]
+ 1 hidden module
Loading

0 comments on commit 890a507

Please sign in to comment.