Skip to content

Commit ad4aaad

Browse files
committed
extract env vars from file system
1 parent 954b885 commit ad4aaad

File tree

6 files changed

+231
-2
lines changed

6 files changed

+231
-2
lines changed

packages/cloudflare/package.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,14 @@
5959
"tsup": "catalog:",
6060
"typescript": "catalog:",
6161
"typescript-eslint": "catalog:",
62-
"vitest": "catalog:"
62+
"vitest": "catalog:",
63+
"mock-fs": "catalog:",
64+
"@types/mock-fs": "catalog:"
6365
},
6466
"dependencies": {
6567
"@opennextjs/aws": "https://pkg.pr.new/@opennextjs/aws@674",
66-
"ts-morph": "catalog:"
68+
"ts-morph": "catalog:",
69+
"@dotenvx/dotenvx": "catalog:"
6770
},
6871
"peerDependencies": {
6972
"wrangler": "catalog:"
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { writeFileSync } from "node:fs";
2+
3+
import { BuildOptions } from "@opennextjs/aws/build/helper.js";
4+
import mockFs from "mock-fs";
5+
import { afterEach, beforeEach, describe, expect, it } from "vitest";
6+
7+
import { extractProjectEnvVars } from "./extract-project-env-vars";
8+
9+
const options = { monorepoRoot: "", appPath: "" } as BuildOptions;
10+
11+
describe("extractProjectEnvVars", () => {
12+
beforeEach(() => {
13+
mockFs({
14+
".env": "ENV_VAR=value",
15+
".env.local": "ENV_LOCAL_VAR=value",
16+
".env.development": "ENV_DEV_VAR=value",
17+
".env.development.local": "ENV_DEV_LOCAL_VAR=value",
18+
".env.production": "ENV_PROD_VAR=value",
19+
".env.production.local": "ENV_PROD_LOCAL_VAR=value",
20+
});
21+
});
22+
23+
afterEach(() => mockFs.restore());
24+
25+
it("should extract production env vars", () => {
26+
const result = extractProjectEnvVars(options);
27+
expect(result).toEqual({
28+
ENV_LOCAL_VAR: "value",
29+
ENV_PROD_LOCAL_VAR: "value",
30+
ENV_PROD_VAR: "value",
31+
ENV_VAR: "value",
32+
});
33+
});
34+
35+
it("should extract development env vars", () => {
36+
writeFileSync(".dev.vars", 'NEXTJS_ENV = "development"');
37+
38+
const result = extractProjectEnvVars(options);
39+
expect(result).toEqual({
40+
ENV_LOCAL_VAR: "value",
41+
ENV_DEV_LOCAL_VAR: "value",
42+
ENV_DEV_VAR: "value",
43+
ENV_VAR: "value",
44+
});
45+
});
46+
47+
it("should override env vars with those in a local file", () => {
48+
writeFileSync(".env.production.local", "ENV_PROD_VAR=overridden");
49+
50+
const result = extractProjectEnvVars(options);
51+
expect(result).toEqual({
52+
ENV_LOCAL_VAR: "value",
53+
ENV_PROD_VAR: "overridden",
54+
ENV_VAR: "value",
55+
});
56+
});
57+
});
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import * as fs from "node:fs";
2+
import * as path from "node:path";
3+
4+
import { parse } from "@dotenvx/dotenvx";
5+
import type { BuildOptions } from "@opennextjs/aws/build/helper.js";
6+
7+
function readEnvFiles(fileNames: string[], { monorepoRoot, appPath }: BuildOptions) {
8+
return fileNames
9+
.flatMap((fileName) => [
10+
...(monorepoRoot !== appPath ? [path.join(monorepoRoot, fileName)] : []),
11+
path.join(appPath, fileName),
12+
])
13+
.filter((filePath) => fs.statSync(filePath, { throwIfNoEntry: false })?.isFile())
14+
.map((filePath) => parse(fs.readFileSync(filePath).toString()))
15+
.reduce<Record<string, string>>((acc, overrides) => ({ ...acc, ...overrides }), {});
16+
}
17+
18+
/**
19+
* Extracts the environment variables defined in various .env files for a project.
20+
*
21+
* The `NEXTJS_ENV` environment variable in `.dev.vars` determines the mode.
22+
*
23+
* Merged variables respect the following priority order.
24+
* 1. `.env.{mode}.local`
25+
* 2. `.env.local`
26+
* 3. `.env.{mode}`
27+
* 4. `.env`
28+
*
29+
* In a monorepo, the env files in an app's directory will take precedence over
30+
* the env files at the root of the monorepo.
31+
*/
32+
export function extractProjectEnvVars(options: BuildOptions) {
33+
const wranglerDevVars = readEnvFiles([".dev.vars"], options);
34+
35+
const mode = wranglerDevVars["NEXTJS_ENV"] ?? "production";
36+
37+
return readEnvFiles([".env", `.env.${mode}`, ".env.local", `.env.${mode}.local`], options);
38+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export * from "./copy-prerendered-routes";
2+
export * from "./extract-project-env-vars";
23
export * from "./normalize-path";
34
export * from "./ts-parse-file";

0 commit comments

Comments
 (0)