1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 | "use strict";
|
7 |
|
8 | const { UsageState } = require("../ExportsInfo");
|
9 | const {
|
10 | numberToIdentifier,
|
11 | NUMBER_OF_IDENTIFIER_START_CHARS,
|
12 | NUMBER_OF_IDENTIFIER_CONTINUATION_CHARS
|
13 | } = require("../Template");
|
14 | const { assignDeterministicIds } = require("../ids/IdHelpers");
|
15 | const { compareSelect, compareStringsNumeric } = require("../util/comparators");
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 | const canMangle = exportsInfo => {
|
26 | if (exportsInfo.otherExportsInfo.getUsed(undefined) !== UsageState.Unused)
|
27 | return false;
|
28 | let hasSomethingToMangle = false;
|
29 | for (const exportInfo of exportsInfo.exports) {
|
30 | if (exportInfo.canMangle === true) {
|
31 | hasSomethingToMangle = true;
|
32 | }
|
33 | }
|
34 | return hasSomethingToMangle;
|
35 | };
|
36 |
|
37 |
|
38 | const comparator = compareSelect(e => e.name, compareStringsNumeric);
|
39 |
|
40 |
|
41 |
|
42 |
|
43 |
|
44 |
|
45 | const mangleExportsInfo = (deterministic, exportsInfo, isNamespace) => {
|
46 | if (!canMangle(exportsInfo)) return;
|
47 | const usedNames = new Set();
|
48 |
|
49 | const mangleableExports = [];
|
50 |
|
51 |
|
52 |
|
53 |
|
54 |
|
55 | let avoidMangleNonProvided = !isNamespace;
|
56 | if (!avoidMangleNonProvided && deterministic) {
|
57 | for (const exportInfo of exportsInfo.ownedExports) {
|
58 | if (exportInfo.provided !== false) {
|
59 | avoidMangleNonProvided = true;
|
60 | break;
|
61 | }
|
62 | }
|
63 | }
|
64 | for (const exportInfo of exportsInfo.ownedExports) {
|
65 | const name = exportInfo.name;
|
66 | if (!exportInfo.hasUsedName()) {
|
67 | if (
|
68 |
|
69 | exportInfo.canMangle !== true ||
|
70 |
|
71 | (name.length === 1 && /^[a-zA-Z0-9_$]/.test(name)) ||
|
72 |
|
73 | (deterministic &&
|
74 | name.length === 2 &&
|
75 | /^[a-zA-Z_$][a-zA-Z0-9_$]|^[1-9][0-9]/.test(name)) ||
|
76 |
|
77 | (avoidMangleNonProvided && exportInfo.provided !== true)
|
78 | ) {
|
79 | exportInfo.setUsedName(name);
|
80 | usedNames.add(name);
|
81 | } else {
|
82 | mangleableExports.push(exportInfo);
|
83 | }
|
84 | }
|
85 | if (exportInfo.exportsInfoOwned) {
|
86 | const used = exportInfo.getUsed(undefined);
|
87 | if (
|
88 | used === UsageState.OnlyPropertiesUsed ||
|
89 | used === UsageState.Unused
|
90 | ) {
|
91 | mangleExportsInfo(deterministic, exportInfo.exportsInfo, false);
|
92 | }
|
93 | }
|
94 | }
|
95 | if (deterministic) {
|
96 | assignDeterministicIds(
|
97 | mangleableExports,
|
98 | e => e.name,
|
99 | comparator,
|
100 | (e, id) => {
|
101 | const name = numberToIdentifier(id);
|
102 | const size = usedNames.size;
|
103 | usedNames.add(name);
|
104 | if (size === usedNames.size) return false;
|
105 | e.setUsedName(name);
|
106 | return true;
|
107 | },
|
108 | [
|
109 | NUMBER_OF_IDENTIFIER_START_CHARS,
|
110 | NUMBER_OF_IDENTIFIER_START_CHARS *
|
111 | NUMBER_OF_IDENTIFIER_CONTINUATION_CHARS
|
112 | ],
|
113 | NUMBER_OF_IDENTIFIER_CONTINUATION_CHARS,
|
114 | usedNames.size
|
115 | );
|
116 | } else {
|
117 | const usedExports = [];
|
118 | const unusedExports = [];
|
119 | for (const exportInfo of mangleableExports) {
|
120 | if (exportInfo.getUsed(undefined) === UsageState.Unused) {
|
121 | unusedExports.push(exportInfo);
|
122 | } else {
|
123 | usedExports.push(exportInfo);
|
124 | }
|
125 | }
|
126 | usedExports.sort(comparator);
|
127 | unusedExports.sort(comparator);
|
128 | let i = 0;
|
129 | for (const list of [usedExports, unusedExports]) {
|
130 | for (const exportInfo of list) {
|
131 | let name;
|
132 | do {
|
133 | name = numberToIdentifier(i++);
|
134 | } while (usedNames.has(name));
|
135 | exportInfo.setUsedName(name);
|
136 | }
|
137 | }
|
138 | }
|
139 | };
|
140 |
|
141 | class MangleExportsPlugin {
|
142 | |
143 |
|
144 |
|
145 | constructor(deterministic) {
|
146 | this._deterministic = deterministic;
|
147 | }
|
148 | |
149 |
|
150 |
|
151 |
|
152 |
|
153 | apply(compiler) {
|
154 | const { _deterministic: deterministic } = this;
|
155 | compiler.hooks.compilation.tap("MangleExportsPlugin", compilation => {
|
156 | const moduleGraph = compilation.moduleGraph;
|
157 | compilation.hooks.optimizeCodeGeneration.tap(
|
158 | "MangleExportsPlugin",
|
159 | modules => {
|
160 | if (compilation.moduleMemCaches) {
|
161 | throw new Error(
|
162 | "optimization.mangleExports can't be used with cacheUnaffected as export mangling is a global effect"
|
163 | );
|
164 | }
|
165 | for (const module of modules) {
|
166 | const isNamespace =
|
167 | module.buildMeta && module.buildMeta.exportsType === "namespace";
|
168 | const exportsInfo = moduleGraph.getExportsInfo(module);
|
169 | mangleExportsInfo(deterministic, exportsInfo, isNamespace);
|
170 | }
|
171 | }
|
172 | );
|
173 | });
|
174 | }
|
175 | }
|
176 |
|
177 | module.exports = MangleExportsPlugin;
|