diff --git a/packages/playground/env-nested/.env b/packages/playground/env-nested/.env new file mode 100644 index 00000000000000..2e8565f90df52b --- /dev/null +++ b/packages/playground/env-nested/.env @@ -0,0 +1 @@ +VITE_PARENT_ENV=dont_load_me \ No newline at end of file diff --git a/packages/playground/env-nested/__tests__/env-nested.spec.ts b/packages/playground/env-nested/__tests__/env-nested.spec.ts new file mode 100644 index 00000000000000..1ceebde7a044b7 --- /dev/null +++ b/packages/playground/env-nested/__tests__/env-nested.spec.ts @@ -0,0 +1,15 @@ +import { isBuild } from 'testUtils' + +const mode = isBuild ? `production` : `development` + +test('mode', async () => { + expect(await page.textContent('.mode')).toBe(mode) +}) + +test('mode file override', async () => { + expect(await page.textContent('.mode-file')).toBe(`.env.${mode}`) +}) + +test('should not load parent .env file', async () => { + expect(await page.textContent('.parent-env')).not.toBe('dont_load_me') +}) diff --git a/packages/playground/env-nested/envs/.env.development b/packages/playground/env-nested/envs/.env.development new file mode 100644 index 00000000000000..1557708651f4dc --- /dev/null +++ b/packages/playground/env-nested/envs/.env.development @@ -0,0 +1 @@ +VITE_EFFECTIVE_MODE_FILE_NAME=.env.development diff --git a/packages/playground/env-nested/envs/.env.production b/packages/playground/env-nested/envs/.env.production new file mode 100644 index 00000000000000..13c1a2ab36869f --- /dev/null +++ b/packages/playground/env-nested/envs/.env.production @@ -0,0 +1 @@ +VITE_EFFECTIVE_MODE_FILE_NAME=.env.production diff --git a/packages/playground/env-nested/index.html b/packages/playground/env-nested/index.html new file mode 100644 index 00000000000000..8f775d5cb30bba --- /dev/null +++ b/packages/playground/env-nested/index.html @@ -0,0 +1,16 @@ +

Nested Environment Variables

+

import.meta.env.MODE:

+

+ import.meta.env.VITE_EFFECTIVE_MODE_FILE_NAME: +

+

Empty import.meta.env.VITE_PARENT_ENV:

+ + diff --git a/packages/playground/env-nested/package.json b/packages/playground/env-nested/package.json new file mode 100644 index 00000000000000..8fecc69a41c2f4 --- /dev/null +++ b/packages/playground/env-nested/package.json @@ -0,0 +1,11 @@ +{ + "name": "test-env-nested", + "private": true, + "version": "0.0.0", + "scripts": { + "dev": "vite", + "build": "vite build", + "debug": "node --inspect-brk ../../vite/bin/vite", + "preview": "vite preview" + } +} diff --git a/packages/playground/env-nested/vite.config.js b/packages/playground/env-nested/vite.config.js new file mode 100644 index 00000000000000..0e46100698650d --- /dev/null +++ b/packages/playground/env-nested/vite.config.js @@ -0,0 +1,5 @@ +const { defineConfig } = require('vite') + +module.exports = defineConfig({ + envDir: './envs' +}) diff --git a/packages/vite/src/node/config.ts b/packages/vite/src/node/config.ts index 224c534c497ece..e5f72e6ac65957 100644 --- a/packages/vite/src/node/config.ts +++ b/packages/vite/src/node/config.ts @@ -401,11 +401,7 @@ export async function resolveConfig( const resolvedBuildOptions = resolveBuildOptions(config.build) // resolve cache directory - const pkgPath = lookupFile( - resolvedRoot, - [`package.json`], - true /* pathOnly */ - ) + const pkgPath = lookupFile(resolvedRoot, [`package.json`], { pathOnly: true }) const cacheDir = config.cacheDir ? path.resolve(resolvedRoot, config.cacheDir) : pkgPath @@ -1083,7 +1079,7 @@ export function loadEnv( } for (const file of envFiles) { - const path = lookupFile(envDir, [file], true) + const path = lookupFile(envDir, [file], { pathOnly: true, rootDir: envDir }) if (path) { const parsed = dotenv.parse(fs.readFileSync(path), { debug: process.env.DEBUG?.includes('vite:dotenv') || undefined diff --git a/packages/vite/src/node/utils.ts b/packages/vite/src/node/utils.ts index 9cba9c7c79b3a3..935c5c8401071b 100644 --- a/packages/vite/src/node/utils.ts +++ b/packages/vite/src/node/utils.ts @@ -298,20 +298,28 @@ export function isDefined(value: T | undefined | null): value is T { return value != null } +interface LookupFileOptions { + pathOnly?: boolean + rootDir?: string +} + export function lookupFile( dir: string, formats: string[], - pathOnly = false + options?: LookupFileOptions ): string | undefined { for (const format of formats) { const fullPath = path.join(dir, format) if (fs.existsSync(fullPath) && fs.statSync(fullPath).isFile()) { - return pathOnly ? fullPath : fs.readFileSync(fullPath, 'utf-8') + return options?.pathOnly ? fullPath : fs.readFileSync(fullPath, 'utf-8') } } const parentDir = path.dirname(dir) - if (parentDir !== dir) { - return lookupFile(parentDir, formats, pathOnly) + if ( + parentDir !== dir && + (!options?.rootDir || parentDir.startsWith(options?.rootDir)) + ) { + return lookupFile(parentDir, formats, options) } }