mirror of https://github.com/webpack/webpack.git
				
				
				
			add node-module option for node.__file/dirname
evaluate __filename and __dirname for common js modules when output.module to fileURLToPath(import.meta.url) and fileURLToPath(import.meta.url + "/..") respectively
This commit is contained in:
		
							parent
							
								
									562f17a8c0
								
							
						
					
					
						commit
						6c3a04d5ce
					
				|  | @ -1633,11 +1633,17 @@ export interface NodeOptions { | ||||||
| 	/** | 	/** | ||||||
| 	 * Include a polyfill for the '__dirname' variable. | 	 * Include a polyfill for the '__dirname' variable. | ||||||
| 	 */ | 	 */ | ||||||
| 	__dirname?: false | true | "warn-mock" | "mock" | "eval-only"; | 	__dirname?: false | true | "warn-mock" | "mock" | "node-module" | "eval-only"; | ||||||
| 	/** | 	/** | ||||||
| 	 * Include a polyfill for the '__filename' variable. | 	 * Include a polyfill for the '__filename' variable. | ||||||
| 	 */ | 	 */ | ||||||
| 	__filename?: false | true | "warn-mock" | "mock" | "eval-only"; | 	__filename?: | ||||||
|  | 		| false | ||||||
|  | 		| true | ||||||
|  | 		| "warn-mock" | ||||||
|  | 		| "mock" | ||||||
|  | 		| "node-module" | ||||||
|  | 		| "eval-only"; | ||||||
| 	/** | 	/** | ||||||
| 	 * Include a polyfill for the 'global' variable. | 	 * Include a polyfill for the 'global' variable. | ||||||
| 	 */ | 	 */ | ||||||
|  |  | ||||||
|  | @ -15,14 +15,19 @@ const { | ||||||
| } = require("./javascript/JavascriptParserHelpers"); | } = require("./javascript/JavascriptParserHelpers"); | ||||||
| const { relative } = require("./util/fs"); | const { relative } = require("./util/fs"); | ||||||
| const { parseResource } = require("./util/identifier"); | const { parseResource } = require("./util/identifier"); | ||||||
|  | const InitFragment = require("./InitFragment"); | ||||||
| 
 | 
 | ||||||
| /** @typedef {import("webpack-sources").ReplaceSource} ReplaceSource */ | /** @typedef {import("webpack-sources").ReplaceSource} ReplaceSource */ | ||||||
| /** @typedef {import("./Compiler")} Compiler */ | /** @typedef {import("./Compiler")} Compiler */ | ||||||
| /** @typedef {import("./Dependency")} Dependency */ | /** @typedef {import("./Dependency")} Dependency */ | ||||||
| /** @typedef {import("./DependencyTemplates")} DependencyTemplates */ | /** @typedef {import("./DependencyTemplates")} DependencyTemplates */ | ||||||
| /** @typedef {import("./RuntimeTemplate")} RuntimeTemplate */ | /** @typedef {import("./RuntimeTemplate")} RuntimeTemplate */ | ||||||
|  | /** @typedef {import("../declarations/WebpackOptions").NodeOptions} NodeOptions */ | ||||||
| 
 | 
 | ||||||
| class NodeStuffPlugin { | class NodeStuffPlugin { | ||||||
|  | 	/** | ||||||
|  | 	 * @param {NodeOptions} options options | ||||||
|  | 	 */ | ||||||
| 	constructor(options) { | 	constructor(options) { | ||||||
| 		this.options = options; | 		this.options = options; | ||||||
| 	} | 	} | ||||||
|  | @ -71,7 +76,7 @@ class NodeStuffPlugin { | ||||||
| 							}); | 							}); | ||||||
| 					} | 					} | ||||||
| 
 | 
 | ||||||
