AbstractLibraryPlugin.js 10 KB

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