Skip to content

Commit c54bed5

Browse files
authored
feat: support load env (#518)
1 parent 8093f73 commit c54bed5

File tree

12 files changed

+221
-16
lines changed

12 files changed

+221
-16
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,5 @@ test-results
2323
.nx/
2424
**/@mf-types
2525
**/@mf-types/**
26+
.env.local
27+
.env.*.local

packages/core/src/cli/commands.ts

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@ import type { RsbuildMode } from '@rsbuild/core';
22
import { type Command, program } from 'commander';
33
import { logger } from '../utils/logger';
44
import { build } from './build';
5-
import { loadRslibConfig } from './init';
5+
import { init } from './init';
66
import { inspect } from './inspect';
77
import { startMFDevServer } from './mf';
88
import { watchFilesForRestart } from './restart';
99

1010
export type CommonOptions = {
1111
root?: string;
1212
config?: string;
13+
envDir?: string;
1314
envMode?: string;
1415
lib?: string[];
1516
};
@@ -37,7 +38,8 @@ const applyCommonOptions = (command: Command) => {
3738
.option(
3839
'--env-mode <mode>',
3940
'specify the env mode to load the `.env.[mode]` file',
40-
);
41+
)
42+
.option('--env-dir <dir>', 'specify the directory to load `.env` files');
4143
};
4244

4345
const repeatableOption = (value: string, previous: string[]) => {
@@ -64,13 +66,12 @@ export function runCli(): void {
6466
.action(async (options: BuildOptions) => {
6567
try {
6668
const cliBuild = async () => {
67-
const { content: rslibConfig, filePath } =
68-
await loadRslibConfig(options);
69+
const { config, watchFiles } = await init(options);
6970

70-
await build(rslibConfig, options);
71+
await build(config, options);
7172

7273
if (options.watch) {
73-
watchFilesForRestart([filePath], async () => {
74+
watchFilesForRestart(watchFiles, async () => {
7475
await cliBuild();
7576
});
7677
}
@@ -100,8 +101,8 @@ export function runCli(): void {
100101
.action(async (options: InspectOptions) => {
101102
try {
102103
// TODO: inspect should output Rslib's config
103-
const { content: rslibConfig } = await loadRslibConfig(options);
104-
await inspect(rslibConfig, {
104+
const { config } = await init(options);
105+
await inspect(config, {
105106
lib: options.lib,
106107
mode: options.mode,
107108
output: options.output,
@@ -119,12 +120,11 @@ export function runCli(): void {
119120
.action(async (options: CommonOptions) => {
120121
try {
121122
const cliMfDev = async () => {
122-
const { content: rslibConfig, filePath } =
123-
await loadRslibConfig(options);
123+
const { config, watchFiles } = await init(options);
124124
// TODO: support lib option in mf dev server
125-
await startMFDevServer(rslibConfig);
125+
await startMFDevServer(config);
126126

127-
watchFilesForRestart([filePath], async () => {
127+
watchFilesForRestart(watchFiles, async () => {
128128
await cliMfDev();
129129
});
130130
};

packages/core/src/cli/init.ts

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,51 @@
1+
import path from 'node:path';
2+
import { loadEnv } from '@rsbuild/core';
13
import { loadConfig } from '../config';
24
import type { RslibConfig } from '../types';
35
import { getAbsolutePath } from '../utils/helper';
46
import type { CommonOptions } from './commands';
7+
import { onBeforeRestart } from './restart';
58

6-
export async function loadRslibConfig(options: CommonOptions): Promise<{
7-
content: RslibConfig;
8-
filePath: string;
9+
const getEnvDir = (cwd: string, envDir?: string) => {
10+
if (envDir) {
11+
return path.isAbsolute(envDir) ? envDir : path.resolve(cwd, envDir);
12+
}
13+
return cwd;
14+
};
15+
16+
export async function init(options: CommonOptions): Promise<{
17+
config: RslibConfig;
18+
configFilePath: string;
19+
watchFiles: string[];
920
}> {
1021
const cwd = process.cwd();
1122
const root = options.root ? getAbsolutePath(cwd, options.root) : cwd;
23+
const envs = loadEnv({
24+
cwd: getEnvDir(root, options.envDir),
25+
mode: options.envMode,
26+
});
27+
28+
onBeforeRestart(envs.cleanup);
1229

13-
return loadConfig({
30+
const { content: config, filePath: configFilePath } = await loadConfig({
1431
cwd: root,
1532
path: options.config,
1633
envMode: options.envMode,
1734
});
35+
36+
config.source ||= {};
37+
config.source.define = {
38+
...envs.publicVars,
39+
...config.source.define,
40+
};
41+
42+
if (options.root) {
43+
config.root = root;
44+
}
45+
46+
return {
47+
config,
48+
configFilePath,
49+
watchFiles: [configFilePath, ...envs.filePaths],
50+
};
1851
}

pnpm-lock.yaml

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/integration/cli/env/.env

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
FOO=1
2+
BAR=2

tests/integration/cli/env/.env.test

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
FOO=5
2+
BAR=6

tests/integration/cli/env/env.test.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { execSync } from 'node:child_process';
2+
import fs from 'node:fs';
3+
import path from 'node:path';
4+
import { beforeEach, describe, expect, test } from 'vitest';
5+
6+
const localFile = path.join(__dirname, '.env.local');
7+
const prodLocalFile = path.join(__dirname, '.env.production.local');
8+
9+
describe('load env file', async () => {
10+
beforeEach(() => {
11+
fs.rmSync(localFile, { force: true });
12+
fs.rmSync(prodLocalFile, { force: true });
13+
});
14+
15+
test('should load .env config and allow rslib.config.ts to read env vars', async () => {
16+
execSync('npx rslib build', {
17+
cwd: __dirname,
18+
});
19+
20+
expect(fs.existsSync(path.join(__dirname, 'dist/1'))).toBeTruthy();
21+
});
22+
23+
test('should load .env.local with higher priority', async () => {
24+
fs.writeFileSync(localFile, 'FOO=2');
25+
26+
execSync('npx rslib build', {
27+
cwd: __dirname,
28+
});
29+
expect(fs.existsSync(path.join(__dirname, 'dist/2'))).toBeTruthy();
30+
});
31+
32+
test('should load .env.production.local with higher priority', async () => {
33+
fs.writeFileSync(localFile, 'FOO=2');
34+
fs.writeFileSync(prodLocalFile, 'FOO=3');
35+
36+
execSync('npx rslib build', {
37+
cwd: __dirname,
38+
});
39+
expect(fs.existsSync(path.join(__dirname, 'dist/3'))).toBeTruthy();
40+
});
41+
42+
test('should allow to specify env mode via --env-mode', async () => {
43+
execSync('npx rslib build --env-mode test', {
44+
cwd: __dirname,
45+
});
46+
47+
expect(fs.existsSync(path.join(__dirname, 'dist/5'))).toBeTruthy();
48+
});
49+
50+
test('should allow to custom env directory via --env-dir', async () => {
51+
execSync('npx rslib build --env-dir env', {
52+
cwd: __dirname,
53+
});
54+
55+
expect(fs.existsSync(path.join(__dirname, 'dist/7'))).toBeTruthy();
56+
});
57+
});

tests/integration/cli/env/env/.env

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
FOO=7
2+
BAR=8
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"name": "cli-env-test",
3+
"version": "1.0.0",
4+
"private": true,
5+
"type": "module"
6+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { defineConfig } from '@rslib/core';
2+
import { generateBundleEsmConfig } from 'test-helper';
3+
4+
export default defineConfig({
5+
lib: [
6+
generateBundleEsmConfig({
7+
output: {
8+
distPath: {
9+
root: `dist/${process.env.FOO}`,
10+
},
11+
},
12+
}),
13+
],
14+
});
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const foo = 'foo';

website/docs/en/guide/basic/cli.mdx

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,93 @@ Options:
3939
-c --config <config> specify the configuration file, can be a relative or absolute path
4040
-r --root <root> specify the project root directory, can be an absolute path or a path relative to cwd
4141
--env-mode <mode> specify the env mode to load the `.env.[mode]` file
42+
--env-dir <dir> specify the directory to load `.env` files
4243
--lib <id> build the specified library (may be repeated)
4344
-w --watch turn on watch mode, watch for changes and rebuild
4445
-h, --help display help for command
4546
```
4647

48+
### Environment Variables
49+
50+
Rslib supports injecting env variables or expressions into the code during build, which is helpful for distinguishing the running environment or replacing constants.
51+
52+
You can see more details in [Environment Variables](https://rsbuild.dev/guide/advanced/env-vars#environment-variables).
53+
54+
::: note
55+
56+
`process.env.NODE_ENV` will be preserved in the build output when [format](/config/lib/format) is set to `esm` or `cjs`. For `mf` format, it will be preserved to make output directly usable.
57+
58+
:::
59+
60+
#### Env Mode
61+
62+
Rslib supports reading `.env.[mode]` and `.env.[mode].local` files. You can specify the env mode using the `--env-mode <mode>` flag.
63+
64+
For example, set the env mode as `test`:
65+
66+
```bash
67+
npx rslib build --env-mode test
68+
```
69+
70+
Rslib will then read the following files in sequence:
71+
72+
- `.env`
73+
- `.env.local`
74+
- `.env.test`
75+
- `.env.test.local`
76+
77+
:::tip
78+
79+
The `--env-mode` option takes precedence over `process.env.NODE_ENV`.
80+
81+
It is recommended to use `--env-mode` to set the env mode, and not to modify `process.env.NODE_ENV`.
82+
83+
:::
84+
85+
#### Env Directory
86+
87+
By default, the `.env` file is located in the root directory of the project. You can specify the env directory by using the `--env-dir <dir>` option in the CLI.
88+
89+
For example, to specify the env directory as `config`:
90+
91+
```bash
92+
npx rslib build --env-dir config
93+
```
94+
95+
In this case, Rslib will read the `./config/.env` and other env files.
96+
97+
##### Example
98+
99+
For example, create a `.env` file and add the following contents:
100+
101+
```shell title=".env"
102+
FOO=hello
103+
BAR=1
104+
```
105+
106+
Then in the `rslib.config.ts` file, you can access the above env variables using `import.meta.env.[name]` or `process.env.[name]`:
107+
108+
```ts title="rslib.config.ts"
109+
console.log(import.meta.env.FOO); // 'hello'
110+
console.log(import.meta.env.BAR); // '1'
111+
112+
console.log(process.env.FOO); // 'hello'
113+
console.log(process.env.BAR); // '1'
114+
```
115+
116+
Now, create a `.env.local` file and add the following contents:
117+
118+
```shell title=".env.local"
119+
BAR=2
120+
```
121+
122+
The value of `BAR` is overwritten to `'2'`:
123+
124+
```ts title="rslib.config.ts"
125+
console.log(import.meta.env.BAR); // '2'
126+
console.log(process.env.BAR); // '2'
127+
```
128+
47129
### Watch Mode
48130

49131
You can use `rslib build --watch` to turn on watch mode for watching for changes and rebuild.
@@ -65,6 +147,7 @@ Options:
65147
-c --config <config> specify the configuration file, can be a relative or absolute path
66148
-r --root <root> specify the project root directory, can be an absolute path or a path relative to cwd
67149
--env-mode <mode> specify the env mode to load the `.env.[mode]` file
150+
--env-dir <dir> specify the directory to load `.env` files
68151
--lib <id> inspect the specified library (may be repeated)
69152
--output <output> specify inspect content output path (default: ".rsbuild")
70153
--verbose show full function definitions in output
@@ -127,5 +210,6 @@ Options:
127210
-c --config <config> specify the configuration file, can be a relative or absolute path
128211
-r --root <root> specify the project root directory, can be an absolute path or a path relative to cwd
129212
--env-mode <mode> specify the env mode to load the `.env.[mode]` file
213+
--env-dir <dir> specify the directory to load `.env` files
130214
-h, --help display help for command
131215
```

0 commit comments

Comments
 (0)