| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375 | /*	MIT License http://www.opensource.org/licenses/mit-license.php	Author Tobias Koppers @sokra*/"use strict";const { ConcatSource } = require("webpack-sources");const { UsageState } = require("../ExportsInfo");const Template = require("../Template");const propertyAccess = require("../util/propertyAccess");const { getEntryRuntime } = require("../util/runtime");const AbstractLibraryPlugin = require("./AbstractLibraryPlugin");/** @typedef {import("webpack-sources").Source} Source *//** @typedef {import("../../declarations/WebpackOptions").LibraryOptions} LibraryOptions *//** @typedef {import("../../declarations/WebpackOptions").LibraryType} LibraryType *//** @typedef {import("../Chunk")} Chunk *//** @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 *//** @template T @typedef {import("./AbstractLibraryPlugin").LibraryContext<T>} LibraryContext<T> */const KEYWORD_REGEX =	/^(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)$/;const IDENTIFIER_REGEX =	/^[\p{L}\p{Nl}$_][\p{L}\p{Nl}$\p{Mn}\p{Mc}\p{Nd}\p{Pc}]*$/iu;/** * Validates the library name by checking for keywords and valid characters * @param {string} name name to be validated * @returns {boolean} true, when valid */const isNameValid = name => {	return !KEYWORD_REGEX.test(name) && IDENTIFIER_REGEX.test(name);};/** * @param {string[]} accessor variable plus properties * @param {number} existingLength items of accessor that are existing already * @param {boolean=} initLast if the last property should also be initialized to an object * @returns {string} code to access the accessor while initializing */const accessWithInit = (accessor, existingLength, initLast = false) => {	// This generates for [a, b, c, d]:	// (((a = typeof a === "undefined" ? {} : a).b = a.b || {}).c = a.b.c || {}).d	const base = accessor[0];	if (accessor.length === 1 && !initLast) return base;	let current =		existingLength > 0			? base			: `(${base} = typeof ${base} === "undefined" ? {} : ${base})`;	// i is the current position in accessor that has been printed	let i = 1;	// all properties printed so far (excluding base)	let propsSoFar;	// if there is existingLength, print all properties until this position as property access	if (existingLength > i) {		propsSoFar = accessor.slice(1, existingLength);		i = existingLength;		current += propertyAccess(propsSoFar);	} else {		propsSoFar = [];	}	// all remaining properties (except the last one when initLast is not set)	// should be printed as initializer	const initUntil = initLast ? accessor.length : accessor.length - 1;	for (; i < initUntil; i++) {		const prop = accessor[i];		propsSoFar.push(prop);		current = `(${current}${propertyAccess([prop])} = ${base}${propertyAccess(			propsSoFar		)} || {})`;	}	// print the last property as property access if not yet printed	if (i < accessor.length)		current = `${current}${propertyAccess([accessor[accessor.length - 1]])}`;	return current;};/** * @typedef {Object} AssignLibraryPluginOptions * @property {LibraryType} type * @property {string[] | "global"} prefix name prefix * @property {string | false} declare declare name as variable * @property {"error"|"static"|"copy"|"assign"} unnamed behavior for unnamed library name * @property {"copy"|"assign"=} named behavior for named library name *//** * @typedef {Object} AssignLibraryPluginParsed * @property {string | string[]} name * @property {string | string[] | undefined} export *//** * @typedef {AssignLibraryPluginParsed} T * @extends {AbstractLibraryPlugin<AssignLibraryPluginParsed>} */class AssignLibraryPlugin extends AbstractLibraryPlugin {	/**	 * @param {AssignLibraryPluginOptions} options the plugin options	 */	constructor(options) {		super({			pluginName: "AssignLibraryPlugin",			type: options.type		});		this.prefix = options.prefix;		this.declare = options.declare;		this.unnamed = options.unnamed;		this.named = options.named || "assign";	}	/**	 * @param {LibraryOptions} library normalized library option	 * @returns {T | false} preprocess as needed by overriding	 */	parseOptions(library) {		const { name } = library;		if (this.unnamed === "error") {			if (typeof name !== "string" && !Array.isArray(name)) {				throw new Error(					`Library name must be a string or string array. ${AbstractLibraryPlugin.COMMON_LIBRARY_NAME_MESSAGE}`				);			}		} else {			if (name && typeof name !== "string" && !Array.isArray(name)) {				throw new Error(					`Library name must be a string, string array or unset. ${AbstractLibraryPlugin.COMMON_LIBRARY_NAME_MESSAGE}`				);			}		}		return {			name: /** @type {string|string[]=} */ (name),			export: library.export		};	}	/**	 * @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,		{ options, compilation, compilation: { moduleGraph } }	) {		const runtime = getEntryRuntime(compilation, entryName);		if (options.export) {			const exportsInfo = moduleGraph.getExportInfo(				module,				Array.isArray(options.export) ? options.export[0] : options.export			);			exportsInfo.setUsed(UsageState.Used, runtime);			exportsInfo.canMangleUse = false;		} else {			const exportsInfo = moduleGraph.getExportsInfo(module);			exportsInfo.setUsedInUnknownWay(runtime);		}		moduleGraph.addExtraReason(module, "used as library export");	}	_getPrefix(compilation) {		return this.prefix === "global"			? [compilation.runtimeTemplate.globalObject]			: this.prefix;	}	_getResolvedFullName(options, chunk, compilation) {		const prefix = this._getPrefix(compilation);		const fullName = options.name ? prefix.concat(options.name) : prefix;		return fullName.map(n =>			compilation.getPath(n, {				chunk			})		);	}	/**	 * @param {Source} source source	 * @param {RenderContext} renderContext render context	 * @param {LibraryContext<T>} libraryContext context	 * @returns {Source} source with library export	 */	render(source, { chunk }, { options, compilation }) {		const fullNameResolved = this._getResolvedFullName(			options,			chunk,			compilation		);		if (this.declare) {			const base = fullNameResolved[0];			if (!isNameValid(base)) {				throw new Error(					`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(						base					)}) or use a different library type (e. g. 'type: "global"', which assign a property on the global scope instead of declaring a variable). ${						AbstractLibraryPlugin.COMMON_LIBRARY_NAME_MESSAGE					}`				);			}			source = new ConcatSource(`${this.declare} ${base};\n`, source);		}		return source;	}	/**	 * @param {Module} module the exporting entry module	 * @param {RenderContext} renderContext render context	 * @param {LibraryContext<T>} libraryContext context	 * @returns {string | undefined} bailout reason	 */	embedInRuntimeBailout(		module,		{ chunk, codeGenerationResults },		{ options, compilation }	) {		const { data } = codeGenerationResults.get(module, chunk.runtime);		const topLevelDeclarations =			(data && data.get("topLevelDeclarations")) ||			(module.buildInfo && module.buildInfo.topLevelDeclarations);		if (!topLevelDeclarations)			return "it doesn't tell about top level declarations.";		const fullNameResolved = this._getResolvedFullName(			options,			chunk,			compilation		);		const base = fullNameResolved[0];		if (topLevelDeclarations.has(base))			return `it declares '${base}' on top-level, which conflicts with the current library output.`;	}	/**	 * @param {RenderContext} renderContext render context	 * @param {LibraryContext<T>} libraryContext context	 * @returns {string | undefined} bailout reason	 */	strictRuntimeBailout({ chunk }, { options, compilation }) {		if (			this.declare ||			this.prefix === "global" ||			this.prefix.length > 0 ||			!options.name		) {			return;		}		return "a global variable is assign and maybe created";	}	/**	 * @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,		{ moduleGraph, chunk },		{ options, compilation }	) {		const fullNameResolved = this._getResolvedFullName(			options,			chunk,			compilation		);		const staticExports = this.unnamed === "static";		const exportAccess = options.export			? propertyAccess(					Array.isArray(options.export) ? options.export : [options.export]			  )			: "";		const result = new ConcatSource(source);		if (staticExports) {			const exportsInfo = moduleGraph.getExportsInfo(module);			const exportTarget = accessWithInit(				fullNameResolved,				this._getPrefix(compilation).length,				true			);			for (const exportInfo of exportsInfo.orderedExports) {				if (!exportInfo.provided) continue;				const nameAccess = propertyAccess([exportInfo.name]);				result.add(					`${exportTarget}${nameAccess} = __webpack_exports__${exportAccess}${nameAccess};\n`				);			}			result.add(				`Object.defineProperty(${exportTarget}, "__esModule", { value: true });\n`			);		} else if (options.name ? this.named === "copy" : this.unnamed === "copy") {			result.add(				`var __webpack_export_target__ = ${accessWithInit(					fullNameResolved,					this._getPrefix(compilation).length,					true				)};\n`			);			let exports = "__webpack_exports__";			if (exportAccess) {				result.add(					`var __webpack_exports_export__ = __webpack_exports__${exportAccess};\n`				);				exports = "__webpack_exports_export__";			}			result.add(				`for(var i in ${exports}) __webpack_export_target__[i] = ${exports}[i];\n`			);			result.add(				`if(${exports}.__esModule) Object.defineProperty(__webpack_export_target__, "__esModule", { value: true });\n`			);		} else {			result.add(				`${accessWithInit(					fullNameResolved,					this._getPrefix(compilation).length,					false				)} = __webpack_exports__${exportAccess};\n`			);		}		return result;	}	/**	 * @param {Chunk} chunk the chunk	 * @param {Set<string>} set runtime requirements	 * @param {LibraryContext<T>} libraryContext context	 * @returns {void}	 */	runtimeRequirements(chunk, set, libraryContext) {		// we don't need to return exports from runtime	}	/**	 * @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, { options, compilation }) {		hash.update("AssignLibraryPlugin");		const fullNameResolved = this._getResolvedFullName(			options,			chunk,			compilation		);		if (options.name ? this.named === "copy" : this.unnamed === "copy") {			hash.update("copy");		}		if (this.declare) {			hash.update(this.declare);		}		hash.update(fullNameResolved.join("."));		if (options.export) {			hash.update(`${options.export}`);		}	}}module.exports = AssignLibraryPlugin;
 |