You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
	
	
		
			302 lines
		
	
	
		
			8.7 KiB
		
	
	
	
		
			JavaScript
		
	
		
		
			
		
	
	
			302 lines
		
	
	
		
			8.7 KiB
		
	
	
	
		
			JavaScript
		
	
| 
											9 months ago
										 | /* | ||
|  | 	MIT License http://www.opensource.org/licenses/mit-license.php
 | ||
|  | 	Author Tobias Koppers @sokra | ||
|  | */ | ||
|  | 
 | ||
|  | "use strict"; | ||
|  | 
 | ||
|  | const RuntimeGlobals = require("../RuntimeGlobals"); | ||
|  | const JavascriptModulesPlugin = require("../javascript/JavascriptModulesPlugin"); | ||
|  | 
 | ||
|  | /** @typedef {import("webpack-sources").Source} Source */ | ||
|  | /** @typedef {import("../../declarations/WebpackOptions").LibraryOptions} LibraryOptions */ | ||
|  | /** @typedef {import("../../declarations/WebpackOptions").LibraryType} LibraryType */ | ||
|  | /** @typedef {import("../Chunk")} Chunk */ | ||
|  | /** @typedef {import("../ChunkGraph")} ChunkGraph */ | ||
|  | /** @typedef {import("../Compilation")} Compilation */ | ||
|  | /** @typedef {import("../Compilation").ChunkHashContext} ChunkHashContext */ | ||
|  | /** @typedef {import("../Compiler")} Compiler */ | ||
|  | /** @typedef {import("../Module")} Module */ | ||
|  | /** @typedef {import("../javascript/JavascriptModulesPlugin").RenderContext} RenderContext */ | ||
|  | /** @typedef {import("../javascript/JavascriptModulesPlugin").StartupRenderContext} StartupRenderContext */ | ||
|  | /** @typedef {import("../util/Hash")} Hash */ | ||
|  | 
 | ||
|  | const COMMON_LIBRARY_NAME_MESSAGE = | ||
|  | 	"Common configuration options that specific library names are 'output.library[.name]', 'entry.xyz.library[.name]', 'ModuleFederationPlugin.name' and 'ModuleFederationPlugin.library[.name]'."; | ||
|  | 
 | ||
|  | /** | ||
|  |  * @template T | ||
|  |  * @typedef {Object} LibraryContext | ||
|  |  * @property {Compilation} compilation | ||
|  |  * @property {ChunkGraph} chunkGraph | ||
|  |  * @property {T} options | ||
|  |  */ | ||
|  | 
 | ||
