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.
		
		
		
		
		
			
		
			
	
	
		
			394 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			JavaScript
		
	
		
		
			
		
	
	
			394 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			JavaScript
		
	
| 
											9 months ago
										 | /* | ||
|  | 	MIT License http://www.opensource.org/licenses/mit-license.php
 | ||
|  | 	Author Tobias Koppers @sokra | ||
|  | */ | ||
|  | 
 | ||
|  | "use strict"; | ||
|  | 
 | ||
|  | const Dependency = require("../Dependency"); | ||
|  | const { UsageState } = require("../ExportsInfo"); | ||
|  | const Template = require("../Template"); | ||
|  | const { equals } = require("../util/ArrayHelpers"); | ||
|  | const makeSerializable = require("../util/makeSerializable"); | ||
|  | const propertyAccess = require("../util/propertyAccess"); | ||
|  | const { handleDependencyBase } = require("./CommonJsDependencyHelpers"); | ||
|  | const ModuleDependency = require("./ModuleDependency"); | ||
|  | const processExportInfo = require("./processExportInfo"); | ||
|  | 
 | ||
|  | /** @typedef {import("webpack-sources").ReplaceSource} ReplaceSource */ | ||
|  | /** @typedef {import("../Dependency").ExportsSpec} ExportsSpec */ | ||
|  | /** @typedef {import("../Dependency").ReferencedExport} ReferencedExport */ | ||
|  | /** @typedef {import("../Dependency").TRANSITIVE} TRANSITIVE */ | ||
|  | /** @typedef {import("../DependencyTemplate").DependencyTemplateContext} DependencyTemplateContext */ | ||
|  | /** @typedef {import("../Module")} Module */ | ||
|  | /** @typedef {import("../ModuleGraph")} ModuleGraph */ | ||
|  | /** @typedef {import("../javascript/JavascriptParser").Range} Range */ | ||
|  | /** @typedef {import("../serialization/ObjectMiddleware").ObjectDeserializerContext} ObjectDeserializerContext */ | ||
|  | /** @typedef {import("../serialization/ObjectMiddleware").ObjectSerializerContext} ObjectSerializerContext */ | ||
|  | /** @typedef {import("../util/runtime").RuntimeSpec} RuntimeSpec */ | ||
|  | /** @typedef {import("./CommonJsDependencyHelpers").CommonJSDependencyBaseKeywords} CommonJSDependencyBaseKeywords */ | ||
|  | 
 | ||
|  | const idsSymbol = Symbol("CommonJsExportRequireDependency.ids"); | ||
|  | 
 | ||
|  | const EMPTY_OBJECT = {}; | ||
|  | 
 | ||
