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
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
FOO=5
2+
BAR=6
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+
});

0 commit comments

Comments
 (0)