source: frontend/node_modules/webpack/lib/library/AssignLibraryPlugin.js

Last change on this file was 9af201e, checked in by MBK <marija.karapandzova@…>, 11 days ago

Fix frontend appearance

  • Property mode set to 100644
File size: 14.3 KB
Line 
1/*
2 MIT License http://www.opensource.org/licenses/mit-license.php
3 Author Tobias Koppers @sokra
4*/
5
6"use strict";
7
8const { ConcatSource } = require("webpack-sources");
9const { UsageState } = require("../ExportsInfo");
10const RuntimeGlobals = require("../RuntimeGlobals");
11const Template = require("../Template");
12const { propertyAccess } = require("../util/property");
13const { getEntryRuntime } = require("../util/runtime");
14const AbstractLibraryPlugin = require("./AbstractLibraryPlugin");
15
16/** @typedef {import("webpack-sources").Source} Source */
17/** @typedef {import("../../declarations/WebpackOptions").LibraryOptions} LibraryOptions */
18/** @typedef {import("../../declarations/WebpackOptions").LibraryType} LibraryType */
19/** @typedef {import("../../declarations/WebpackOptions").LibraryExport} LibraryExport */
20/** @typedef {import("../Chunk")} Chunk */
21/** @typedef {import("../Compilation")} Compilation */
22/** @typedef {import("../Compilation").ChunkHashContext} ChunkHashContext */
23/** @typedef {import("../Module")} Module */
24/** @typedef {import("../Module").RuntimeRequirements} RuntimeRequirements */
25/** @typedef {import("../ExportsInfo").ExportInfoName} ExportInfoName */
26/** @typedef {import("../javascript/JavascriptModulesPlugin").RenderContext} RenderContext */
27/** @typedef {import("../javascript/JavascriptModulesPlugin").StartupRenderContext} StartupRenderContext */
28/** @typedef {import("../util/Hash")} Hash */
29
30/**
31 * Defines the shared type used by this module.
32 * @template T
33 * @typedef {import("./AbstractLibraryPlugin").LibraryContext<T>} LibraryContext<T>
34 */
35
36const KEYWORD_REGEX =
37 /^(?:await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|false|finally|for|function|if|implements|import|in|instanceof|interface|let|new|null|package|private|protected|public|return|super|switch|static|this|throw|try|true|typeof|var|void|while|with|yield)$/;
38const IDENTIFIER_REGEX =
39 /^[\p{L}\p{Nl}$_][\p{L}\p{Nl}$\p{Mn}\p{Mc}\p{Nd}\p{Pc}]*$/iu;
40
41/**
42 * Validates the library name by checking for keywords and valid characters
43 * @param {string} name name to be validated
44 * @returns {boolean} true, when valid
45 */
46const isNameValid = (name) =>
47 !KEYWORD_REGEX.test(name) && IDENTIFIER_REGEX.test(name);
48
49/**
50 * Returns code to access the accessor while initializing.
51 * @param {string[]} accessor variable plus properties
52 * @param {number} existingLength items of accessor that are existing already
53 * @param {boolean=} initLast if the last property should also be initialized to an object
54 * @returns {string} code to access the accessor while initializing
55 */
56const accessWithInit = (accessor, existingLength, initLast = false) => {
57 // This generates for [a, b, c, d]:
58 // (((a = typeof a === "undefined" ? {} : a).b = a.b || {}).c = a.b.c || {}).d
59 const base = accessor[0];
60 if (accessor.length === 1 && !initLast) return base;
61 let current =
62 existingLength > 0
63 ? base
64 : `(${base} = typeof ${base} === "undefined" ? {} : ${base})`;
65
66 // i is the current position in accessor that has been printed
67 let i = 1;
68
69 // all properties printed so far (excluding base)
70 /** @type {string[] | undefined} */
71 let propsSoFar;
72
73 // if there is existingLength, print all properties until this position as property access
74 if (existingLength > i) {
75 propsSoFar = accessor.slice(1, existingLength);
76 i = existingLength;
77 current += propertyAccess(propsSoFar);
78 } else {
79 propsSoFar = [];
80 }
81
82 // all remaining properties (except the last one when initLast is not set)
83 // should be printed as initializer
84 const initUntil = initLast ? accessor.length : accessor.length - 1;
85 for (; i < initUntil; i++) {
86 const prop = accessor[i];
87 propsSoFar.push(prop);
88 current = `(${current}${propertyAccess([prop])} = ${base}${propertyAccess(
89 propsSoFar
90 )} || {})`;
91 }
92
93 // print the last property as property access if not yet printed
94 if (i < accessor.length) {
95 current = `${current}${propertyAccess([accessor[accessor.length - 1]])}`;
96 }
97
98 return current;
99};
100
101/** @typedef {string[] | "global"} LibraryPrefix */
102
103/**
104 * Defines the assign library plugin options type used by this module.
105 * @typedef {object} AssignLibraryPluginOptions
106 * @property {LibraryType} type
107 * @property {LibraryPrefix} prefix name prefix
108 * @property {string | false} declare declare name as variable
109 * @property {"error" | "static" | "copy" | "assign"} unnamed behavior for unnamed library name
110 * @property {"copy" | "assign"=} named behavior for named library name
111 */
112
113/** @typedef {string | string[]} LibraryName */
114
115/**
116 * Defines the assign library plugin parsed type used by this module.
117 * @typedef {object} AssignLibraryPluginParsed
118 * @property {LibraryName} name
119 * @property {LibraryExport=} export
120 */
121
122/**
123 * Represents the assign library plugin runtime component.
124 * @typedef {AssignLibraryPluginParsed} T
125 * @extends {AbstractLibraryPlugin<AssignLibraryPluginParsed>}
126 */
127class AssignLibraryPlugin extends AbstractLibraryPlugin {
128 /**
129 * Creates an instance of AssignLibraryPlugin.
130 * @param {AssignLibraryPluginOptions} options the plugin options
131 */
132 constructor(options) {
133 super({
134 pluginName: "AssignLibraryPlugin",
135 type: options.type
136 });
137 /** @type {AssignLibraryPluginOptions["prefix"]} */
138 this.prefix = options.prefix;
139 /** @type {AssignLibraryPluginOptions["declare"]} */
140 this.declare = options.declare;
141 /** @type {AssignLibraryPluginOptions["unnamed"]} */
142 this.unnamed = options.unnamed;
143 /** @type {AssignLibraryPluginOptions["named"]} */
144 this.named = options.named || "assign";
145 }
146
147 /**
148 * Returns preprocess as needed by overriding.
149 * @param {LibraryOptions} library normalized library option
150 * @returns {T} preprocess as needed by overriding
151 */
152 parseOptions(library) {
153 const { name } = library;
154 if (this.unnamed === "error") {
155 if (typeof name !== "string" && !Array.isArray(name)) {
156 throw new Error(
157 `Library name must be a string or string array. ${AbstractLibraryPlugin.COMMON_LIBRARY_NAME_MESSAGE}`
158 );
159 }
160 } else if (name && typeof name !== "string" && !Array.isArray(name)) {
161 throw new Error(
162 `Library name must be a string, string array or unset. ${AbstractLibraryPlugin.COMMON_LIBRARY_NAME_MESSAGE}`
163 );
164 }
165 const _name = /** @type {LibraryName} */ (name);
166 return {
167 name: _name,
168 export: library.export
169 };
170 }
171
172 /**
173 * Finish entry module.
174 * @param {Module} module the exporting entry module
175 * @param {string} entryName the name of the entrypoint
176 * @param {LibraryContext<T>} libraryContext context
177 * @returns {void}
178 */
179 finishEntryModule(
180 module,
181 entryName,
182 { options, compilation, compilation: { moduleGraph } }
183 ) {
184 const runtime = getEntryRuntime(compilation, entryName);
185 if (options.export) {
186 const exportsInfo = moduleGraph.getExportInfo(
187 module,
188 Array.isArray(options.export) ? options.export[0] : options.export
189 );
190 exportsInfo.setUsed(UsageState.Used, runtime);
191 exportsInfo.canMangleUse = false;
192 } else {
193 const exportsInfo = moduleGraph.getExportsInfo(module);
194 exportsInfo.setUsedInUnknownWay(runtime);
195 }
196 moduleGraph.addExtraReason(module, "used as library export");
197 }
198
199 /**
200 * Returns the prefix.
201 * @param {Compilation} compilation the compilation
202 * @returns {LibraryPrefix} the prefix
203 */
204 _getPrefix(compilation) {
205 return this.prefix === "global"
206 ? [compilation.runtimeTemplate.globalObject]
207 : this.prefix;
208 }
209
210 /**
211 * Get resolved full name.
212 * @param {AssignLibraryPluginParsed} options the library options
213 * @param {Chunk} chunk the chunk
214 * @param {Compilation} compilation the compilation
215 * @returns {string[]} the resolved full name
216 */
217 _getResolvedFullName(options, chunk, compilation) {
218 const prefix = this._getPrefix(compilation);
219 const fullName = options.name
220 ? [
221 ...prefix,
222 ...(Array.isArray(options.name) ? options.name : [options.name])
223 ]
224 : /** @type {string[]} */ (prefix);
225 return fullName.map((n) =>
226 compilation.getPath(n, {
227 chunk
228 })
229 );
230 }
231
232 /**
233 * Returns source with library export.
234 * @param {Source} source source
235 * @param {RenderContext} renderContext render context
236 * @param {LibraryContext<T>} libraryContext context
237 * @returns {Source} source with library export
238 */
239 render(source, { chunk }, { options, compilation }) {
240 const fullNameResolved = this._getResolvedFullName(
241 options,
242 chunk,
243 compilation
244 );
245 if (this.declare) {
246 const base = fullNameResolved[0];
247 if (!isNameValid(base)) {
248 throw new Error(
249 `Library name base (${base}) must be a valid identifier when using a var declaring library type. Either use a valid identifier (e. g. ${Template.toIdentifier(
250 base
251 )}) or use a different library type (e. g. 'type: "global"', which assign a property on the global scope instead of declaring a variable). ${
252 AbstractLibraryPlugin.COMMON_LIBRARY_NAME_MESSAGE
253 }`
254 );
255 }
256 source = new ConcatSource(`${this.declare} ${base};\n`, source);
257 }
258 return source;
259 }
260
261 /**
262 * Embed in runtime bailout.
263 * @param {Module} module the exporting entry module
264 * @param {RenderContext} renderContext render context
265 * @param {LibraryContext<T>} libraryContext context
266 * @returns {string | undefined} bailout reason
267 */
268 embedInRuntimeBailout(
269 module,
270 { chunk, codeGenerationResults },
271 { options, compilation }
272 ) {
273 const { data } = codeGenerationResults.get(module, chunk.runtime);
274 const topLevelDeclarations =
275 (data && data.get("topLevelDeclarations")) ||
276 (module.buildInfo && module.buildInfo.topLevelDeclarations);
277 if (!topLevelDeclarations) {
278 return "it doesn't tell about top level declarations.";
279 }
280 const fullNameResolved = this._getResolvedFullName(
281 options,
282 chunk,
283 compilation
284 );
285 const base = fullNameResolved[0];
286 if (topLevelDeclarations.has(base)) {
287 return `it declares '${base}' on top-level, which conflicts with the current library output.`;
288 }
289 }
290
291 /**
292 * Strict runtime bailout.
293 * @param {RenderContext} renderContext render context
294 * @param {LibraryContext<T>} libraryContext context
295 * @returns {string | undefined} bailout reason
296 */
297 strictRuntimeBailout({ chunk }, { options, compilation }) {
298 if (
299 this.declare ||
300 this.prefix === "global" ||
301 this.prefix.length > 0 ||
302 !options.name
303 ) {
304 return;
305 }
306 return "a global variable is assign and maybe created";
307 }
308
309 /**
310 * Renders source with library export.
311 * @param {Source} source source
312 * @param {Module} module module
313 * @param {StartupRenderContext} renderContext render context
314 * @param {LibraryContext<T>} libraryContext context
315 * @returns {Source} source with library export
316 */
317 renderStartup(
318 source,
319 module,
320 { moduleGraph, chunk },
321 { options, compilation }
322 ) {
323 const fullNameResolved = this._getResolvedFullName(
324 options,
325 chunk,
326 compilation
327 );
328 const staticExports = this.unnamed === "static";
329 const exportAccess = options.export
330 ? propertyAccess(
331 Array.isArray(options.export) ? options.export : [options.export]
332 )
333 : "";
334 const result = new ConcatSource(source);
335 if (staticExports) {
336 const exportsInfo = moduleGraph.getExportsInfo(module);
337 const exportTarget = accessWithInit(
338 fullNameResolved,
339 this._getPrefix(compilation).length,
340 true
341 );
342
343 /** @type {ExportInfoName[]} */
344 const provided = [];
345 for (const exportInfo of exportsInfo.orderedExports) {
346 if (!exportInfo.provided) continue;
347 const nameAccess = propertyAccess([exportInfo.name]);
348 result.add(
349 `${exportTarget}${nameAccess} = ${RuntimeGlobals.exports}${exportAccess}${nameAccess};\n`
350 );
351 provided.push(exportInfo.name);
352 }
353
354 const webpackExportTarget = accessWithInit(
355 fullNameResolved,
356 this._getPrefix(compilation).length,
357 true
358 );
359 /** @type {string} */
360 let exports = RuntimeGlobals.exports;
361 if (exportAccess) {
362 result.add(
363 `var __webpack_exports_export__ = ${RuntimeGlobals.exports}${exportAccess};\n`
364 );
365
366 exports = "__webpack_exports_export__";
367 }
368 result.add(`for(var __webpack_i__ in ${exports}) {\n`);
369 const hasProvided = provided.length > 0;
370 if (hasProvided) {
371 result.add(
372 ` if (${JSON.stringify(provided)}.indexOf(__webpack_i__) === -1) {\n`
373 );
374 }
375 result.add(
376 ` ${
377 hasProvided ? " " : ""
378 }${webpackExportTarget}[__webpack_i__] = ${exports}[__webpack_i__];\n`
379 );
380 if (hasProvided) {
381 result.add(" }\n");
382 }
383 result.add("}\n");
384 result.add(
385 `Object.defineProperty(${exportTarget}, "__esModule", { value: true });\n`
386 );
387 } else if (options.name ? this.named === "copy" : this.unnamed === "copy") {
388 result.add(
389 `var __webpack_export_target__ = ${accessWithInit(
390 fullNameResolved,
391 this._getPrefix(compilation).length,
392 true
393 )};\n`
394 );
395 /** @type {string} */
396 let exports = RuntimeGlobals.exports;
397 if (exportAccess) {
398 result.add(
399 `var __webpack_exports_export__ = ${RuntimeGlobals.exports}${exportAccess};\n`
400 );
401
402 exports = "__webpack_exports_export__";
403 }
404 result.add(
405 `for(var __webpack_i__ in ${exports}) __webpack_export_target__[__webpack_i__] = ${exports}[__webpack_i__];\n`
406 );
407 result.add(
408 `if(${exports}.__esModule) Object.defineProperty(__webpack_export_target__, "__esModule", { value: true });\n`
409 );
410 } else {
411 result.add(
412 `${accessWithInit(
413 fullNameResolved,
414 this._getPrefix(compilation).length,
415 false
416 )} = ${RuntimeGlobals.exports}${exportAccess};\n`
417 );
418 }
419 return result;
420 }
421
422 /**
423 * Processes the provided chunk.
424 * @param {Chunk} chunk the chunk
425 * @param {RuntimeRequirements} set runtime requirements
426 * @param {LibraryContext<T>} libraryContext context
427 * @returns {void}
428 */
429 runtimeRequirements(chunk, set, libraryContext) {
430 set.add(RuntimeGlobals.exports);
431 }
432
433 /**
434 * Processes the provided chunk.
435 * @param {Chunk} chunk the chunk
436 * @param {Hash} hash hash
437 * @param {ChunkHashContext} chunkHashContext chunk hash context
438 * @param {LibraryContext<T>} libraryContext context
439 * @returns {void}
440 */
441 chunkHash(chunk, hash, chunkHashContext, { options, compilation }) {
442 hash.update("AssignLibraryPlugin");
443 const fullNameResolved = this._getResolvedFullName(
444 options,
445 chunk,
446 compilation
447 );
448 if (options.name ? this.named === "copy" : this.unnamed === "copy") {
449 hash.update("copy");
450 }
451 if (this.declare) {
452 hash.update(this.declare);
453 }
454 hash.update(fullNameResolved.join("."));
455 if (options.export) {
456 hash.update(`${options.export}`);
457 }
458 }
459}
460
461module.exports = AssignLibraryPlugin;
Note: See TracBrowser for help on using the repository browser.