|  | /** | ||
|  |  * @template T | ||
|  |  */ | ||
|  | class AbstractLibraryPlugin { | ||
|  | 	/** | ||
|  | 	 * @param {Object} options options | ||
|  | 	 * @param {string} options.pluginName name of the plugin | ||
|  | 	 * @param {LibraryType} options.type used library type | ||
|  | 	 */ | ||
|  | 	constructor({ pluginName, type }) { | ||
|  | 		this._pluginName = pluginName; | ||
|  | 		this._type = type; | ||
|  | 		this._parseCache = new WeakMap(); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Apply the plugin | ||
|  | 	 * @param {Compiler} compiler the compiler instance | ||
|  | 	 * @returns {void} | ||
|  | 	 */ | ||
|  | 	apply(compiler) { | ||
|  | 		const { _pluginName } = this; | ||
|  | 		compiler.hooks.thisCompilation.tap(_pluginName, compilation => { | ||
|  | 			compilation.hooks.finishModules.tap( | ||
|  | 				{ name: _pluginName, stage: 10 }, | ||
|  | 				() => { | ||
|  | 					for (const [ | ||
|  | 						name, | ||
|  | 						{ | ||
|  | 							dependencies: deps, | ||
|  | 							options: { library } | ||
|  | 						} | ||
|  | 					] of compilation.entries) { | ||
|  | 						const options = this._parseOptionsCached( | ||
|  | 							library !== undefined | ||
|  | 								? library | ||
|  | 								: compilation.outputOptions.library | ||
|  | 						); | ||
|  | 						if (options !== false) { | ||
|  | 							const dep = deps[deps.length - 1]; | ||
|  | 							if (dep) { | ||
|  | 								const module = compilation.moduleGraph.getModule(dep); | ||
|  | 								if (module) { | ||
|  | 									this.finishEntryModule(module, name, { | ||
|  | 										options, | ||
|  | 										compilation, | ||
|  | 										chunkGraph: compilation.chunkGraph | ||
|  | 									}); | ||
|  | 								} | ||
|  | 							} | ||
|  | 						} | ||
|  | 					} | ||
|  | 				} | ||
|  | 			); | ||
|  | 
 | ||
|  | 			/** | ||
|  | 			 * @param {Chunk} chunk chunk | ||
|  | 			 * @returns {TODO} options for the chunk | ||
|  | 			 */ | ||
|  | 			const getOptionsForChunk = chunk => { | ||
|  | 				if (compilation.chunkGraph.getNumberOfEntryModules(chunk) === 0) | ||
|  | 					return false; | ||
|  | 				const options = chunk.getEntryOptions(); | ||
|  | 				const library = options && options.library; | ||
|  | 				return this._parseOptionsCached( | ||
|  | 					library !== undefined ? library : compilation.outputOptions.library | ||
|  | 				); | ||
|  | 			}; | ||
|  | 
 | ||
|  | 			if ( | ||
|  | 				this.render !== AbstractLibraryPlugin.prototype.render || | ||
|  | 				this.runtimeRequirements !== | ||
|  | 					AbstractLibraryPlugin.prototype.runtimeRequirements | ||
|  | 			) { | ||
|  | 				compilation.hooks.additionalChunkRuntimeRequirements.tap( | ||
|  | 					_pluginName, | ||
|  | 					(chunk, set, { chunkGraph }) => { | ||
|  | 						const options = getOptionsForChunk(chunk); | ||
|  | 						if (options !== false) { | ||
|  | 							this.runtimeRequirements(chunk, set, { | ||
|  | 								options, | ||
|  | 								compilation, | ||
|  | 								chunkGraph | ||
|  | 							}); | ||
|  | 						} | ||
|  | 					} | ||
|  | 				); | ||
|  | 			} | ||
|  | 
 | ||
|  | 			const hooks = JavascriptModulesPlugin.getCompilationHooks(compilation); | ||
|  | 
 | ||
|  | 			if (this.render !== AbstractLibraryPlugin.prototype.render) { | ||
|  | 				hooks.render.tap(_pluginName, (source, renderContext) => { | ||
|  | 					const options = getOptionsForChunk(renderContext.chunk); | ||
|  | 					if (options === false) return source; | ||
|  | 					return this.render(source, renderContext, { | ||
|  | 						options, | ||
|  | 						compilation, | ||
|  | 						chunkGraph: compilation.chunkGraph | ||
|  | 					}); | ||
|  | 				}); | ||
|  | 			} | ||
|  | 
 | ||
|  | 			if ( | ||
|  | 				this.embedInRuntimeBailout !== | ||
|  | 				AbstractLibraryPlugin.prototype.embedInRuntimeBailout | ||
|  | 			) { | ||
|  | 				hooks.embedInRuntimeBailout.tap( | ||
|  | 					_pluginName, | ||
|  | 					(module, renderContext) => { | ||
|  | 						const options = getOptionsForChunk(renderContext.chunk); | ||
|  | 						if (options === false) return; | ||
|  | 						return this.embedInRuntimeBailout(module, renderContext, { | ||
|  | 							options, | ||
|  | 							compilation, | ||
|  | 							chunkGraph: compilation.chunkGraph | ||
|  | 						}); | ||
|  | 					} | ||
|  | 				); | ||
|  | 			} | ||
|  | 
 | ||
|  | 			if ( | ||
|  | 				this.strictRuntimeBailout !== | ||
|  | 				AbstractLibraryPlugin.prototype.strictRuntimeBailout | ||
|  | 			) { | ||
|  | 				hooks.strictRuntimeBailout.tap(_pluginName, renderContext => { | ||
|  | 					const options = getOptionsForChunk(renderContext.chunk); | ||
|  | 					if (options === false) return; | ||
|  | 					return this.strictRuntimeBailout(renderContext, { | ||
|  | 						options, | ||
|  | 						compilation, | ||
|  | 						chunkGraph: compilation.chunkGraph | ||
|  | 					}); | ||
|  | 				}); | ||
|  | 			} | ||
|  | 
 | ||
|  | 			if ( | ||
|  | 				this.renderStartup !== AbstractLibraryPlugin.prototype.renderStartup | ||
|  | 			) { | ||
|  | 				hooks.renderStartup.tap( | ||
|  | 					_pluginName, | ||
|  | 					(source, module, renderContext) => { | ||
|  | 						const options = getOptionsForChunk(renderContext.chunk); | ||
|  | 						if (options === false) return source; | ||
|  | 						return this.renderStartup(source, module, renderContext, { | ||
|  | 							options, | ||
|  | 							compilation, | ||
|  | 							chunkGraph: compilation.chunkGraph | ||
|  | 						}); | ||
|  | 					} | ||
|  | 				); | ||
|  | 			} | ||
|  | 
 | ||
|  | 			hooks.chunkHash.tap(_pluginName, (chunk, hash, context) => { | ||
|  | 				const options = getOptionsForChunk(chunk); | ||
|  | 				if (options === false) return; | ||
|  | 				this.chunkHash(chunk, hash, context, { | ||
|  | 					options, | ||
|  | 					compilation, | ||
|  | 					chunkGraph: compilation.chunkGraph | ||
|  | 				}); | ||
|  | 			}); | ||
|  | 		}); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * @param {LibraryOptions=} library normalized library option | ||
|  | 	 * @returns {T | false} preprocess as needed by overriding | ||
|  | 	 */ | ||
|  | 	_parseOptionsCached(library) { | ||
|  | 		if (!library) return false; | ||
|  | 		if (library.type !== this._type) return false; | ||
|  | 		const cacheEntry = this._parseCache.get(library); | ||
|  | 		if (cacheEntry !== undefined) return cacheEntry; | ||
|  | 		const result = this.parseOptions(library); | ||
|  | 		this._parseCache.set(library, result); | ||
|  | 		return result; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/* istanbul ignore next */ | ||
|  | 	/** | ||
|  | 	 * @abstract | ||
|  | 	 * @param {LibraryOptions} library normalized library option | ||
|  | 	 * @returns {T | false} preprocess as needed by overriding | ||
|  | 	 */ | ||
|  | 	parseOptions(library) { | ||
|  | 		const AbstractMethodError = require("../AbstractMethodError"); | ||
|  | 		throw new AbstractMethodError(); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * @param {Module} module the exporting entry module | ||
|  | 	 * @param {string} entryName the name of the entrypoint | ||
|  | 	 * @param {LibraryContext<T>} libraryContext context | ||
|  | 	 * @returns {void} | ||
|  | 	 */ | ||
|  | 	finishEntryModule(module, entryName, libraryContext) {} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * @param {Module} module the exporting entry module | ||
|  | 	 * @param {RenderContext} renderContext render context | ||
|  | 	 * @param {LibraryContext<T>} libraryContext context | ||
|  | 	 * @returns {string | undefined} bailout reason | ||
|  | 	 */ | ||
|  | 	embedInRuntimeBailout(module, renderContext, libraryContext) { | ||
|  | 		return undefined; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * @param {RenderContext} renderContext render context | ||
|  | 	 * @param {LibraryContext<T>} libraryContext context | ||
|  | 	 * @returns {string | undefined} bailout reason | ||
|  | 	 */ | ||
|  | 	strictRuntimeBailout(renderContext, libraryContext) { | ||
|  | 		return undefined; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * @param {Chunk} chunk the chunk | ||
|  | 	 * @param {Set<string>} set runtime requirements | ||
|  | 	 * @param {LibraryContext<T>} libraryContext context | ||
|  | 	 * @returns {void} | ||
|  | 	 */ | ||
|  | 	runtimeRequirements(chunk, set, libraryContext) { | ||
|  | 		if (this.render !== AbstractLibraryPlugin.prototype.render) | ||
|  | 			set.add(RuntimeGlobals.returnExportsFromRuntime); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * @param {Source} source source | ||
|  | 	 * @param {RenderContext} renderContext render context | ||
|  | 	 * @param {LibraryContext<T>} libraryContext context | ||
|  | 	 * @returns {Source} source with library export | ||
|  | 	 */ | ||
|  | 	render(source, renderContext, libraryContext) { | ||
|  | 		return source; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * @param {Source} source source | ||
|  | 	 * @param {Module} module module | ||
|  | 	 * @param {StartupRenderContext} renderContext render context | ||
|  | 	 * @param {LibraryContext<T>} libraryContext context | ||
|  | 	 * @returns {Source} source with library export | ||
|  | 	 */ | ||
|  | 	renderStartup(source, module, renderContext, libraryContext) { | ||
|  | 		return source; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * @param {Chunk} chunk the chunk | ||
|  | 	 * @param {Hash} hash hash | ||
|  | 	 * @param {ChunkHashContext} chunkHashContext chunk hash context | ||
|  | 	 * @param {LibraryContext<T>} libraryContext context | ||
|  | 	 * @returns {void} | ||
|  | 	 */ | ||
|  | 	chunkHash(chunk, hash, chunkHashContext, libraryContext) { | ||
|  | 		const options = this._parseOptionsCached( | ||
|  | 			libraryContext.compilation.outputOptions.library | ||
|  | 		); | ||
|  | 		hash.update(this._pluginName); | ||
|  | 		hash.update(JSON.stringify(options)); | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | AbstractLibraryPlugin.COMMON_LIBRARY_NAME_MESSAGE = COMMON_LIBRARY_NAME_MESSAGE; | ||
|  | module.exports = AbstractLibraryPlugin; |