|  | class CommonJsExportRequireDependency extends ModuleDependency { | ||
|  | 	/** | ||
|  | 	 * @param {Range} range range | ||
|  | 	 * @param {Range} valueRange value range | ||
|  | 	 * @param {CommonJSDependencyBaseKeywords} base base | ||
|  | 	 * @param {string[]} names names | ||
|  | 	 * @param {string} request request | ||
|  | 	 * @param {string[]} ids ids | ||
|  | 	 * @param {boolean} resultUsed true, when the result is used | ||
|  | 	 */ | ||
|  | 	constructor(range, valueRange, base, names, request, ids, resultUsed) { | ||
|  | 		super(request); | ||
|  | 		this.range = range; | ||
|  | 		this.valueRange = valueRange; | ||
|  | 		this.base = base; | ||
|  | 		this.names = names; | ||
|  | 		this.ids = ids; | ||
|  | 		this.resultUsed = resultUsed; | ||
|  | 		this.asiSafe = undefined; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	get type() { | ||
|  | 		return "cjs export require"; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * @returns {boolean | TRANSITIVE} true, when changes to the referenced module could affect the referencing module; TRANSITIVE, when changes to the referenced module could affect referencing modules of the referencing module | ||
|  | 	 */ | ||
|  | 	couldAffectReferencingModule() { | ||
|  | 		return Dependency.TRANSITIVE; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * @param {ModuleGraph} moduleGraph the module graph | ||
|  | 	 * @returns {string[]} the imported id | ||
|  | 	 */ | ||
|  | 	getIds(moduleGraph) { | ||
|  | 		return moduleGraph.getMeta(this)[idsSymbol] || this.ids; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * @param {ModuleGraph} moduleGraph the module graph | ||
|  | 	 * @param {string[]} ids the imported ids | ||
|  | 	 * @returns {void} | ||
|  | 	 */ | ||
|  | 	setIds(moduleGraph, ids) { | ||
|  | 		moduleGraph.getMeta(this)[idsSymbol] = ids; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Returns list of exports referenced by this dependency | ||
|  | 	 * @param {ModuleGraph} moduleGraph module graph | ||
|  | 	 * @param {RuntimeSpec} runtime the runtime for which the module is analysed | ||
|  | 	 * @returns {(string[] | ReferencedExport)[]} referenced exports | ||
|  | 	 */ | ||
|  | 	getReferencedExports(moduleGraph, runtime) { | ||
|  | 		const ids = this.getIds(moduleGraph); | ||
|  | 		const getFullResult = () => { | ||
|  | 			if (ids.length === 0) { | ||
|  | 				return Dependency.EXPORTS_OBJECT_REFERENCED; | ||
|  | 			} else { | ||
|  | 				return [ | ||
|  | 					{ | ||
|  | 						name: ids, | ||
|  | 						canMangle: false | ||
|  | 					} | ||
|  | 				]; | ||
|  | 			} | ||
|  | 		}; | ||
|  | 		if (this.resultUsed) return getFullResult(); | ||
|  | 		let exportsInfo = moduleGraph.getExportsInfo( | ||
|  | 			moduleGraph.getParentModule(this) | ||
|  | 		); | ||
|  | 		for (const name of this.names) { | ||
|  | 			const exportInfo = exportsInfo.getReadOnlyExportInfo(name); | ||
|  | 			const used = exportInfo.getUsed(runtime); | ||
|  | 			if (used === UsageState.Unused) return Dependency.NO_EXPORTS_REFERENCED; | ||
|  | 			if (used !== UsageState.OnlyPropertiesUsed) return getFullResult(); | ||
|  | 			exportsInfo = exportInfo.exportsInfo; | ||
|  | 			if (!exportsInfo) return getFullResult(); | ||
|  | 		} | ||
|  | 		if (exportsInfo.otherExportsInfo.getUsed(runtime) !== UsageState.Unused) { | ||
|  | 			return getFullResult(); | ||
|  | 		} | ||
|  | 		/** @type {string[][]} */ | ||
|  | 		const referencedExports = []; | ||
|  | 		for (const exportInfo of exportsInfo.orderedExports) { | ||
|  | 			processExportInfo( | ||
|  | 				runtime, | ||
|  | 				referencedExports, | ||
|  | 				ids.concat(exportInfo.name), | ||
|  | 				exportInfo, | ||
|  | 				false | ||
|  | 			); | ||
|  | 		} | ||
|  | 		return referencedExports.map(name => ({ | ||
|  | 			name, | ||
|  | 			canMangle: false | ||
|  | 		})); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Returns the exported names | ||
|  | 	 * @param {ModuleGraph} moduleGraph module graph | ||
|  | 	 * @returns {ExportsSpec | undefined} export names | ||
|  | 	 */ | ||
|  | 	getExports(moduleGraph) { | ||
|  | 		const ids = this.getIds(moduleGraph); | ||
|  | 		if (this.names.length === 1) { | ||
|  | 			const name = this.names[0]; | ||
|  | 			const from = moduleGraph.getConnection(this); | ||
|  | 			if (!from) return; | ||
|  | 			return { | ||
|  | 				exports: [ | ||
|  | 					{ | ||
|  | 						name, | ||
|  | 						from, | ||
|  | 						export: ids.length === 0 ? null : ids, | ||
|  | 						// we can't mangle names that are in an empty object
 | ||
|  | 						// because one could access the prototype property
 | ||
|  | 						// when export isn't set yet
 | ||
|  | 						canMangle: !(name in EMPTY_OBJECT) && false | ||
|  | 					} | ||
|  | 				], | ||
|  | 				dependencies: [from.module] | ||
|  | 			}; | ||
|  | 		} else if (this.names.length > 0) { | ||
|  | 			const name = this.names[0]; | ||
|  | 			return { | ||
|  | 				exports: [ | ||
|  | 					{ | ||
|  | 						name, | ||
|  | 						// we can't mangle names that are in an empty object
 | ||
|  | 						// because one could access the prototype property
 | ||
|  | 						// when export isn't set yet
 | ||
|  | 						canMangle: !(name in EMPTY_OBJECT) && false | ||
|  | 					} | ||
|  | 				], | ||
|  | 				dependencies: undefined | ||
|  | 			}; | ||
|  | 		} else { | ||
|  | 			const from = moduleGraph.getConnection(this); | ||
|  | 			if (!from) return; | ||
|  | 			const reexportInfo = this.getStarReexports( | ||
|  | 				moduleGraph, | ||
|  | 				undefined, | ||
|  | 				from.module | ||
|  | 			); | ||
|  | 			if (reexportInfo) { | ||
|  | 				return { | ||
|  | 					exports: Array.from(reexportInfo.exports, name => { | ||
|  | 						return { | ||
|  | 							name, | ||
|  | 							from, | ||
|  | 							export: ids.concat(name), | ||
|  | 							canMangle: !(name in EMPTY_OBJECT) && false | ||
|  | 						}; | ||
|  | 					}), | ||
|  | 					// TODO handle deep reexports
 | ||
|  | 					dependencies: [from.module] | ||
|  | 				}; | ||
|  | 			} else { | ||
|  | 				return { | ||
|  | 					exports: true, | ||
|  | 					from: ids.length === 0 ? from : undefined, | ||
|  | 					canMangle: false, | ||
|  | 					dependencies: [from.module] | ||
|  | 				}; | ||
|  | 			} | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * @param {ModuleGraph} moduleGraph the module graph | ||
|  | 	 * @param {RuntimeSpec} runtime the runtime | ||
|  | 	 * @param {Module} importedModule the imported module (optional) | ||
|  | 	 * @returns {{exports?: Set<string>, checked?: Set<string>}} information | ||
|  | 	 */ | ||
|  | 	getStarReexports( | ||
|  | 		moduleGraph, | ||
|  | 		runtime, | ||
|  | 		importedModule = moduleGraph.getModule(this) | ||
|  | 	) { | ||
|  | 		let importedExportsInfo = moduleGraph.getExportsInfo(importedModule); | ||
|  | 		const ids = this.getIds(moduleGraph); | ||
|  | 		if (ids.length > 0) | ||
|  | 			importedExportsInfo = importedExportsInfo.getNestedExportsInfo(ids); | ||
|  | 		let exportsInfo = moduleGraph.getExportsInfo( | ||
|  | 			moduleGraph.getParentModule(this) | ||
|  | 		); | ||
|  | 		if (this.names.length > 0) | ||
|  | 			exportsInfo = exportsInfo.getNestedExportsInfo(this.names); | ||
|  | 
 | ||
|  | 		const noExtraExports = | ||
|  | 			importedExportsInfo && | ||
|  | 			importedExportsInfo.otherExportsInfo.provided === false; | ||
|  | 		const noExtraImports = | ||
|  | 			exportsInfo && | ||
|  | 			exportsInfo.otherExportsInfo.getUsed(runtime) === UsageState.Unused; | ||
|  | 
 | ||
|  | 		if (!noExtraExports && !noExtraImports) { | ||
|  | 			return; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		const isNamespaceImport = | ||
|  | 			importedModule.getExportsType(moduleGraph, false) === "namespace"; | ||
|  | 
 | ||
|  | 		/** @type {Set<string>} */ | ||
|  | 		const exports = new Set(); | ||
|  | 		/** @type {Set<string>} */ | ||
|  | 		const checked = new Set(); | ||
|  | 
 | ||
|  | 		if (noExtraImports) { | ||
|  | 			for (const exportInfo of exportsInfo.orderedExports) { | ||
|  | 				const name = exportInfo.name; | ||
|  | 				if (exportInfo.getUsed(runtime) === UsageState.Unused) continue; | ||
|  | 				if (name === "__esModule" && isNamespaceImport) { | ||
|  | 					exports.add(name); | ||
|  | 				} else if (importedExportsInfo) { | ||
|  | 					const importedExportInfo = | ||
|  | 						importedExportsInfo.getReadOnlyExportInfo(name); | ||
|  | 					if (importedExportInfo.provided === false) continue; | ||
|  | 					exports.add(name); | ||
|  | 					if (importedExportInfo.provided === true) continue; | ||
|  | 					checked.add(name); | ||
|  | 				} else { | ||
|  | 					exports.add(name); | ||
|  | 					checked.add(name); | ||
|  | 				} | ||
|  | 			} | ||
|  | 		} else if (noExtraExports) { | ||
|  | 			for (const importedExportInfo of importedExportsInfo.orderedExports) { | ||
|  | 				const name = importedExportInfo.name; | ||
|  | 				if (importedExportInfo.provided === false) continue; | ||
|  | 				if (exportsInfo) { | ||
|  | 					const exportInfo = exportsInfo.getReadOnlyExportInfo(name); | ||
|  | 					if (exportInfo.getUsed(runtime) === UsageState.Unused) continue; | ||
|  | 				} | ||
|  | 				exports.add(name); | ||
|  | 				if (importedExportInfo.provided === true) continue; | ||
|  | 				checked.add(name); | ||
|  | 			} | ||
|  | 			if (isNamespaceImport) { | ||
|  | 				exports.add("__esModule"); | ||
|  | 				checked.delete("__esModule"); | ||
|  | 			} | ||
|  | 		} | ||
|  | 
 | ||
|  | 		return { exports, checked }; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * @param {ObjectSerializerContext} context context | ||
|  | 	 */ | ||
|  | 	serialize(context) { | ||
|  | 		const { write } = context; | ||
|  | 		write(this.asiSafe); | ||
|  | 		write(this.range); | ||
|  | 		write(this.valueRange); | ||
|  | 		write(this.base); | ||
|  | 		write(this.names); | ||
|  | 		write(this.ids); | ||
|  | 		write(this.resultUsed); | ||
|  | 		super.serialize(context); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * @param {ObjectDeserializerContext} context context | ||
|  | 	 */ | ||
|  | 	deserialize(context) { | ||
|  | 		const { read } = context; | ||
|  | 		this.asiSafe = read(); | ||
|  | 		this.range = read(); | ||
|  | 		this.valueRange = read(); | ||
|  | 		this.base = read(); | ||
|  | 		this.names = read(); | ||
|  | 		this.ids = read(); | ||
|  | 		this.resultUsed = read(); | ||
|  | 		super.deserialize(context); | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | makeSerializable( | ||
|  | 	CommonJsExportRequireDependency, | ||
|  | 	"webpack/lib/dependencies/CommonJsExportRequireDependency" | ||
|  | ); | ||
|  | 
 | ||
|  | CommonJsExportRequireDependency.Template = class CommonJsExportRequireDependencyTemplate extends ( | ||
|  | 	ModuleDependency.Template | ||
|  | ) { | ||
|  | 	/** | ||
|  | 	 * @param {Dependency} dependency the dependency for which the template should be applied | ||
|  | 	 * @param {ReplaceSource} source the current replace source which can be modified | ||
|  | 	 * @param {DependencyTemplateContext} templateContext the context object | ||
|  | 	 * @returns {void} | ||
|  | 	 */ | ||
|  | 	apply( | ||
|  | 		dependency, | ||
|  | 		source, | ||
|  | 		{ | ||
|  | 			module, | ||
|  | 			runtimeTemplate, | ||
|  | 			chunkGraph, | ||
|  | 			moduleGraph, | ||
|  | 			runtimeRequirements, | ||
|  | 			runtime | ||
|  | 		} | ||
|  | 	) { | ||
|  | 		const dep = /** @type {CommonJsExportRequireDependency} */ (dependency); | ||
|  | 		const used = moduleGraph | ||
|  | 			.getExportsInfo(module) | ||
|  | 			.getUsedName(dep.names, runtime); | ||
|  | 
 | ||
|  | 		const [type, base] = handleDependencyBase( | ||
|  | 			dep.base, | ||
|  | 			module, | ||
|  | 			runtimeRequirements | ||
|  | 		); | ||
|  | 
 | ||
|  | 		const importedModule = moduleGraph.getModule(dep); | ||
|  | 		let requireExpr = runtimeTemplate.moduleExports({ | ||
|  | 			module: importedModule, | ||
|  | 			chunkGraph, | ||
|  | 			request: dep.request, | ||
|  | 			weak: dep.weak, | ||
|  | 			runtimeRequirements | ||
|  | 		}); | ||
|  | 		if (importedModule) { | ||
|  | 			const ids = dep.getIds(moduleGraph); | ||
|  | 			const usedImported = moduleGraph | ||
|  | 				.getExportsInfo(importedModule) | ||
|  | 				.getUsedName(ids, runtime); | ||
|  | 			if (usedImported) { | ||
|  | 				const comment = equals(usedImported, ids) | ||
|  | 					? "" | ||
|  | 					: Template.toNormalComment(propertyAccess(ids)) + " "; | ||
|  | 				requireExpr += `${comment}${propertyAccess(usedImported)}`; | ||
|  | 			} | ||
|  | 		} | ||
|  | 
 | ||
|  | 		switch (type) { | ||
|  | 			case "expression": | ||
|  | 				source.replace( | ||
|  | 					dep.range[0], | ||
|  | 					dep.range[1] - 1, | ||
|  | 					used | ||
|  | 						? `${base}${propertyAccess(used)} = ${requireExpr}` | ||
|  | 						: `/* unused reexport */ ${requireExpr}` | ||
|  | 				); | ||
|  | 				return; | ||
|  | 			case "Object.defineProperty": | ||
|  | 				throw new Error("TODO"); | ||
|  | 			default: | ||
|  | 				throw new Error("Unexpected type"); | ||
|  | 		} | ||
|  | 	} | ||
|  | }; | ||
|  | 
 | ||
|  | module.exports = CommonJsExportRequireDependency; |