AbstractLibraryPlugin.js 8.7 KB

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