source: frontend/node_modules/webpack/lib/library/ModuleLibraryPlugin.js

Last change on this file was 9af201e, checked in by MBK <marija.karapandzova@…>, 11 days ago

Fix frontend appearance

  • Property mode set to 100644
File size: 17.3 KB
Line 
1/*
2 MIT License http://www.opensource.org/licenses/mit-license.php
3 Author Tobias Koppers @sokra
4*/
5
6"use strict";
7
8const { ConcatSource } = require("webpack-sources");
9const { UsageState } = require("../ExportsInfo");
10const ExternalModule = require("../ExternalModule");
11const RuntimeGlobals = require("../RuntimeGlobals");
12const Template = require("../Template");
13const HarmonyExportImportedSpecifierDependency = require("../dependencies/HarmonyExportImportedSpecifierDependency");
14const JavascriptModulesPlugin = require("../javascript/JavascriptModulesPlugin");
15const ConcatenatedModule = require("../optimize/ConcatenatedModule");
16const { propertyAccess } = require("../util/property");
17const { getEntryRuntime, getRuntimeKey } = require("../util/runtime");
18const AbstractLibraryPlugin = require("./AbstractLibraryPlugin");
19
20/** @typedef {import("webpack-sources").Source} Source */
21/** @typedef {import("../../declarations/WebpackOptions").LibraryOptions} LibraryOptions */
22/** @typedef {import("../../declarations/WebpackOptions").LibraryType} LibraryType */
23/** @typedef {import("../../declarations/WebpackOptions").LibraryExport} LibraryExport */
24/** @typedef {import("../Chunk")} Chunk */
25/** @typedef {import("../Compiler")} Compiler */
26/** @typedef {import("../ModuleGraph")} ModuleGraph */
27/** @typedef {import("../Module")} Module */
28/** @typedef {import("../Module").BuildMeta} BuildMeta */
29/** @typedef {import("../Module").RuntimeRequirements} RuntimeRequirements */
30/** @typedef {import("../javascript/JavascriptModulesPlugin").StartupRenderContext} StartupRenderContext */
31/** @typedef {import("../javascript/JavascriptModulesPlugin").ModuleRenderContext} ModuleRenderContext */
32/** @typedef {import("../util/runtime").RuntimeSpec} RuntimeSpec */
33/** @typedef {import("../RuntimeTemplate")} RuntimeTemplate */
34
35/**
36 * Defines the shared type used by this module.
37 * @template T
38 * @typedef {import("./AbstractLibraryPlugin").LibraryContext<T>} LibraryContext<T>
39 */
40
41/**
42 * Defines the module library plugin options type used by this module.
43 * @typedef {object} ModuleLibraryPluginOptions
44 * @property {LibraryType} type
45 */
46
47/**
48 * Defines the module library plugin parsed type used by this module.
49 * @typedef {object} ModuleLibraryPluginParsed
50 * @property {string} name
51 * @property {LibraryExport=} export
52 */
53
54const PLUGIN_NAME = "ModuleLibraryPlugin";
55
56/**
57 * Represents the module library plugin runtime component.
58 * @typedef {ModuleLibraryPluginParsed} T
59 * @extends {AbstractLibraryPlugin<ModuleLibraryPluginParsed>}
60 */
61class ModuleLibraryPlugin extends AbstractLibraryPlugin {
62 /**
63 * Creates an instance of ModuleLibraryPlugin.
64 * @param {ModuleLibraryPluginOptions} options the plugin options
65 */
66 constructor(options) {
67 super({
68 pluginName: "ModuleLibraryPlugin",
69 type: options.type
70 });
71 }
72
73 /**
74 * Applies the plugin by registering its hooks on the compiler.
75 * @param {Compiler} compiler the compiler instance
76 * @returns {void}
77 */
78 apply(compiler) {
79 super.apply(compiler);
80
81 compiler.hooks.thisCompilation.tap(PLUGIN_NAME, (compilation) => {
82 const { onDemandExportsGeneration } =
83 ConcatenatedModule.getCompilationHooks(compilation);
84 const javascriptHooks =
85 JavascriptModulesPlugin.getCompilationHooks(compilation);
86 onDemandExportsGeneration.tap(
87 PLUGIN_NAME,
88 (module, runtimes, source, finalName) => {
89 /** @type {BuildMeta} */
90 const buildMeta = module.buildMeta || (module.buildMeta = {});
91
92 /** @type {BuildMeta["exportsSourceByRuntime"]} */
93 const exportsSourceByRuntime =
94 buildMeta.exportsSourceByRuntime ||
95 (buildMeta.exportsSourceByRuntime = new Map());
96
97 /** @type {BuildMeta["exportsFinalNameByRuntime"]} */
98 const exportsFinalNameByRuntime =
99 buildMeta.exportsFinalNameByRuntime ||
100 (buildMeta.exportsFinalNameByRuntime = new Map());
101
102 for (const runtime of runtimes) {
103 const key = getRuntimeKey(runtime);
104 exportsSourceByRuntime.set(key, source);
105 exportsFinalNameByRuntime.set(key, finalName);
106 }
107
108 return true;
109 }
110 );
111
112 // `ModuleLibraryPlugin` stashes the on-demand exports source via
113 // `onDemandExportsGeneration` and only re-emits it when the
114 // module is wrapped in an IIFE/factory. When a single concatenated
115 // entry is inlined directly, the stashed source — and the
116 // `definePropertyGetters` / `requireScope` runtime helpers it
117 // pulled in — never make it into the output. Drop those helpers
118 // from the chunk's set in that simple shape so the bundle stays
119 // clean.
120 compilation.hooks.additionalChunkRuntimeRequirements.tap(
121 PLUGIN_NAME,
122 (chunk, set, { chunkGraph, codeGenerationResults }) => {
123 if (!set.has(RuntimeGlobals.definePropertyGetters)) return;
124
125 // Only handle the simple "single concatenated entry"
126 // shape. Anything else (additional modules, multiple
127 // entries, sibling runtime chunks, or chunk-level
128 // requirements that disable inline startup) forces the
129 // module through factory/IIFE rendering, which re-emits
130 // the source.
131 if (chunkGraph.getNumberOfChunkModules(chunk) !== 1) return;
132 if (chunkGraph.getNumberOfEntryModules(chunk) !== 1) return;
133 if (chunkGraph.hasChunkEntryDependentChunks(chunk)) return;
134 if (
135 set.has(RuntimeGlobals.moduleFactories) ||
136 set.has(RuntimeGlobals.moduleCache) ||
137 set.has(RuntimeGlobals.interceptModuleExecution) ||
138 set.has(RuntimeGlobals.module) ||
139 set.has(RuntimeGlobals.thisAsExports)
140 ) {
141 return;
142 }
143 // Anyone tapping `inlineInRuntimeBailout` may force factory
144 // rendering at render time, so conservatively bail out.
145 if (javascriptHooks.inlineInRuntimeBailout.isUsed()) return;
146
147 const [module] = chunkGraph.getChunkEntryModulesIterable(chunk);
148 const exportsSourceByRuntime =
149 module.buildMeta && module.buildMeta.exportsSourceByRuntime;
150 if (
151 !exportsSourceByRuntime ||
152 !exportsSourceByRuntime.has(getRuntimeKey(chunk.runtime))
153 ) {
154 return;
155 }
156 // If the generated source references any
157 // `__webpack_require__.<helper>` (the on-demand `.d(...)`
158 // is stashed, but `.r(__webpack_exports__)` from the ESM
159 // compat flag, namespace objects, deferred externals, ...
160 // stay in the result) the helpers and the require scope
161 // they live in are still needed. The dot in the substring
162 // avoids matching the bare `"__webpack_require__"` string
163 // literals that some test fixtures include.
164 const codeGenResult = codeGenerationResults.get(
165 module,
166 chunk.runtime
167 );
168 const jsSource =
169 codeGenResult && codeGenResult.sources.get("javascript");
170 if (
171 jsSource &&
172 String(jsSource.source()).includes(`${RuntimeGlobals.require}.`)
173 ) {
174 return;
175 }
176
177 set.delete(RuntimeGlobals.definePropertyGetters);
178 set.delete(RuntimeGlobals.exports);
179 set.delete(RuntimeGlobals.requireScope);
180 }
181 );
182 });
183 }
184
185 /**
186 * Finish entry module.
187 * @param {Module} module the exporting entry module
188 * @param {string} entryName the name of the entrypoint
189 * @param {LibraryContext<T>} libraryContext context
190 * @returns {void}
191 */
192 finishEntryModule(
193 module,
194 entryName,
195 { options, compilation, compilation: { moduleGraph } }
196 ) {
197 const runtime = getEntryRuntime(compilation, entryName);
198 if (options.export) {
199 const exportsInfo = moduleGraph.getExportInfo(
200 module,
201 Array.isArray(options.export) ? options.export[0] : options.export
202 );
203 exportsInfo.setUsed(UsageState.Used, runtime);
204 exportsInfo.canMangleUse = false;
205 } else {
206 const exportsInfo = moduleGraph.getExportsInfo(module);
207
208 if (
209 // If the entry module is commonjs, its exports cannot be mangled
210 (module.buildMeta && module.buildMeta.treatAsCommonJs) ||
211 // The entry module provides unknown exports
212 exportsInfo._otherExportsInfo.provided === null
213 ) {
214 exportsInfo.setUsedInUnknownWay(runtime);
215 } else {
216 exportsInfo.setAllKnownExportsUsed(runtime);
217 }
218 }
219 moduleGraph.addExtraReason(module, "used as library export");
220 }
221
222 /**
223 * Returns preprocess as needed by overriding.
224 * @param {LibraryOptions} library normalized library option
225 * @returns {T} preprocess as needed by overriding
226 */
227 parseOptions(library) {
228 const { name } = library;
229 if (name) {
230 throw new Error(
231 `Library name must be unset. ${AbstractLibraryPlugin.COMMON_LIBRARY_NAME_MESSAGE}`
232 );
233 }
234 const _name = /** @type {string} */ (name);
235 return {
236 name: _name,
237 export: library.export
238 };
239 }
240
241 /**
242 * Analyze unknown provided exports.
243 * @param {Source} source source
244 * @param {Module} module module
245 * @param {ModuleGraph} moduleGraph moduleGraph
246 * @param {RuntimeSpec} runtime chunk runtime
247 * @param {[string, string][]} exports exports
248 * @param {Set<string>} alreadyRenderedExports already rendered exports
249 * @returns {ConcatSource} source with null provided exports
250 */
251 _analyzeUnknownProvidedExports(
252 source,
253 module,
254 moduleGraph,
255 runtime,
256 exports,
257 alreadyRenderedExports
258 ) {
259 const result = new ConcatSource(source);
260 /** @type {Set<string>} */
261 const moduleRequests = new Set();
262 /** @type {Map<string, string>} */
263 const unknownProvidedExports = new Map();
264
265 /**
266 * Resolves dynamic star reexport.
267 * @param {Module} module the module
268 * @param {boolean} isDynamicReexport if module is dynamic reexported
269 */
270 const resolveDynamicStarReexport = (module, isDynamicReexport) => {
271 for (const connection of moduleGraph.getOutgoingConnections(module)) {
272 const dep = connection.dependency;
273
274 // Only handle star-reexport statement
275 if (
276 dep instanceof HarmonyExportImportedSpecifierDependency &&
277 dep.name === null
278 ) {
279 const importedModule = connection.resolvedModule;
280 const importedModuleExportsInfo =
281 moduleGraph.getExportsInfo(importedModule);
282
283 // The imported module provides unknown exports
284 // So keep the reexports rendered in the bundle
285 if (
286 dep.getMode(moduleGraph, runtime).type === "dynamic-reexport" &&
287 importedModuleExportsInfo._otherExportsInfo.provided === null
288 ) {
289 // Handle export * from 'external'
290 if (importedModule instanceof ExternalModule) {
291 moduleRequests.add(importedModule.userRequest);
292 } else {
293 resolveDynamicStarReexport(importedModule, true);
294 }
295 }
296 // If importer modules existing `dynamic-reexport` dependency
297 // We should keep export statement rendered in the bundle
298 else if (isDynamicReexport) {
299 for (const exportInfo of importedModuleExportsInfo.orderedExports) {
300 if (!exportInfo.provided || exportInfo.name === "default") {
301 continue;
302 }
303 const originalName = exportInfo.name;
304 const usedName = exportInfo.getUsedName(originalName, runtime);
305
306 if (!alreadyRenderedExports.has(originalName) && usedName) {
307 unknownProvidedExports.set(originalName, usedName);
308 }
309 }
310 }
311 }
312 }
313 };
314
315 resolveDynamicStarReexport(module, false);
316
317 for (const request of moduleRequests) {
318 result.add(`export * from "${request}";\n`);
319 }
320
321 for (const [origin, used] of unknownProvidedExports) {
322 exports.push([
323 origin,
324 `${RuntimeGlobals.exports}${propertyAccess([used])}`
325 ]);
326 }
327
328 return result;
329 }
330
331 /**
332 * Renders source with library export.
333 * @param {Source} source source
334 * @param {Module} module module
335 * @param {StartupRenderContext} renderContext render context
336 * @param {LibraryContext<T>} libraryContext context
337 * @returns {Source} source with library export
338 */
339 renderStartup(source, module, renderContext, { options, compilation }) {
340 const {
341 moduleGraph,
342 chunk,
343 codeGenerationResults,
344 inlined,
345 inlinedInIIFE,
346 runtimeTemplate
347 } = renderContext;
348 let result = new ConcatSource(source);
349 const exportInfos = options.export
350 ? [
351 moduleGraph.getExportInfo(
352 module,
353 Array.isArray(options.export) ? options.export[0] : options.export
354 )
355 ]
356 : moduleGraph.getExportsInfo(module).orderedExports;
357
358 const exportsFinalNameByRuntime =
359 (module.buildMeta &&
360 module.buildMeta.exportsFinalNameByRuntime &&
361 module.buildMeta.exportsFinalNameByRuntime.get(
362 getRuntimeKey(chunk.runtime)
363 )) ||
364 {};
365
366 const isInlinedEntryWithoutIIFE = inlined && !inlinedInIIFE;
367 // Direct export bindings from on-demand concatenation
368 const definitions = isInlinedEntryWithoutIIFE
369 ? exportsFinalNameByRuntime
370 : {};
371
372 /** @type {string[]} */
373 const shortHandedExports = [];
374 /** @type {[string, string][]} */
375 const exports = [];
376 /** @type {Set<string>} */
377 const alreadyRenderedExports = new Set();
378
379 const isAsync = moduleGraph.isAsync(module);
380
381 const treatAsCommonJs =
382 module.buildMeta && module.buildMeta.treatAsCommonJs;
383 const skipRenderDefaultExport = Boolean(treatAsCommonJs);
384
385 const moduleExportsInfo = moduleGraph.getExportsInfo(module);
386
387 // Define ESM compatibility flag will rely on `__webpack_exports__`
388 const needHarmonyCompatibilityFlag =
389 moduleExportsInfo.otherExportsInfo.getUsed(chunk.runtime) !==
390 UsageState.Unused ||
391 moduleExportsInfo
392 .getReadOnlyExportInfo("__esModule")
393 .getUsed(chunk.runtime) !== UsageState.Unused;
394
395 let needExportsDeclaration =
396 !isInlinedEntryWithoutIIFE || isAsync || needHarmonyCompatibilityFlag;
397
398 if (isAsync) {
399 result.add(
400 `${RuntimeGlobals.exports} = await ${RuntimeGlobals.exports};\n`
401 );
402 }
403
404 // Try to find all known exports of the entry module
405 outer: for (const exportInfo of exportInfos) {
406 if (!exportInfo.provided) continue;
407
408 const originalName = exportInfo.name;
409 // Skip rendering the default export in some cases
410 if (skipRenderDefaultExport && originalName === "default") continue;
411
412 // Try to find all exports from the reexported modules
413 const target = exportInfo.findTarget(moduleGraph, (_m) => true);
414 if (target) {
415 const reexportsInfo = moduleGraph.getExportsInfo(target.module);
416 for (const reexportInfo of reexportsInfo.orderedExports) {
417 if (
418 reexportInfo.provided === false &&
419 reexportInfo.name !== "default" &&
420 reexportInfo.name === /** @type {string[]} */ (target.export)[0]
421 ) {
422 continue outer;
423 }
424 }
425 }
426
427 const usedName =
428 /** @type {string} */
429 (exportInfo.getUsedName(originalName, chunk.runtime));
430 /** @type {string | undefined} */
431 const definition = definitions[usedName];
432 /** @type {string | undefined} */
433 let finalName;
434
435 if (definition) {
436 finalName = definition;
437 } else {
438 // Fallback to `__webpack_exports__` property access
439 // when no direct export binding was found
440 finalName = `${RuntimeGlobals.exports}${Template.toIdentifier(originalName)}`;
441 needExportsDeclaration = true;
442 result.add(
443 `${runtimeTemplate.renderConst()} ${finalName} = ${RuntimeGlobals.exports}${propertyAccess(
444 [usedName]
445 )};\n`
446 );
447 }
448
449 if (
450 // If the name includes `property access` and `call expressions`
451 finalName &&
452 (finalName.includes(".") ||
453 finalName.includes("[") ||
454 finalName.includes("("))
455 ) {
456 if (exportInfo.isReexport()) {
457 const { data } = codeGenerationResults.get(module, chunk.runtime);
458 const topLevelDeclarations =
459 (data && data.get("topLevelDeclarations")) ||
460 (module.buildInfo && module.buildInfo.topLevelDeclarations);
461
462 if (topLevelDeclarations && topLevelDeclarations.has(originalName)) {
463 const name = `${RuntimeGlobals.exports}${Template.toIdentifier(originalName)}`;
464 result.add(
465 `${runtimeTemplate.renderConst()} ${name} = ${finalName};\n`
466 );
467 shortHandedExports.push(`${name} as ${originalName}`);
468 } else {
469 exports.push([originalName, finalName]);
470 }
471 } else {
472 exports.push([originalName, finalName]);
473 }
474 } else {
475 shortHandedExports.push(
476 definition && finalName === originalName
477 ? finalName
478 : `${finalName} as ${originalName}`
479 );
480 }
481
482 alreadyRenderedExports.add(originalName);
483 }
484
485 // Add default export `__webpack_exports__` statement to keep better compatibility
486 if (treatAsCommonJs) {
487 needExportsDeclaration = true;
488 shortHandedExports.push(`${RuntimeGlobals.exports} as default`);
489 }
490
491 if (shortHandedExports.length > 0) {
492 result.add(`export { ${shortHandedExports.join(", ")} };\n`);
493 }
494
495 result = this._analyzeUnknownProvidedExports(
496 result,
497 module,
498 moduleGraph,
499 chunk.runtime,
500 exports,
501 alreadyRenderedExports
502 );
503
504 for (const [exportName, final] of exports) {
505 result.add(
506 `export ${runtimeTemplate.renderConst()} ${exportName} = ${final};\n`
507 );
508 }
509
510 if (!needExportsDeclaration) {
511 renderContext.needExportsDeclaration = false;
512 }
513
514 return result;
515 }
516
517 /**
518 * Renders module content.
519 * @param {Source} source source
520 * @param {Module} module module
521 * @param {ModuleRenderContext} renderContext render context
522 * @param {Omit<LibraryContext<T>, "options">} libraryContext context
523 * @returns {Source} source with library export
524 */
525 renderModuleContent(
526 source,
527 module,
528 { factory, inlinedInIIFE, chunk },
529 libraryContext
530 ) {
531 const exportsSource =
532 module.buildMeta &&
533 module.buildMeta.exportsSourceByRuntime &&
534 module.buildMeta.exportsSourceByRuntime.get(getRuntimeKey(chunk.runtime));
535
536 // Re-add the module's exports source when rendered in factory
537 // or as an inlined startup module wrapped in an IIFE
538 if ((inlinedInIIFE || factory) && exportsSource) {
539 return new ConcatSource(exportsSource, source);
540 }
541 return source;
542 }
543}
544
545module.exports = ModuleLibraryPlugin;
Note: See TracBrowser for help on using the repository browser.