Skip to content

Commit be94089

Browse files
authored
feat(dotenv-flow): add type definitions (#77)
Add "in package" typings for better support of typescript and improved code completion. BREAKING CHANGE: new type definitions do replace the `@types/dotenv-flow` package but might be conflicting. The recommendation is to remove `@types/dotenv-flow` from dependencies if using dotenv-flow v4 or above.
1 parent 89a9d5c commit be94089

File tree

7 files changed

+293
-5
lines changed

7 files changed

+293
-5
lines changed

config.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export {};

lib/dotenv-flow.d.ts

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
import { ObjectEncodingOptions } from 'node:fs';
2+
3+
export type DotenvFlowListFilesOptions = {
4+
node_env?: string;
5+
path?: string;
6+
pattern?: string;
7+
debug?: boolean;
8+
};
9+
10+
/**
11+
* Returns a list of existing `.env*` filenames depending on the given `options`.
12+
*
13+
* The resulting list is ordered by the env files'
14+
* variables overwriting priority from lowest to highest.
15+
*
16+
* This can also be referenced as "env files' environment cascade"
17+
* or "order of ascending priority."
18+
*
19+
* ⚠️ Note that the `.env.local` file is not listed for "test" environment,
20+
* since normally you expect tests to produce the same results for everyone.
21+
*
22+
* @param options - `.env*` files listing options
23+
* @param options.node_env - node environment (development/test/production/etc.)
24+
* @param options.path - path to the working directory (default: `process.cwd()`)
25+
* @param options.pattern - `.env*` files' naming convention pattern
26+
* (default: ".env[.node_env][.local]")
27+
* @param options.debug - turn on debug messages
28+
*/
29+
export function listFiles(options?: DotenvFlowListFilesOptions): string[];
30+
31+
// --
32+
33+
export type DotenvFlowParseOptions = ObjectEncodingOptions & {
34+
debug?: boolean;
35+
};
36+
37+
export type DotenvFlowParseResult = {
38+
[name: string]: string;
39+
};
40+
41+
/**
42+
* Parses a given file and returns an object (map) of `varname => value` entries.
43+
*
44+
* @param filename - the name of the file to parse
45+
* @param options - parse options
46+
*/
47+
export function parse<T extends DotenvFlowParseResult = DotenvFlowParseResult>(
48+
filename: string,
49+
options?: DotenvFlowParseOptions
50+
): T;
51+
52+
/**
53+
* Parses a list of given files and returns a merged object (map) of `varname => value` entries.
54+
*
55+
* Note that files are parsed and merged in the same order as given. For example,
56+
* if `['.env', '.env.local']` is given, variables defined in `.env.local` (the second one)
57+
* will overwrite those are defined in `.env` (the first one) while merging.
58+
*
59+
* @param filenames - a list of filenames to parse and merge
60+
* @param options - parse options
61+
*/
62+
export function parse<T extends DotenvFlowParseResult = DotenvFlowParseResult>(
63+
filenames: string[],
64+
options?: DotenvFlowParseOptions
65+
): T;
66+
67+
// --
68+
69+
export type DotenvFlowLoadOptions = DotenvFlowParseOptions & {
70+
silent?: boolean;
71+
};
72+
73+
export type DotenvFlowLoadResult<T extends DotenvFlowParseResult = DotenvFlowParseResult> = {
74+
parsed?: T,
75+
error?: Error
76+
};
77+
78+
/**
79+
* Parses and assigns variables defined in a given file to `process.env`.
80+
*
81+
* @param filename - the name of the file to load from
82+
* @param options - parse/load options
83+
*/
84+
export function load<T extends DotenvFlowParseResult = DotenvFlowParseResult>(
85+
filename: string,
86+
options?: DotenvFlowLoadOptions
87+
): DotenvFlowLoadResult<T>;
88+
89+
/**
90+
* Parses, merges, and assigns variables from the given files to `process.env`.
91+
*
92+
* @param filenames - a list of filenames to load from
93+
* @param options - parse/load options
94+
*/
95+
export function load<T extends DotenvFlowParseResult = DotenvFlowParseResult>(
96+
filenames: string[],
97+
options?: DotenvFlowLoadOptions
98+
): DotenvFlowLoadResult<T>;
99+
100+
// --
101+
102+
/**
103+
* Unload variables defined in a given file from `process.env`.
104+
*
105+
* @param filename - the name of the file to unload
106+
* @param options - parse/unload options
107+
*/
108+
export function unload(filename: string, options?: DotenvFlowParseOptions): void;
109+
110+
/**
111+
* Unload variables defined in given files from `process.env`.
112+
*
113+
* @param filenames - a list of filenames to unload
114+
* @param options - parse/unload options
115+
*/
116+
export function unload(filenames: string[], options?: DotenvFlowParseOptions): void;
117+
118+
// --
119+
120+
export type DotenvFlowConfigOptions = DotenvFlowListFilesOptions & DotenvFlowLoadOptions & {
121+
default_node_env?: string;
122+
purge_dotenv?: boolean;
123+
}
124+
125+
export type DotenvFlowConfigResult<T extends DotenvFlowParseResult = DotenvFlowParseResult> = DotenvFlowLoadResult<T>;
126+
127+
/**
128+
* "dotenv-flow" initialization function (API entry point). Allows configuring dotenv-flow programmatically.
129+
*
130+
* @param options - configuration options
131+
*/
132+
export function config<T extends DotenvFlowParseResult = DotenvFlowParseResult>(
133+
options?: DotenvFlowConfigOptions
134+
): DotenvFlowConfigResult<T>;
135+
136+
// --
137+
138+
declare const DotenvFlow: {
139+
listFiles: typeof listFiles;
140+
parse: typeof parse;
141+
load: typeof load;
142+
unload: typeof unload;
143+
config: typeof config;
144+
};
145+
146+
export default DotenvFlow;

lib/dotenv-flow.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ function composeFilename(pattern, options) {
4949
*
5050
* @param {object} [options] - `.env*` files listing options
5151
* @param {string} [options.node_env] - node environment (development/test/production/etc.)
52-
* @param {object} [options.path] - path to the working directory (default: `process.cwd()`)
52+
* @param {string} [options.path] - path to the working directory (default: `process.cwd()`)
5353
* @param {string} [options.pattern] - `.env*` files' naming convention pattern
5454
* (default: ".env[.node_env][.local]")
5555
* @param {boolean} [options.debug] - turn on debug messages
@@ -119,12 +119,12 @@ function listFiles(options = {}) {
119119
}
120120

121121
/**
122-
* Parse a given file(s) to use the result programmatically.
122+
* Parses a given file or a list of files.
123123
*
124124
* When a list of filenames is given, the files will be parsed and merged in the same order as given.
125125
*
126126
* @param {string|string[]} filenames - filename or a list of filenames to parse and merge
127-
* @param {{ encoding?: string, debug:? boolean }} [options] - parse options
127+
* @param {{ encoding?: string, debug?: boolean }} [options] - parse options
128128
* @return {Object<string, string>} the resulting map of `{ env_var: value }` as an object
129129
*/
130130
function parse(filenames, options = {}) {

package.json

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
"url": "https://github.com/kerimdzhanov/dotenv-flow/issues"
2020
},
2121
"main": "lib/dotenv-flow.js",
22+
"types": "lib/dotenv-flow.d.ts",
2223
"exports": {
2324
".": "./lib/dotenv-flow.js",
2425
"./config": "./config.js",
@@ -33,18 +34,23 @@
3334
"dotenv": "^8.6.0"
3435
},
3536
"devDependencies": {
37+
"@types/node": "^20.6.2",
3638
"chai": "^4.3.7",
3739
"conventional-changelog-cli": "^2.0.35",
3840
"mocha": "^10.2.0",
3941
"sinon": "^15.2.0",
4042
"sinon-chai": "^3.7.0",
41-
"tmp": "^0.2.1"
43+
"tmp": "^0.2.1",
44+
"typescript": "^5.2.2"
4245
},
4346
"engines": {
4447
"node": ">= 8.0.0"
4548
},
4649
"scripts": {
47-
"test": "mocha -r mocha.conf.js test/{unit,integration}/*.spec.{m,}js",
50+
"test": "yarn run test:unit && yarn run test:integration && yarn run test:types",
51+
"test:unit": "mocha -r mocha.conf.js test/unit/*.spec.js",
52+
"test:integration": "mocha -r mocha.conf.js test/integration/*.spec.{m,}js",
53+
"test:types": "tsc",
4854
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s"
4955
},
5056
"author": "Dan Kerimdzhanov",

test/types/dotenv-flow.ts

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
import dotenvFlow from '../../lib/dotenv-flow';
2+
3+
const filenames: string[] = dotenvFlow.listFiles();
4+
dotenvFlow.listFiles({});
5+
dotenvFlow.listFiles({ node_env: 'development' });
6+
dotenvFlow.listFiles({ path: '/path/to/project' });
7+
dotenvFlow.listFiles({ pattern: '.env[.node_env][.local]' });
8+
dotenvFlow.listFiles({ debug: true });
9+
dotenvFlow.listFiles({
10+
node_env: 'development',
11+
path: '/path/to/project',
12+
pattern: '.env[.node_env][.local]',
13+
debug: true
14+
});
15+
16+
dotenvFlow.parse('/path/to/project/.env');
17+
dotenvFlow.parse('/path/to/project/.env', {});
18+
dotenvFlow.parse('/path/to/project/.env', { encoding: 'utf8' });
19+
dotenvFlow.parse('/path/to/project/.env', { debug: true });
20+
dotenvFlow.parse('/path/to/project/.env', {
21+
encoding: 'utf8',
22+
debug: true
23+
});
24+
25+
dotenvFlow.parse(['/path/to/project/.env']);
26+
dotenvFlow.parse(['/path/to/project/.env'], {});
27+
dotenvFlow.parse(['/path/to/project/.env'], { encoding: 'utf8' });
28+
dotenvFlow.parse(['/path/to/project/.env'], { debug: true });
29+
dotenvFlow.parse(['/path/to/project/.env'], {
30+
encoding: 'utf8',
31+
debug: true
32+
});
33+
34+
const parsed: { [name: string]: string } = dotenvFlow.parse('/path/to/project/.env');
35+
const typed: { VARNAME: string } = dotenvFlow.parse('/path/to/project/.env');
36+
37+
// --
38+
39+
dotenvFlow.load('/path/to/project/.env');
40+
dotenvFlow.load('/path/to/project/.env', {});
41+
dotenvFlow.load('/path/to/project/.env', { encoding: 'utf8' });
42+
dotenvFlow.load('/path/to/project/.env', { debug: true });
43+
dotenvFlow.load('/path/to/project/.env', { silent: true });
44+
dotenvFlow.load('/path/to/project/.env', {
45+
encoding: 'utf8',
46+
debug: true,
47+
silent: false
48+
});
49+
50+
dotenvFlow.load(['/path/to/project/.env']);
51+
dotenvFlow.load(['/path/to/project/.env'], {});
52+
dotenvFlow.load(['/path/to/project/.env'], { encoding: 'utf8' });
53+
dotenvFlow.load(['/path/to/project/.env'], { debug: true });
54+
dotenvFlow.load(['/path/to/project/.env'], { silent: true });
55+
dotenvFlow.load(['/path/to/project/.env'], {
56+
encoding: 'utf8',
57+
debug: true,
58+
silent: false
59+
});
60+
61+
const defaultLoadResult = dotenvFlow.load('/path/to/project/.env');
62+
const value1: string | undefined = defaultLoadResult.parsed?.['VARNAME'];
63+
const error1: Error | undefined = defaultLoadResult.error;
64+
65+
const typedLoadResult = dotenvFlow.load<{ VARNAME: string }>('/path/to/project/.env');
66+
const value2: string | undefined = typedLoadResult.parsed?.VARNAME;
67+
const error2: Error | undefined = typedLoadResult.error;
68+
69+
// --
70+
71+
dotenvFlow.unload('/path/to/project/.env');
72+
dotenvFlow.unload('/path/to/project/.env', {});
73+
dotenvFlow.unload('/path/to/project/.env', { encoding: 'utf8' });
74+
75+
dotenvFlow.unload(['/path/to/project/.env']);
76+
dotenvFlow.unload(['/path/to/project/.env'], {});
77+
dotenvFlow.unload(['/path/to/project/.env'], { encoding: 'utf8' });
78+
79+
// --
80+
81+
dotenvFlow.config();
82+
dotenvFlow.config({});
83+
dotenvFlow.config({ node_env: 'production' });
84+
dotenvFlow.config({ default_node_env: 'development' });
85+
dotenvFlow.config({ path: '/path/to/project' });
86+
dotenvFlow.config({ pattern: '.env[.node_env][.local]' });
87+
dotenvFlow.config({ encoding: 'utf8' });
88+
dotenvFlow.config({ purge_dotenv: true });
89+
dotenvFlow.config({ debug: true });
90+
dotenvFlow.config({ silent: true });
91+
dotenvFlow.config({
92+
node_env: 'production',
93+
default_node_env: 'development',
94+
path: '/path/to/project',
95+
pattern: '.env[.node_env][.local]',
96+
encoding: 'utf8',
97+
purge_dotenv: true,
98+
debug: true,
99+
silent: false
100+
});
101+
102+
const defaultConfigResult = dotenvFlow.config();
103+
const value3: string | undefined = defaultConfigResult.parsed?.['VARNAME'];
104+
const error3: Error | undefined = defaultConfigResult.error;
105+
106+
const typedConfigResult = dotenvFlow.config<{ VARNAME: string }>();
107+
const value4: string | undefined = typedConfigResult.parsed?.VARNAME;
108+
const error4: Error | undefined = typedConfigResult.error;

tsconfig.json

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"compilerOptions": {
3+
"target": "ES2019",
4+
"module": "CommonJS",
5+
"noEmit": true,
6+
"noImplicitAny": true,
7+
"noImplicitThis": true,
8+
"strict": true,
9+
"strictFunctionTypes": true,
10+
"strictNullChecks": true,
11+
"types": ["node"]
12+
},
13+
"files": [
14+
"lib/dotenv-flow.d.ts",
15+
"test/types/dotenv-flow.ts"
16+
]
17+
}

yarn.lock

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,11 @@
6969
resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.2.tgz#ee771e2ba4b3dc5b372935d549fd9617bf345b8c"
7070
integrity sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==
7171

72+
"@types/node@^20.6.2":
73+
version "20.6.2"
74+
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.6.2.tgz#a065925409f59657022e9063275cd0b9bd7e1b12"
75+
integrity sha512-Y+/1vGBHV/cYk6OI1Na/LHzwnlNCAfU3ZNGrc1LdRe/LAIbdDPTTv/HU3M7yXN448aTVDq3eKRm2cg7iKLb8gw==
76+
7277
"@types/normalize-package-data@^2.4.0":
7378
version "2.4.1"
7479
resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301"
@@ -1591,6 +1596,11 @@ type-fest@^0.8.1:
15911596
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d"
15921597
integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==
15931598

1599+
typescript@^5.2.2:
1600+
version "5.2.2"
1601+
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.2.2.tgz#5ebb5e5a5b75f085f22bc3f8460fba308310fa78"
1602+
integrity sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==
1603+
15941604
uglify-js@^3.1.4:
15951605
version "3.17.4"
15961606
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.17.4.tgz#61678cf5fa3f5b7eb789bb345df29afb8257c22c"

0 commit comments

Comments
 (0)