马宇豪
2024-07-16 f591c27b57e2418c9495bc02ae8cfff84d35bc18
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
/*
    MIT License http://www.opensource.org/licenses/mit-license.php
    Author Tobias Koppers @sokra
*/
 
"use strict";
 
const { STAGE_BASIC } = require("../OptimizationStages");
const { runtimeEqual } = require("../util/runtime");
 
/** @typedef {import("../Compiler")} Compiler */
 
class MergeDuplicateChunksPlugin {
    /**
     * @param {Compiler} compiler the compiler
     * @returns {void}
     */
    apply(compiler) {
        compiler.hooks.compilation.tap(
            "MergeDuplicateChunksPlugin",
            compilation => {
                compilation.hooks.optimizeChunks.tap(
                    {
                        name: "MergeDuplicateChunksPlugin",
                        stage: STAGE_BASIC
                    },
                    chunks => {
                        const { chunkGraph, moduleGraph } = compilation;
 
                        // remember already tested chunks for performance
                        const notDuplicates = new Set();
 
                        // for each chunk
                        for (const chunk of chunks) {
                            // track a Set of all chunk that could be duplicates
                            let possibleDuplicates;
                            for (const module of chunkGraph.getChunkModulesIterable(chunk)) {
                                if (possibleDuplicates === undefined) {
                                    // when possibleDuplicates is not yet set,
                                    // create a new Set from chunks of the current module
                                    // including only chunks with the same number of modules
                                    for (const dup of chunkGraph.getModuleChunksIterable(
                                        module
                                    )) {
                                        if (
                                            dup !== chunk &&
                                            chunkGraph.getNumberOfChunkModules(chunk) ===
                                                chunkGraph.getNumberOfChunkModules(dup) &&
                                            !notDuplicates.has(dup)
                                        ) {
                                            // delay allocating the new Set until here, reduce memory pressure
                                            if (possibleDuplicates === undefined) {
                                                possibleDuplicates = new Set();
                                            }
                                            possibleDuplicates.add(dup);
                                        }
                                    }
                                    // when no chunk is possible we can break here
                                    if (possibleDuplicates === undefined) break;
                                } else {
                                    // validate existing possible duplicates
                                    for (const dup of possibleDuplicates) {
                                        // remove possible duplicate when module is not contained
                                        if (!chunkGraph.isModuleInChunk(module, dup)) {
                                            possibleDuplicates.delete(dup);
                                        }
                                    }
                                    // when all chunks has been removed we can break here
                                    if (possibleDuplicates.size === 0) break;
                                }
                            }
 
                            // when we found duplicates
                            if (
                                possibleDuplicates !== undefined &&
                                possibleDuplicates.size > 0
                            ) {
                                outer: for (const otherChunk of possibleDuplicates) {
                                    if (otherChunk.hasRuntime() !== chunk.hasRuntime()) continue;
                                    if (chunkGraph.getNumberOfEntryModules(chunk) > 0) continue;
                                    if (chunkGraph.getNumberOfEntryModules(otherChunk) > 0)
                                        continue;
                                    if (!runtimeEqual(chunk.runtime, otherChunk.runtime)) {
                                        for (const module of chunkGraph.getChunkModulesIterable(
                                            chunk
                                        )) {
                                            const exportsInfo = moduleGraph.getExportsInfo(module);
                                            if (
                                                !exportsInfo.isEquallyUsed(
                                                    chunk.runtime,
                                                    otherChunk.runtime
                                                )
                                            ) {
                                                continue outer;
                                            }
                                        }
                                    }
                                    // merge them
                                    if (chunkGraph.canChunksBeIntegrated(chunk, otherChunk)) {
                                        chunkGraph.integrateChunks(chunk, otherChunk);
                                        compilation.chunks.delete(otherChunk);
                                    }
                                }
                            }
 
                            // don't check already processed chunks twice
                            notDuplicates.add(chunk);
                        }
                    }
                );
            }
        );
    }
}
module.exports = MergeDuplicateChunksPlugin;