UNPKG

3.42 kBJavaScriptView Raw
1/*
2 MIT License http://www.opensource.org/licenses/mit-license.php
3 Author Tobias Koppers @sokra
4*/
5"use strict";
6
7/** @typedef {import("./Module")} Module */
8/** @typedef {import("./DependenciesBlock")} DependenciesBlock */
9
10/** @typedef {false | true | string[]} UsedExports */
11
12const addToSet = (a, b) => {
13 for (const item of b) {
14 if (!a.includes(item)) a.push(item);
15 }
16 return a;
17};
18
19const isSubset = (biggerSet, subset) => {
20 if (biggerSet === true) return true;
21 if (subset === true) return false;
22 return subset.every(item => biggerSet.indexOf(item) >= 0);
23};
24
25class FlagDependencyUsagePlugin {
26 apply(compiler) {
27 compiler.hooks.compilation.tap("FlagDependencyUsagePlugin", compilation => {
28 compilation.hooks.optimizeDependencies.tap(
29 "FlagDependencyUsagePlugin",
30 modules => {
31 const processModule = (module, usedExports) => {
32 module.used = true;
33 if (module.usedExports === true) return;
34 if (usedExports === true) {
35 module.usedExports = true;
36 } else if (Array.isArray(usedExports)) {
37 const old = module.usedExports ? module.usedExports.length : -1;
38 module.usedExports = addToSet(
39 module.usedExports || [],
40 usedExports
41 );
42 if (module.usedExports.length === old) {
43 return;
44 }
45 } else if (Array.isArray(module.usedExports)) {
46 return;
47 } else {
48 module.usedExports = false;
49 }
50
51 // for a module without side effects we stop tracking usage here when no export is used
52 // This module won't be evaluated in this case
53 if (module.factoryMeta.sideEffectFree) {
54 if (module.usedExports === false) return;
55 if (
56 Array.isArray(module.usedExports) &&
57 module.usedExports.length === 0
58 )
59 return;
60 }
61
62 queue.push([module, module, module.usedExports]);
63 };
64
65 const processDependenciesBlock = (module, depBlock, usedExports) => {
66 for (const dep of depBlock.dependencies) {
67 processDependency(module, dep);
68 }
69 for (const variable of depBlock.variables) {
70 for (const dep of variable.dependencies) {
71 processDependency(module, dep);
72 }
73 }
74 for (const block of depBlock.blocks) {
75 queue.push([module, block, usedExports]);
76 }
77 };
78
79 const processDependency = (module, dep) => {
80 const reference = compilation.getDependencyReference(module, dep);
81 if (!reference) return;
82 const referenceModule = reference.module;
83 const importedNames = reference.importedNames;
84 const oldUsed = referenceModule.used;
85 const oldUsedExports = referenceModule.usedExports;
86 if (
87 !oldUsed ||
88 (importedNames &&
89 (!oldUsedExports || !isSubset(oldUsedExports, importedNames)))
90 ) {
91 processModule(referenceModule, importedNames);
92 }
93 };
94
95 for (const module of modules) {
96 module.used = false;
97 }
98
99 /** @type {[Module, DependenciesBlock, UsedExports][]} */
100 const queue = [];
101 for (const preparedEntrypoint of compilation._preparedEntrypoints) {
102 if (preparedEntrypoint.module) {
103 processModule(preparedEntrypoint.module, true);
104 }
105 }
106
107 while (queue.length) {
108 const queueItem = queue.pop();
109 processDependenciesBlock(queueItem[0], queueItem[1], queueItem[2]);
110 }
111 }
112 );
113 });
114 }
115}
116module.exports = FlagDependencyUsagePlugin;