@@ -30,6 +30,66 @@ function createTmpFile(content = 'console.log("running");', ext = '.js', basenam
3030 return file;
3131}
3232
33+ function runInBackground({ args = [], options = {}, completed = 'Completed running', shouldFail = false }) {
34+ let future = Promise.withResolvers();
35+ let child;
36+ let stderr = '';
37+ let stdout = [];
38+
39+ const run = () => {
40+ args.unshift('--no-warnings');
41+ child = spawn(execPath, args, { encoding: 'utf8', stdio: 'pipe', ...options });
42+
43+ child.stderr.on('data', (data) => {
44+ stderr += data;
45+ });
46+
47+ const rl = createInterface({ input: child.stdout });
48+ rl.on('line', (data) => {
49+ if (!data.startsWith('Waiting for graceful termination') && !data.startsWith('Gracefully restarted')) {
50+ stdout.push(data);
51+ if (data.startsWith(completed)) {
52+ future.resolve({ stderr, stdout });
53+ future = Promise.withResolvers();
54+ stdout = [];
55+ stderr = '';
56+ } else if (data.startsWith('Failed running')) {
57+ if (shouldFail) {
58+ future.resolve({ stderr, stdout });
59+ } else {
60+ future.reject({ stderr, stdout });
61+ }
62+ future = Promise.withResolvers();
63+ stdout = [];
64+ stderr = '';
65+ }
66+ }
67+ });
68+ };
69+
70+ return {
71+ async done() {
72+ child?.kill();
73+ future.resolve();
74+ return { stdout, stderr };
75+ },
76+ restart(timeout = 1000) {
77+ if (!child) {
78+ run();
79+ }
80+ const timer = setTimeout(() => {
81+ if (!future.resolved) {
82+ child.kill();
83+ future.reject(new Error('Timed out waiting for restart'));
84+ }
85+ }, timeout);
86+ return future.promise.finally(() => {
87+ clearTimeout(timer);
88+ });
89+ }
90+ };
91+ }
92+
3393async function runWriteSucceed({
3494 file,
3595 watchedFile,
@@ -132,6 +192,56 @@ describe('watch mode', { concurrency: !process.env.TEST_PARALLEL, timeout: 60_00
132192 ]);
133193 });
134194
195+ it('should reload env variables when --env-file changes', async () => {
196+ const envKey = `TEST_ENV_${Date.now()}`;
197+ const jsFile = createTmpFile(`console.log('ENV: ' + process.env.${envKey});`);
198+ const envFile = createTmpFile(`${envKey}=value1`, '.env');
199+ const { done, restart } = runInBackground({ args: ['--watch', `--env-file=${envFile}`, jsFile] });
200+
201+ try {
202+ await restart();
203+ writeFileSync(envFile, `${envKey}=value2`);
204+
205+ // Second restart, after env change
206+ const { stdout, stderr } = await restart();
207+
208+ assert.strictEqual(stderr, '');
209+ assert.deepStrictEqual(stdout, [
210+ `Restarting ${inspect(jsFile)}`,
211+ 'ENV: value2',
212+ `Completed running ${inspect(jsFile)}`,
213+ ]);
214+ } finally {
215+ await done();
216+ }
217+ });
218+
219+ it('should load new env variables when --env-file changes', async () => {
220+ const envKey = `TEST_ENV_${Date.now()}`;
221+ const envKey2 = `TEST_ENV_2_${Date.now()}`;
222+ const jsFile = createTmpFile(`console.log('ENV: ' + process.env.${envKey} + '\\n' + 'ENV2: ' + process.env.${envKey2});`);
223+ const envFile = createTmpFile(`${envKey}=value1`, '.env');
224+ const { done, restart } = runInBackground({ args: ['--watch', `--env-file=${envFile}`, jsFile] });
225+
226+ try {
227+ await restart();
228+ await writeFileSync(envFile, `${envKey}=value1\n${envKey2}=newValue`);
229+
230+ // Second restart, after env change
231+ const { stderr, stdout } = await restart();
232+
233+ assert.strictEqual(stderr, '');
234+ assert.deepStrictEqual(stdout, [
235+ `Restarting ${inspect(jsFile)}`,
236+ 'ENV: value1',
237+ 'ENV2: newValue',
238+ `Completed running ${inspect(jsFile)}`,
239+ ]);
240+ } finally {
241+ await done();
242+ }
243+ });
244+
135245 it('should watch changes to a failing file', async () => {
136246 const file = createTmpFile('throw new Error("fails");');
137247 const { stderr, stdout } = await runWriteSucceed({
0 commit comments