| 					const setModuleConstant = (expressionName, fn, warning) => { | 					const setCjsModuleConstant = (expressionName, fn, warning) => { | ||||||
| 						parser.hooks.expression | 						parser.hooks.expression | ||||||
| 							.for(expressionName) | 							.for(expressionName) | ||||||
| 							.tap("NodeStuffPlugin", expr => { | 							.tap("NodeStuffPlugin", expr => { | ||||||
|  | @ -94,24 +99,54 @@ class NodeStuffPlugin { | ||||||
| 							}); | 							}); | ||||||
| 					}; | 					}; | ||||||
| 
 | 
 | ||||||
| 					const setConstant = (expressionName, value, warning) => | 					const setEsmModuleConstant = (expressionName, fn) => { | ||||||
| 						setModuleConstant(expressionName, () => value, warning); | 						parser.hooks.expression | ||||||
|  | 							.for(expressionName) | ||||||
|  | 							.tap("NodeStuffPlugin", expr => { | ||||||
|  | 								const dep = new CachedConstDependency( | ||||||
|  | 									JSON.stringify(fn(parser.state.module)), | ||||||
|  | 									expr.range, | ||||||
|  | 									expressionName, | ||||||
|  | 									[ | ||||||
|  | 										new InitFragment( | ||||||
|  | 											'import url from "url"', | ||||||
|  | 											InitFragment.STAGE_CONSTANTS, | ||||||
|  | 											0, | ||||||
|  | 											"import url" | ||||||
|  | 										) | ||||||
|  | 									] | ||||||
|  | 								); | ||||||
|  | 								dep.loc = expr.loc; | ||||||
|  | 								parser.state.module.addPresentationalDependency(dep); | ||||||
|  | 
 | ||||||
|  | 								return true; | ||||||
|  | 							}); | ||||||
|  | 					}; | ||||||
|  | 
 | ||||||
|  | 					const setCjsConstant = (expressionName, value, warning) => | ||||||
|  | 						setCjsModuleConstant(expressionName, () => value, warning); | ||||||
|  | 
 | ||||||
|  | 					const setEsmConstant = (expressionName, value) => | ||||||
|  | 						setEsmModuleConstant(expressionName, () => value); | ||||||
| 
 | 
 | ||||||
| 					const context = compiler.context; | 					const context = compiler.context; | ||||||
| 					if (localOptions.__filename) { | 					if (localOptions.__filename) { | ||||||
| 						switch (localOptions.__filename) { | 						switch (localOptions.__filename) { | ||||||
| 							case "mock": | 							case "mock": | ||||||
| 								setConstant("__filename", "/index.js"); | 								setCjsConstant("__filename", "/index.js"); | ||||||
| 								break; | 								break; | ||||||
| 							case "warn-mock": | 							case "warn-mock": | ||||||
| 								setConstant( | 								setCjsConstant( | ||||||
| 									"__filename", | 									"__filename", | ||||||
| 									"/index.js", | 									"/index.js", | ||||||
| 									"The __filename is Node.js feature and doesn't present in browser." | 									"The __filename is Node.js feature and doesn't present in browser." | ||||||
| 								); | 								); | ||||||
| 								break; | 								break; | ||||||
|  | 							case "node-module": | ||||||
|  | 								setEsmConstant("__filename", "fileURLToPath(import.meta.url)"); | ||||||
|  | 								break; | ||||||
| 							case true: | 							case true: | ||||||
| 								setModuleConstant("__filename", module => | 								setCjsModuleConstant("__filename", module => | ||||||
| 									relative(compiler.inputFileSystem, context, module.resource) | 									relative(compiler.inputFileSystem, context, module.resource) | ||||||
| 								); | 								); | ||||||
| 								break; | 								break; | ||||||
|  | @ -128,17 +163,23 @@ class NodeStuffPlugin { | ||||||
| 					if (localOptions.__dirname) { | 					if (localOptions.__dirname) { | ||||||
| 						switch (localOptions.__dirname) { | 						switch (localOptions.__dirname) { | ||||||
| 							case "mock": | 							case "mock": | ||||||
| 								setConstant("__dirname", "/"); | 								setCjsConstant("__dirname", "/"); | ||||||
| 								break; | 								break; | ||||||
| 							case "warn-mock": | 							case "warn-mock": | ||||||
| 								setConstant( | 								setCjsConstant( | ||||||
| 									"__dirname", | 									"__dirname", | ||||||
| 									"/", | 									"/", | ||||||
| 									"The __dirname is Node.js feature and doesn't present in browser." | 									"The __dirname is Node.js feature and doesn't present in browser." | ||||||
| 								); | 								); | ||||||
| 								break; | 								break; | ||||||
|  | 							case "node-module": | ||||||
|  | 								setEsmConstant( | ||||||
|  | 									"__filename", | ||||||
|  | 									'fileURLToPath(import.meta.url + "/..")' | ||||||
|  | 								); | ||||||
|  | 								break; | ||||||
| 							case true: | 							case true: | ||||||
| 								setModuleConstant("__dirname", module => | 								setCjsModuleConstant("__dirname", module => | ||||||
| 									relative(compiler.inputFileSystem, context, module.context) | 									relative(compiler.inputFileSystem, context, module.context) | ||||||
| 								); | 								); | ||||||
| 								break; | 								break; | ||||||
|  |  | ||||||
|  | @ -213,6 +213,7 @@ const applyWebpackOptionsDefaults = options => { | ||||||
| 
 | 
 | ||||||
| 	applyNodeDefaults(options.node, { | 	applyNodeDefaults(options.node, { | ||||||
| 		futureDefaults: options.experiments.futureDefaults, | 		futureDefaults: options.experiments.futureDefaults, | ||||||
|  | 		outputModule: options.output.module, | ||||||
| 		targetProperties | 		targetProperties | ||||||
| 	}); | 	}); | ||||||
| 
 | 
 | ||||||
|  | @ -948,9 +949,13 @@ const applyLoaderDefaults = (loader, { targetProperties }) => { | ||||||
|  * @param {Object} options options |  * @param {Object} options options | ||||||
|  * @param {TargetProperties | false} options.targetProperties target properties |  * @param {TargetProperties | false} options.targetProperties target properties | ||||||
|  * @param {boolean} options.futureDefaults is future defaults enabled |  * @param {boolean} options.futureDefaults is future defaults enabled | ||||||
|  |  * @param {boolean} options.outputModule is output type is module | ||||||
|  * @returns {void} |  * @returns {void} | ||||||
|  */ |  */ | ||||||
| const applyNodeDefaults = (node, { futureDefaults, targetProperties }) => { | const applyNodeDefaults = ( | ||||||
|  | 	node, | ||||||
|  | 	{ futureDefaults, outputModule, targetProperties } | ||||||
|  | ) => { | ||||||
| 	if (node === false) return; | 	if (node === false) return; | ||||||
| 
 | 
 | ||||||
| 	F(node, "global", () => { | 	F(node, "global", () => { | ||||||
|  | @ -958,16 +963,16 @@ const applyNodeDefaults = (node, { futureDefaults, targetProperties }) => { | ||||||
| 		// TODO webpack 6 should always default to false
 | 		// TODO webpack 6 should always default to false
 | ||||||
| 		return futureDefaults ? "warn" : true; | 		return futureDefaults ? "warn" : true; | ||||||
| 	}); | 	}); | ||||||
| 	F(node, "__filename", () => { | 
 | ||||||
| 		if (targetProperties && targetProperties.node) return "eval-only"; | 	const handlerForNames = () => { | ||||||
|  | 		if (targetProperties && targetProperties.node) | ||||||
|  | 			return outputModule ? "node-module" : "eval-only"; | ||||||
| 		// TODO webpack 6 should always default to false
 | 		// TODO webpack 6 should always default to false
 | ||||||
| 		return futureDefaults ? "warn-mock" : "mock"; | 		return futureDefaults ? "warn-mock" : "mock"; | ||||||
| 	}); | 	}; | ||||||
| 	F(node, "__dirname", () => { | 
 | ||||||
| 		if (targetProperties && targetProperties.node) return "eval-only"; | 	F(node, "__filename", handlerForNames); | ||||||
| 		// TODO webpack 6 should always default to false
 | 	F(node, "__dirname", handlerForNames); | ||||||
| 		return futureDefaults ? "warn-mock" : "mock"; |  | ||||||
| 	}); |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  |  | ||||||
|  | @ -13,6 +13,7 @@ const NullDependency = require("./NullDependency"); | ||||||
| /** @typedef {import("webpack-sources").ReplaceSource} ReplaceSource */ | /** @typedef {import("webpack-sources").ReplaceSource} ReplaceSource */ | ||||||
| /** @typedef {import("../ChunkGraph")} ChunkGraph */ | /** @typedef {import("../ChunkGraph")} ChunkGraph */ | ||||||
| /** @typedef {import("../Dependency")} Dependency */ | /** @typedef {import("../Dependency")} Dependency */ | ||||||
|  | /** @typedef {import("../Generator").GenerateContext} GenerateContext */ | ||||||
| /** @typedef {import("../Dependency").UpdateHashContext} UpdateHashContext */ | /** @typedef {import("../Dependency").UpdateHashContext} UpdateHashContext */ | ||||||
| /** @typedef {import("../DependencyTemplate").DependencyTemplateContext} DependencyTemplateContext */ | /** @typedef {import("../DependencyTemplate").DependencyTemplateContext} DependencyTemplateContext */ | ||||||
| /** @typedef {import("../DependencyTemplates")} DependencyTemplates */ | /** @typedef {import("../DependencyTemplates")} DependencyTemplates */ | ||||||
|  | @ -21,12 +22,19 @@ const NullDependency = require("./NullDependency"); | ||||||
| /** @typedef {import("../util/Hash")} Hash */ | /** @typedef {import("../util/Hash")} Hash */ | ||||||
| 
 | 
 | ||||||
| class CachedConstDependency extends NullDependency { | class CachedConstDependency extends NullDependency { | ||||||
| 	constructor(expression, range, identifier) { | 	/** | ||||||
|  | 	 * @param {string} expression expression | ||||||
|  | 	 * @param {number|[number, number]} range range | ||||||
|  | 	 * @param {string} identifier identifier | ||||||
|  | 	 * @param {InitFragment<GenerateContext>[]=} initFragments init fragments | ||||||
|  | 	 */ | ||||||
|  | 	constructor(expression, range, identifier, initFragments) { | ||||||
| 		super(); | 		super(); | ||||||
| 
 | 
 | ||||||
| 		this.expression = expression; | 		this.expression = expression; | ||||||
| 		this.range = range; | 		this.range = range; | ||||||
| 		this.identifier = identifier; | 		this.identifier = identifier; | ||||||
|  | 		this.initFragments = initFragments; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	/** | 	/** | ||||||
|  | @ -36,9 +44,14 @@ class CachedConstDependency extends NullDependency { | ||||||
| 	 * @returns {void} | 	 * @returns {void} | ||||||
| 	 */ | 	 */ | ||||||
| 	updateHash(hash, context) { | 	updateHash(hash, context) { | ||||||
| 		hash.update(this.identifier + ""); | 		hash.update(`${this.identifier}`); | ||||||
| 		hash.update(this.range + ""); | 		hash.update(`${this.range}`); | ||||||
| 		hash.update(this.expression + ""); | 		hash.update(`${this.expression}`); | ||||||
|  | 
 | ||||||
|  | 		if (this.initFragments) { | ||||||
|  | 			for (const fragment of this.initFragments) | ||||||
|  | 				hash.update(`${fragment.key}_${fragment.position}`); | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	serialize(context) { | 	serialize(context) { | ||||||
|  | @ -47,6 +60,7 @@ class CachedConstDependency extends NullDependency { | ||||||
| 		write(this.expression); | 		write(this.expression); | ||||||
| 		write(this.range); | 		write(this.range); | ||||||
| 		write(this.identifier); | 		write(this.identifier); | ||||||
|  | 		write(this.initFragments); | ||||||
| 
 | 
 | ||||||
| 		super.serialize(context); | 		super.serialize(context); | ||||||
| 	} | 	} | ||||||
|  | @ -57,6 +71,7 @@ class CachedConstDependency extends NullDependency { | ||||||
| 		this.expression = read(); | 		this.expression = read(); | ||||||
| 		this.range = read(); | 		this.range = read(); | ||||||
| 		this.identifier = read(); | 		this.identifier = read(); | ||||||
|  | 		this.initFragments = read(); | ||||||
| 
 | 
 | ||||||
| 		super.deserialize(context); | 		super.deserialize(context); | ||||||
| 	} | 	} | ||||||
|  | @ -83,11 +98,22 @@ CachedConstDependency.Template = class CachedConstDependencyTemplate extends ( | ||||||
| 	) { | 	) { | ||||||
| 		const dep = /** @type {CachedConstDependency} */ (dependency); | 		const dep = /** @type {CachedConstDependency} */ (dependency); | ||||||
| 
 | 
 | ||||||
|  | 		let position = 0; | ||||||
|  | 
 | ||||||
|  | 		if (dep.initFragments && dep.initFragments.length > 0) { | ||||||
|  | 			for (const fragment of dep.initFragments) { | ||||||
|  | 				initFragments.push(fragment); | ||||||
|  | 			} | ||||||
|  | 			position = dep.initFragments[dep.initFragments.length - 1].position + 1; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		initFragments.push( | 		initFragments.push( | ||||||
| 			new InitFragment( | 			new InitFragment( | ||||||
| 				`var ${dep.identifier} = ${dep.expression};\n`, | 				`${runtimeTemplate.supportsConst() ? "const" : "var"} ${ | ||||||
|  | 					dep.identifier | ||||||
|  | 				} = ${dep.expression};\n`,
 | ||||||
| 				InitFragment.STAGE_CONSTANTS, | 				InitFragment.STAGE_CONSTANTS, | ||||||
| 				0, | 				position, | ||||||
| 				`const ${dep.identifier}` | 				`const ${dep.identifier}` | ||||||
| 			) | 			) | ||||||
| 		); | 		); | ||||||
|  |  | ||||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							|  | @ -1944,11 +1944,11 @@ | ||||||
|       "properties": { |       "properties": { | ||||||
|         "__dirname": { |         "__dirname": { | ||||||
|           "description": "Include a polyfill for the '__dirname' variable.", |           "description": "Include a polyfill for the '__dirname' variable.", | ||||||
|           "enum": [false, true, "warn-mock", "mock", "eval-only"] |           "enum": [false, true, "warn-mock", "mock", "node-module", "eval-only"] | ||||||
|         }, |         }, | ||||||
|         "__filename": { |         "__filename": { | ||||||
|           "description": "Include a polyfill for the '__filename' variable.", |           "description": "Include a polyfill for the '__filename' variable.", | ||||||
|           "enum": [false, true, "warn-mock", "mock", "eval-only"] |           "enum": [false, true, "warn-mock", "mock", "node-module", "eval-only"] | ||||||
|         }, |         }, | ||||||
|         "global": { |         "global": { | ||||||
|           "description": "Include a polyfill for the 'global' variable.", |           "description": "Include a polyfill for the 'global' variable.", | ||||||
|  |  | ||||||
|  | @ -7161,12 +7161,12 @@ declare interface NodeOptions { | ||||||
| 	/** | 	/** | ||||||
| 	 * Include a polyfill for the '__dirname' variable. | 	 * Include a polyfill for the '__dirname' variable. | ||||||
| 	 */ | 	 */ | ||||||
| 	__dirname?: boolean | "warn-mock" | "mock" | "eval-only"; | 	__dirname?: boolean | "warn-mock" | "mock" | "node-module" | "eval-only"; | ||||||
| 
 | 
 | ||||||
| 	/** | 	/** | ||||||
| 	 * Include a polyfill for the '__filename' variable. | 	 * Include a polyfill for the '__filename' variable. | ||||||
| 	 */ | 	 */ | ||||||
| 	__filename?: boolean | "warn-mock" | "mock" | "eval-only"; | 	__filename?: boolean | "warn-mock" | "mock" | "node-module" | "eval-only"; | ||||||
| 
 | 
 | ||||||
| 	/** | 	/** | ||||||
| 	 * Include a polyfill for the 'global' variable. | 	 * Include a polyfill for the 'global' variable. | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue