-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathindex.js
234 lines (202 loc) · 5.88 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
'use strict';
const dotProp = require('dot-prop');
/**
* Replace a character in the string provided taking care of the escaped chars.
* @private
* @param {string} str A string.
* @param {from} from A character.
* @param {to} to A character.
* @return {string} A new string with the character replaced.
*/
function transform(str, from, to) {
let out = '';
const escaped = '\\' + to;
for (let i = 0; i < str.length; i++) {
if (str[i] === to) {
out += escaped;
} else if (str[i] === from) {
out += to;
} else if (str[i] === '\\' && i + 1 < str.length && str[i + 1] === from) {
out += from;
i++;
} else {
out += str[i];
}
}
return out;
}
/**
* Converts a dot-path to an underscore-path.
* @private
* @param {string} path A string separated by dots.
* @return {string} A new string separated by underscores.
*/
function toUnderscore(path) {
return transform(path, '.', '_');
}
/**
* Converts an underscore-path to a dot-path.
* @private
* @param {string} env A string separated by underscores.
* @return {string} A new string separated by dots.
*/
function toDot(env) {
return transform(env, '_', '.');
}
/**
* Return a list of environment variables that matches the path provided.
* @private
* @param {string} path A string separated by dots.
* @param {Object} [opts] Additional options.
* @return {string[]} An array of environment variables.
*/
function keys(path, opts) {
let env = toUnderscore(path);
if (!opts.caseSensitive) {
env = env.toUpperCase();
return Object.keys(process.env).filter(key =>
key.toUpperCase().startsWith(env)
);
}
return Object.keys(process.env).filter(key => key.startsWith(env));
}
function parse(str, opts) {
if (typeof str !== 'string' || !opts.parse) {
return str;
}
if (/^\s*undefined\s*$/.test(str)) {
return undefined;
}
const val = Number(str);
if (!isNaN(val) || /^\s*NaN\s*$/.test(str)) {
return val;
}
try {
return JSON.parse(str);
// eslint-disable-next-line no-unused-vars
} catch (error) {
return str;
}
}
function stringify(val, opts) {
if (typeof val === 'string' || !opts.stringify) {
return val;
}
if (typeof val === 'object') {
return JSON.stringify(val);
}
return String(val);
}
/**
* Gets the values of environment variables at the path specified.
* @public
* @param {string} path Dot separated path.
* @param {any} [defaultValue=undefined] Default value to return if there is
* not any environment variable that matches the path provided.
* @param {Object} [opts] Additional options.
* @param {boolean} [opts.parse=false] If true the value retrieved is converted
* to the proper type.
* @param {boolean} [opts.caseSensitive=false] If true no case conversion will
* be performed from the dot path provided to the env key search.
* Eg: 'tesT.kEy' will look for tesT_kEy environment variable instead of TEST_KEY.
* @return {any} The values of environment variables associated with the path specified.
*/
function get(path, defaultValue, opts) {
let obj;
const args = [].slice.call(arguments);
path = args.shift();
if (typeof args[args.length - 1] === 'object') {
opts = args.pop();
} else {
opts = {};
}
defaultValue = args.pop();
keys(path, opts)
.sort((a, b) => a.length - b.length)
.forEach(key => {
let dotp = toDot(key, opts);
if (!opts.caseSensitive) {
dotp = dotp.toLowerCase();
}
const val = parse(process.env[key], opts);
if (dotp === '') {
obj = val;
} else {
if (typeof obj !== 'object') {
obj = {};
}
dotProp.set(obj, dotp, val);
}
});
let prefix = path;
if (!opts.caseSensitive) {
prefix = prefix.toLowerCase();
}
if (path === '') {
return obj;
}
return dotProp.get(obj, prefix, defaultValue);
}
/**
* Sets an env key at the path specified. If nested keys are present they will
* be deleted.
* @public
* @param {string} path Dot separated path.
* @param {string} value Value to set.
* @param {object} [opts] Additional options.
* @param {boolean} [opts.stringify=false] If true the value provided is
* converted to string.
* @param {boolean} [opts.caseSensitive=false] If true no case conversion is
* performed from the dot path provided to the env key search.
* Eg: 'tesT.kEy' will look for tesT_kEy environment variable instead of TEST_KEY.
*/
function set(path, value, opts) {
if (typeof opts === 'undefined') {
opts = {};
}
let env = toUnderscore(path);
if (!opts.caseSensitive) {
env = env.toUpperCase();
}
del(path, opts);
process.env[env] = stringify(value, opts);
}
/**
* Deletes an env key at the path specified.
* If nested keys are present they will be deleted too.
* @public
* @param {string} path A dot separated path.
* @param {object} [opts] Additional options.
* @param {boolean} [opts.caseSensitive=false] If true no case conversion is
* performed from the dot path provided to the env key search.
* Eg: 'tesT.kEy' will look for tesT_kEy environment variable instead of TEST_KEY.
*/
function del(path, opts) {
if (typeof opts === 'undefined') {
opts = {};
}
keys(path, opts).forEach(key => delete process.env[key]);
}
/**
* Returns whether an env key exists at the path specified.
* @public
* @param {string} path Dot separated path.
* @param {object} [opts] Additional options.
* @param {boolean} [opts.caseSensitive=false] If true no case conversion is
* performed from the dot path provided to the env key search.
* Eg: 'tesT.kEy' will look for tesT_kEy environment variable instead of TEST_KEY.
* @return {boolean} true if exists at least one environment variables with that
* path prefix.
*/
function has(path, opts) {
if (typeof opts === 'undefined') {
opts = {};
}
return keys(path, opts).length > 0;
}
module.exports = {
get: get,
set: set,
delete: del,
has: has
};