ModuleLibraryPlugin.js 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  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 RuntimeGlobals = require("../RuntimeGlobals");
  8. const Template = require("../Template");
  9. const CommonJsSelfReferenceDependency = require("../dependencies/CommonJsSelfReferenceDependency");
  10. const ConcatenatedModule = require("../optimize/ConcatenatedModule");
  11. const propertyAccess = require("../util/propertyAccess");
  12. const AbstractLibraryPlugin = require("./AbstractLibraryPlugin");
  13. /** @typedef {import("webpack-sources").Source} Source */
  14. /** @typedef {import("../../declarations/WebpackOptions").LibraryOptions} LibraryOptions */
  15. /** @typedef {import("../../declarations/WebpackOptions").LibraryType} LibraryType */
  16. /** @typedef {import("../Chunk")} Chunk */
  17. /** @typedef {import("../Compilation").ChunkHashContext} ChunkHashContext */
  18. /** @typedef {import("../Compiler")} Compiler */
  19. /** @typedef {import("../Module")} Module */
  20. /** @typedef {import("../Module").BuildMeta} BuildMeta */
  21. /** @typedef {import("../javascript/JavascriptModulesPlugin").StartupRenderContext} StartupRenderContext */
  22. /** @typedef {import("../javascript/JavascriptModulesPlugin").ModuleRenderContext} ModuleRenderContext */
  23. /** @typedef {import("../util/Hash")} Hash */
  24. /**
  25. * @template T
  26. * @typedef {import("./AbstractLibraryPlugin").LibraryContext<T>} LibraryContext<T>
  27. */
  28. /**
  29. * @typedef {object} ModuleLibraryPluginOptions
  30. * @property {LibraryType} type
  31. */
  32. /**
  33. * @typedef {object} ModuleLibraryPluginParsed
  34. * @property {string} name
  35. * @property {string | string[]=} export
  36. */
  37. const PLUGIN_NAME = "ModuleLibraryPlugin";
  38. /**
  39. * @typedef {ModuleLibraryPluginParsed} T
  40. * @extends {AbstractLibraryPlugin<ModuleLibraryPluginParsed>}
  41. */
  42. class ModuleLibraryPlugin extends AbstractLibraryPlugin {
  43. /**
  44. * @param {ModuleLibraryPluginOptions} options the plugin options
  45. */
  46. constructor(options) {
  47. super({
  48. pluginName: "ModuleLibraryPlugin",
  49. type: options.type
  50. });
  51. }
  52. /**
  53. * Apply the plugin
  54. * @param {Compiler} compiler the compiler instance
  55. * @returns {void}
  56. */
  57. apply(compiler) {
  58. super.apply(compiler);
  59. compiler.hooks.thisCompilation.tap(PLUGIN_NAME, (compilation) => {
  60. const { onDemandExportsGeneration } =
  61. ConcatenatedModule.getCompilationHooks(compilation);
  62. onDemandExportsGeneration.tap(PLUGIN_NAME, (_module) => true);
  63. });
  64. }
  65. /**
  66. * @param {LibraryOptions} library normalized library option
  67. * @returns {T | false} preprocess as needed by overriding
  68. */
  69. parseOptions(library) {
  70. const { name } = library;
  71. if (name) {
  72. throw new Error(
  73. `Library name must be unset. ${AbstractLibraryPlugin.COMMON_LIBRARY_NAME_MESSAGE}`
  74. );
  75. }
  76. const _name = /** @type {string} */ (name);
  77. return {
  78. name: _name,
  79. export: library.export
  80. };
  81. }
  82. /**
  83. * @param {Source} source source
  84. * @param {Module} module module
  85. * @param {StartupRenderContext} renderContext render context
  86. * @param {LibraryContext<T>} libraryContext context
  87. * @returns {Source} source with library export
  88. */
  89. renderStartup(
  90. source,
  91. module,
  92. { moduleGraph, chunk, codeGenerationResults, inlined, inlinedInIIFE },
  93. { options, compilation }
  94. ) {
  95. const result = new ConcatSource(source);
  96. if (!module.buildMeta || !module.buildMeta.exportsType) {
  97. for (const dependency of module.dependencies) {
  98. if (dependency instanceof CommonJsSelfReferenceDependency) {
  99. result.add(`export { ${RuntimeGlobals.exports} as default }`);
  100. break;
  101. }
  102. }
  103. return result;
  104. }
  105. const exportsInfo = options.export
  106. ? [
  107. moduleGraph.getExportInfo(
  108. module,
  109. Array.isArray(options.export) ? options.export[0] : options.export
  110. )
  111. ]
  112. : moduleGraph.getExportsInfo(module).orderedExports;
  113. const definitions =
  114. inlined && !inlinedInIIFE
  115. ? (module.buildMeta &&
  116. /** @type {GenerationMeta} */ module.buildMeta.exportsFinalName) ||
  117. {}
  118. : {};
  119. /** @type {string[]} */
  120. const shortHandedExports = [];
  121. /** @type {[string, string][]} */
  122. const exports = [];
  123. const isAsync = moduleGraph.isAsync(module);
  124. if (isAsync) {
  125. result.add(
  126. `${RuntimeGlobals.exports} = await ${RuntimeGlobals.exports};\n`
  127. );
  128. }
  129. const varType = compilation.outputOptions.environment.const
  130. ? "const"
  131. : "var";
  132. for (const exportInfo of exportsInfo) {
  133. if (!exportInfo.provided) continue;
  134. let shouldContinue = false;
  135. const reexport = exportInfo.findTarget(moduleGraph, (_m) => true);
  136. if (reexport) {
  137. const exp = moduleGraph.getExportsInfo(reexport.module);
  138. for (const reexportInfo of exp.orderedExports) {
  139. if (
  140. reexportInfo.provided === false &&
  141. reexportInfo.name !== "default" &&
  142. reexportInfo.name === /** @type {string[]} */ (reexport.export)[0]
  143. ) {
  144. shouldContinue = true;
  145. }
  146. }
  147. }
  148. if (shouldContinue) continue;
  149. const originalName = exportInfo.name;
  150. const usedName =
  151. /** @type {string} */
  152. (exportInfo.getUsedName(originalName, chunk.runtime));
  153. /** @type {string | undefined} */
  154. const definition = definitions[usedName];
  155. const finalName =
  156. definition ||
  157. `${RuntimeGlobals.exports}${Template.toIdentifier(originalName)}`;
  158. if (!definition) {
  159. result.add(
  160. `${varType} ${finalName} = ${RuntimeGlobals.exports}${propertyAccess([
  161. usedName
  162. ])};\n`
  163. );
  164. }
  165. if (
  166. finalName &&
  167. (finalName.includes(".") ||
  168. finalName.includes("[") ||
  169. finalName.includes("("))
  170. ) {
  171. if (exportInfo.isReexport()) {
  172. const { data } = codeGenerationResults.get(module, chunk.runtime);
  173. const topLevelDeclarations =
  174. (data && data.get("topLevelDeclarations")) ||
  175. (module.buildInfo && module.buildInfo.topLevelDeclarations);
  176. if (topLevelDeclarations && topLevelDeclarations.has(originalName)) {
  177. const name = `${RuntimeGlobals.exports}${Template.toIdentifier(originalName)}`;
  178. result.add(`${varType} ${name} = ${finalName};\n`);
  179. shortHandedExports.push(`${name} as ${originalName}`);
  180. } else {
  181. exports.push([originalName, finalName]);
  182. }
  183. } else {
  184. exports.push([originalName, finalName]);
  185. }
  186. } else {
  187. shortHandedExports.push(
  188. definition && finalName === originalName
  189. ? finalName
  190. : `${finalName} as ${originalName}`
  191. );
  192. }
  193. }
  194. if (shortHandedExports.length > 0) {
  195. result.add(`export { ${shortHandedExports.join(", ")} };\n`);
  196. }
  197. for (const [exportName, final] of exports) {
  198. result.add(`export ${varType} ${exportName} = ${final};\n`);
  199. }
  200. return result;
  201. }
  202. /**
  203. * @param {Source} source source
  204. * @param {Module} module module
  205. * @param {ModuleRenderContext} renderContext render context
  206. * @param {Omit<LibraryContext<T>, 'options'>} libraryContext context
  207. * @returns {Source} source with library export
  208. */
  209. renderModuleContent(
  210. source,
  211. module,
  212. { factory, inlinedInIIFE },
  213. libraryContext
  214. ) {
  215. // Re-add `factoryExportsBinding` to the source
  216. // when the module is rendered as a factory or treated as an inlined (startup) module but wrapped in an IIFE
  217. if (
  218. (inlinedInIIFE || factory) &&
  219. module.buildMeta &&
  220. module.buildMeta.factoryExportsBinding
  221. ) {
  222. return new ConcatSource(module.buildMeta.factoryExportsBinding, source);
  223. }
  224. return source;
  225. }
  226. }
  227. module.exports = ModuleLibraryPlugin;