ModuleLibraryPlugin.js 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const { ConcatSource } = require("webpack-sources");
  7. const { UsageState } = require("../ExportsInfo");
  8. const ExternalModule = require("../ExternalModule");
  9. const { JAVASCRIPT_TYPE } = require("../ModuleSourceTypeConstants");
  10. const RuntimeGlobals = require("../RuntimeGlobals");
  11. const Template = require("../Template");
  12. const HarmonyExportExpressionDependency = require("../dependencies/HarmonyExportExpressionDependency");
  13. const HarmonyExportImportedSpecifierDependency = require("../dependencies/HarmonyExportImportedSpecifierDependency");
  14. const HarmonyExportSpecifierDependency = require("../dependencies/HarmonyExportSpecifierDependency");
  15. const JavascriptModulesPlugin = require("../javascript/JavascriptModulesPlugin");
  16. const ConcatenatedModule = require("../optimize/ConcatenatedModule");
  17. const { InlinedUsedName } = require("../optimize/InlineExports");
  18. const {
  19. RESERVED_IDENTIFIER,
  20. SAFE_IDENTIFIER,
  21. propertyAccess
  22. } = require("../util/property");
  23. const { getEntryRuntime, getRuntimeKey } = require("../util/runtime");
  24. const AbstractLibraryPlugin = require("./AbstractLibraryPlugin");
  25. /** @import { Source } from "webpack-sources" */
  26. /**
  27. * @import {
  28. * LibraryOptions,
  29. * LibraryType,
  30. * LibraryExport
  31. * } from "../../declarations/WebpackOptions"
  32. */
  33. /** @import Chunk from "../Chunk" */
  34. /** @import Compilation from "../Compilation" */
  35. /** @import Compiler from "../Compiler" */
  36. /** @import ModuleGraph from "../ModuleGraph" */
  37. /** @import Module, { BuildMeta } from "../Module" */
  38. /**
  39. * @import {
  40. * StartupRenderContext,
  41. * ModuleRenderContext
  42. * } from "../javascript/JavascriptModulesPlugin"
  43. */
  44. /** @import { RuntimeSpec } from "../util/runtime" */
  45. /**
  46. * @import {
  47. * JavascriptModuleBuildMeta
  48. * } from "../javascript/JavascriptModule"
  49. */
  50. /**
  51. * Defines the shared type used by this module.
  52. * @template T
  53. * @typedef {import("./AbstractLibraryPlugin").LibraryContext<T>} LibraryContext<T>
  54. */
  55. /**
  56. * Defines the module library plugin options type used by this module.
  57. * @typedef {object} ModuleLibraryPluginOptions
  58. * @property {LibraryType} type
  59. */
  60. /**
  61. * Defines the module library plugin parsed type used by this module.
  62. * @typedef {object} ModuleLibraryPluginParsed
  63. * @property {string} name
  64. * @property {LibraryExport=} export
  65. */
  66. const PLUGIN_NAME = "ModuleLibraryPlugin";
  67. /**
  68. * The entry modules this plugin renders `export { … }` for, per compilation — the
  69. * only ones whose export definitions may be taken over.
  70. * @type {WeakMap<Compilation, WeakSet<Module>>}
  71. */
  72. const libraryEntryModules = new WeakMap();
  73. /**
  74. * Whether every export the module defines is a binding the library can export
  75. * natively, so the definitions written into the exports object are read by nobody.
  76. * A re-export or a renamed import is not: `renderStartup` reads those back off the
  77. * object, and `treatAsCommonJs` hands the object out as the default export.
  78. * @param {Module} module the entry module
  79. * @param {ModuleGraph} moduleGraph the module graph
  80. * @returns {boolean} true when the definitions may be taken over
  81. */
  82. const definesOnlyNativeExports = (module, moduleGraph) => {
  83. const buildMeta = /** @type {JavascriptModuleBuildMeta} */ (module.buildMeta);
  84. if (buildMeta && buildMeta.treatAsCommonJs) return false;
  85. // A top-level `await` wraps the body in `__webpack_require__.a`, and the
  86. // definitions read declarations inside that wrapper — re-emitting them after the
  87. // module would put them out of scope.
  88. if (moduleGraph.isAsync(module)) return false;
  89. const topLevelDeclarations =
  90. module.buildInfo && module.buildInfo.topLevelDeclarations;
  91. if (!topLevelDeclarations) return false;
  92. for (const dep of module.dependencies) {
  93. // A re-export or an `export default` has no declaration of its own to bind to,
  94. // so `renderStartup` reads its value back off the exports object.
  95. if (
  96. dep instanceof HarmonyExportImportedSpecifierDependency ||
  97. dep instanceof HarmonyExportExpressionDependency
  98. ) {
  99. return false;
  100. }
  101. if (!(dep instanceof HarmonyExportSpecifierDependency)) continue;
  102. if (!topLevelDeclarations.has(dep.id)) return false;
  103. }
  104. return true;
  105. };
  106. /**
  107. * An export name is either an identifier name (reserved words included, e.g.
  108. * `default`) or a string literal (arbitrary module namespace names).
  109. * @param {string} name export name
  110. * @returns {string} name as it can be written after `as`
  111. */
  112. const toExportName = (name) =>
  113. SAFE_IDENTIFIER.test(name) ? name : JSON.stringify(name);
  114. /**
  115. * Whether the export name can also be used as the declared binding name.
  116. * @param {string} name export name
  117. * @returns {boolean} true, when the name can be declared
  118. */
  119. const isDeclarableExportName = (name) =>
  120. SAFE_IDENTIFIER.test(name) && !RESERVED_IDENTIFIER.has(name);
  121. /**
  122. * Represents the module library plugin runtime component.
  123. * @typedef {ModuleLibraryPluginParsed} T
  124. * @extends {AbstractLibraryPlugin<ModuleLibraryPluginParsed>}
  125. */
  126. class ModuleLibraryPlugin extends AbstractLibraryPlugin {
  127. /**
  128. * Creates an instance of ModuleLibraryPlugin.
  129. * @param {ModuleLibraryPluginOptions} options the plugin options
  130. */
  131. constructor(options) {
  132. super({
  133. pluginName: "ModuleLibraryPlugin",
  134. type: options.type
  135. });
  136. }
  137. /**
  138. * Applies the plugin by registering its hooks on the compiler.
  139. * @param {Compiler} compiler the compiler instance
  140. * @returns {void}
  141. */
  142. apply(compiler) {
  143. super.apply(compiler);
  144. compiler.hooks.thisCompilation.tap(PLUGIN_NAME, (compilation) => {
  145. const { onDemandExportsGeneration } =
  146. ConcatenatedModule.getCompilationHooks(compilation);
  147. const javascriptHooks =
  148. JavascriptModulesPlugin.getCompilationHooks(compilation);
  149. onDemandExportsGeneration.tap(
  150. PLUGIN_NAME,
  151. (module, runtimes, source, finalName) => {
  152. /** @type {BuildMeta} */
  153. const buildMeta = module.buildMeta || (module.buildMeta = {});
  154. /** @type {BuildMeta["exportsSourceByRuntime"]} */
  155. const exportsSourceByRuntime =
  156. buildMeta.exportsSourceByRuntime ||
  157. (buildMeta.exportsSourceByRuntime = new Map());
  158. /** @type {BuildMeta["exportsFinalNameByRuntime"]} */
  159. const exportsFinalNameByRuntime =
  160. buildMeta.exportsFinalNameByRuntime ||
  161. (buildMeta.exportsFinalNameByRuntime = new Map());
  162. for (const runtime of runtimes) {
  163. const key = getRuntimeKey(runtime);
  164. exportsSourceByRuntime.set(key, source);
  165. exportsFinalNameByRuntime.set(key, finalName);
  166. }
  167. return true;
  168. }
  169. );
  170. // The same for a plain entry, whose definitions come from a dependency template.
  171. // Placed after the module body, which its value bindings read.
  172. javascriptHooks.onDemandExportsGeneration.tap(
  173. PLUGIN_NAME,
  174. (module, runtime, source, placeAtEnd) => {
  175. const entries = libraryEntryModules.get(compilation);
  176. if (!entries || !entries.has(module)) return;
  177. if (!definesOnlyNativeExports(module, compilation.moduleGraph)) {
  178. return;
  179. }
  180. /** @type {BuildMeta} */
  181. const buildMeta = module.buildMeta || (module.buildMeta = {});
  182. const key = getRuntimeKey(runtime);
  183. if (placeAtEnd) {
  184. const bindings =
  185. buildMeta.exportsBindingSourceByRuntime ||
  186. (buildMeta.exportsBindingSourceByRuntime = new Map());
  187. bindings.set(key, source);
  188. } else {
  189. const sources =
  190. buildMeta.exportsSourceByRuntime ||
  191. (buildMeta.exportsSourceByRuntime = new Map());
  192. // The `__esModule` marker and the definitions both land here, so
  193. // keep whichever arrived first rather than replacing it.
  194. const before = sources.get(key);
  195. sources.set(key, before ? `${before}${source}` : source);
  196. }
  197. return true;
  198. }
  199. );
  200. // `ModuleLibraryPlugin` stashes the on-demand exports source via
  201. // `onDemandExportsGeneration` and only re-emits it when the
  202. // module is wrapped in an IIFE/factory. When the entry is
  203. // inlined directly, the stashed source — and the
  204. // `definePropertyGetters` / `requireScope` runtime helpers it
  205. // pulled in — never make it into the output. Drop those helpers
  206. // from the chunk's set so the bundle stays clean.
  207. compilation.hooks.additionalChunkRuntimeRequirements.tap(
  208. PLUGIN_NAME,
  209. (chunk, set, { chunkGraph, codeGenerationResults }) => {
  210. if (!set.has(RuntimeGlobals.definePropertyGetters)) return;
  211. if (chunkGraph.getNumberOfEntryModules(chunk) !== 1) return;
  212. if (
  213. set.has(RuntimeGlobals.moduleFactories) ||
  214. set.has(RuntimeGlobals.moduleCache) ||
  215. set.has(RuntimeGlobals.interceptModuleExecution) ||
  216. set.has(RuntimeGlobals.module) ||
  217. set.has(RuntimeGlobals.thisAsExports)
  218. ) {
  219. return;
  220. }
  221. if (javascriptHooks.inlineInRuntimeBailout.isUsed()) return;
  222. const [module] = chunkGraph.getChunkEntryModulesIterable(chunk);
  223. const runtimeKey = getRuntimeKey(chunk.runtime);
  224. const buildMeta = module.buildMeta;
  225. const takenOver =
  226. (buildMeta &&
  227. buildMeta.exportsSourceByRuntime &&
  228. buildMeta.exportsSourceByRuntime.has(runtimeKey)) ||
  229. (buildMeta &&
  230. buildMeta.exportsBindingSourceByRuntime &&
  231. buildMeta.exportsBindingSourceByRuntime.has(runtimeKey));
  232. if (!takenOver) return;
  233. const codeGenResult = codeGenerationResults.get(
  234. module,
  235. chunk.runtime
  236. );
  237. const jsSource =
  238. codeGenResult && codeGenResult.sources.get("javascript");
  239. if (
  240. jsSource &&
  241. String(jsSource.source()).includes(`${RuntimeGlobals.require}.`)
  242. ) {
  243. return;
  244. }
  245. // Without `avoidEntryIife`, another rendered module wraps the entry in
  246. // an IIFE, which re-emits the stashed source — its helpers stay.
  247. if (!compilation.options.optimization.avoidEntryIife) {
  248. for (const m of chunkGraph.getChunkModulesIterableBySourceType(
  249. chunk,
  250. JAVASCRIPT_TYPE
  251. ) || []) {
  252. if (m !== module) return;
  253. }
  254. }
  255. // The helpers land in the runtime, not in this chunk, so what still
  256. // needs them is asked of every chunk sharing the entrypoint — the
  257. // runtime chunk, and whatever `splitChunks` moved out of the entry.
  258. /** @type {Set<Chunk>} */
  259. const siblings = new Set([chunk]);
  260. for (const [
  261. ,
  262. entrypoint
  263. ] of chunkGraph.getChunkEntryModulesWithChunkGroupIterable(chunk)) {
  264. if (!entrypoint) continue;
  265. for (const sibling of entrypoint.chunks) siblings.add(sibling);
  266. }
  267. let otherNeedsDefine = false;
  268. let otherNeedsNamespaceObject = false;
  269. let otherNeedsExports = false;
  270. let otherNeedsRequireScope = false;
  271. outer: for (const sibling of siblings) {
  272. for (const m of chunkGraph.getChunkModulesIterable(sibling)) {
  273. if (m === module) continue;
  274. const requirements = chunkGraph.getModuleRuntimeRequirements(
  275. m,
  276. sibling.runtime
  277. );
  278. if (!requirements) continue;
  279. if (requirements.has(RuntimeGlobals.definePropertyGetters)) {
  280. otherNeedsDefine = true;
  281. }
  282. if (requirements.has(RuntimeGlobals.makeNamespaceObject)) {
  283. otherNeedsNamespaceObject = true;
  284. }
  285. if (requirements.has(RuntimeGlobals.exports)) {
  286. otherNeedsExports = true;
  287. }
  288. if (requirements.has(RuntimeGlobals.requireScope)) {
  289. otherNeedsRequireScope = true;
  290. }
  291. if (otherNeedsDefine) break outer;
  292. }
  293. }
  294. if (otherNeedsDefine) return;
  295. set.delete(RuntimeGlobals.definePropertyGetters);
  296. if (!otherNeedsNamespaceObject) {
  297. set.delete(RuntimeGlobals.makeNamespaceObject);
  298. }
  299. if (!otherNeedsExports) set.delete(RuntimeGlobals.exports);
  300. if (!otherNeedsRequireScope) set.delete(RuntimeGlobals.requireScope);
  301. }
  302. );
  303. });
  304. }
  305. /**
  306. * Finish entry module.
  307. * @param {Module} module the exporting entry module
  308. * @param {string} entryName the name of the entrypoint
  309. * @param {LibraryContext<T>} libraryContext context
  310. * @returns {void}
  311. */
  312. finishEntryModule(
  313. module,
  314. entryName,
  315. { options, compilation, compilation: { moduleGraph } }
  316. ) {
  317. const runtime = getEntryRuntime(compilation, entryName);
  318. if (options.export) {
  319. const exportsInfo = moduleGraph.getExportInfo(
  320. module,
  321. Array.isArray(options.export) ? options.export[0] : options.export
  322. );
  323. exportsInfo.setUsed(UsageState.Used, runtime);
  324. exportsInfo.canMangleUse = false;
  325. exportsInfo.canInlineUse = false;
  326. } else {
  327. const exportsInfo = moduleGraph.getExportsInfo(module);
  328. if (
  329. // If the entry module is commonjs, its exports cannot be mangled
  330. (module.buildMeta &&
  331. /** @type {JavascriptModuleBuildMeta} */ (module.buildMeta)
  332. .treatAsCommonJs) ||
  333. // The entry module provides unknown exports
  334. exportsInfo._otherExportsInfo.provided === null
  335. ) {
  336. exportsInfo.setUsedInUnknownWay(runtime);
  337. } else {
  338. exportsInfo.setAllKnownExportsUsed(runtime);
  339. for (const exportInfo of exportsInfo.ownedExports) {
  340. exportInfo.canInlineUse = false;
  341. }
  342. }
  343. }
  344. moduleGraph.addExtraReason(module, "used as library export");
  345. let entries = libraryEntryModules.get(compilation);
  346. if (entries === undefined) {
  347. entries = new WeakSet();
  348. libraryEntryModules.set(compilation, entries);
  349. }
  350. entries.add(module);
  351. }
  352. /**
  353. * Returns preprocess as needed by overriding.
  354. * @param {LibraryOptions} library normalized library option
  355. * @returns {T} preprocess as needed by overriding
  356. */
  357. parseOptions(library) {
  358. const { name } = library;
  359. if (name) {
  360. throw new Error(
  361. `Library name must be unset. ${AbstractLibraryPlugin.COMMON_LIBRARY_NAME_MESSAGE}`
  362. );
  363. }
  364. const _name = /** @type {string} */ (name);
  365. return {
  366. name: _name,
  367. export: library.export
  368. };
  369. }
  370. /**
  371. * Analyze unknown provided exports.
  372. * @param {Source} source source
  373. * @param {Module} module module
  374. * @param {ModuleGraph} moduleGraph moduleGraph
  375. * @param {RuntimeSpec} runtime chunk runtime
  376. * @param {[string, string][]} exports exports
  377. * @param {Set<string>} alreadyRenderedExports already rendered exports
  378. * @returns {ConcatSource} source with null provided exports
  379. */
  380. _analyzeUnknownProvidedExports(
  381. source,
  382. module,
  383. moduleGraph,
  384. runtime,
  385. exports,
  386. alreadyRenderedExports
  387. ) {
  388. const result = new ConcatSource(source);
  389. /** @type {Set<string>} */
  390. const moduleRequests = new Set();
  391. /** @type {Map<string, string | InlinedUsedName>} */
  392. const unknownProvidedExports = new Map();
  393. /**
  394. * Resolves dynamic star reexport.
  395. * @param {Module} module the module
  396. * @param {boolean} isDynamicReexport if module is dynamic reexported
  397. */
  398. const resolveDynamicStarReexport = (module, isDynamicReexport) => {
  399. for (const connection of moduleGraph.getOutgoingConnections(module)) {
  400. const dep = connection.dependency;
  401. // Only handle star-reexport statement
  402. if (
  403. dep instanceof HarmonyExportImportedSpecifierDependency &&
  404. dep.name === null
  405. ) {
  406. const importedModule = connection.resolvedModule;
  407. const importedModuleExportsInfo =
  408. moduleGraph.getExportsInfo(importedModule);
  409. // The imported module provides unknown exports
  410. // So keep the reexports rendered in the bundle
  411. if (
  412. dep.getMode(moduleGraph, runtime).type === "dynamic-reexport" &&
  413. importedModuleExportsInfo._otherExportsInfo.provided === null
  414. ) {
  415. // Handle export * from 'external'
  416. if (importedModule instanceof ExternalModule) {
  417. moduleRequests.add(importedModule.userRequest);
  418. } else {
  419. resolveDynamicStarReexport(importedModule, true);
  420. }
  421. }
  422. // If importer modules existing `dynamic-reexport` dependency
  423. // We should keep export statement rendered in the bundle
  424. else if (isDynamicReexport) {
  425. for (const exportInfo of importedModuleExportsInfo.orderedExports) {
  426. if (!exportInfo.provided || exportInfo.name === "default") {
  427. continue;
  428. }
  429. const originalName = exportInfo.name;
  430. const usedName =
  431. /** @type {string | InlinedUsedName | false} */
  432. (exportInfo.getUsedName(originalName, runtime));
  433. if (!alreadyRenderedExports.has(originalName) && usedName) {
  434. unknownProvidedExports.set(originalName, usedName);
  435. }
  436. }
  437. }
  438. }
  439. }
  440. };
  441. resolveDynamicStarReexport(module, false);
  442. for (const request of moduleRequests) {
  443. result.add(`export * from "${request}";\n`);
  444. }
  445. for (const [origin, used] of unknownProvidedExports) {
  446. exports.push([
  447. origin,
  448. used instanceof InlinedUsedName
  449. ? used.render(
  450. Template.toNormalComment(
  451. `inlined export ${propertyAccess([origin])}`
  452. )
  453. )
  454. : `${RuntimeGlobals.exports}${propertyAccess([used])}`
  455. ]);
  456. }
  457. return result;
  458. }
  459. /**
  460. * Renders source with library export.
  461. * @param {Source} source source
  462. * @param {Module} module module
  463. * @param {StartupRenderContext} renderContext render context
  464. * @param {LibraryContext<T>} libraryContext context
  465. * @returns {Source} source with library export
  466. */
  467. renderStartup(source, module, renderContext, { options, compilation }) {
  468. const {
  469. moduleGraph,
  470. chunk,
  471. codeGenerationResults,
  472. inlined,
  473. inlinedInIIFE,
  474. runtimeTemplate
  475. } = renderContext;
  476. let result = new ConcatSource(source);
  477. const exportInfos = options.export
  478. ? [
  479. moduleGraph.getExportInfo(
  480. module,
  481. Array.isArray(options.export) ? options.export[0] : options.export
  482. )
  483. ]
  484. : moduleGraph.getExportsInfo(module).orderedExports;
  485. const exportsFinalNameByRuntime =
  486. (module.buildMeta &&
  487. module.buildMeta.exportsFinalNameByRuntime &&
  488. module.buildMeta.exportsFinalNameByRuntime.get(
  489. getRuntimeKey(chunk.runtime)
  490. )) ||
  491. {};
  492. const isInlinedEntryWithoutIIFE = inlined && !inlinedInIIFE;
  493. // Direct export bindings from on-demand concatenation
  494. const definitions = isInlinedEntryWithoutIIFE
  495. ? exportsFinalNameByRuntime
  496. : {};
  497. /** @type {string[]} */
  498. const shortHandedExports = [];
  499. /** @type {[string, string][]} */
  500. const exports = [];
  501. /** @type {Set<string>} */
  502. const alreadyRenderedExports = new Set();
  503. /** @type {Set<string>} */
  504. const usedLocalNames = new Set();
  505. /**
  506. * `toIdentifier` is lossy, so distinct export names (`"a b"` and `"a-b"`)
  507. * would otherwise be bound to the same generated name twice.
  508. * @param {string} exportName export name
  509. * @returns {string} unique local name for the export
  510. */
  511. const toLocalName = (exportName) => {
  512. const base = `${RuntimeGlobals.exports}${Template.toIdentifier(exportName)}`;
  513. let name = base;
  514. let i = 0;
  515. while (usedLocalNames.has(name)) name = `${base}_${i++}`;
  516. usedLocalNames.add(name);
  517. return name;
  518. };
  519. const isAsync = moduleGraph.isAsync(module);
  520. const treatAsCommonJs =
  521. module.buildMeta &&
  522. /** @type {JavascriptModuleBuildMeta} */ (module.buildMeta)
  523. .treatAsCommonJs;
  524. const skipRenderDefaultExport = Boolean(treatAsCommonJs);
  525. // A single (non-concatenated) inlined entry is rendered at the top
  526. // level, so its own declarations are top-level bindings we can export
  527. // directly — keeping ESM live bindings instead of snapshotting
  528. // `__webpack_exports__.x` into a `const`. Concatenated entries already
  529. // get direct bindings via `definitions` above.
  530. /** @type {Map<string, string> | undefined} */
  531. let directBindings;
  532. if (isInlinedEntryWithoutIIFE && !treatAsCommonJs) {
  533. const { data } = codeGenerationResults.get(module, chunk.runtime);
  534. const topLevelDeclarations =
  535. (data && data.get("topLevelDeclarations")) ||
  536. (module.buildInfo && module.buildInfo.topLevelDeclarations);
  537. if (topLevelDeclarations) {
  538. for (const dep of module.dependencies) {
  539. if (
  540. dep instanceof HarmonyExportSpecifierDependency &&
  541. topLevelDeclarations.has(dep.id)
  542. ) {
  543. (directBindings || (directBindings = new Map())).set(
  544. dep.name,
  545. dep.id
  546. );
  547. }
  548. }
  549. }
  550. }
  551. // Taken over by `onDemandExportsGeneration` above, and re-emitted by
  552. // `renderModuleContent` only where the module is wrapped — which is not here.
  553. const bindingsTakenOver = Boolean(
  554. module.buildMeta &&
  555. module.buildMeta.exportsBindingSourceByRuntime &&
  556. module.buildMeta.exportsBindingSourceByRuntime.has(
  557. getRuntimeKey(chunk.runtime)
  558. )
  559. );
  560. const moduleExportsInfo = moduleGraph.getExportsInfo(module);
  561. // Define ESM compatibility flag will rely on `__webpack_exports__`
  562. const needHarmonyCompatibilityFlag =
  563. moduleExportsInfo.otherExportsInfo.getUsed(chunk.runtime) !==
  564. UsageState.Unused ||
  565. moduleExportsInfo
  566. .getReadOnlyExportInfo("__esModule")
  567. .getUsed(chunk.runtime) !== UsageState.Unused;
  568. let needExportsDeclaration =
  569. !isInlinedEntryWithoutIIFE || isAsync || needHarmonyCompatibilityFlag;
  570. if (isAsync) {
  571. result.add(
  572. `${RuntimeGlobals.exports} = await ${RuntimeGlobals.exports};\n`
  573. );
  574. }
  575. // Try to find all known exports of the entry module
  576. outer: for (const exportInfo of exportInfos) {
  577. if (!exportInfo.provided) continue;
  578. const originalName = exportInfo.name;
  579. // Skip rendering the default export in some cases
  580. if (skipRenderDefaultExport && originalName === "default") continue;
  581. // Try to find all exports from the reexported modules
  582. const target = exportInfo.findTarget(moduleGraph, (_m) => true);
  583. // A namespace object target (`export * as ns from "./module"`) has no
  584. // export name, so there is nothing that could be missing
  585. if (target && target.export) {
  586. const targetExport = target.export[0];
  587. const reexportsInfo = moduleGraph.getExportsInfo(target.module);
  588. for (const reexportInfo of reexportsInfo.orderedExports) {
  589. if (
  590. reexportInfo.provided === false &&
  591. reexportInfo.name !== "default" &&
  592. reexportInfo.name === targetExport
  593. ) {
  594. continue outer;
  595. }
  596. }
  597. }
  598. // The exports of entry module are never inlined
  599. const usedName =
  600. /** @type {string} */
  601. (exportInfo.getUsedName(originalName, chunk.runtime));
  602. /** @type {string | undefined} */
  603. const definition = definitions[usedName];
  604. /** @type {string | undefined} */
  605. const directLocal =
  606. definition === undefined && directBindings
  607. ? directBindings.get(originalName)
  608. : undefined;
  609. /** @type {string | undefined} */
  610. let finalName;
  611. if (definition) {
  612. finalName = definition;
  613. } else if (directLocal !== undefined) {
  614. // Live binding to the inlined entry's own top-level declaration
  615. finalName = directLocal;
  616. // The declaration is only there for the definitions written into it; with
  617. // those taken over (and not re-emitted here) nothing reads the object.
  618. if (!bindingsTakenOver) needExportsDeclaration = true;
  619. } else {
  620. // Fallback to `__webpack_exports__` property access
  621. // when no direct export binding was found
  622. finalName = toLocalName(originalName);
  623. needExportsDeclaration = true;
  624. result.add(
  625. `${runtimeTemplate.renderConst()} ${finalName} = ${RuntimeGlobals.exports}${propertyAccess(
  626. [usedName]
  627. )};\n`
  628. );
  629. }
  630. if (
  631. // If the name includes `property access` and `call expressions`
  632. finalName &&
  633. (finalName.includes(".") ||
  634. finalName.includes("[") ||
  635. finalName.includes("("))
  636. ) {
  637. if (exportInfo.isReexport()) {
  638. const { data } = codeGenerationResults.get(module, chunk.runtime);
  639. const topLevelDeclarations =
  640. (data && data.get("topLevelDeclarations")) ||
  641. (module.buildInfo && module.buildInfo.topLevelDeclarations);
  642. if (topLevelDeclarations && topLevelDeclarations.has(originalName)) {
  643. const name = toLocalName(originalName);
  644. result.add(
  645. `${runtimeTemplate.renderConst()} ${name} = ${finalName};\n`
  646. );
  647. shortHandedExports.push(`${name} as ${toExportName(originalName)}`);
  648. } else {
  649. exports.push([originalName, finalName]);
  650. }
  651. } else {
  652. exports.push([originalName, finalName]);
  653. }
  654. } else {
  655. shortHandedExports.push(
  656. finalName === originalName
  657. ? finalName
  658. : `${finalName} as ${toExportName(originalName)}`
  659. );
  660. }
  661. alreadyRenderedExports.add(originalName);
  662. }
  663. if (shortHandedExports.length > 0) {
  664. result.add(`export { ${shortHandedExports.join(", ")} };\n`);
  665. }
  666. // Add default export `__webpack_exports__` statement to keep better compatibility
  667. if (treatAsCommonJs) {
  668. needExportsDeclaration = true;
  669. result.add(`export default ${RuntimeGlobals.exports};\n`);
  670. }
  671. result = this._analyzeUnknownProvidedExports(
  672. result,
  673. module,
  674. moduleGraph,
  675. chunk.runtime,
  676. exports,
  677. alreadyRenderedExports
  678. );
  679. for (const [exportName, final] of exports) {
  680. if (isDeclarableExportName(exportName)) {
  681. result.add(
  682. `export ${runtimeTemplate.renderConst()} ${exportName} = ${final};\n`
  683. );
  684. continue;
  685. }
  686. // Bind to a generated name, the export name can't be declared
  687. const name = toLocalName(exportName);
  688. result.add(
  689. `${runtimeTemplate.renderConst()} ${name} = ${final};\nexport { ${name} as ${toExportName(exportName)} };\n`
  690. );
  691. }
  692. if (!needExportsDeclaration) {
  693. renderContext.needExportsDeclaration = false;
  694. }
  695. return result;
  696. }
  697. /**
  698. * Renders module content.
  699. * @param {Source} source source
  700. * @param {Module} module module
  701. * @param {ModuleRenderContext} renderContext render context
  702. * @param {Omit<LibraryContext<T>, "options">} libraryContext context
  703. * @returns {Source} source with library export
  704. */
  705. renderModuleContent(
  706. source,
  707. module,
  708. { factory, inlinedInIIFE, chunk },
  709. libraryContext
  710. ) {
  711. const runtimeKey = getRuntimeKey(chunk.runtime);
  712. const buildMeta = module.buildMeta;
  713. const exportsSource =
  714. buildMeta &&
  715. buildMeta.exportsSourceByRuntime &&
  716. buildMeta.exportsSourceByRuntime.get(runtimeKey);
  717. const bindingSource =
  718. buildMeta &&
  719. buildMeta.exportsBindingSourceByRuntime &&
  720. buildMeta.exportsBindingSourceByRuntime.get(runtimeKey);
  721. // Re-add the exports source in a factory or IIFE-wrapped startup module; value
  722. // bindings read the declarations above them, so those go after the body.
  723. if ((inlinedInIIFE || factory) && (exportsSource || bindingSource)) {
  724. return new ConcatSource(
  725. ...(exportsSource ? [exportsSource] : []),
  726. source,
  727. ...(bindingSource ? ["\n", bindingSource] : [])
  728. );
  729. }
  730. return source;
  731. }
  732. }
  733. module.exports = ModuleLibraryPlugin;