AbstractLibraryPlugin.js 10 KB

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