add missingDependencies

This commit is contained in:
xiaoxiaojx 2025-10-07 01:46:08 +08:00
parent ada334b199
commit fc406b758d
5 changed files with 32 additions and 15 deletions

View File

@ -260,6 +260,8 @@ class DotenvPlugin {
/** @type {string[] | undefined} */ /** @type {string[] | undefined} */
let fileDependenciesCache; let fileDependenciesCache;
/** @type {string[] | undefined} */
let missingDependenciesCache;
compiler.hooks.beforeCompile.tapAsync(PLUGIN_NAME, (_params, callback) => { compiler.hooks.beforeCompile.tapAsync(PLUGIN_NAME, (_params, callback) => {
const inputFileSystem = /** @type {InputFileSystem} */ ( const inputFileSystem = /** @type {InputFileSystem} */ (
@ -272,7 +274,7 @@ class DotenvPlugin {
inputFileSystem, inputFileSystem,
mode, mode,
context, context,
(err, env, fileDependencies) => { (err, env, fileDependencies, missingDependencies) => {
if (err) return callback(err); if (err) return callback(err);
const definitions = envToDefinitions(env || {}); const definitions = envToDefinitions(env || {});
@ -281,6 +283,8 @@ class DotenvPlugin {
definePlugin.definitions = definitions; definePlugin.definitions = definitions;
// update the file dependencies // update the file dependencies
fileDependenciesCache = fileDependencies; fileDependenciesCache = fileDependencies;
// update the missing dependencies
missingDependenciesCache = missingDependencies;
callback(); callback();
} }
@ -289,6 +293,7 @@ class DotenvPlugin {
compiler.hooks.compilation.tap(PLUGIN_NAME, (compilation) => { compiler.hooks.compilation.tap(PLUGIN_NAME, (compilation) => {
compilation.fileDependencies.addAll(fileDependenciesCache || []); compilation.fileDependencies.addAll(fileDependenciesCache || []);
compilation.missingDependencies.addAll(missingDependenciesCache || []);
}); });
definePlugin.apply(compiler); definePlugin.apply(compiler);
@ -321,7 +326,7 @@ class DotenvPlugin {
* @param {InputFileSystem} fs the input file system * @param {InputFileSystem} fs the input file system
* @param {string | undefined} mode the mode * @param {string | undefined} mode the mode
* @param {string} context the compiler context * @param {string} context the compiler context
* @param {(err: Error | null, env?: Record<string, string>, fileDependencies?: string[]) => void} callback callback function * @param {(err: Error | null, env?: Record<string, string>, fileDependencies?: string[], missingDependencies?: string[]) => void} callback callback function
* @returns {void} * @returns {void}
*/ */
loadEnv(fs, mode, context, callback) { loadEnv(fs, mode, context, callback) {
@ -353,26 +358,30 @@ class DotenvPlugin {
const envFiles = this.getEnvFilesForMode(fs, dir, mode); const envFiles = this.getEnvFilesForMode(fs, dir, mode);
/** @type {string[]} */ /** @type {string[]} */
const fileDependencies = []; const fileDependencies = [];
/** @type {string[]} */
const missingDependencies = [];
// Read all files // Read all files
const readPromises = envFiles.map((filePath) => const readPromises = envFiles.map((filePath) =>
this.loadFile(fs, filePath).then( this.loadFile(fs, filePath).then(
(content) => { (content) => {
fileDependencies.push(filePath); fileDependencies.push(filePath);
return { content, filePath }; return content;
}, },
() => () => {
// File doesn't exist, skip it (this is normal) // File doesn't exist, add to missingDependencies (this is normal)
({ content: "", filePath }) missingDependencies.push(filePath);
return "";
}
) )
); );
Promise.all(readPromises) Promise.all(readPromises)
.then((results) => { .then((contents) => {
// Parse all files and merge (later files override earlier ones) // Parse all files and merge (later files override earlier ones)
// Similar to Vite's implementation // Similar to Vite's implementation
const parsed = /** @type {Record<string, string>} */ ({}); const parsed = /** @type {Record<string, string>} */ ({});
for (const { content } of results) { for (const content of contents) {
if (!content) continue; if (!content) continue;
const entries = parse(content); const entries = parse(content);
for (const key in entries) { for (const key in entries) {
@ -403,7 +412,7 @@ class DotenvPlugin {
} }
} }
callback(null, env, fileDependencies); callback(null, env, fileDependencies, missingDependencies);
}) })
.catch((err) => { .catch((err) => {
callback(err); callback(err);

View File

@ -5,4 +5,12 @@ it("should override .env values with .env.development in step 2", function () {
expect(process.env.WEBPACK_NEW_VAR).toBe("added-in-step-1"); expect(process.env.WEBPACK_NEW_VAR).toBe("added-in-step-1");
// New variable from .env.development // New variable from .env.development
expect(process.env.WEBPACK_DEV_ONLY).toBe("development-value"); expect(process.env.WEBPACK_DEV_ONLY).toBe("development-value");
// missingDependencies test
if (WATCH_STEP === "3") {
// In step 3, .env.myLocal from missingDependencies was created, so it has a value
expect(process.env.WEBPACK_MY_LOCAL).toBe("3");
} else {
// In step 2, .env.myLocal doesn't exist yet
expect(process.env.WEBPACK_MY_LOCAL).toBeUndefined();
}
}); });

View File

@ -0,0 +1 @@
WEBPACK_MY_LOCAL=3

View File

@ -1,6 +1,5 @@
"use strict"; "use strict";
const path = require("path");
const DotenvPlugin = require("../../../../").DotenvPlugin; const DotenvPlugin = require("../../../../").DotenvPlugin;
/** @type {(env: Env, options: TestOptions) => import("../../../../").Configuration} */ /** @type {(env: Env, options: TestOptions) => import("../../../../").Configuration} */
@ -8,14 +7,14 @@ const DotenvPlugin = require("../../../../").DotenvPlugin;
module.exports = (env, { srcPath, testPath }) => { module.exports = (env, { srcPath, testPath }) => {
const dotenvPlugin = new DotenvPlugin({ const dotenvPlugin = new DotenvPlugin({
prefix: "WEBPACK_", prefix: "WEBPACK_",
dir: "" dir: "",
template: [".env", ".env.myLocal", ".env.[mode]", ".env.[mode].myLocal"]
}); });
return { return {
mode: "development", mode: "development",
dotenv: false, dotenv: false,
plugins: [ plugins: [
(compiler) => { (compiler) => {
let i = 0;
// Update dotenvPlugin.config.dir before each compile // Update dotenvPlugin.config.dir before each compile
// Use beforeCompile with stage -1 to run before DotenvPlugin // Use beforeCompile with stage -1 to run before DotenvPlugin
compiler.hooks.beforeCompile.tap( compiler.hooks.beforeCompile.tap(
@ -24,8 +23,7 @@ module.exports = (env, { srcPath, testPath }) => {
stage: -1 stage: -1
}, },
() => { () => {
dotenvPlugin.config.dir = path.join(__dirname, String(i)); dotenvPlugin.config.dir = srcPath;
i++;
} }
); );
}, },

3
types.d.ts vendored
View File

@ -4463,7 +4463,8 @@ declare class DotenvPlugin {
callback: ( callback: (
err: null | Error, err: null | Error,
env?: Record<string, string>, env?: Record<string, string>,
fileDependencies?: string[] fileDependencies?: string[],
missingDependencies?: string[]
) => void ) => void
): void; ): void;