@karmaniverous/get-dotenv 3.0.6 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,450 @@
1
+ // npm imports
2
+ import { Command, Option } from 'commander';
3
+ import { execaCommand } from 'execa';
4
+
5
+ // lib imports
6
+ import { dotenvExpand } from './dotenvExpand.js';
7
+ import { getDotenv } from './getDotenv.js';
8
+ import { cli2getdotenvOptions, cliDefaultOptions } from './options.js';
9
+
10
+ /**
11
+ * GetDotenv CLI Pre-hook Callback type. Transforms inbound options & executes side effects.
12
+ *
13
+ * @async
14
+ * @callback GetDotenvPreHookCallback
15
+ * @param {GetDotenvCliOptions} options - inbound GetDotenv CLI Options object
16
+ * @returns {GetDotenvCliOptions} transformed GetDotenv CLI Options object (undefined return value is ignored)
17
+ */
18
+
19
+ /**
20
+ * GetDotenv CLI Post-hook Callback type. Executes side effects within getdotenv context.
21
+ *
22
+ * @async
23
+ * @callback GetDotenvPostHookCallback
24
+ * @param {object} dotenv - dotenv object
25
+ */
26
+
27
+ /**
28
+ * Generate a CLI for get-dotenv.
29
+ *
30
+ * @function getDotenvCli
31
+ * @param {object} [options] - options object
32
+ * @param {string} [options.alias] - cli alias (used for cli help)
33
+ * @param {string} [options.command] - {@link https://github.com/motdotla/dotenv-expand/blob/master/tests/.env dotenv-expanded} shell command string
34
+ * @param {bool} [options.debug] - debug mode
35
+ * @param {string} [options.defaultEnv] - default target environment
36
+ * @param {string} [options.description] - cli description (used for cli help)
37
+ * @param {string} [options.dotenvToken] - {@link https://github.com/motdotla/dotenv-expand/blob/master/tests/.env dotenv-expanded} token indicating a dotenv file
38
+ * @param {string} [options.dynamicPath] - path to file exporting an object keyed to dynamic variable functions
39
+ * @param {bool} [options.excludeDynamic] - exclude dynamic dotenv variables
40
+ * @param {bool} [options.excludeEnv] - exclude environment-specific dotenv variables
41
+ * @param {bool} [options.excludeGlobal] - exclude global dotenv variables
42
+ * @param {bool} [options.excludePrivate] - exclude private dotenv variables
43
+ * @param {bool} [options.excludePublic] - exclude public dotenv variables
44
+ * @param {bool} [options.loadProcess] - load variables to process.env
45
+ * @param {bool} [options.log] - log result to console
46
+ * @param {function} [options.logger] - logger function
47
+ * @param {string} [options.outputPath] - consolidated output file, {@link https://github.com/motdotla/dotenv-expand/blob/master/tests/.env dotenv-expanded} using loaded env vars
48
+ * @param {string} [options.paths] - {@link https://github.com/motdotla/dotenv-expand/blob/master/tests/.env dotenv-expanded} delimited list of input directory paths
49
+ * @param {string} [options.pathsDelimiter] - paths delimiter string
50
+ * @param {string} [options.pathsDelimiterPattern] - paths delimiter regex pattern
51
+ * @param {GetDotenvPreHookCallback} [config.preHook] - transforms cli options & executes side effects
52
+ * @param {string} [options.privateToken] - {@link https://github.com/motdotla/dotenv-expand/blob/master/tests/.env dotenv-expanded} token indicating private variables
53
+ * @param {GetDotenvPostHookCallback} [config.postHook] - executes side effects within getdotenv context
54
+ * @param {string} [options.vars] - {@link https://github.com/motdotla/dotenv-expand/blob/master/tests/.env dotenv-expanded} delimited list of explicit environment variable key-value pairs
55
+ * @param {string} [options.varsAssignor] - variable key-value assignor string
56
+ * @param {string} [options.varsAssignorPattern] - variable key-value assignor regex pattern
57
+ * @param {string} [options.varsDelimiter] - variable key-value pair delimiter string
58
+ * @param {string} [options.varsDelimiterPattern] - variable key-value pair delimiter regex pattern
59
+ * @returns {object} The CLI command.
60
+ */
61
+ export const getDotenvCli = ({
62
+ logger = console.log,
63
+ preHook,
64
+ postHook,
65
+ ...cliOptions
66
+ } = {}) => {
67
+ let {
68
+ alias,
69
+ command,
70
+ debug,
71
+ defaultEnv,
72
+ description,
73
+ dotenvToken,
74
+ dynamicPath,
75
+ env,
76
+ excludeDynamic,
77
+ excludeEnv,
78
+ excludeGlobal,
79
+ excludePrivate,
80
+ excludePublic,
81
+ loadProcess,
82
+ log,
83
+ outputPath,
84
+ paths,
85
+ pathsDelimiter,
86
+ pathsDelimiterPattern,
87
+ privateToken,
88
+ vars,
89
+ varsAssignor,
90
+ varsAssignorPattern,
91
+ varsDelimiter,
92
+ varsDelimiterPattern,
93
+ } = {
94
+ ...cliDefaultOptions,
95
+ ...cliOptions,
96
+ };
97
+
98
+ const excludeAll =
99
+ excludeDynamic &&
100
+ ((excludeEnv && excludeGlobal) || (excludePrivate && excludePublic));
101
+
102
+ return new Command()
103
+ .name(alias)
104
+ .description(description)
105
+ .enablePositionalOptions()
106
+ .passThroughOptions()
107
+ .option('-e, --env <string>', 'target environment', dotenvExpand, env)
108
+ .option(
109
+ '-v, --vars <string>',
110
+ `dotenv-expanded delimited key-value pairs: ${[
111
+ ['KEY1', 'VAL1'],
112
+ ['KEY2', 'VAL2'],
113
+ ]
114
+ .map((v) => v.join(varsAssignor))
115
+ .join(varsDelimiter)}`,
116
+ dotenvExpand,
117
+ vars
118
+ )
119
+ .option(
120
+ '-c, --command <string>',
121
+ 'dotenv-expanded shell command string',
122
+ dotenvExpand,
123
+ command
124
+ )
125
+ .option(
126
+ '-o, --output-path <string>',
127
+ 'consolidated output file, follows dotenv-expand rules using loaded env vars',
128
+ dotenvExpand,
129
+ outputPath
130
+ )
131
+ .addOption(
132
+ new Option(
133
+ '-p, --load-process',
134
+ `load variables to process.env ON${loadProcess ? ' (default)' : ''}`
135
+ ).conflicts('loadProcessOff')
136
+ )
137
+ .addOption(
138
+ new Option(
139
+ '-P, --load-process-off',
140
+ `load variables to process.env OFF${!loadProcess ? ' (default)' : ''}`
141
+ ).conflicts('loadProcess')
142
+ )
143
+ .addOption(
144
+ new Option(
145
+ '-a, --exclude-all',
146
+ `exclude all dotenv variables from loading ON${
147
+ excludeAll ? ' (default)' : ''
148
+ }`
149
+ ).conflicts('excludeAllOff')
150
+ )
151
+ .addOption(
152
+ new Option(
153
+ '-A, --exclude-all-off',
154
+ `exclude all dotenv variables from loading OFF${
155
+ !excludeAll ? ' (default)' : ''
156
+ }`
157
+ ).conflicts('excludeAll')
158
+ )
159
+ .addOption(
160
+ new Option(
161
+ '-z, --exclude-dynamic',
162
+ `exclude dynamic dotenv variables from loading ON${
163
+ excludeDynamic ? ' (default)' : ''
164
+ }`
165
+ ).conflicts('excludeDynamicOff')
166
+ )
167
+ .addOption(
168
+ new Option(
169
+ '-Z, --exclude-dynamic-off',
170
+ `exclude dynamic dotenv variables from loading OFF${
171
+ !excludeDynamic ? ' (default)' : ''
172
+ }`
173
+ ).conflicts('excludeDynamic')
174
+ )
175
+ .addOption(
176
+ new Option(
177
+ '-n, --exclude-env',
178
+ `exclude environment-specific dotenv variables from loading${
179
+ excludeEnv ? ' (default)' : ''
180
+ }`
181
+ ).conflicts('excludeEnvOff')
182
+ )
183
+ .addOption(
184
+ new Option(
185
+ '-N, --exclude-env-off',
186
+ `exclude environment-specific dotenv variables from loading OFF${
187
+ !excludeEnv ? ' (default)' : ''
188
+ }`
189
+ ).conflicts('excludeEnv')
190
+ )
191
+ .addOption(
192
+ new Option(
193
+ '-g, --exclude-global',
194
+ `exclude global dotenv variables from loading ON${
195
+ excludeGlobal ? ' (default)' : ''
196
+ }`
197
+ ).conflicts('excludeGlobalOff')
198
+ )
199
+ .addOption(
200
+ new Option(
201
+ '-G, --exclude-global-off',
202
+ `exclude global dotenv variables from loading OFF${
203
+ !excludeGlobal ? ' (default)' : ''
204
+ }`
205
+ ).conflicts('excludeGlobal')
206
+ )
207
+ .addOption(
208
+ new Option(
209
+ '-r, --exclude-private',
210
+ `exclude private dotenv variables from loading ON${
211
+ excludePrivate ? ' (default)' : ''
212
+ }`
213
+ ).conflicts('excludePrivateOff')
214
+ )
215
+ .addOption(
216
+ new Option(
217
+ '-R, --exclude-private-off',
218
+ `exclude private dotenv variables from loading OFF${
219
+ !excludePrivate ? ' (default)' : ''
220
+ }`
221
+ ).conflicts('excludePrivate')
222
+ )
223
+ .addOption(
224
+ new Option(
225
+ '-u, --exclude-public',
226
+ `exclude public dotenv variables from loading ON${
227
+ excludePublic ? ' (default)' : ''
228
+ }`
229
+ ).conflicts('excludePublicOff')
230
+ )
231
+ .addOption(
232
+ new Option(
233
+ '-U, --exclude-public-off',
234
+ `exclude public dotenv variables from loading OFF${
235
+ !excludePublic ? ' (default)' : ''
236
+ }`
237
+ ).conflicts('excludePublic')
238
+ )
239
+ .addOption(
240
+ new Option(
241
+ '-l, --log',
242
+ `console log loaded variables ON${log ? ' (default)' : ''}`
243
+ ).conflicts('logOff')
244
+ )
245
+ .addOption(
246
+ new Option(
247
+ '-L, --log-off',
248
+ `console log loaded variables OFF${!log ? ' (default)' : ''}`
249
+ ).conflicts('log')
250
+ )
251
+ .addOption(
252
+ new Option(
253
+ '-d, --debug',
254
+ `debug mode ON${debug ? ' (default)' : ''}`
255
+ ).conflicts('debugOff')
256
+ )
257
+ .addOption(
258
+ new Option(
259
+ '-D, --debug-off',
260
+ `debug mode OFF${!debug ? ' (default)' : ''}`
261
+ ).conflicts('debug')
262
+ )
263
+ .option(
264
+ '--default-env <string>',
265
+ 'default target environment',
266
+ dotenvExpand,
267
+ defaultEnv
268
+ )
269
+ .option(
270
+ '--dotenv-token <string>',
271
+ 'dotenv-expanded token indicating a dotenv file',
272
+ dotenvExpand,
273
+ dotenvToken
274
+ )
275
+ .option(
276
+ '--dynamic-path <string>',
277
+ 'dynamic variables path',
278
+ dotenvExpand,
279
+ dynamicPath
280
+ )
281
+ .option(
282
+ '--paths <string>',
283
+ 'dotenv-expanded delimited list of paths to dotenv directory',
284
+ dotenvExpand,
285
+ paths
286
+ )
287
+ .option(
288
+ '--paths-delimiter <string>',
289
+ 'paths delimiter string',
290
+ pathsDelimiter
291
+ )
292
+ .option(
293
+ '--paths-delimiter-pattern <string>',
294
+ 'paths delimiter regex pattern',
295
+ pathsDelimiterPattern
296
+ )
297
+ .option(
298
+ '--private-token <string>',
299
+ 'dotenv-expanded token indicating private variables',
300
+ dotenvExpand,
301
+ privateToken
302
+ )
303
+ .option('--vars-delimiter <string>', 'vars delimiter string', varsDelimiter)
304
+ .option(
305
+ '--vars-delimiter-pattern <string>',
306
+ 'vars delimiter regex pattern',
307
+ varsDelimiterPattern
308
+ )
309
+ .option(
310
+ '--vars-assignor <string>',
311
+ 'vars assignment operator string',
312
+ varsAssignor
313
+ )
314
+ .option(
315
+ '--vars-assignor-pattern <string>',
316
+ 'vars assignment operator regex pattern',
317
+ varsAssignorPattern
318
+ )
319
+ .addCommand(
320
+ new Command()
321
+ .name('cmd')
322
+ .description('execute shell command string (default command)')
323
+ .configureHelp({ showGlobalOptions: true })
324
+ .enablePositionalOptions()
325
+ .passThroughOptions()
326
+ .action(async (options, { args }) => {
327
+ if (args.length)
328
+ await execaCommand(args.join('\\ '), {
329
+ stdio: 'inherit',
330
+ shell: true,
331
+ });
332
+ }),
333
+ { isDefault: true }
334
+ )
335
+ .hook('preSubcommand', async (thisCommand) => {
336
+ const rawOptions = thisCommand.opts();
337
+
338
+ if (rawOptions.debug) logger('\n*** raw options ***\n', { rawOptions });
339
+
340
+ // Load options.
341
+ let {
342
+ command,
343
+ debugOff,
344
+ excludeAllOff,
345
+ excludeDynamicOff,
346
+ excludeEnvOff,
347
+ excludeGlobalOff,
348
+ excludePrivateOff,
349
+ excludePublicOff,
350
+ loadProcessOff,
351
+ logOff,
352
+ ...cliOptions
353
+ } = rawOptions;
354
+
355
+ // Resolve flags.
356
+ const resolveExclusion = (exclude, excludeOff, defaultValue) =>
357
+ exclude ? true : excludeOff ? false : defaultValue;
358
+
359
+ const resolveExclusionAll = (exclude, excludeOff, defaultValue) =>
360
+ excludeAll && !excludeOff
361
+ ? true
362
+ : excludeAllOff && !exclude
363
+ ? false
364
+ : defaultValue;
365
+
366
+ cliOptions.debug = resolveExclusion(cliOptions.debug, debugOff, debug);
367
+
368
+ cliOptions.excludeDynamic = resolveExclusionAll(
369
+ cliOptions.excludeDynamic,
370
+ excludeDynamicOff,
371
+ excludeDynamic
372
+ );
373
+
374
+ cliOptions.excludeEnv = resolveExclusionAll(
375
+ cliOptions.excludeEnv,
376
+ excludeEnvOff,
377
+ excludeEnv
378
+ );
379
+
380
+ cliOptions.excludeGlobal = resolveExclusionAll(
381
+ cliOptions.excludeGlobal,
382
+ excludeGlobalOff,
383
+ excludeGlobal
384
+ );
385
+
386
+ cliOptions.excludePrivate = resolveExclusionAll(
387
+ cliOptions.excludePrivate,
388
+ excludePrivateOff,
389
+ excludePrivate
390
+ );
391
+
392
+ cliOptions.excludePublic = resolveExclusionAll(
393
+ cliOptions.excludePublic,
394
+ excludePublicOff,
395
+ excludePublic
396
+ );
397
+
398
+ cliOptions.log = resolveExclusion(cliOptions.log, logOff, log);
399
+
400
+ cliOptions.loadProcess = resolveExclusion(
401
+ cliOptions.loadProcess,
402
+ loadProcessOff,
403
+ loadProcess
404
+ );
405
+
406
+ if (cliOptions.debug)
407
+ logger('\n*** after default resolution ***\n', { cliOptions });
408
+
409
+ // Execute pre-hook.
410
+ if (preHook) {
411
+ cliOptions = (await preHook(cliOptions)) ?? cliOptions;
412
+ if (cliOptions.debug) logger('\n*** after pre-hook ***\n', cliOptions);
413
+ }
414
+
415
+ // Get getdotenv options from parent command.
416
+ const parentGetdotenvOptions = process.env['getdotenvOptions']
417
+ ? JSON.parse(process.env['getdotenvOptions'])
418
+ : {};
419
+
420
+ const cliGetdotenvOptions = cli2getdotenvOptions(cliOptions);
421
+
422
+ const getdotenvOptions = {
423
+ ...parentGetdotenvOptions,
424
+ ...cliGetdotenvOptions,
425
+ };
426
+
427
+ if (cliOptions.debug)
428
+ logger('\n*** after getdotenv option resolution ***\n', {
429
+ parentGetdotenvOptions,
430
+ cliGetdotenvOptions,
431
+ getdotenvOptions,
432
+ });
433
+
434
+ // Execute getdotenv.
435
+ const dotenv = await getDotenv({ ...getdotenvOptions, logger });
436
+
437
+ if (cliOptions.debug)
438
+ console.log('\n*** after getdotenv execution ***\n', { dotenv });
439
+
440
+ // Execute post-hook.
441
+ if (postHook) await postHook(dotenv);
442
+
443
+ // Execute shell command.
444
+ if (command)
445
+ await execaCommand(command.replace(/ /g, '\\ '), {
446
+ stdio: 'inherit',
447
+ shell: true,
448
+ });
449
+ });
450
+ };
package/lib/index.js CHANGED
@@ -1,3 +1,3 @@
1
1
  export { dotenvExpand } from './dotenvExpand.js';
