UNPKG

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