AbstractLibraryPlugin.js 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const RuntimeGlobals = require("../RuntimeGlobals");
  7. const JavascriptModulesPlugin = require("../javascript/JavascriptModulesPlugin");
  8. /** @typedef {import("webpack-sources").Source} Source */
  9. /** @typedef {import("../../declarations/WebpackOptions").LibraryOptions} LibraryOptions */
  10. /** @typedef {import("../../declarations/WebpackOptions").LibraryType} LibraryType */
  11. /** @typedef {import("../Chunk")} Chunk */
  12. /** @typedef {import("../ChunkGraph")} ChunkGraph */
  13. /** @typedef {import("../Compilation")} Compilation */
  14. /** @typedef {import("../Compilation").ChunkHashContext} ChunkHashContext */
  15. /** @typedef {import("../Compiler")} Compiler */
  16. /** @typedef {import("../Module")} Module */
  17. /** @typedef {import("../javascript/JavascriptModulesPlugin").RenderContext} RenderContext */
  18. /** @typedef {import("../javascript/JavascriptModulesPlugin").StartupRenderContext} StartupRenderContext */
  19. /** @typedef {import("../util/Hash")} Hash */
  20. const COMMON_LIBRARY_NAME_MESSAGE =
  21. "Common configuration options that specific library names are 'output.library[.name]', 'entry.xyz.library[.name]', 'ModuleFederationPlugin.name' and 'ModuleFederationPlugin.library[.name]'.";
  22. /**
  23. * @template T
  24. * @typedef {object} LibraryContext
  25. * @property {Compilation} compilation
  26. * @property {ChunkGraph} chunkGraph
  27. * @property {T} options
  28. */
  29. /**
  30. * @typedef {object} AbstractLibraryPluginOptions
  31. * @property {string} pluginName name of the plugin
  32. * @property {LibraryType} type used library type
  33. */
  34. /**
  35. * @template T
  36. */
  37. class AbstractLibraryPlugin {
  38. /**
  39. * @param {AbstractLibraryPluginOptions} options options
  40. */
  41. constructor({ pluginName, type }) {
  42. this._pluginName = pluginName;
  43. this._type = type;
  44. this._parseCache = new WeakMap();
  45. }
  46. /**
  47. * Apply the plugin
  48. * @param {Compiler} compiler the compiler instance
  49. * @returns {void}
  50. */
  51. apply(compiler) {
  52. const { _pluginName } = this;
  53. compiler.hooks.thisCompilation.tap(_pluginName, compilation => {
  54. compilation.hooks.finishModules.tap(
  55. { name: _pluginName, stage: 10 },
  56. () => {
  57. for (const [
  58. name,
  59. {
  60. dependencies: deps,
  61. options: { library }
  62. }
  63. ] of compilation.entries) {
  64. const options = this._parseOptionsCached(
  65. library !== undefined
  66. ? library
  67. : compilation.outputOptions.library
  68. );
  69. if (options !== false) {
  70. const dep = deps[deps.length - 1];
  71. if (dep) {
  72. const module = compilation.moduleGraph.getModule(dep);
  73. if (module) {
  74. this.finishEntryModule(module, name, {
  75. options,
  76. compilation,
  77. chunkGraph: compilation.chunkGraph
  78. });
  79. }
  80. }
  81. }
  82. }
  83. }
  84. );
  85. /**
  86. * @param {Chunk} chunk chunk
  87. * @returns {T | false} options for the chunk
  88. */
  89. const getOptionsForChunk = chunk => {
  90. if (compilation.chunkGraph.getNumberOfEntryModules(chunk) === 0) {
  91. return false;
  92. }
  93. const options = chunk.getEntryOptions();
  94. const library = options && options.library;
  95. return this._parseOptionsCached(
  96. library !== undefined ? library : compilation.outputOptions.library
  97. );
  98. };
  99. if (
  100. this.render !== AbstractLibraryPlugin.prototype.render ||
  101. this.runtimeRequirements !==
  102. AbstractLibraryPlugin.prototype.runtimeRequirements
  103. ) {
  104. compilation.hooks.additionalChunkRuntimeRequirements.tap(
  105. _pluginName,
  106. (chunk, set, { chunkGraph }) => {
  107. const options = getOptionsForChunk(chunk);
  108. if (options !== false) {
  109. this.runtimeRequirements(chunk, set, {
  110. options,
  111. compilation,
  112. chunkGraph
  113. });
  114. }
  115. }
  116. );
  117. }
  118. const hooks = JavascriptModulesPlugin.getCompilationHooks(compilation);
  119. if (this.render !== AbstractLibraryPlugin.prototype.render) {
  120. hooks.render.tap(_pluginName, (source, renderContext) => {
  121. const options = getOptionsForChunk(renderContext.chunk);
  122. if (options === false) return source;
  123. return this.render(source, renderContext, {
  124. options,
  125. compilation,
  126. chunkGraph: compilation.chunkGraph
  127. });
  128. });
  129. }
  130. if (
  131. this.embedInRuntimeBailout !==
  132. AbstractLibraryPlugin.prototype.embedInRuntimeBailout
  133. ) {
  134. hooks.embedInRuntimeBailout.tap(
  135. _pluginName,
  136. (module, renderContext) => {
  137. const options = getOptionsForChunk(renderContext.chunk);
  138. if (options === false) return;
  139. return this.embedInRuntimeBailout(module, renderContext, {
  140. options,
  141. compilation,
  142. chunkGraph: compilation.chunkGraph
  143. });
  144. }
  145. );
  146. }
  147. if (
  148. this.strictRuntimeBailout !==
  149. AbstractLibraryPlugin.prototype.strictRuntimeBailout
  150. ) {
  151. hooks.strictRuntimeBailout.tap(_pluginName, renderContext => {
  152. const options = getOptionsForChunk(renderContext.chunk);
  153. if (options === false) return;
  154. return this.strictRuntimeBailout(renderContext, {
  155. options,
  156. compilation,
  157. chunkGraph: compilation.chunkGraph
  158. });
  159. });
  160. }
  161. if (
  162. this.renderStartup !== AbstractLibraryPlugin.prototype.renderStartup
  163. ) {
  164. hooks.renderStartup.tap(
  165. _pluginName,
  166. (source, module, renderContext) => {
  167. const options = getOptionsForChunk(renderContext.chunk);
  168. if (options === false) return source;
  169. return this.renderStartup(source, module, renderContext, {
  170. options,
  171. compilation,
  172. chunkGraph: compilation.chunkGraph
  173. });
  174. }
  175. );
  176. }
  177. hooks.chunkHash.tap(_pluginName, (chunk, hash, context) => {
  178. const options = getOptionsForChunk(chunk);
  179. if (options === false) return;
  180. this.chunkHash(chunk, hash, context, {
  181. options,
  182. compilation,
  183. chunkGraph: compilation.chunkGraph
  184. });
  185. });
  186. });
  187. }
  188. /**
  189. * @param {LibraryOptions=} library normalized library option
  190. * @returns {T | false} preprocess as needed by overriding
  191. */
  192. _parseOptionsCached(library) {
  193. if (!library) return false;
  194. if (library.type !== this._type) return false;
  195. const cacheEntry = this._parseCache.get(library);
  196. if (cacheEntry !== undefined) return cacheEntry;
  197. const result = this.parseOptions(library);
  198. this._parseCache.set(library, result);
  199. return result;
  200. }
  201. /* istanbul ignore next */
  202. /**
  203. * @abstract
  204. * @param {LibraryOptions} library normalized library option
  205. * @returns {T | false} preprocess as needed by overriding
  206. */
  207. parseOptions(library) {
  208. const AbstractMethodError = require("../AbstractMethodError");
  209. throw new AbstractMethodError();
  210. }
  211. /**
  212. * @param {Module} module the exporting entry module
  213. * @param {string} entryName the name of the entrypoint
  214. * @param {LibraryContext<T>} libraryContext context
  215. * @returns {void}
  216. */
  217. finishEntryModule(module, entryName, libraryContext) {}
  218. /**
  219. * @param {Module} module the exporting entry module
  220. * @param {RenderContext} renderContext render context
  221. * @param {LibraryContext<T>} libraryContext context
  222. * @returns {string | undefined} bailout reason
  223. */
  224. embedInRuntimeBailout(module, renderContext, libraryContext) {
  225. return undefined;
  226. }
  227. /**
  228. * @param {RenderContext} renderContext render context
  229. * @param {LibraryContext<T>} libraryContext context
  230. * @returns {string | undefined} bailout reason
  231. */
  232. strictRuntimeBailout(renderContext, libraryContext) {
  233. return undefined;
  234. }
  235. /**
  236. * @param {Chunk} chunk the chunk
  237. * @param {Set<string>} set runtime requirements
  238. * @param {LibraryContext<T>} libraryContext context
  239. * @returns {void}
  240. */
  241. runtimeRequirements(chunk, set, libraryContext) {
  242. if (this.render !== AbstractLibraryPlugin.prototype.render) {
  243. set.add(RuntimeGlobals.returnExportsFromRuntime);
  244. }
  245. }
  246. /**
  247. * @param {Source} source source
  248. * @param {RenderContext} renderContext render context
  249. * @param {LibraryContext<T>} libraryContext context
  250. * @returns {Source} source with library export
  251. */
  252. render(source, renderContext, libraryContext) {
  253. return source;
  254. }
  255. /**
  256. * @param {Source} source source
  257. * @param {Module} module module
  258. * @param {StartupRenderContext} renderContext render context
  259. * @param {LibraryContext<T>} libraryContext context
  260. * @returns {Source} source with library export
  261. */
  262. renderStartup(source, module, renderContext, libraryContext) {
  263. return source;
  264. }
  265. /**
  266. * @param {Chunk} chunk the chunk
  267. * @param {Hash} hash hash
  268. * @param {ChunkHashContext} chunkHashContext chunk hash context
  269. * @param {LibraryContext<T>} libraryContext context
  270. * @returns {void}
  271. */
  272. chunkHash(chunk, hash, chunkHashContext, libraryContext) {
  273. const options = this._parseOptionsCached(
  274. libraryContext.compilation.outputOptions.library
  275. );
  276. hash.update(this._pluginName);
  277. hash.update(JSON.stringify(options));
  278. }
  279. }
  280. AbstractLibraryPlugin.COMMON_LIBRARY_NAME_MESSAGE = COMMON_LIBRARY_NAME_MESSAGE;
  281. module.exports = AbstractLibraryPlugin;