Skip to content

Commit a1eddb0

Browse files
authored
Merge pull request #4 from expatfile/@HofmannZ/feature/new-utilites
New utilities
2 parents 76f7001 + 23c6f30 commit a1eddb0

10 files changed

+254
-23
lines changed

README.md

+88-14
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
# Next.js Runtime Environment Configuration
2-
31
![GitHub branch checks state][build-url] [![codecov][cov-img]][cov-url]
42

5-
Populates your environment at **runtime** rather than **build time**.
3+
# Next.js Runtime Environment Configuration
4+
5+
Populate your environment at **runtime** rather than **build time**.
66

77
- Isomorphic - Server and browser compatible (and even in middleware.)
88
- Static site generation support.
@@ -49,7 +49,7 @@ containing allow-listed environment variables with a `NEXT_PUBLIC_` prefix.
4949

5050
2. Add the following to the head section of your `pages/_document.js`:
5151

52-
```jsx
52+
```tsx
5353
// pages/_document.tsx
5454
<script src="/__ENV.js" />
5555
```
@@ -59,21 +59,20 @@ This will load the generated file in the browser.
5959
### Usage 🧑‍💻
6060

6161
In the browser, your variables will now be available at
62-
`window.__ENV.NEXT_PUBLIC_FOO` and on the server at `process.env.NEXT_PUBLIC_FOO`.
63-
64-
#### Helper function 🙌
65-
66-
We have included the `env()` helper function to make retrieving a value easier:
62+
`window.__ENV.NEXT_PUBLIC_FOO` and on the server at
63+
`process.env.NEXT_PUBLIC_FOO`. For example:
6764

6865
```bash
6966
# .env
7067
NEXT_PUBLIC_FOO="foo"
7168
BAR="bar"
69+
NEXT_PUBLIC_BAZ="baz"
7270
```
7371

74-
```jsx
72+
```tsx
73+
// pages/some-page.tsx
7574
type Props = {
76-
bar: string,
75+
bar: string;
7776
};
7877

7978
export default function SomePage({ bar }: Props) {
@@ -93,13 +92,24 @@ export const getStaticProps: GetStaticProps = async (context) => {
9392
};
9493
```
9594

96-
Becomes...
95+
### Utilities 🛠
9796

98-
```jsx
97+
We have included some utility function to make it even easier to work with
98+
environment variables.
99+
100+
#### `env(key: string): string | undefined`
101+
102+
Returns the value of the environment variable with the given key. If the
103+
environment variable is not found, it returns undefined.
104+
105+
##### Example
106+
107+
```tsx
108+
// pages/some-page.tsx
99109
import { env } from 'next-runtime-env';
100110

101111
type Props = {
102-
bar: string,
112+
bar: string;
103113
};
104114

