diff --git a/e2e/nx-misc/src/extras.test.ts b/e2e/nx-misc/src/extras.test.ts
index 1297adbf6985e..5949da61785af 100644
--- a/e2e/nx-misc/src/extras.test.ts
+++ b/e2e/nx-misc/src/extras.test.ts
@@ -273,4 +273,51 @@ describe('Extra Nx Misc Tests', () => {
expect(output).not.toContain('Installed');
});
});
+
+ describe('Env File', () => {
+ it('should have the right env', () => {
+ const appName = uniq('app');
+ runCLI(
+ `generate @nx/react:app ${appName} --style=css --bundler=webpack --no-interactive`
+ );
+ updateFile(
+ '.env',
+ `FIRSTNAME="firstname"
+ LASTNAME="lastname"
+ NX_USERNAME=$FIRSTNAME $LASTNAME`
+ );
+ updateFile(
+ `apps/${appName}/src/app/app.tsx`,
+ `
+ import NxWelcome from './nx-welcome';
+
+ export function App() {
+ return (
+ <>
+
+ >
+ );
+ }
+
+ export default App;
+ `
+ );
+ updateFile(
+ `apps/${appName}/src/app/app.spec.tsx`,
+ `import { render } from '@testing-library/react';
+
+ import App from './app';
+
+ describe('App', () => {
+ it('should have a greeting as the title', () => {
+ const { getByText } = render();
+ expect(getByText(/Welcome firstname lastname/gi)).toBeTruthy();
+ });
+ });
+ `
+ );
+ const unitTestsOutput = runCLI(`test ${appName}`);
+ expect(unitTestsOutput).toContain('Successfully ran target test');
+ });
+ });
});
diff --git a/package.json b/package.json
index 2dd062390260a..0a921f1c800d6 100644
--- a/package.json
+++ b/package.json
@@ -158,6 +158,7 @@
"czg": "^1.4.0",
"detect-port": "^1.5.1",
"dotenv": "~16.3.1",
+ "dotenv-expand": "^10.0.0",
"ejs": "^3.1.7",
"enhanced-resolve": "^5.8.3",
"esbuild": "^0.17.5",
diff --git a/packages/nx/bin/nx.ts b/packages/nx/bin/nx.ts
index 6851730d816cf..8755ad02ae3b1 100644
--- a/packages/nx/bin/nx.ts
+++ b/packages/nx/bin/nx.ts
@@ -5,6 +5,7 @@ import {
} from '../src/utils/find-workspace-root';
import * as chalk from 'chalk';
import { config as loadDotEnvFile } from 'dotenv';
+import { expand } from 'dotenv-expand';
import { initLocal } from './init-local';
import { output } from '../src/utils/output';
import {
@@ -109,9 +110,10 @@ function main() {
*/
function loadDotEnvFiles() {
for (const file of ['.local.env', '.env.local', '.env']) {
- loadDotEnvFile({
+ const myEnv = loadDotEnvFile({
path: file,
});
+ expand(myEnv);
}
}
diff --git a/packages/nx/package.json b/packages/nx/package.json
index ba3402bc61d3d..df38a9d3d9862 100644
--- a/packages/nx/package.json
+++ b/packages/nx/package.json
@@ -42,6 +42,7 @@
"cli-spinners": "2.6.1",
"cliui": "^7.0.2",
"dotenv": "~16.3.1",
+ "dotenv-expand": "~10.0.0",
"enquirer": "~2.3.6",
"fast-glob": "3.2.7",
"figures": "3.2.0",
diff --git a/packages/nx/src/tasks-runner/forked-process-task-runner.ts b/packages/nx/src/tasks-runner/forked-process-task-runner.ts
index f91a0631fe790..22a90dab04907 100644
--- a/packages/nx/src/tasks-runner/forked-process-task-runner.ts
+++ b/packages/nx/src/tasks-runner/forked-process-task-runner.ts
@@ -1,5 +1,6 @@
import { readFileSync, writeFileSync } from 'fs';
import { config as loadDotEnvFile } from 'dotenv';
+import { expand } from 'dotenv-expand';
import { ChildProcess, fork, Serializable } from 'child_process';
import * as chalk from 'chalk';
import * as logTransformer from 'strong-log-transformer';
@@ -443,12 +444,19 @@ export class ForkedProcessTaskRunner {
];
for (const file of dotEnvFiles) {
- loadDotEnvFile({
+ const myEnv = loadDotEnvFile({
path: file,
processEnv: environmentVariables,
// Do not override existing env variables as we load
override: false,
});
+ environmentVariables = {
+ ...expand({
+ ...myEnv,
+ ignoreProcessEnv: true, // Do not override existing env variables as we load
+ }).parsed,
+ ...environmentVariables,
+ };
}
return environmentVariables;
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index c2d5bd34c08b1..f376b8ceae722 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -549,6 +549,9 @@ devDependencies:
dotenv:
specifier: ~16.3.1
version: 16.3.1
+ dotenv-expand:
+ specifier: ^10.0.0
+ version: 10.0.0
ejs:
specifier: ^3.1.7
version: 3.1.8
diff --git a/tools/documentation/create-embeddings/src/main.mts b/tools/documentation/create-embeddings/src/main.mts
index 6b91be01c0b9b..f4e379ac17dbc 100644
--- a/tools/documentation/create-embeddings/src/main.mts
+++ b/tools/documentation/create-embeddings/src/main.mts
@@ -2,7 +2,8 @@
// https://github.com/supabase-community/nextjs-openai-doc-search/blob/main/lib/generate-embeddings.ts
import { createClient } from '@supabase/supabase-js';
-import * as dotenv from 'dotenv';
+import { config as loadDotEnvFile } from 'dotenv';
+import { expand } from 'dotenv-expand';
import { readFile } from 'fs/promises';
import 'openai';
import { Configuration, OpenAIApi } from 'openai';
@@ -24,7 +25,8 @@ import manifestsTags from '../../../../docs/generated/manifests/tags.json' asser
let identityMap = {};
-dotenv.config();
+const myEnv = loadDotEnvFile();
+expand(myEnv);
type ProcessedMdx = {
checksum: string;