Skip to content

Commit 0527925

Browse files
feat(core): update dotenv and load root env variables earlier (#18456)
Co-authored-by: FrozenPandaz <jasonjean1993@gmail.com>
1 parent 118faf4 commit 0527925

File tree

6 files changed

+109
-70
lines changed

6 files changed

+109
-70
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@
155155
"cz-git": "^1.4.0",
156156
"czg": "^1.4.0",
157157
"detect-port": "^1.5.1",
158-
"dotenv": "~10.0.0",
158+
"dotenv": "~16.3.1",
159159
"ejs": "^3.1.7",
160160
"enhanced-resolve": "^5.8.3",
161161
"esbuild": "^0.17.5",

packages/nx/bin/init-local.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@ export function initLocal(workspace: WorkspaceTypeAndRoot) {
1919

2020
try {
2121
performance.mark('init-local');
22-
require('nx/src/utils/perf-logging');
23-
2422
monkeyPatchRequire();
2523

2624
if (workspace.type !== 'nx' && shouldDelegateToAngularCLI()) {

packages/nx/bin/nx.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
WorkspaceTypeAndRoot,
55
} from '../src/utils/find-workspace-root';
66
import * as chalk from 'chalk';
7+
import { config as loadDotEnvFile } from 'dotenv';
78
import { initLocal } from './init-local';
89
import { output } from '../src/utils/output';
910
import {
@@ -16,6 +17,7 @@ import { readModulePackageJson } from '../src/utils/package-json';
1617
import { execSync } from 'child_process';
1718
import { join } from 'path';
1819
import { assertSupportedPlatform } from '../src/native/assert-supported-platform';
20+
import { performance } from 'perf_hooks';
1921

2022
function main() {
2123
if (
@@ -26,6 +28,17 @@ function main() {
2628
assertSupportedPlatform();
2729
}
2830

31+
require('nx/src/utils/perf-logging');
32+
33+
performance.mark('loading dotenv files:start');
34+
loadDotEnvFiles();
35+
performance.mark('loading dotenv files:end');
36+
performance.measure(
37+
'loading dotenv files',
38+
'loading dotenv files:start',
39+
'loading dotenv files:end'
40+
);
41+
2942
const workspace = findWorkspaceRoot(process.cwd());
3043
// new is a special case because there is no local workspace to load
3144
if (
@@ -88,6 +101,20 @@ function main() {
88101
}
89102
}
90103

104+
/**
105+
* This loads dotenv files from:
106+
* - .env
107+
* - .local.env
108+
* - .env.local
109+
*/
110+
function loadDotEnvFiles() {
111+
for (const file of ['.env', '.local.env', '.env.local']) {
112+
loadDotEnvFile({
113+
path: file,
114+
});
115+
}
116+
}
117+
91118
function handleNoWorkspace(globalNxVersion?: string) {
92119
output.log({
93120
title: `The current directory isn't part of an Nx workspace.`,

packages/nx/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
"cli-cursor": "3.1.0",
4242
"cli-spinners": "2.6.1",
4343
"cliui": "^7.0.2",
44-
"dotenv": "~10.0.0",
44+
"dotenv": "~16.3.1",
4545
"enquirer": "~2.3.6",
4646
"fast-glob": "3.2.7",
4747
"figures": "3.2.0",

packages/nx/src/tasks-runner/forked-process-task-runner.ts

Lines changed: 75 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { readFileSync, writeFileSync } from 'fs';
2-
import * as dotenv from 'dotenv';
2+
import { config as loadDotEnvFile } from 'dotenv';
33
import { ChildProcess, fork, Serializable } from 'child_process';
44
import * as chalk from 'chalk';
55
import * as logTransformer from 'strong-log-transformer';
@@ -301,8 +301,6 @@ export class ForkedProcessTaskRunner {
301301
// region Environment Variables
302302
private getEnvVariablesForProcess() {
303303
return {
304-
// Start With Dotenv Variables
305-
...this.getDotenvVariablesForForkedProcess(),
306304
// User Process Env Variables override Dotenv Variables
307305
...process.env,
308306
// Nx Env Variables overrides everything
@@ -318,11 +316,15 @@ export class ForkedProcessTaskRunner {
318316
outputPath: string,
319317
streamOutput: boolean
320318
) {
319+
// Unload any dot env files at the root of the workspace that were loaded on init of Nx.
320+
const taskEnv = this.unloadDotEnvFiles({ ...process.env });
321+
321322
const res = {
322323
// Start With Dotenv Variables
323-
...this.getDotenvVariablesForTask(task),
324-
// User Process Env Variables override Dotenv Variables
325-
...process.env,
324+
...(process.env.NX_LOAD_DOT_ENV_FILES === 'true'
325+
? this.loadDotEnvFilesForTask(task, taskEnv)
326+
: // If not loading dot env files, ensure env vars created by system are still loaded
327+
taskEnv),
326328
// Nx Env Variables overrides everything
327329
...this.getNxEnvVariablesForTask(
328330
task,
@@ -397,57 +399,76 @@ export class ForkedProcessTaskRunner {
397399
};
398400
}
399401

400-
private getDotenvVariablesForForkedProcess() {
401-
return {
402-
...parseEnv('.env'),
403-
...parseEnv('.local.env'),
404-
...parseEnv('.env.local'),
405-
};
402+
private loadDotEnvFilesForTask(
403+
task: Task,
404+
environmentVariables: NodeJS.ProcessEnv
405+
) {
406+
// Collect dot env files that may pertain to a task
407+
const dotEnvFiles = [
408+
// Load DotEnv Files for a configuration in the project root
409+
...(task.target.configuration
410+
? [
411+
`${task.projectRoot}/.env.${task.target.target}.${task.target.configuration}`,
412+
`${task.projectRoot}/.env.${task.target.configuration}`,
413+
`${task.projectRoot}/.${task.target.target}.${task.target.configuration}.env`,
414+
`${task.projectRoot}/.${task.target.configuration}.env`,
415+
]
416+
: []),
417+
418+
// Load DotEnv Files for a target in the project root
419+
`${task.projectRoot}/.env.${task.target.target}`,
420+
`${task.projectRoot}/.${task.target.target}.env`,
421+
`${task.projectRoot}/.env.local`,
422+
`${task.projectRoot}/.local.env`,
423+
`${task.projectRoot}/.env`,
424+
425+
// Load DotEnv Files for a configuration in the workspace root
426+
...(task.target.configuration
427+
? [
428+
`.env.${task.target.target}.${task.target.configuration}`,
429+
`.env.${task.target.configuration}`,
430+
`.${task.target.target}.${task.target.configuration}.env`,
431+
`.${task.target.configuration}.env`,
432+
]
433+
: []),
434+
435+
// Load DotEnv Files for a target in the workspace root
436+
`.env.${task.target.target}`,
437+
`.${task.target.target}.env`,
438+
439+
// Load base DotEnv Files at workspace root
440+
`.env`,
441+
`.local.env`,
442+
`.env.local`,
443+
];
444+
445+
for (const file of dotEnvFiles) {
446+
loadDotEnvFile({
447+
path: file,
448+
processEnv: environmentVariables,
449+
// Do not override existing env variables as we load
450+
override: false,
451+
});
452+
}
453+
454+
return environmentVariables;
406455
}
407456

408-
private getDotenvVariablesForTask(task: Task) {
409-
if (process.env.NX_LOAD_DOT_ENV_FILES == 'true') {
410-
return {
411-
...this.getDotenvVariablesForForkedProcess(),
412-
...parseEnv(`.${task.target.target}.env`),
413-
...parseEnv(`.env.${task.target.target}`),
414-
...(task.target.configuration
415-
? {
416-
...parseEnv(`.${task.target.configuration}.env`),
417-
...parseEnv(
418-
`.${task.target.target}.${task.target.configuration}.env`
419-
),
420-
...parseEnv(`.env.${task.target.configuration}`),
421-
...parseEnv(
422-
`.env.${task.target.target}.${task.target.configuration}`
423-
),
424-
}
425-
: {}),
426-
...parseEnv(`${task.projectRoot}/.env`),
427-
...parseEnv(`${task.projectRoot}/.local.env`),
428-
...parseEnv(`${task.projectRoot}/.env.local`),
429-
...parseEnv(`${task.projectRoot}/.${task.target.target}.env`),
430-
...parseEnv(`${task.projectRoot}/.env.${task.target.target}`),
431-
...(task.target.configuration
432-
? {
433-
...parseEnv(
434-
`${task.projectRoot}/.${task.target.configuration}.env`
435-
),
436-
...parseEnv(
437-
`${task.projectRoot}/.${task.target.target}.${task.target.configuration}.env`
438-
),
439-
...parseEnv(
440-
`${task.projectRoot}/.env.${task.target.configuration}`
441-
),
442-
...parseEnv(
443-
`${task.projectRoot}/.env.${task.target.target}.${task.target.configuration}`
444-
),
445-
}
446-
: {}),
447-
};
448-
} else {
449-
return {};
457+
private unloadDotEnvFiles(environmentVariables: NodeJS.ProcessEnv) {
458+
const unloadDotEnvFile = (filename: string) => {
459+
let parsedDotEnvFile: NodeJS.ProcessEnv = {};
460+
loadDotEnvFile({ path: filename, processEnv: parsedDotEnvFile });
461+
Object.keys(parsedDotEnvFile).forEach((envVarKey) => {
462+
if (environmentVariables[envVarKey] === parsedDotEnvFile[envVarKey]) {
463+
delete environmentVariables[envVarKey];
464+
}
465+
});
466+
};
467+
468+
for (const file of ['.env', '.local.env', '.env.local']) {
469+
unloadDotEnvFile(file);
450470
}
471+
return environmentVariables;
451472
}
452473

453474
// endregion Environment Variables
@@ -507,13 +528,6 @@ export class ForkedProcessTaskRunner {
507528
}
508529
}
509530

510-
function parseEnv(path: string) {
511-
try {
512-
const envContents = readFileSync(path);
513-
return dotenv.parse(envContents);
514-
} catch (e) {}
515-
}
516-
517531
const colors = [
518532
chalk.green,
519533
chalk.greenBright,

pnpm-lock.yaml

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

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy