Skip to content

Commit f28d77a

Browse files
committed
feat(webpack-cli): add --env-file flag support
Add support for import.meta.env and introduce --env-file flag to load environment variables from .env files into the bundled files. - Support for import.meta.env in webpack builds - New --env-file CLI flag for specifying whether to load .env files for configuration - Renamed dotenv plugin for clarity
1 parent fd02fa0 commit f28d77a

File tree

5 files changed

+57
-42
lines changed

5 files changed

+57
-42
lines changed

packages/webpack-cli/src/bootstrap.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,8 @@
11
import { type IWebpackCLI } from "./types";
2-
import { EnvLoader } from "./utils/env-loader";
32

43
const WebpackCLI = require("./webpack-cli");
54

65
const runCLI = async (args: Parameters<IWebpackCLI["run"]>[0]) => {
7-
// Load environment variables from .env files
8-
try {
9-
EnvLoader.loadEnvFiles();
10-
} catch (error) {
11-
console.warn("Warning: Error loading environment variables:", error);
12-
}
13-
146
// Create a new instance of the CLI object
157
const cli: IWebpackCLI = new WebpackCLI();
168

packages/webpack-cli/src/plugins/dotenv-plugin.ts renamed to packages/webpack-cli/src/plugins/dotenv-webpack-plugin.ts

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,41 @@ export class DotenvPlugin {
66
logger!: ReturnType<Compiler["getInfrastructureLogger"]>;
77
options: DotenvPluginOptions;
88

9-
constructor(options: DotenvPluginOptions) {
9+
constructor(options: DotenvPluginOptions = {}) {
1010
this.options = options;
1111
}
12-
1312
apply(compiler: Compiler) {
1413
this.logger = compiler.getInfrastructureLogger("DotenvPlugin");
1514

1615
try {
1716
const env = EnvLoader.loadEnvFiles({
1817
mode: process.env.NODE_ENV,
19-
prefixes: this.options.prefixes,
18+
prefix: this.options.prefix,
19+
dir: this.options.dir,
2020
});
21+
const envObj = JSON.stringify(env);
22+
23+
const runtimeEnvObject = `(() => {
24+
const env = ${envObj};
25+
// Make it read-only
26+
return Object.freeze(env);
27+
})()`;
2128

22-
new DefinePlugin(
23-
Object.fromEntries(
29+
const definitions = {
30+
"process.env": envObj,
31+
...Object.fromEntries(
2432
Object.entries(env).map(([key, value]) => [`process.env.${key}`, JSON.stringify(value)]),
2533
),
26-
).apply(compiler);
34+
"import.meta.env": runtimeEnvObject,
35+
...Object.fromEntries(
36+
Object.entries(env).map(([key, value]) => [
37+
`import.meta.env.${key}`,
38+
JSON.stringify(value),
39+
]),
40+
),
41+
};
42+
43+
new DefinePlugin(definitions).apply(compiler);
2744
} catch (error) {
2845
this.logger.error(error);
2946
}

packages/webpack-cli/src/types.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ type WebpackDevServerOptions = DevServerConfig &
177177
disableInterpret?: boolean;
178178
extends?: string[];
179179
argv: Argv;
180+
envFile?: boolean;
180181
};
181182

182183
type Callback<T extends unknown[]> = (...args: T) => void;
@@ -240,7 +241,8 @@ interface CLIPluginOptions {
240241
}
241242

242243
interface DotenvPluginOptions {
243-
prefixes?: string | string[];
244+
prefix?: string | string[];
245+
dir?: string;
244246
}
245247

246248
type BasicPrimitive = string | boolean | number;

packages/webpack-cli/src/utils/env-loader.ts

Lines changed: 10 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ import { loadEnvFile } from "process";
55

66
export interface EnvLoaderOptions {
77
mode?: string;
8-
envDir?: string;
9-
prefixes?: string | string[];
8+
dir?: string;
9+
prefix?: string | string[];
1010
}
1111

1212
export class EnvLoader {
@@ -20,25 +20,17 @@ export class EnvLoader {
2020
}
2121

2222
static loadEnvFiles(options: EnvLoaderOptions = {}): Record<string, string> {
23-
const {
24-
mode = process.env.NODE_ENV || "development",
25-
envDir = process.cwd(),
26-
prefixes,
27-
} = options;
23+
const { mode = process.env.NODE_ENV, dir = process.cwd(), prefix } = options;
2824

29-
const normalizedPrefixes = prefixes
30-
? Array.isArray(prefixes)
31-
? prefixes
32-
: [prefixes]
33-
: undefined;
25+
const normalizedPrefixes = prefix ? (Array.isArray(prefix) ? prefix : [prefix]) : ["WEBPACK_"];
3426

3527
if (mode === "local") {
3628
throw new Error(
3729
'"local" cannot be used as a mode name because it conflicts with the .local postfix for .env files.',
3830
);
3931
}
4032

41-
const envFiles = this.getEnvFilePaths(mode, envDir);
33+
const envFiles = this.getEnvFilePaths(mode, dir);
4234
const env: Record<string, string> = {};
4335

4436
// Load all env files
@@ -50,17 +42,13 @@ export class EnvLoader {
5042
}
5143
});
5244

53-
// If prefixes are specified, filter environment variables
54-
if (normalizedPrefixes?.length) {
55-
for (const [key, value] of Object.entries(process.env)) {
56-
if (normalizedPrefixes.some((prefix) => key.startsWith(prefix))) {
57-
env[key] = value as string;
58-
}
45+
// Filter env vars based on prefix
46+
for (const [key, value] of Object.entries(process.env)) {
47+
if (normalizedPrefixes.some((prefix) => key.startsWith(prefix))) {
48+
env[key] = value as string;
5949
}
60-
return env;
6150
}
6251

63-
// Return all environment variables if no prefixes specified
64-
return { ...process.env } as Record<string, string>;
52+
return env;
6553
}
6654
}

packages/webpack-cli/src/webpack-cli.ts

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@ import { type stringifyChunked } from "@discoveryjs/json-ext";
5656
import { type Help, type ParseOptions } from "commander";
5757

5858
import { type CLIPlugin as CLIPluginClass } from "./plugins/cli-plugin";
59-
import { type DotenvPlugin as DotenvPluginClass } from "./plugins/dotenv-plugin";
59+
import { type DotenvPlugin as DotenvPluginClass } from "./plugins/dotenv-webpack-plugin";
60+
import { EnvLoader } from "./utils/env-loader";
6061

6162
const fs = require("fs");
6263
const { Readable } = require("stream");
@@ -1021,6 +1022,18 @@ class WebpackCLI implements IWebpackCLI {
10211022
description: "Print compilation progress during build.",
10221023
helpLevel: "minimum",
10231024
},
1025+
{
1026+
name: "env-file",
1027+
configs: [
1028+
{
1029+
type: "enum",
1030+
values: [true],
1031+
},
1032+
],
1033+
description:
1034+
"Load environment variables from .env files for access within the configuration.",
1035+
helpLevel: "minimum",
1036+
},
10241037

10251038
// Output options
10261039
{
@@ -1793,6 +1806,10 @@ class WebpackCLI implements IWebpackCLI {
17931806
}
17941807

17951808
async loadConfig(options: Partial<WebpackDevServerOptions>) {
1809+
if (options.envFile) {
1810+
EnvLoader.loadEnvFiles();
1811+
}
1812+
17961813
const disableInterpret =
17971814
typeof options.disableInterpret !== "undefined" && options.disableInterpret;
17981815

@@ -2172,10 +2189,9 @@ class WebpackCLI implements IWebpackCLI {
21722189
"./plugins/cli-plugin",
21732190
);
21742191

2175-
const DotenvPlugin =
2176-
await this.tryRequireThenImport<Instantiable<DotenvPluginClass, [DotenvPluginOptions]>>(
2177-
"./plugins/dotenv-plugin",
2178-
);
2192+
const DotenvPlugin = await this.tryRequireThenImport<
2193+
Instantiable<DotenvPluginClass, [DotenvPluginOptions?]>
2194+
>("./plugins/dotenv-webpack-plugin");
21792195

21802196
const internalBuildConfig = (item: WebpackConfiguration) => {
21812197
const originalWatchValue = item.watch;

0 commit comments

Comments
 (0)