2
2
  export { getDotenv, getDotenvSync } from './getDotenv.js';
3
- export { getCli } from './getCli.js';
3
+ export { getDotenvCli } from './getDotenvCli.js';
package/lib/options.js ADDED
@@ -0,0 +1,79 @@
1
+ // npm imports
2
+ import fs from 'fs-extra';
3
+ import fromPairs from 'lodash.frompairs';
4
+ import pick from 'lodash.pick';
5
+ import { packageDirectory } from 'pkg-dir';
6
+ import { dirname, resolve } from 'path';
7
+ import { fileURLToPath } from 'url';
8
+
9
+ const __dirname = dirname(fileURLToPath(import.meta.url));
10
+
11
+ // Load default options.
12
+ const cliDefaultOptionsGlobalPath = resolve(
13
+ __dirname,
14
+ '../getdotenv.config.json'
15
+ );
16
+
17
+ const cliDefaultOptionsGlobal = (await fs.exists(cliDefaultOptionsGlobalPath))
18
+ ? JSON.parse(await fs.readFile(cliDefaultOptionsGlobalPath))
19
+ : {};
20
+
21
+ const cliDefaultOptionsLocalPath = resolve(
22
+ await packageDirectory(),
23
+ 'getdotenv.config.json'
24
+ );
25
+
26
+ const cliDefaultOptionsLocal = (await fs.exists(cliDefaultOptionsLocalPath))
27
+ ? JSON.parse(await fs.readFile(cliDefaultOptionsLocalPath))
28
+ : {};
29
+
30
+ export const cliDefaultOptions = {
31
+ ...cliDefaultOptionsGlobal,
32
+ ...cliDefaultOptionsLocal,
33
+ };
34
+
35
+ export const cli2getdotenvOptions = ({
36
+ env,
37
+ defaultEnv,
38
+ paths,
39
+ pathsDelimiter,
40
+ pathsDelimiterPattern,
41
+ vars,
42
+ varsAssignor,
43
+ varsAssignorPattern,
44
+ varsDelimiter,
45
+ varsDelimiterPattern,
46
+ ...rest
47
+ } = {}) => ({
48
+ ...pick(rest, [
49
+ 'dotenvToken',
50
+ 'dynamicPath',
51
+ 'excludeDynamic',
52
+ 'excludeEnv',
53
+ 'excludeGlobal',
54
+ 'excludePrivate',
55
+ 'excludePublic',
56
+ 'loadProcess',
57
+ 'log',
58
+ 'outputPath',
59
+ 'privateToken',
60
+ ]),
61
+ env: env ?? defaultEnv,
62
+ paths:
63
+ paths?.split(
64
+ pathsDelimiterPattern ? RegExp(pathsDelimiterPattern) : pathsDelimiter
65
+ ) ?? [],
66
+ vars: fromPairs(
67
+ vars
68
+ ?.split(
69
+ varsDelimiterPattern ? RegExp(varsDelimiterPattern) : varsDelimiter
70
+ )
71
+ .map((v) =>
72
+ v.split(
73
+ varsAssignorPattern ? RegExp(varsAssignorPattern) : varsAssignor
74
+ )
75
+ )
76
+ ),
77
+ });
78
+
79
+ export const getdotenvDefaultOptions = cli2getdotenvOptions(cliDefaultOptions);
package/lib/readDotenv.js CHANGED
@@ -14,7 +14,7 @@ import fs from 'fs-extra';
14
14
  */