105115
export default function SomePage({ bar }: Props) {
@@ -119,6 +129,70 @@ export const getStaticProps: GetStaticProps = async (context) => {
119129
};
120130
```
121131

132+
#### `allEnv(): NodeJS.ProcessEnv`
133+
134+
Returns all environment variables as a `NodeJS.ProcessEnv` object regardless of
135+
the platform. This is useful if you want to destructure multiple env vars at
136+
once.
137+
138+
##### Example
139+
140+
```tsx
141+
// pages/some-page.tsx
142+
import { allEnv } from 'next-runtime-env';
143+
144+
type Props = {
145+
bar: string;
146+
};
147+
148+
export default function SomePage({ bar }: Props) {
149+
const { NEXT_PUBLIC_FOO, NEXT_PUBLIC_BAZ } = allEnv();
150+
151+
return (
152+
<div>
153+
{NEXT_PUBLIC_FOO} {NEXT_PUBLIC_BAZ} {bar}
154+
</div>
155+
);
156+
}
157+
158+
export const getStaticProps: GetStaticProps = async (context) => {
159+
const { BAR } = allEnv();
160+
161+
return {
162+
props: {
163+
bar: BAR,
164+
},
165+
};
166+
};
167+
```
168+
169+
#### `makeEnvPublic(key: string | string[]): void`
170+
171+
Makes an environment variable with a given key public. This is useful if you
172+
want to use an environment variable in the browser, but it was was not declared
173+
with a `NEXT_PUBLIC_` prefix.
174+
175+
For ease of use you can also make multiple env vars public at once by passing an
176+
array of keys.
177+
178+
##### Example
179+
180+
```js
181+
// next.config.js
182+
const { configureRuntimeEnv } = require('next-runtime-env/build/configure');
183+
const { makeEnvPublic } = require('next-runtime-env/build/make-env-public');
184+
185+
// Given that `FOO` is declared as a regular env var, not a public one. This
186+
// will make it public and available as `NEXT_PUBLIC_FOO`.
187+
makeEnvPublic('FOO');
188+
189+
// Or you can make multiple env vars public at once.
190+
makeEnvPublic(['BAR', 'BAZ']);
191+
192+
// This will generate the `__ENV.js` file and include `NEXT_PUBLIC_FOO`.
193+
configureRuntimeEnv();
194+
```
195+
122196
### Maintenance 👷
123197

124198
This package is maintained and actively used by [Expatfile.tax][expatfile-site].

src/all-env.spec.ts

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { allEnv } from './all-env';
2+
3+
let envBackup: NodeJS.ProcessEnv;
4+
5+
beforeAll(() => {
6+
envBackup = process.env;
7+
process.env = {};
8+
});
9+
10+
afterAll(() => {
11+
process.env = envBackup;
12+
});
13+
14+
describe('allEnv()', () => {
15+
afterEach(() => {
16+
delete process.env.FOO;
17+
delete process.env.NEXT_PUBLIC_BAR;
18+
delete process.env.NEXT_PUBLIC_BAZ;
19+
20+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
21+
global.window = undefined as any;
22+
});
23+
24+
it('should return all values from the server', () => {
25+
process.env.FOO = 'foo';
26+
process.env.NEXT_PUBLIC_BAR = 'bar';
27+
process.env.NEXT_PUBLIC_BAZ = 'baz';
28+
29+
expect(allEnv()).toEqual({
30+
FOO: 'foo',
31+
NEXT_PUBLIC_BAR: 'bar',
32+
NEXT_PUBLIC_BAZ: 'baz',
33+
});
34+
});
35+
36+
it('should return all values from the browser', () => {
37+
Object.defineProperty(global, 'window', {
38+
value: {
39+
__ENV: {
40+
NEXT_PUBLIC_BAR: 'bar',
41+
NEXT_PUBLIC_BAZ: 'baz',
42+
},
43+
},
44+
writable: true,
45+
});
46+
47+
expect(allEnv()).toEqual({
48+
NEXT_PUBLIC_BAR: 'bar',
49+
NEXT_PUBLIC_BAZ: 'baz',
50+
});
51+
});
52+
});

src/all-env.ts

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { isBrowser } from './utils/is-browser';
2+
3+
/**
4+
* Reads all safe environment variables from the browser or all environment
5+
* variables from the server.
6+
*/
7+
export function allEnv(): NodeJS.ProcessEnv {
8+
if (isBrowser()) {
9+
// eslint-disable-next-line no-underscore-dangle
10+
return window.__ENV;
11+
}
12+
13+
return process.env;
14+
}

src/env.spec.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ describe('env()', () => {
1111
it('should return a value from the server', () => {
1212
process.env.FOO = 'foo';
1313

14-
expect(env('FOO')).toBe('foo');
14+
expect(env('FOO')).toEqual('foo');
1515
});
1616

1717
it('should return a value from the browser', () => {
@@ -24,11 +24,11 @@ describe('env()', () => {
2424
writable: true,
2525
});
2626

27-
expect(env('NEXT_PUBLIC_FOO')).toBe('foo');
27+
expect(env('NEXT_PUBLIC_FOO')).toEqual('foo');
2828
});
2929

3030
it('should return undefined when variable does not exist on the server', () => {
31-
expect(env('BAM_BAM')).toBe(undefined);
31+
expect(env('BAM_BAM')).toEqual(undefined);
3232
});
3333

3434
it('should return undefined when variable does not exist in the browser', () => {
@@ -41,6 +41,6 @@ describe('env()', () => {
4141
writable: true,
4242
});
4343

44-
expect(env('BAM_BAM')).toBe(undefined);
44+
expect(env('BAM_BAM')).toEqual(undefined);
4545
});
4646
});

src/env.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { isBrowser } from './utils/is-browser';
44
* Reads a safe environment variable from the browser or any environment
55
* variable from the server (process.env).
66
*/
7-
export function env(key: string) {
7+
export function env(key: string): string | undefined {
88
if (isBrowser()) {
99
// eslint-disable-next-line no-underscore-dangle
1010
return window.__ENV[key];

src/helpers/write-browser-env.spec.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ describe('writeBrowserEnv()', () => {
2929
writeBrowserEnv({});
3030

3131
expect(infoSpy).toHaveBeenCalledWith(
32-
'next-runtime-env: Writing browser runtime env',
32+
'> [next-runtime-env] Writing browser runtime env',
3333
file
3434
);
3535

@@ -44,7 +44,7 @@ describe('writeBrowserEnv()', () => {
4444
});
4545

4646
expect(infoSpy).toHaveBeenCalledWith(
47-
'next-runtime-env: Writing browser runtime env',
47+
'> [next-runtime-env] Writing browser runtime env',
4848
file
4949
);
5050

@@ -61,7 +61,7 @@ describe('writeBrowserEnv()', () => {
6161
});
6262

6363
expect(infoSpy).toHaveBeenCalledWith(
64-
'next-runtime-env: Writing browser runtime env',
64+
'> [next-runtime-env] Writing browser runtime env',
6565
file
6666
);
6767

src/helpers/write-browser-env.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ export function writeBrowserEnv(env: NodeJS.ProcessEnv) {
99
const path = `${base}/public/__ENV.js`;
1010

1111
// eslint-disable-next-line no-console
12-
console.info('next-runtime-env: Writing browser runtime env', path);
12+
console.info('> [next-runtime-env] Writing browser runtime env', path);
1313

1414
const content = `window.__ENV = ${JSON.stringify(env)};`;
1515

src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ declare global {
77
}
88
}
99

10+
export { allEnv } from './all-env';
1011
export { env } from './env';

src/make-env-public.spec.ts

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { makeEnvPublic } from './make-env-public';
2+
3+
const warnSpy = jest.spyOn(console, 'warn');
4+
5+
beforeAll(() => {
6+
warnSpy.mockImplementation();
7+
});
8+
9+
afterAll(() => {
10+
warnSpy.mockRestore();
11+
});
12+
13+
describe('makeEnvPublic()', () => {
14+
afterEach(() => {
15+
delete process.env.FOO;
16+
delete process.env.BAR;
17+
delete process.env.BAZ;
18+
delete process.env.NEXT_PUBLIC_FOO;
19+
delete process.env.NEXT_PUBLIC_BAR;
20+
delete process.env.NEXT_PUBLIC_BAZ;
21+
});
22+
23+
it('should prefix an env var with NEXT_PUBLIC_', () => {
24+
process.env.FOO = 'foo';
25+
26+
makeEnvPublic('FOO');
27+
28+
expect(process.env.FOO).toEqual('foo');
29+
expect(process.env.NEXT_PUBLIC_FOO).toEqual('foo');
30+
});
31+
32+
it('should prefix multiple env vars with NEXT_PUBLIC_', () => {
33+
process.env.FOO = 'foo';
34+
process.env.BAR = 'bar';
35+
process.env.BAZ = 'baz';
36+
37+
makeEnvPublic(['FOO', 'BAR', 'BAZ']);
38+
39+
expect(process.env.FOO).toEqual('foo');
40+
expect(process.env.NEXT_PUBLIC_FOO).toEqual('foo');
41+
expect(process.env.BAR).toEqual('bar');
42+
expect(process.env.NEXT_PUBLIC_BAR).toEqual('bar');
43+
expect(process.env.BAZ).toEqual('baz');
44+
expect(process.env.NEXT_PUBLIC_BAZ).toEqual('baz');
45+
});
46+
47+
it('should warn when the env var already starts with NEXT_PUBLIC_', () => {
48+
process.env.NEXT_PUBLIC_FOO = 'foo';
49+
50+
makeEnvPublic('NEXT_PUBLIC_FOO');
51+
52+
expect(warnSpy).toHaveBeenCalledWith(
53+
'> [next-runtime-env] The environment variable "NEXT_PUBLIC_FOO" is already public.'
54+
);
55+
});
56+
});

src/make-env-public.ts

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
function prefixKey(key: string) {
2+
// Check if the key already is already public.
3+
if (/^NEXT_PUBLIC_/i.test(key)) {
4+
// eslint-disable-next-line no-console
5+
console.warn(
6+
`> [next-runtime-env] The environment variable "${key}" is already public.`
7+
);
8+
}
9+
10+
const prefixedKey = `NEXT_PUBLIC_${key}`;
11+
12+
process.env[prefixedKey] = process.env[key];
13+
}
14+
15+
/**
16+
* Make a private environment variable public, so that it can be accessed in the
17+
* browser.
18+
*
19+
* Usage:
20+
* ```ts
21+
* // Make a single variable public.
22+
* makeEnvPublic('FOO');
23+
*
24+
* // Make multiple variables public.
25+
* makeEnvPublic(['FOO', 'BAR', 'BAZ']);
26+
* ```
27+
*/
28+
export function makeEnvPublic(key: string | string[]): void {
29+
if (typeof key === 'string') {
30+
prefixKey(key);
31+
} else {
32+
key.forEach(prefixKey);
33+
}
34+
}

0 commit comments

Comments
 (0)