Skip to content

Commit

Permalink
feat: Add prefix option (#176)
Browse files Browse the repository at this point in the history
  • Loading branch information
djdmbrwsk authored Jul 4, 2021
1 parent 6594a1d commit 4c402cc
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 1 deletion.
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,18 @@ files you load on `NODE_ENV`. For example, you may want `NODE_ENV` set to
dotenvCra.config({ env: process.env.AWS_ENV });
```

### Prefix

Default: none

You may specify a required prefix for your dotenv variables. For example, you
may want to prefix your variables with `WEB_API_` to ensure there aren't any
collisions with other environment variables.

```ts
dotenvCra.config({ prefix: 'WEB_API_' });
```

### Path

Default: `path.resolve(process.cwd(), '.env')`
Expand Down
27 changes: 26 additions & 1 deletion src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { basename, resolve } from 'path';
import {
DotenvConfigOptions,
DotenvConfigOutput,
DotenvParseOutput,
config as dotenvConfig,
} from 'dotenv';
import dotenvExpand from 'dotenv-expand';
Expand All @@ -13,6 +14,10 @@ export interface DotenvCraOptions extends DotenvConfigOptions {
* You may specify a custom environment if NODE_ENV isn't sufficient.
*/
env?: string;
/**
* You may specify a required prefix for your dotenv variables (ex. `REACT_APP_`).
*/
prefix?: string;
}

export function config(options?: DotenvCraOptions): DotenvConfigOutput {
Expand Down Expand Up @@ -42,7 +47,7 @@ export function config(options?: DotenvCraOptions): DotenvConfigOutput {

// Reference:
// https://github.com/facebook/create-react-app/blob/8b7b819b4b9e6ba457e011e92e33266690e26957/packages/react-scripts/config/env.js#L36-L49
let parsed: { [name: string]: string } = {};
let parsed: DotenvParseOutput = {};
for (const dotenvFile of dotenvFiles) {
if (!dotenvFile) {
continue;
Expand All @@ -68,6 +73,26 @@ export function config(options?: DotenvCraOptions): DotenvConfigOutput {
parsed = { ...result.parsed, ...parsed };
}

// Reference:
// https://github.com/facebook/create-react-app/blob/8b7b819b4b9e6ba457e011e92e33266690e26957/packages/react-scripts/config/env.js#L72-L89
if (options?.prefix) {
const prefixRegExp = new RegExp(`^${options.prefix}`, 'i');
parsed = Object.keys(parsed)
.filter((key) => {
const match = prefixRegExp.test(key);
log(
`Prefix for key \`${key}\` ${
match ? 'matches' : 'does not match'
} \`${options.prefix}\``,
);
return match;
})
.reduce((obj, key) => {
obj[key] = parsed[key];
return obj;
}, {} as DotenvParseOutput);
}

return {
parsed,
};
Expand Down
19 changes: 19 additions & 0 deletions test/main.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,25 @@ test('should exclude .env.local when NODE_ENV set to test', () => {
expect(secondCallPath?.endsWith('.env.local')).toEqual(true);
});

test('should filter out values when provided prefix does not match', () => {
process.env.NODE_ENV = 'development';
jest.spyOn(fs, 'existsSync').mockReturnValue(true);
jest.spyOn(dotenv, 'config').mockImplementation(
makeMockDotenvConfig({
'.env.local': { parsed: { MY_APP_SETTING: 'FOO' } },
'.env': { parsed: { NOT_MY_SETTING: 'BAR', MY_APP_SETTING_2: 'BAZ' } },
}),
);

const output = config({ prefix: 'MY_APP_' });
expect(output.error).toBeFalsy();
expect(output.parsed).toBeTruthy();
expect(output.parsed).toEqual({
MY_APP_SETTING: 'FOO',
MY_APP_SETTING_2: 'BAZ',
});
});

test('should return error objects', () => {
jest.spyOn(fs, 'existsSync').mockReturnValue(true);
jest.spyOn(dotenv, 'config').mockReturnValue({ error: new Error('foobar') });
Expand Down

0 comments on commit 4c402cc

Please sign in to comment.