15
15
  export const readDotenv = async (path) => {
16
16
  try {
17
- return parse(await fs.readFile(path));
17
+ return (await fs.exists(path)) ? parse(await fs.readFile(path)) : {};
18
18
  } catch {
19
19
  return {};
20
20
  }
@@ -32,7 +32,7 @@ export const readDotenv = async (path) => {
32
32
  */
33
33
  export const readDotenvSync = (path) => {
34
34
  try {
35
- return parse(fs.readFileSync(path));
35
+ return fs.existsSync(path) ? parse(fs.readFileSync(path)) : {};
36
36
  } catch {
37
37
  return {};
38
38
  }
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "bin": {
4
4
  "getdotenv": "bin/getdotenv/index.js"
5
5
  },
6
- "version": "3.0.6",
6
+ "version": "3.1.0",
7
7
  "publishConfig": {
8
8
  "access": "public"
9
9
  },
@@ -31,15 +31,16 @@
31
31
  ],
32
32
  "license": "BSD-3-Clause",
33
33
  "dependencies": {
34
- "boolean": "^3.2.0",
35
34
  "commander": "^11.0.0",
36
35
  "dotenv": "^16.3.1",
37
36
  "dotenv-expand": "^10.0.0",
38
37
  "execa": "^7.1.1",
39
38
  "fs-extra": "^11.1.1",
40
39
  "lodash.frompairs": "^4.0.1",
41
- "lodash.isstring": "^4.0.1",
42
- "nanoid": "^4.0.2"
40
+ "lodash.pick": "^4.4.0",
41
+ "lodash.pickby": "^4.6.0",
42
+ "nanoid": "^4.0.2",
43
+ "pkg-dir": "^7.0.0"
43
44
  },
44
45
  "devDependencies": {
45
46
  "@babel/cli": "^7.22.5",
@@ -1,20 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.dotenvDefaults = void 0;
7
- const dotenvDefaults = {
8
- dotenvToken: '.env',
9
- excludeDynamic: false,
10
- excludeEnv: false,
11
- excludeGlobal: false,
12
- excludePrivate: false,
13
- excludePublic: false,
14
- loadProcess: false,
15
- log: false,
16
- logger: console.log,
17
- paths: ['./'],
18
- privateToken: 'local'
19
- };
20
- exports.dotenvDefaults = dotenvDefaults;