UNPKG

10.5 kBJavaScriptView Raw
1/*
2 MIT License http://www.opensource.org/licenses/mit-license.php
3 Author Tobias Koppers @sokra
4*/
5
6"use strict";
7
8const Dependency = require("../Dependency");
9const { UsageState } = require("../ExportsInfo");
10const Template = require("../Template");
11const { equals } = require("../util/ArrayHelpers");
12const makeSerializable = require("../util/makeSerializable");
13const propertyAccess = require("../util/propertyAccess");
14const { handleDependencyBase } = require("./CommonJsDependencyHelpers");
15const ModuleDependency = require("./ModuleDependency");
16const processExportInfo = require("./processExportInfo");
17
18/** @typedef {import("webpack-sources").ReplaceSource} ReplaceSource */
19/** @typedef {import("../Dependency").ExportsSpec} ExportsSpec */
20/** @typedef {import("../Dependency").ReferencedExport} ReferencedExport */
21/** @typedef {import("../Dependency").TRANSITIVE} TRANSITIVE */
22/** @typedef {import("../DependencyTemplate").DependencyTemplateContext} DependencyTemplateContext */
23/** @typedef {import("../Module")} Module */
24/** @typedef {import("../ModuleGraph")} ModuleGraph */
25/** @typedef {import("../util/runtime").RuntimeSpec} RuntimeSpec */
26
27const idsSymbol = Symbol("CommonJsExportRequireDependency.ids");
28
29const EMPTY_OBJECT = {};
30
31class CommonJsExportRequireDependency extends ModuleDependency {
32 constructor(range, valueRange, base, names, request, ids, resultUsed) {
33 super(request);
34 this.range = range;
35 this.valueRange = valueRange;
36 this.base = base;
37 this.names = names;
38 this.ids = ids;
39 this.resultUsed = resultUsed;
40 this.asiSafe = undefined;
41 }
42
43 get type() {
44 return "cjs export require";
45 }
46
47 /**
48 * @returns {boolean | TRANSITIVE} true, when changes to the referenced module could affect the referencing module; TRANSITIVE, when changes to the referenced module could affect referencing modules of the referencing module
49 */
50 couldAffectReferencingModule() {
51 return Dependency.TRANSITIVE;
52 }
53
54 /**
55 * @param {ModuleGraph} moduleGraph the module graph
56 * @returns {string[]} the imported id
57 */
58 getIds(moduleGraph) {
59 return moduleGraph.getMeta(this)[idsSymbol] || this.ids;
60 }
61
62 /**
63 * @param {ModuleGraph} moduleGraph the module graph
64 * @param {string[]} ids the imported ids
65 * @returns {void}
66 */
67 setIds(moduleGraph, ids) {
68 moduleGraph.getMeta(this)[idsSymbol] = ids;
69 }
70
71 /**
72 * Returns list of exports referenced by this dependency
73 * @param {ModuleGraph} moduleGraph module graph
74 * @param {RuntimeSpec} runtime the runtime for which the module is analysed
75 * @returns {(string[] | ReferencedExport)[]} referenced exports
76 */
77 getReferencedExports(moduleGraph, runtime) {
78 const ids = this.getIds(moduleGraph);
79 const getFullResult = () => {
80 if (ids.length === 0) {
81 return Dependency.EXPORTS_OBJECT_REFERENCED;
82 } else {
83 return [
84 {
85 name: ids,
86 canMangle: false
87 }
88 ];
89 }
90 };
91 if (this.resultUsed) return getFullResult();
92 let exportsInfo = moduleGraph.getExportsInfo(
93 moduleGraph.getParentModule(this)
94 );
95 for (const name of this.names) {
96 const exportInfo = exportsInfo.getReadOnlyExportInfo(name);
97 const used = exportInfo.getUsed(runtime);
98 if (used === UsageState.Unused) return Dependency.NO_EXPORTS_REFERENCED;
99 if (used !== UsageState.OnlyPropertiesUsed) return getFullResult();
100 exportsInfo = exportInfo.exportsInfo;
101 if (!exportsInfo) return getFullResult();
102 }
103 if (exportsInfo.otherExportsInfo.getUsed(runtime) !== UsageState.Unused) {
104 return getFullResult();
105 }
106 /** @type {string[][]} */
107 const referencedExports = [];
108 for (const exportInfo of exportsInfo.orderedExports) {
109 processExportInfo(
110 runtime,
111 referencedExports,
112 ids.concat(exportInfo.name),
113 exportInfo,
114 false
115 );
116 }
117 return referencedExports.map(name => ({
118 name,
119 canMangle: false
120 }));
121 }
122
123 /**
124 * Returns the exported names
125 * @param {ModuleGraph} moduleGraph module graph
126 * @returns {ExportsSpec | undefined} export names
127 */
128 getExports(moduleGraph) {
129 const ids = this.getIds(moduleGraph);
130 if (this.names.length === 1) {
131 const name = this.names[0];
132 const from = moduleGraph.getConnection(this);
133 if (!from) return;
134 return {
135 exports: [
136 {
137 name,
138 from,
139 export: ids.length === 0 ? null : ids,
140 // we can't mangle names that are in an empty object
141 // because one could access the prototype property
142 // when export isn't set yet
143 canMangle: !(name in EMPTY_OBJECT) && false
144 }
145 ],
146 dependencies: [from.module]
147 };
148 } else if (this.names.length > 0) {
149 const name = this.names[0];
150 return {
151 exports: [
152 {
153 name,
154 // we can't mangle names that are in an empty object
155 // because one could access the prototype property
156 // when export isn't set yet
157 canMangle: !(name in EMPTY_OBJECT) && false
158 }
159 ],
160 dependencies: undefined
161 };
162 } else {
163 const from = moduleGraph.getConnection(this);
164 if (!from) return;
165 const reexportInfo = this.getStarReexports(
166 moduleGraph,
167 undefined,
168 from.module
169 );
170 if (reexportInfo) {
171 return {
172 exports: Array.from(reexportInfo.exports, name => {
173 return {
174 name,
175 from,
176 export: ids.concat(name),
177 canMangle: !(name in EMPTY_OBJECT) && false
178 };
179 }),
180 // TODO handle deep reexports
181 dependencies: [from.module]
182 };
183 } else {
184 return {
185 exports: true,
186 from: ids.length === 0 ? from : undefined,
187 canMangle: false,
188 dependencies: [from.module]
189 };
190 }
191 }
192 }
193
194 /**
195 * @param {ModuleGraph} moduleGraph the module graph
196 * @param {RuntimeSpec} runtime the runtime
197 * @param {Module} importedModule the imported module (optional)
198 * @returns {{exports?: Set<string>, checked?: Set<string>}} information
199 */
200 getStarReexports(
201 moduleGraph,
202 runtime,
203 importedModule = moduleGraph.getModule(this)
204 ) {
205 let importedExportsInfo = moduleGraph.getExportsInfo(importedModule);
206 const ids = this.getIds(moduleGraph);
207 if (ids.length > 0)
208 importedExportsInfo = importedExportsInfo.getNestedExportsInfo(ids);
209 let exportsInfo = moduleGraph.getExportsInfo(
210 moduleGraph.getParentModule(this)
211 );
212 if (this.names.length > 0)
213 exportsInfo = exportsInfo.getNestedExportsInfo(this.names);
214
215 const noExtraExports =
216 importedExportsInfo &&
217 importedExportsInfo.otherExportsInfo.provided === false;
218 const noExtraImports =
219 exportsInfo &&
220 exportsInfo.otherExportsInfo.getUsed(runtime) === UsageState.Unused;
221
222 if (!noExtraExports && !noExtraImports) {
223 return;
224 }
225
226 const isNamespaceImport =
227 importedModule.getExportsType(moduleGraph, false) === "namespace";
228
229 /** @type {Set<string>} */
230 const exports = new Set();
231 /** @type {Set<string>} */
232 const checked = new Set();
233
234 if (noExtraImports) {
235 for (const exportInfo of exportsInfo.orderedExports) {
236 const name = exportInfo.name;
237 if (exportInfo.getUsed(runtime) === UsageState.Unused) continue;
238 if (name === "__esModule" && isNamespaceImport) {
239 exports.add(name);
240 } else if (importedExportsInfo) {
241 const importedExportInfo =
242 importedExportsInfo.getReadOnlyExportInfo(name);
243 if (importedExportInfo.provided === false) continue;
244 exports.add(name);
245 if (importedExportInfo.provided === true) continue;
246 checked.add(name);
247 } else {
248 exports.add(name);
249 checked.add(name);
250 }
251 }
252 } else if (noExtraExports) {
253 for (const importedExportInfo of importedExportsInfo.orderedExports) {
254 const name = importedExportInfo.name;
255 if (importedExportInfo.provided === false) continue;
256 if (exportsInfo) {
257 const exportInfo = exportsInfo.getReadOnlyExportInfo(name);
258 if (exportInfo.getUsed(runtime) === UsageState.Unused) continue;
259 }
260 exports.add(name);
261 if (importedExportInfo.provided === true) continue;
262 checked.add(name);
263 }
264 if (isNamespaceImport) {
265 exports.add("__esModule");
266 checked.delete("__esModule");
267 }
268 }
269
270 return { exports, checked };
271 }
272
273 serialize(context) {
274 const { write } = context;
275 write(this.asiSafe);
276 write(this.range);
277 write(this.valueRange);
278 write(this.base);
279 write(this.names);
280 write(this.ids);
281 write(this.resultUsed);
282 super.serialize(context);
283 }
284
285 deserialize(context) {
286 const { read } = context;
287 this.asiSafe = read();
288 this.range = read();
289 this.valueRange = read();
290 this.base = read();
291 this.names = read();
292 this.ids = read();
293 this.resultUsed = read();
294 super.deserialize(context);
295 }
296}
297
298makeSerializable(
299 CommonJsExportRequireDependency,
300 "webpack/lib/dependencies/CommonJsExportRequireDependency"
301);
302
303CommonJsExportRequireDependency.Template = class CommonJsExportRequireDependencyTemplate extends (
304 ModuleDependency.Template
305) {
306 /**
307 * @param {Dependency} dependency the dependency for which the template should be applied
308 * @param {ReplaceSource} source the current replace source which can be modified
309 * @param {DependencyTemplateContext} templateContext the context object
310 * @returns {void}
311 */
312 apply(
313 dependency,
314 source,
315 {
316 module,
317 runtimeTemplate,
318 chunkGraph,
319 moduleGraph,
320 runtimeRequirements,
321 runtime
322 }
323 ) {
324 const dep = /** @type {CommonJsExportRequireDependency} */ (dependency);
325 const used = moduleGraph
326 .getExportsInfo(module)
327 .getUsedName(dep.names, runtime);
328
329 const [type, base] = handleDependencyBase(
330 dep.base,
331 module,
332 runtimeRequirements
333 );
334
335 const importedModule = moduleGraph.getModule(dep);
336 let requireExpr = runtimeTemplate.moduleExports({
337 module: importedModule,
338 chunkGraph,
339 request: dep.request,
340 weak: dep.weak,
341 runtimeRequirements
342 });
343 if (importedModule) {
344 const ids = dep.getIds(moduleGraph);
345 const usedImported = moduleGraph
346 .getExportsInfo(importedModule)
347 .getUsedName(ids, runtime);
348 if (usedImported) {
349 const comment = equals(usedImported, ids)
350 ? ""
351 : Template.toNormalComment(propertyAccess(ids)) + " ";
352 requireExpr += `${comment}${propertyAccess(usedImported)}`;
353 }
354 }
355
356 switch (type) {
357 case "expression":
358 source.replace(
359 dep.range[0],
360 dep.range[1] - 1,
361 used
362 ? `${base}${propertyAccess(used)} = ${requireExpr}`
363 : `/* unused reexport */ ${requireExpr}`
364 );
365 return;
366 case "Object.defineProperty":
367 throw new Error("TODO");
368 default:
369 throw new Error("Unexpected type");
370 }
371 }
372};
373
374module.exports = CommonJsExportRequireDependency;