Repository: webpack/webpack
File: lib/DotenvPlugin.js (JavaScript)

Code before:
			typeof this.options.dir === "string"
				? this.options.dir
				: typeof this.options.dir === "undefined"
					? compiler.context
					: this.options.dir;

		/** @type {undefined | Snapshot} */
		let snapshot;
		/** @type {Record<string, string>} */
		let envCache = {};

		const cache = compiler.getCache(PLUGIN_NAME);
		const identifier = JSON.stringify(this.options.template);
		const itemCache = cache.getItemCache(identifier, null);

		compiler.hooks.beforeCompile.tapPromise(PLUGIN_NAME, async () => {
			const { parsed, snapshot: newSnapshot } = dir
				? await this._loadEnv(compiler, itemCache, dir)
				: { parsed: {} };
			const env = this._getEnv(prefixes, parsed);

			envCache = env;
			snapshot = newSnapshot;
		});

		compiler.hooks.thisCompilation.tap(PLUGIN_NAME, (compilation) => {
			definePlugin.definitions = envToDefinitions(
				envCache || {},
				compilation.outputOptions.importMetaName
			);

			if (snapshot) {
				compilation.fileDependencies.addAll(snapshot.getFileIterable());
				compilation.missingDependencies.addAll(snapshot.getMissingIterable());
			}
		});

		definePlugin.apply(compiler);
	}

	/**
	 * Get list of env files to load based on mode and template
	 * Similar to Vite's getEnvFilesForMode
	 * @private
	 * @param {InputFileSystem} inputFileSystem the input file system
	 * @param {string | false} dir the directory containing .env files
	 * @param {string | undefined} mode the mode (e.g., 'production', 'development')
	 * @returns {string[]} array of file paths to load
	 */
	_getEnvFilesForMode(inputFileSystem, dir, mode) {
		if (!dir) {

Review comment (suggestion):
We can't use `thisCompilation` here, because we need to replace it in child compilation too...

Code after:
				? this.options.dir
				: typeof this.options.dir === "undefined"
					? compiler.context
					: this.options.dir;

		/** @type {undefined | Snapshot} */
		let snapshot;

		const cache = compiler.getCache(PLUGIN_NAME);
		const identifier = JSON.stringify(this.options.template);
		const itemCache = cache.getItemCache(identifier, null);

		compiler.hooks.beforeCompile.tapPromise(PLUGIN_NAME, async () => {
			const { parsed, snapshot: newSnapshot } = dir
				? await this._loadEnv(compiler, itemCache, dir)
				: { parsed: {} };
			const env = this._getEnv(prefixes, parsed);

			definePlugin.definitions = envToDefinitions(env || {});
			snapshot = newSnapshot;
		});

		compiler.hooks.compilation.tap(PLUGIN_NAME, (compilation) => {
			if (snapshot) {
				compilation.fileDependencies.addAll(snapshot.getFileIterable());
				compilation.missingDependencies.addAll(snapshot.getMissingIterable());
			}
		});

		definePlugin.apply(compiler);
	}

	/**
	 * Get list of env files to load based on mode and template
	 * Similar to Vite's getEnvFilesForMode
	 * @private
	 * @param {InputFileSystem} inputFileSystem the input file system
	 * @param {string | false} dir the directory containing .env files
	 * @param {string | undefined} mode the mode (e.g., 'production', 'development')
	 * @returns {string[]} array of file paths to load
	 */
	_getEnvFilesForMode(inputFileSystem, dir, mode) {
		if (!dir) {
			return [];
		}

		const { template } = /** @type {DotenvPluginOptions} */ (this.options);
		const templates = template || [];

		return templates
			.map((pattern) => pattern.replace(/\[mode\]/g, mode || "development"))