WXL
4 天以前 3bd962a6d7f61239c020e2dbbeb7341e5b842dd1
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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
/*
    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("../../declarations/plugins/optimize/MergeDuplicateChunksPlugin").MergeDuplicateChunksPluginOptions} MergeDuplicateChunksPluginOptions */
/** @typedef {import("../Compiler")} Compiler */
/** @typedef {import("../Chunk")} Chunk */
 
const PLUGIN_NAME = "MergeDuplicateChunksPlugin";
 
class MergeDuplicateChunksPlugin {
    /**
     * Creates an instance of MergeDuplicateChunksPlugin.
     * @param {MergeDuplicateChunksPluginOptions=} options options object
     */
    constructor(options = { stage: STAGE_BASIC }) {
        /** @type {MergeDuplicateChunksPluginOptions} */
        this.options = options;
    }
 
    /**
     * Applies the plugin by registering its hooks on the compiler.
     * @param {Compiler} compiler the compiler
     * @returns {void}
     */
    apply(compiler) {
        compiler.hooks.validate.tap(PLUGIN_NAME, () => {
            compiler.validate(
                () =>
                    require("../../schemas/plugins/optimize/MergeDuplicateChunksPlugin.json"),
                this.options,
                {
                    name: "Merge Duplicate Chunks Plugin",
                    baseDataPath: "options"
                },
                (options) =>
                    require("../../schemas/plugins/optimize/MergeDuplicateChunksPlugin.check")(
                        options
                    )
            );
        });
        compiler.hooks.compilation.tap(PLUGIN_NAME, (compilation) => {
            compilation.hooks.optimizeChunks.tap(
                {
                    name: PLUGIN_NAME,
                    stage: this.options.stage
                },
                (chunks) => {
                    const { chunkGraph, moduleGraph } = compilation;
 
                    // remember already tested chunks for performance
                    /** @type {Set<Chunk>} */
                    const notDuplicates = new Set();
 
                    // for each chunk
                    for (const chunk of chunks) {
                        // track a Set of all chunk that could be duplicates
                        /** @type {Set<Chunk> | undefined} */
                        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;