Env Loading
@mongez/vite calls into @mongez/dotenv from inside its config hook to load the right .env.<environment> file for the current Vite command.
Resolution chain
From process.cwd(), the plugin walks one of these chains depending on the command:
| Command (Vite) | File search order |
|---|---|
vite build | .env.production → .env.build → .env |
vite dev (serve) | .env.development → .env.local → .env |
Whichever file exists first wins. If none exist, the helper is a no-op.
The productionEnvName override
To lock the build to a specific file name (e.g. .env.staging):
mongezVite({ productionEnvName: "staging" });Effects:
- During
vite build, loads ONLY.env.staging. If missing, the helper returns early — it does NOT fall back to.env.production/.env.build/.env. - During
vite dev, the option is ignored; the standardservechain applies.
// "Build for staging, dev with .env.development as usual"mongezVite({ productionEnvName: "staging" });STAGE=staging vite build # loads .env.stagingvite dev # loads .env.developmentWhat @mongez/dotenv does to values
The dotenv parser is intentionally narrow about which strings get coerced:
| Input | Output | Type |
|---|---|---|
"3000" | 3000 | number |
"3.14" | 3.14 | number |
"true" / "false" | true / false | boolean |
"null" | null | object |
"hello" | "hello" | string |
'"3000"' (quoted) | "3000" | string — quotes opt out of coercion |
${VAR} interpolation works between keys when the referenced key appears earlier in the file:
APP_HOST=localhostAPP_PORT=3000APP_URL=http://${APP_HOST}:${APP_PORT}Result: env("APP_URL") === "http://localhost:3000".
Reading values back
import { env } from "@mongez/dotenv";
env("APP_PORT"); // 3000 (typed)env("DEBUG"); // true (typed)env("MISSING", "x"); // "x" (default fallback)
process.env.APP_PORT; // "3000" (string — Node coerces every assignment)The plugin loads with override: true, so process.env also gets a string-coerced copy of every loaded key. If you want the typed value, go through env().
.env.shared layering
@mongez/dotenv defaults to loading .env.shared before the environment-specific file. The plugin uses these defaults:
loadEnv(envPath, { override: true, loadSharedEnv: true,});Layering:
APP_NAME="My App"APP_URL=https://example.com
# .env.productionDB_HOST=prod-db.example.comDEBUG=falseAfter load:
env("APP_NAME")→"My App"(from shared).env("DEBUG")→false(from production).
If a key appears in both, the environment-specific file wins.
Gotchas
process.cwd()is the only resolution root. Runvitefrom the project root; from a subdirectory the files won’t be found.- No fallback when
productionEnvNameis set. If you setproductionEnvName: "staging"and forget to create.env.staging, the loader silently does nothing. - No event when the load completes. The plugin loads env files inside the
confighook, which runs once. After that, env values are static until the next vite restart. env()preserves a deliberately-loadednull.@mongez/dotenv’senv()useskey in envDatarather than??, so a value of literalnullreads back asnull(not the supplied default). If you only want the default when the key is missing, that behaviour is now correct.