Skip to content

Commit f6aabc8

Browse files
committed
fix(netlify): add runtime env fallback generation for server env reads
1 parent 10d2e45 commit f6aabc8

4 files changed

Lines changed: 59 additions & 2 deletions

File tree

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import 'server-only';
2+
3+
export const RUNTIME_ENV: Readonly<Record<string, string>> = {};

frontend/lib/env/server-env.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import 'server-only';
22

3+
import { RUNTIME_ENV } from './runtime-env.generated';
4+
35
type NetlifyEnv = {
46
get?: (key: string) => string | undefined;
57
};
@@ -29,5 +31,13 @@ export function readServerEnv(key: string): string | undefined {
2931
const fromProcess = process.env[key]?.trim();
3032
if (fromProcess) return fromProcess;
3133

32-
return readFromNetlifyEnv(key);
34+
const fromNetlify = readFromNetlifyEnv(key);
35+
if (fromNetlify) return fromNetlify;
36+
37+
return readFromGeneratedRuntimeEnv(key);
3338
}
39+
40+
function readFromGeneratedRuntimeEnv(key: string): string | undefined {
41+
const value = RUNTIME_ENV[key];
42+
return typeof value === 'string' && value.trim() ? value.trim() : undefined;
43+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { readFileSync, writeFileSync } from 'node:fs';
2+
import { resolve } from 'node:path';
3+
4+
const root = process.cwd();
5+
const examplePath = resolve(root, '.env.example');
6+
const outputPath = resolve(root, 'lib/env/runtime-env.generated.ts');
7+
8+
const keyRegex = /^([A-Z][A-Z0-9_]*)=/;
9+
10+
const keys = Array.from(
11+
new Set(
12+
readFileSync(examplePath, 'utf8')
13+
.split(/\r?\n/)
14+
.map(line => line.trim())
15+
.filter(line => line && !line.startsWith('#'))
16+
.map(line => {
17+
const match = line.match(keyRegex);
18+
return match ? match[1] : null;
19+
})
20+
.filter(Boolean)
21+
)
22+
);
23+
24+
const entries = [];
25+
26+
for (const key of keys) {
27+
const value = process.env[key];
28+
if (typeof value !== 'string' || value.length === 0) continue;
29+
entries.push([key, value]);
30+
}
31+
32+
const objectBody = entries
33+
.map(([key, value]) => ` ${JSON.stringify(key)}: ${JSON.stringify(value)},`)
34+
.join('\n');
35+
36+
const fileContent = `import 'server-only';
37+
38+
export const RUNTIME_ENV: Readonly<Record<string, string>> = {
39+
${objectBody}
40+
};
41+
`;
42+
43+
writeFileSync(outputPath, fileContent, 'utf8');
44+
console.log(`[env] generated runtime-env.generated.ts with ${entries.length} keys`);

netlify.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[build]
22
base = "frontend"
3-
command = "npm ci --include=optional && npm run build"
3+
command = "npm ci --include=optional && node scripts/generate-env-runtime.mjs && npm run build"
44
publish = ".next"
55

66
[build.environment]

0 commit comments

Comments
 (0)