@karmaniverous/get-dotenv 4.3.2 → 4.4.1

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.
package/README.md CHANGED
@@ -10,7 +10,7 @@ Load environment variables with a cascade of environment-aware dotenv files. You
10
10
  - Private files (e.g. `.env.local`, `env.dev.local`, `env.test.local`) are protected by `.gitignore`.
11
11
  - Global files (e.g. `.env`, `env.local`) apply to all environments.
12
12
  - Env files (e.g. `.env.dev`, `.env.dev.local`, `.env.test`, `.env.test.local`) apply to a specific environment.
13
- - Dynamic files (`.env.js`) export logic that dynamically & progressively generates new variables or overrides current ones.
13
+ - [Dynamic files](#dynamic-processing) (`.env.js`) export logic that dynamically & progressively generates new variables or overrides current ones.
14
14
 
15
15
  ✅ Dynamically specify which variables to load by type.
16
16
 
@@ -20,11 +20,13 @@ Load environment variables with a cascade of environment-aware dotenv files. You
20
20
 
21
21
  ✅ Customize your dotenv file directories & naming patterns.
22
22
 
23
- ✅ Perform all of the above either programmatically or from the command line, where you can also execute additional shell commands within the resulting context... including nested `getdotenv` commands that inherit the parent command's settings & context!
23
+ ✅ Perform all of the above either programmatically or [from the command line](#command-line-interface), where you can also execute additional commands within the resulting context... including nested `getdotenv` commands that inherit the parent command's settings & context!
24
+
25
+ ✅ [Execute batched CLI commands](#batch-command) across multiple working directories, with each command inheriting the `getdotenv` context.
24
26
 
25
27
  ✅ Set defaults for all options in a `getdotenv.config.json` file in your project root directory.
26
28
 
27
- ✅ Generate an extensible `getdotenv`-based CLI for use in your own projects.
29
+ [Generate an extensible `getdotenv`-based CLI](https://github.com/karmaniverous/get-dotenv-child) for use in your own projects.
28
30
 
29
31
  `getdotenv` relies on the excellent [`dotenv`](https://www.npmjs.com/package/dotenv) parser and somewhat improves on [`dotenv-expand`](https://www.npmjs.com/package/dotenv-expand) for recursive variable expansion.
30
32
 
@@ -111,8 +113,10 @@ You can also use `getdotenv` from the command line:
111
113
  # Options:
112
114
  # -e, --env <string> target environment (dotenv-expanded)
113
115
  # -v, --vars <string> extra variables expressed as delimited key-value pairs (dotenv-expanded): KEY1=VAL1 KEY2=VAL2
114
- # -c, --command <string> shell command string, conflicts with cmd subcommand (dotenv-expanded)
116
+ # -c, --command <string> command string executed according to the --shell option, conflicts with cmd subcommand (dotenv-expanded)
115
117
  # -o, --output-path <string> consolidated output file (dotenv-expanded)
118
+ # -s, --shell [string] command execution shell, no argument for default OS shell or provide shell string (default OS shell)
119
+ # -S, --shell-off command execution shell OFF
116
120
  # -p, --load-process load variables to process.env ON (default)
117
121
  # -P, --load-process-off load variables to process.env OFF
118
122
  # -a, --exclude-all exclude all dotenv variables from loading ON
@@ -146,10 +150,16 @@ You can also use `getdotenv` from the command line:
146
150
  #
147
151
  # Commands:
148
152
  # batch [options] Batch shell commands across multiple working directories.
149
- # cmd execute shell command, conflicts with --command option (default command)
153
+ # cmd batch execute command string according to the --shell option, conflicts with --command option (default command)
150
154
  # help [command] display help for command
151
155
  ```
152
156
 
157
+ By default, commands (`-c` or `--command` or the `cmd` subcommand either in the base CLI or in the `batch` subcommand) execute in the default OS shell with the `dotenv` context applied. The `-S` or `--shell-off` options will turn this off, and Execa will [execute your command as Javascript](https://github.com/sindresorhus/execa/blob/main/docs/bash.md).
158
+
159
+ Alternatively, you can use the `-s` or `--shell` option to specify a different shell [following the Execa spec](https://github.com/sindresorhus/execa/blob/main/docs/shell.md). This is useful if you're running a command that requires a specific shell, like `bash` or `zsh`.
160
+
161
+ Finally, you can set the [`shell`](https://github.com/karmaniverous/get-dotenv-child?tab=readme-ov-file#options) default globally in your `getdotenv.config.json` file.
162
+
153
163
  See [this example repo](https://github.com/karmaniverous/get-dotenv-child) for a deep dive on using the `getDotenv` CLI and how to extend it for your own projects.
154
164
 
155
165
  ### Batch Command
@@ -5055,11 +5055,11 @@ function resolveCommandAttempt(parsed, withoutPathExt) {
5055
5055
  return resolved;
5056
5056
  }
5057
5057
 
5058
- function resolveCommand$1(parsed) {
5058
+ function resolveCommand$2(parsed) {
5059
5059
  return resolveCommandAttempt(parsed) || resolveCommandAttempt(parsed, true);
5060
5060
  }
5061
5061
 
5062
- var resolveCommand_1 = resolveCommand$1;
5062
+ var resolveCommand_1 = resolveCommand$2;
5063
5063
 
5064
5064
  var _escape = {};
5065
5065
 
@@ -5151,7 +5151,7 @@ function readShebang$1(command) {
5151
5151
  var readShebang_1 = readShebang$1;
5152
5152
 
5153
5153
  const path$o = require$$0$2;
5154
- const resolveCommand = resolveCommand_1;
5154
+ const resolveCommand$1 = resolveCommand_1;
5155
5155
  const escape = _escape;
5156
5156
  const readShebang = readShebang_1;
5157
5157
 
@@ -5160,7 +5160,7 @@ const isExecutableRegExp = /\.(?:com|exe)$/i;
5160
5160
  const isCmdShimRegExp = /node_modules[\\/].bin[\\/][^\\/]+\.cmd$/i;
5161
5161
 
5162
5162
  function detectShebang(parsed) {
5163
- parsed.file = resolveCommand(parsed);
5163
+ parsed.file = resolveCommand$1(parsed);
5164
5164
 
5165
5165
  const shebang = parsed.file && readShebang(parsed.file);
5166
5166
 
@@ -5168,7 +5168,7 @@ function detectShebang(parsed) {
5168
5168
  parsed.args.unshift(parsed.file);
5169
5169
  parsed.command = shebang;
5170
5170
 
5171
- return resolveCommand(parsed);
5171
+ return resolveCommand$1(parsed);
5172
5172
  }
5173
5173
 
5174
5174
  return parsed.file;
@@ -31149,9 +31149,13 @@ const baseGetDotenvCliOptions = {
31149
31149
  paths: './',
31150
31150
  pathsDelimiter: ' ',
31151
31151
  privateToken: 'local',
31152
- shellScripts: {
31153
- 'git-status': 'git branch --show-current && git status -s -u',
31152
+ scripts: {
31153
+ 'git-status': {
31154
+ cmd: 'git branch --show-current && git status -s -u',
31155
+ shell: true,
31156
+ },
31154
31157
  },
31158
+ shell: true,
31155
31159
  vars: '',
31156
31160
  varsAssignor: '=',
31157
31161
  varsDelimiter: ' ',
@@ -31166,7 +31170,7 @@ const getDotenvOptionsFilename = 'getdotenv.config.json';
31166
31170
  * @returns `getDotenv` options.
31167
31171
  */
31168
31172
  const getDotenvCliOptions2Options = ({ paths, pathsDelimiter, pathsDelimiterPattern, vars, varsAssignor, varsAssignorPattern, varsDelimiter, varsDelimiterPattern, ...rest }) => ({
31169
- ..._.omit(rest, ['debug', 'shellScripts']),
31173
+ ..._.omit(rest, ['debug', 'scripts']),
31170
31174
  paths: paths?.split(pathsDelimiterPattern
31171
31175
  ? RegExp(pathsDelimiterPattern)
31172
31176
  : pathsDelimiter ?? ' ') ?? [],
@@ -31738,6 +31742,13 @@ const getDotenv = async (options = {}) => {
31738
31742
  return dotenv;
31739
31743
  };
31740
31744
 
31745
+ const resolveCommand = (scripts, command) => (scripts && _.isObject(scripts[command])
31746
+ ? scripts[command].cmd
31747
+ : scripts?.[command] ?? command);
31748
+ const resolveShell = (scripts, command, shell) => scripts && _.isObject(scripts[command])
31749
+ ? scripts[command].shell
31750
+ : shell;
31751
+
31741
31752
  /*
31742
31753
  * merge2
31743
31754
  * https://github.com/teambition/merge2
@@ -39479,7 +39490,7 @@ const globPaths = async ({ globs, logger, pkgCwd, rootPath, }) => {
39479
39490
  }
39480
39491
  return { absRootPath, paths };
39481
39492
  };
39482
- const execShellCommandBatch = async ({ command, globs, ignoreErrors, list, logger, pkgCwd, rootPath, }) => {
39493
+ const execShellCommandBatch = async ({ command, getDotenvCliOptions, globs, ignoreErrors, list, logger, pkgCwd, rootPath, shell, }) => {
39483
39494
  if (!command) {
39484
39495
  logger.error(`No command provided.`);
39485
39496
  process.exit(0);
@@ -39491,8 +39502,8 @@ const execShellCommandBatch = async ({ command, globs, ignoreErrors, list, logge
39491
39502
  rootPath,
39492
39503
  });
39493
39504
  const headerTitle = list
39494
- ? 'Listing shell command working directories...'
39495
- : 'Executing shell command batch...';
39505
+ ? 'Listing working directories...'
39506
+ : 'Executing command batch...';
39496
39507
  logger.info('');
39497
39508
  const headerRootPath = `ROOT: ${absRootPath}`;
39498
39509
  const headerGlobs = `GLOBS: ${globs}`;
@@ -39514,12 +39525,17 @@ const execShellCommandBatch = async ({ command, globs, ignoreErrors, list, logge
39514
39525
  logger.info('*'.repeat(pathLabel.length));
39515
39526
  logger.info(pathLabel);
39516
39527
  logger.info(headerCommand);
39517
- // Execute shell command.
39528
+ // Execute command.
39518
39529
  try {
39519
39530
  await execaCommand(command, {
39520
39531
  cwd: path,
39532
+ env: {
39533
+ getDotenvCliOptions: getDotenvCliOptions
39534
+ ? JSON.stringify(getDotenvCliOptions)
39535
+ : undefined,
39536
+ },
39521
39537
  stdio: 'inherit',
39522
- shell: true,
39538
+ shell,
39523
39539
  });
39524
39540
  }
39525
39541
  catch (error) {
@@ -39533,7 +39549,7 @@ const execShellCommandBatch = async ({ command, globs, ignoreErrors, list, logge
39533
39549
 
39534
39550
  const cmdCommand$1 = new Command()
39535
39551
  .name('cmd')
39536
- .description('execute shell command, conflicts with --command option (default command)')
39552
+ .description('execute command, conflicts with --command option (default subcommand)')
39537
39553
  .enablePositionalOptions()
39538
39554
  .passThroughOptions()
39539
39555
  .action(async (options, thisCommand) => {
@@ -39541,60 +39557,64 @@ const cmdCommand$1 = new Command()
39541
39557
  throw new Error(`unable to resolve parent command`);
39542
39558
  if (!thisCommand.parent.parent)
39543
39559
  throw new Error(`unable to resolve root command`);
39544
- const { getDotenvCliOptions: { logger = console, shellScripts }, } = thisCommand.parent.parent;
39560
+ const { getDotenvCliOptions: { logger = console, ...getDotenvCliOptions }, } = thisCommand.parent.parent;
39545
39561
  const { ignoreErrors, globs, list, pkgCwd, rootPath } = thisCommand.parent.opts();
39546
- // Execute shell command.
39562
+ // Execute command.
39547
39563
  {
39548
39564
  const command = thisCommand.args.join(' ');
39549
39565
  await execShellCommandBatch({
39550
- command: shellScripts?.[command] ?? command,
39566
+ command: resolveCommand(getDotenvCliOptions.scripts, command),
39567
+ getDotenvCliOptions,
39551
39568
  globs,
39552
39569
  ignoreErrors,
39553
39570
  list,
39554
39571
  logger,
39555
39572
  pkgCwd,
39556
39573
  rootPath,
39574
+ shell: resolveShell(getDotenvCliOptions.scripts, command, getDotenvCliOptions.shell),
39557
39575
  });
39558
39576
  }
39559
39577
  });
39560
39578
 
39561
39579
  const batchCommand = new Command()
39562
39580
  .name('batch')
39563
- .description('Batch shell commands across multiple working directories.')
39581
+ .description('Batch command execution across multiple working directories.')
39564
39582
  .enablePositionalOptions()
39565
39583
  .passThroughOptions()
39566
39584
  .option('-p, --pkg-cwd', 'use nearest package directory as current working directory')
39567
39585
  .option('-r, --root-path <string>', 'path to batch root directory from current working directory', './')
39568
39586
  .option('-g, --globs <string>', 'space-delimited globs from root path', '*')
39569
- .option('-c, --command <string>', 'shell command string, conflicts with cmd subcommand (dotenv-expanded)', dotenvExpandFromProcessEnv)
39587
+ .option('-c, --command <string>', 'command executed according to the base --shell option, conflicts with cmd subcommand (dotenv-expanded)', dotenvExpandFromProcessEnv)
39570
39588
  .option('-l, --list', 'list working directories without executing command')
39571
39589
  .option('-e, --ignore-errors', 'ignore errors and continue with next path')
39572
39590
  .hook('preSubcommand', async (thisCommand) => {
39573
39591
  if (!thisCommand.parent)
39574
39592
  throw new Error(`unable to resolve root command`);
39575
- const { getDotenvCliOptions: { logger = console, shellScripts }, } = thisCommand.parent;
39593
+ const { getDotenvCliOptions: { logger = console, ...getDotenvCliOptions }, } = thisCommand.parent;
39576
39594
  const { command, ignoreErrors, globs, list, pkgCwd, rootPath } = thisCommand.opts();
39577
39595
  if (command && thisCommand.args.length) {
39578
39596
  logger.error(`--command option conflicts with cmd subcommand.`);
39579
39597
  process.exit(0);
39580
39598
  }
39581
- // Execute shell command.
39599
+ // Execute command.
39582
39600
  if (command)
39583
39601
  await execShellCommandBatch({
39584
- command: shellScripts?.[command] ?? command,
39602
+ command: resolveCommand(getDotenvCliOptions.scripts, command),
39603
+ getDotenvCliOptions,
39585
39604
  globs,
39586
39605
  ignoreErrors,
39587
39606
  list,
39588
39607
  logger,
39589
39608
  pkgCwd,
39590
39609
  rootPath,
39610
+ shell: resolveShell(getDotenvCliOptions.scripts, command, getDotenvCliOptions.shell),
39591
39611
  });
39592
39612
  })
39593
39613
  .addCommand(cmdCommand$1, { isDefault: true });
39594
39614
 
39595
39615
  const cmdCommand = new Command()
39596
39616
  .name('cmd')
39597
- .description('batch execute shell command, conflicts with --command option (default command)')
39617
+ .description('batch execute command string according to the --shell option, conflicts with --command option (default subcommand)')
39598
39618
  .configureHelp({ showGlobalOptions: true })
39599
39619
  .enablePositionalOptions()
39600
39620
  .passThroughOptions()
@@ -39603,13 +39623,14 @@ const cmdCommand = new Command()
39603
39623
  return;
39604
39624
  if (!thisCommand.parent)
39605
39625
  throw new Error('parent command not found');
39606
- const { getDotenvCliOptions: { debug, logger = console, shellScripts }, } = thisCommand.parent;
39626
+ const { getDotenvCliOptions: { logger = console, ...getDotenvCliOptions }, } = thisCommand.parent;
39607
39627
  const command = thisCommand.args.join(' ');
39608
- const shellCommand = shellScripts?.[command] ?? command;
39609
- if (debug)
39610
- logger.log('\n*** shell command ***\n', `'${shellCommand}'`);
39611
- await execaCommand(shellCommand, {
39612
- shell: true,
39628
+ const cmd = resolveCommand(getDotenvCliOptions.scripts, command);
39629
+ if (getDotenvCliOptions.debug)
39630
+ logger.log('\n*** command ***\n', `'${cmd}'`);
39631
+ await execaCommand(cmd, {
39632
+ env: { getDotenvCliOptions: JSON.stringify(getDotenvCliOptions) },
39633
+ shell: resolveShell(getDotenvCliOptions.scripts, command, getDotenvCliOptions.shell),
39613
39634
  stdio: 'inherit',
39614
39635
  });
39615
39636
  });
@@ -39658,7 +39679,7 @@ const resolveExclusionAll = (exclude, excludeOff, defaultValue, excludeAll, excl
39658
39679
  * Generate a Commander CLI Command for get-dotenv.
39659
39680
  */
39660
39681
  const generateGetDotenvCli = async (customOptions) => {
39661
- const { alias, debug, defaultEnv, description, dotenvToken, dynamicPath, env, excludeDynamic, excludeEnv, excludeGlobal, excludePrivate, excludePublic, loadProcess, log, logger, outputPath, paths, pathsDelimiter, pathsDelimiterPattern, postHook, preHook, privateToken, shellScripts, varsAssignor, varsAssignorPattern, varsDelimiter, varsDelimiterPattern, } = await resolveGetDotenvCliGenerateOptions(customOptions);
39682
+ const { alias, debug, defaultEnv, description, dotenvToken, dynamicPath, env, excludeDynamic, excludeEnv, excludeGlobal, excludePrivate, excludePublic, loadProcess, log, logger, outputPath, paths, pathsDelimiter, pathsDelimiterPattern, postHook, preHook, privateToken, scripts, shell, varsAssignor, varsAssignorPattern, varsDelimiter, varsDelimiterPattern, } = await resolveGetDotenvCliGenerateOptions(customOptions);
39662
39683
  const excludeAll = !!excludeDynamic &&
39663
39684
  ((!!excludeEnv && !!excludeGlobal) ||
39664
39685
  (!!excludePrivate && !!excludePublic));
@@ -39674,8 +39695,10 @@ const generateGetDotenvCli = async (customOptions) => {
39674
39695
  ]
39675
39696
  .map((v) => v.join(varsAssignor))
39676
39697
  .join(varsDelimiter)}`, dotenvExpandFromProcessEnv)
39677
- .option('-c, --command <string>', 'shell command string, conflicts with cmd subcommand (dotenv-expanded)', dotenvExpandFromProcessEnv)
39698
+ .option('-c, --command <string>', 'command executed according to the --shell option, conflicts with cmd subcommand (dotenv-expanded)', dotenvExpandFromProcessEnv)
39678
39699
  .option('-o, --output-path <string>', 'consolidated output file (dotenv-expanded)', dotenvExpandFromProcessEnv, outputPath)
39700
+ .addOption(new Option('-s, --shell [string]', `command execution shell, no argument for default OS shell or provide shell string${shell ? ` (default ${_.isBoolean(shell) ? 'OS shell' : shell})` : ''}`).conflicts('shellOff'))
39701
+ .addOption(new Option('-S, --shell-off', `command execution shell OFF${!shell ? ' (default)' : ''}`).conflicts('shell'))
39679
39702
  .addOption(new Option('-p, --load-process', `load variables to process.env ON${loadProcess ? ' (default)' : ''}`).conflicts('loadProcessOff'))
39680
39703
  .addOption(new Option('-P, --load-process-off', `load variables to process.env OFF${!loadProcess ? ' (default)' : ''}`).conflicts('loadProcess'))
39681
39704
  .addOption(new Option('-a, --exclude-all', `exclude all dotenv variables from loading ON${excludeAll ? ' (default)' : ''}`).conflicts('excludeAllOff'))
@@ -39705,8 +39728,8 @@ const generateGetDotenvCli = async (customOptions) => {
39705
39728
  .option('--vars-delimiter-pattern <string>', 'vars delimiter regex pattern', varsDelimiterPattern)
39706
39729
  .option('--vars-assignor <string>', 'vars assignment operator string', varsAssignor)
39707
39730
  .option('--vars-assignor-pattern <string>', 'vars assignment operator regex pattern', varsAssignorPattern)
39708
- .addOption(new Option('--shell-scripts <string>')
39709
- .default(JSON.stringify(shellScripts))
39731
+ .addOption(new Option('--scripts <string>')
39732
+ .default(JSON.stringify(scripts))
39710
39733
  .hideHelp())
39711
39734
  .addCommand(batchCommand)
39712
39735
  .addCommand(cmdCommand, { isDefault: true })
@@ -39718,10 +39741,10 @@ const generateGetDotenvCli = async (customOptions) => {
39718
39741
  // Get raw CLI options from commander.
39719
39742
  const rawCliOptions = thisCommand.opts();
39720
39743
  // Extract current GetDotenvCliOptions from raw CLI options.
39721
- const { command, debugOff, excludeAll, excludeAllOff, excludeDynamicOff, excludeEnvOff, excludeGlobalOff, excludePrivateOff, excludePublicOff, loadProcessOff, logOff, shellScripts, ...rawCliOptionsRest } = rawCliOptions;
39744
+ const { command, debugOff, excludeAll, excludeAllOff, excludeDynamicOff, excludeEnvOff, excludeGlobalOff, excludePrivateOff, excludePublicOff, loadProcessOff, logOff, scripts, shellOff, ...rawCliOptionsRest } = rawCliOptions;
39722
39745
  const currentGetDotenvCliOptions = rawCliOptionsRest;
39723
- if (shellScripts)
39724
- currentGetDotenvCliOptions.shellScripts = JSON.parse(shellScripts);
39746
+ if (scripts)
39747
+ currentGetDotenvCliOptions.scripts = JSON.parse(scripts);
39725
39748
  // Merge current & parent GetDotenvCliOptions.
39726
39749
  const mergedGetDotenvCliOptions = _.defaultsDeep(currentGetDotenvCliOptions, parentGetDotenvCliOptions ?? {});
39727
39750
  // Resolve flags.
@@ -39733,12 +39756,17 @@ const generateGetDotenvCli = async (customOptions) => {
39733
39756
  mergedGetDotenvCliOptions.excludePublic = resolveExclusionAll(mergedGetDotenvCliOptions.excludePublic, excludePublicOff, excludePublic, excludeAll, excludeAllOff);
39734
39757
  mergedGetDotenvCliOptions.log = resolveExclusion(mergedGetDotenvCliOptions.log, logOff, log);
39735
39758
  mergedGetDotenvCliOptions.loadProcess = resolveExclusion(mergedGetDotenvCliOptions.loadProcess, loadProcessOff, loadProcess);
39759
+ mergedGetDotenvCliOptions.shell = shellOff
39760
+ ? false
39761
+ : !_.isUndefined(mergedGetDotenvCliOptions.shell)
39762
+ ? mergedGetDotenvCliOptions.shell
39763
+ : !_.isUndefined(shell)
39764
+ ? shell
39765
+ : false;
39736
39766
  if (mergedGetDotenvCliOptions.debug && parentGetDotenvCliOptions)
39737
39767
  logger.debug('\n*** parent command GetDotenvCliOptions ***\n', parentGetDotenvCliOptions);
39738
39768
  if (mergedGetDotenvCliOptions.debug)
39739
39769
  logger.debug('\n*** current command raw options ***\n', rawCliOptions);
39740
- if (mergedGetDotenvCliOptions.debug)
39741
- logger.debug('\n*** current command GetDotenvCliOptions ***\n', currentGetDotenvCliOptions);
39742
39770
  if (mergedGetDotenvCliOptions.debug)
39743
39771
  logger.debug('\n*** merged GetDotenvCliOptions ***\n', {
39744
39772
  mergedGetDotenvCliOptions,
@@ -39758,21 +39786,21 @@ const generateGetDotenvCli = async (customOptions) => {
39758
39786
  // Execute post-hook.
39759
39787
  if (postHook)
39760
39788
  await postHook(dotenv);
39761
- // Execute shell command.
39789
+ // Execute command.
39762
39790
  if (command && thisCommand.args.length) {
39763
39791
  logger.error(`--command option conflicts with cmd subcommand.`);
39764
39792
  process.exit(0);
39765
39793
  }
39766
39794
  if (command) {
39767
- const shellCommand = mergedGetDotenvCliOptions.shellScripts?.[command] ?? command;
39795
+ const cmd = resolveCommand(mergedGetDotenvCliOptions.scripts, command);
39768
39796
  if (mergedGetDotenvCliOptions.debug)
39769
- logger.debug('\n*** shell command ***\n', shellCommand);
39770
- await execaCommand(shellCommand, {
39797
+ logger.debug('\n*** command ***\n', cmd);
39798
+ await execaCommand(cmd, {
39771
39799
  env: {
39772
39800
  ...process.env,
39773
39801
  getDotenvCliOptions: JSON.stringify(_.omit(mergedGetDotenvCliOptions, ['logger'])),
39774
39802
  },
39775
- shell: true,
39803
+ shell: resolveShell(mergedGetDotenvCliOptions.scripts, command, mergedGetDotenvCliOptions.shell),
39776
39804
  stdio: 'inherit',
39777
39805
  });
39778
39806
  }
package/dist/index.cjs CHANGED
@@ -5056,11 +5056,11 @@ function resolveCommandAttempt(parsed, withoutPathExt) {
5056
5056
  return resolved;
5057
5057
  }
5058
5058
 
5059
- function resolveCommand$1(parsed) {
5059
+ function resolveCommand$2(parsed) {
5060
5060
  return resolveCommandAttempt(parsed) || resolveCommandAttempt(parsed, true);
5061
5061
  }
5062
5062
 
5063
- var resolveCommand_1 = resolveCommand$1;
5063
+ var resolveCommand_1 = resolveCommand$2;
5064
5064
 
5065
5065
  var _escape = {};
5066
5066
 
@@ -5152,7 +5152,7 @@ function readShebang$1(command) {
5152
5152
  var readShebang_1 = readShebang$1;
5153
5153
 
5154
5154
  const path$o = require$$0$2;
5155
- const resolveCommand = resolveCommand_1;
5155
+ const resolveCommand$1 = resolveCommand_1;
5156
5156
  const escape = _escape;
5157
5157
  const readShebang = readShebang_1;
5158
5158
 
@@ -5161,7 +5161,7 @@ const isExecutableRegExp = /\.(?:com|exe)$/i;
5161
5161
  const isCmdShimRegExp = /node_modules[\\/].bin[\\/][^\\/]+\.cmd$/i;
5162
5162
 
5163
5163
  function detectShebang(parsed) {
5164
- parsed.file = resolveCommand(parsed);
5164
+ parsed.file = resolveCommand$1(parsed);
5165
5165
 
5166
5166
  const shebang = parsed.file && readShebang(parsed.file);
5167
5167
 
@@ -5169,7 +5169,7 @@ function detectShebang(parsed) {
5169
5169
  parsed.args.unshift(parsed.file);
5170
5170
  parsed.command = shebang;
5171
5171
 
5172
- return resolveCommand(parsed);
5172
+ return resolveCommand$1(parsed);
5173
5173
  }
5174
5174
 
5175
5175
  return parsed.file;
@@ -31150,9 +31150,13 @@ const baseGetDotenvCliOptions = {
31150
31150
  paths: './',
31151
31151
  pathsDelimiter: ' ',
31152
31152
  privateToken: 'local',
31153
- shellScripts: {
31154
- 'git-status': 'git branch --show-current && git status -s -u',
31153
+ scripts: {
31154
+ 'git-status': {
31155
+ cmd: 'git branch --show-current && git status -s -u',
31156
+ shell: true,
31157
+ },
31155
31158
  },
31159
+ shell: true,
31156
31160
  vars: '',
31157
31161
  varsAssignor: '=',
31158
31162
  varsDelimiter: ' ',
@@ -31167,7 +31171,7 @@ const getDotenvOptionsFilename = 'getdotenv.config.json';
31167
31171
  * @returns `getDotenv` options.
31168
31172
  */
31169
31173
  const getDotenvCliOptions2Options = ({ paths, pathsDelimiter, pathsDelimiterPattern, vars, varsAssignor, varsAssignorPattern, varsDelimiter, varsDelimiterPattern, ...rest }) => ({
31170
- ..._.omit(rest, ['debug', 'shellScripts']),
31174
+ ..._.omit(rest, ['debug', 'scripts']),
31171
31175
  paths: paths?.split(pathsDelimiterPattern
31172
31176
  ? RegExp(pathsDelimiterPattern)
31173
31177
  : pathsDelimiter ?? ' ') ?? [],
@@ -31739,6 +31743,13 @@ const getDotenv = async (options = {}) => {
31739
31743
  return dotenv;
31740
31744
  };
31741
31745
 
31746
+ const resolveCommand = (scripts, command) => (scripts && _.isObject(scripts[command])
31747
+ ? scripts[command].cmd
31748
+ : scripts?.[command] ?? command);
31749
+ const resolveShell = (scripts, command, shell) => scripts && _.isObject(scripts[command])
31750
+ ? scripts[command].shell
31751
+ : shell;
31752
+
31742
31753
  /*
31743
31754
  * merge2
31744
31755
  * https://github.com/teambition/merge2
@@ -39480,7 +39491,7 @@ const globPaths = async ({ globs, logger, pkgCwd, rootPath, }) => {
39480
39491
  }
39481
39492
  return { absRootPath, paths };
39482
39493
  };
39483
- const execShellCommandBatch = async ({ command, globs, ignoreErrors, list, logger, pkgCwd, rootPath, }) => {
39494
+ const execShellCommandBatch = async ({ command, getDotenvCliOptions, globs, ignoreErrors, list, logger, pkgCwd, rootPath, shell, }) => {
39484
39495
  if (!command) {
39485
39496
  logger.error(`No command provided.`);
39486
39497
  process.exit(0);
@@ -39492,8 +39503,8 @@ const execShellCommandBatch = async ({ command, globs, ignoreErrors, list, logge
39492
39503
  rootPath,
39493
39504
  });
39494
39505
  const headerTitle = list
39495
- ? 'Listing shell command working directories...'
39496
- : 'Executing shell command batch...';
39506
+ ? 'Listing working directories...'
39507
+ : 'Executing command batch...';
39497
39508
  logger.info('');
39498
39509
  const headerRootPath = `ROOT: ${absRootPath}`;
39499
39510
  const headerGlobs = `GLOBS: ${globs}`;
@@ -39515,12 +39526,17 @@ const execShellCommandBatch = async ({ command, globs, ignoreErrors, list, logge
39515
39526
  logger.info('*'.repeat(pathLabel.length));
39516
39527
  logger.info(pathLabel);
39517
39528
  logger.info(headerCommand);
39518
- // Execute shell command.
39529
+ // Execute command.
39519
39530
  try {
39520
39531
  await execaCommand(command, {
39521
39532
  cwd: path,
39533
+ env: {
39534
+ getDotenvCliOptions: getDotenvCliOptions
39535
+ ? JSON.stringify(getDotenvCliOptions)
39536
+ : undefined,
39537
+ },
39522
39538
  stdio: 'inherit',
39523
- shell: true,
39539
+ shell,
39524
39540
  });
39525
39541
  }
39526
39542
  catch (error) {
@@ -39534,7 +39550,7 @@ const execShellCommandBatch = async ({ command, globs, ignoreErrors, list, logge
39534
39550
 
39535
39551
  const cmdCommand$1 = new Command()
39536
39552
  .name('cmd')
39537
- .description('execute shell command, conflicts with --command option (default command)')
39553
+ .description('execute command, conflicts with --command option (default subcommand)')
39538
39554
  .enablePositionalOptions()
39539
39555
  .passThroughOptions()
39540
39556
  .action(async (options, thisCommand) => {
@@ -39542,60 +39558,64 @@ const cmdCommand$1 = new Command()
39542
39558
  throw new Error(`unable to resolve parent command`);
39543
39559
  if (!thisCommand.parent.parent)
39544
39560
  throw new Error(`unable to resolve root command`);
39545
- const { getDotenvCliOptions: { logger = console, shellScripts }, } = thisCommand.parent.parent;
39561
+ const { getDotenvCliOptions: { logger = console, ...getDotenvCliOptions }, } = thisCommand.parent.parent;
39546
39562
  const { ignoreErrors, globs, list, pkgCwd, rootPath } = thisCommand.parent.opts();
39547
- // Execute shell command.
39563
+ // Execute command.
39548
39564
  {
39549
39565
  const command = thisCommand.args.join(' ');
39550
39566
  await execShellCommandBatch({
39551
- command: shellScripts?.[command] ?? command,
39567
+ command: resolveCommand(getDotenvCliOptions.scripts, command),
39568
+ getDotenvCliOptions,
39552
39569
  globs,
39553
39570
  ignoreErrors,
39554
39571
  list,
39555
39572
  logger,
39556
39573
  pkgCwd,
39557
39574
  rootPath,
39575
+ shell: resolveShell(getDotenvCliOptions.scripts, command, getDotenvCliOptions.shell),
39558
39576
  });
39559
39577
  }
39560
39578
  });
39561
39579
 
39562
39580
  const batchCommand = new Command()
39563
39581
  .name('batch')
39564
- .description('Batch shell commands across multiple working directories.')
39582
+ .description('Batch command execution across multiple working directories.')
39565
39583
  .enablePositionalOptions()
39566
39584
  .passThroughOptions()
39567
39585
  .option('-p, --pkg-cwd', 'use nearest package directory as current working directory')
39568
39586
  .option('-r, --root-path <string>', 'path to batch root directory from current working directory', './')
39569
39587
  .option('-g, --globs <string>', 'space-delimited globs from root path', '*')
39570
- .option('-c, --command <string>', 'shell command string, conflicts with cmd subcommand (dotenv-expanded)', dotenvExpandFromProcessEnv)
39588
+ .option('-c, --command <string>', 'command executed according to the base --shell option, conflicts with cmd subcommand (dotenv-expanded)', dotenvExpandFromProcessEnv)
39571
39589
  .option('-l, --list', 'list working directories without executing command')
39572
39590
  .option('-e, --ignore-errors', 'ignore errors and continue with next path')
39573
39591
  .hook('preSubcommand', async (thisCommand) => {
39574
39592
  if (!thisCommand.parent)
39575
39593
  throw new Error(`unable to resolve root command`);
39576
- const { getDotenvCliOptions: { logger = console, shellScripts }, } = thisCommand.parent;
39594
+ const { getDotenvCliOptions: { logger = console, ...getDotenvCliOptions }, } = thisCommand.parent;
39577
39595
  const { command, ignoreErrors, globs, list, pkgCwd, rootPath } = thisCommand.opts();
39578
39596
  if (command && thisCommand.args.length) {
39579
39597
  logger.error(`--command option conflicts with cmd subcommand.`);
39580
39598
  process.exit(0);
39581
39599
  }
39582
- // Execute shell command.
39600
+ // Execute command.
39583
39601
  if (command)
39584
39602
  await execShellCommandBatch({
39585
- command: shellScripts?.[command] ?? command,
39603
+ command: resolveCommand(getDotenvCliOptions.scripts, command),
39604
+ getDotenvCliOptions,
39586
39605
  globs,
39587
39606
  ignoreErrors,
39588
39607
  list,
39589
39608
  logger,
39590
39609
  pkgCwd,
39591
39610
  rootPath,
39611
+ shell: resolveShell(getDotenvCliOptions.scripts, command, getDotenvCliOptions.shell),
39592
39612
  });
39593
39613
  })
39594
39614
  .addCommand(cmdCommand$1, { isDefault: true });
39595
39615
 
39596
39616
  const cmdCommand = new Command()
39597
39617
  .name('cmd')
39598
- .description('batch execute shell command, conflicts with --command option (default command)')
39618
+ .description('batch execute command string according to the --shell option, conflicts with --command option (default subcommand)')
39599
39619
  .configureHelp({ showGlobalOptions: true })
39600
39620
  .enablePositionalOptions()
39601
39621
  .passThroughOptions()
@@ -39604,13 +39624,14 @@ const cmdCommand = new Command()
39604
39624
  return;
39605
39625
  if (!thisCommand.parent)
39606
39626
  throw new Error('parent command not found');
39607
- const { getDotenvCliOptions: { debug, logger = console, shellScripts }, } = thisCommand.parent;
39627
+ const { getDotenvCliOptions: { logger = console, ...getDotenvCliOptions }, } = thisCommand.parent;
39608
39628
  const command = thisCommand.args.join(' ');
39609
- const shellCommand = shellScripts?.[command] ?? command;
39610
- if (debug)
39611
- logger.log('\n*** shell command ***\n', `'${shellCommand}'`);
39612
- await execaCommand(shellCommand, {
39613
- shell: true,
39629
+ const cmd = resolveCommand(getDotenvCliOptions.scripts, command);
39630
+ if (getDotenvCliOptions.debug)
39631
+ logger.log('\n*** command ***\n', `'${cmd}'`);
39632
+ await execaCommand(cmd, {
39633
+ env: { getDotenvCliOptions: JSON.stringify(getDotenvCliOptions) },
39634
+ shell: resolveShell(getDotenvCliOptions.scripts, command, getDotenvCliOptions.shell),
39614
39635
  stdio: 'inherit',
39615
39636
  });
39616
39637
  });
@@ -39659,7 +39680,7 @@ const resolveExclusionAll = (exclude, excludeOff, defaultValue, excludeAll, excl
39659
39680
  * Generate a Commander CLI Command for get-dotenv.
39660
39681
  */
39661
39682
  const generateGetDotenvCli = async (customOptions) => {
39662
- const { alias, debug, defaultEnv, description, dotenvToken, dynamicPath, env, excludeDynamic, excludeEnv, excludeGlobal, excludePrivate, excludePublic, loadProcess, log, logger, outputPath, paths, pathsDelimiter, pathsDelimiterPattern, postHook, preHook, privateToken, shellScripts, varsAssignor, varsAssignorPattern, varsDelimiter, varsDelimiterPattern, } = await resolveGetDotenvCliGenerateOptions(customOptions);
39683
+ const { alias, debug, defaultEnv, description, dotenvToken, dynamicPath, env, excludeDynamic, excludeEnv, excludeGlobal, excludePrivate, excludePublic, loadProcess, log, logger, outputPath, paths, pathsDelimiter, pathsDelimiterPattern, postHook, preHook, privateToken, scripts, shell, varsAssignor, varsAssignorPattern, varsDelimiter, varsDelimiterPattern, } = await resolveGetDotenvCliGenerateOptions(customOptions);
39663
39684
  const excludeAll = !!excludeDynamic &&
39664
39685
  ((!!excludeEnv && !!excludeGlobal) ||
39665
39686
  (!!excludePrivate && !!excludePublic));
@@ -39675,8 +39696,10 @@ const generateGetDotenvCli = async (customOptions) => {
39675
39696
  ]
39676
39697
  .map((v) => v.join(varsAssignor))
39677
39698
  .join(varsDelimiter)}`, dotenvExpandFromProcessEnv)
39678
- .option('-c, --command <string>', 'shell command string, conflicts with cmd subcommand (dotenv-expanded)', dotenvExpandFromProcessEnv)
39699
+ .option('-c, --command <string>', 'command executed according to the --shell option, conflicts with cmd subcommand (dotenv-expanded)', dotenvExpandFromProcessEnv)
39679
39700
  .option('-o, --output-path <string>', 'consolidated output file (dotenv-expanded)', dotenvExpandFromProcessEnv, outputPath)
39701
+ .addOption(new Option('-s, --shell [string]', `command execution shell, no argument for default OS shell or provide shell string${shell ? ` (default ${_.isBoolean(shell) ? 'OS shell' : shell})` : ''}`).conflicts('shellOff'))
39702
+ .addOption(new Option('-S, --shell-off', `command execution shell OFF${!shell ? ' (default)' : ''}`).conflicts('shell'))
39680
39703
  .addOption(new Option('-p, --load-process', `load variables to process.env ON${loadProcess ? ' (default)' : ''}`).conflicts('loadProcessOff'))
39681
39704
  .addOption(new Option('-P, --load-process-off', `load variables to process.env OFF${!loadProcess ? ' (default)' : ''}`).conflicts('loadProcess'))
39682
39705
  .addOption(new Option('-a, --exclude-all', `exclude all dotenv variables from loading ON${excludeAll ? ' (default)' : ''}`).conflicts('excludeAllOff'))
@@ -39706,8 +39729,8 @@ const generateGetDotenvCli = async (customOptions) => {
39706
39729
  .option('--vars-delimiter-pattern <string>', 'vars delimiter regex pattern', varsDelimiterPattern)
39707
39730
  .option('--vars-assignor <string>', 'vars assignment operator string', varsAssignor)
39708
39731
  .option('--vars-assignor-pattern <string>', 'vars assignment operator regex pattern', varsAssignorPattern)
39709
- .addOption(new Option('--shell-scripts <string>')
39710
- .default(JSON.stringify(shellScripts))
39732
+ .addOption(new Option('--scripts <string>')
39733
+ .default(JSON.stringify(scripts))
39711
39734
  .hideHelp())
39712
39735
  .addCommand(batchCommand)
39713
39736
  .addCommand(cmdCommand, { isDefault: true })
@@ -39719,10 +39742,10 @@ const generateGetDotenvCli = async (customOptions) => {
39719
39742
  // Get raw CLI options from commander.
39720
39743
  const rawCliOptions = thisCommand.opts();
39721
39744
  // Extract current GetDotenvCliOptions from raw CLI options.
39722
- const { command, debugOff, excludeAll, excludeAllOff, excludeDynamicOff, excludeEnvOff, excludeGlobalOff, excludePrivateOff, excludePublicOff, loadProcessOff, logOff, shellScripts, ...rawCliOptionsRest } = rawCliOptions;
39745
+ const { command, debugOff, excludeAll, excludeAllOff, excludeDynamicOff, excludeEnvOff, excludeGlobalOff, excludePrivateOff, excludePublicOff, loadProcessOff, logOff, scripts, shellOff, ...rawCliOptionsRest } = rawCliOptions;
39723
39746
  const currentGetDotenvCliOptions = rawCliOptionsRest;
39724
- if (shellScripts)
39725
- currentGetDotenvCliOptions.shellScripts = JSON.parse(shellScripts);
39747
+ if (scripts)
39748
+ currentGetDotenvCliOptions.scripts = JSON.parse(scripts);
39726
39749
  // Merge current & parent GetDotenvCliOptions.
39727
39750
  const mergedGetDotenvCliOptions = _.defaultsDeep(currentGetDotenvCliOptions, parentGetDotenvCliOptions ?? {});
39728
39751
  // Resolve flags.
@@ -39734,12 +39757,17 @@ const generateGetDotenvCli = async (customOptions) => {
39734
39757
  mergedGetDotenvCliOptions.excludePublic = resolveExclusionAll(mergedGetDotenvCliOptions.excludePublic, excludePublicOff, excludePublic, excludeAll, excludeAllOff);
39735
39758
  mergedGetDotenvCliOptions.log = resolveExclusion(mergedGetDotenvCliOptions.log, logOff, log);
39736
39759
  mergedGetDotenvCliOptions.loadProcess = resolveExclusion(mergedGetDotenvCliOptions.loadProcess, loadProcessOff, loadProcess);
39760
+ mergedGetDotenvCliOptions.shell = shellOff
39761
+ ? false
39762
+ : !_.isUndefined(mergedGetDotenvCliOptions.shell)
39763
+ ? mergedGetDotenvCliOptions.shell
39764
+ : !_.isUndefined(shell)
39765
+ ? shell
39766
+ : false;
39737
39767
  if (mergedGetDotenvCliOptions.debug && parentGetDotenvCliOptions)
39738
39768
  logger.debug('\n*** parent command GetDotenvCliOptions ***\n', parentGetDotenvCliOptions);
39739
39769
  if (mergedGetDotenvCliOptions.debug)
39740
39770
  logger.debug('\n*** current command raw options ***\n', rawCliOptions);
39741
- if (mergedGetDotenvCliOptions.debug)
39742
- logger.debug('\n*** current command GetDotenvCliOptions ***\n', currentGetDotenvCliOptions);
39743
39771
  if (mergedGetDotenvCliOptions.debug)
39744
39772
  logger.debug('\n*** merged GetDotenvCliOptions ***\n', {
39745
39773
  mergedGetDotenvCliOptions,
@@ -39759,21 +39787,21 @@ const generateGetDotenvCli = async (customOptions) => {
39759
39787
  // Execute post-hook.
39760
39788
  if (postHook)
39761
39789
  await postHook(dotenv);
39762
- // Execute shell command.
39790
+ // Execute command.
39763
39791
  if (command && thisCommand.args.length) {
39764
39792
  logger.error(`--command option conflicts with cmd subcommand.`);
39765
39793
  process.exit(0);
39766
39794
  }
39767
39795
  if (command) {
39768
- const shellCommand = mergedGetDotenvCliOptions.shellScripts?.[command] ?? command;
39796
+ const cmd = resolveCommand(mergedGetDotenvCliOptions.scripts, command);
39769
39797
  if (mergedGetDotenvCliOptions.debug)
39770
- logger.debug('\n*** shell command ***\n', shellCommand);
39771
- await execaCommand(shellCommand, {
39798
+ logger.debug('\n*** command ***\n', cmd);
39799
+ await execaCommand(cmd, {
39772
39800
  env: {
39773
39801
  ...process.env,
39774
39802
  getDotenvCliOptions: JSON.stringify(_.omit(mergedGetDotenvCliOptions, ['logger'])),
39775
39803
  },
39776
- shell: true,
39804
+ shell: resolveShell(mergedGetDotenvCliOptions.scripts, command, mergedGetDotenvCliOptions.shell),
39777
39805
  stdio: 'inherit',
39778
39806
  });
39779
39807
  }
package/dist/index.d.cts CHANGED
@@ -1,5 +1,9 @@
1
1
  import { Command } from '@commander-js/extra-typings';
2
2
 
3
+ type Scripts = Record<string, string | {
4
+ cmd: string;
5
+ shell?: string | boolean;
6
+ }>;
3
7
  /**
4
8
  * Options passed programmatically to `getDotenvCli`.
5
9
  */
@@ -23,9 +27,17 @@ interface GetDotenvCliOptions extends Omit<GetDotenvOptions, 'paths' | 'vars'> {
23
27
  */
24
28
  pathsDelimiterPattern?: string;
25
29
  /**
26
- * Shell scripts that can be executed from the CLI, either individually or via the batch subcommand.
30
+ * Scripts that can be executed from the CLI, either individually or via the batch subcommand.
27
31
  */
28
- shellScripts?: Record<string, string>;
32
+ scripts?: Scripts;
33
+ /**
34
+ * Determines how commands and scripts are executed. If `false` or
35
+ * `undefined`, commands are executed as plain Javascript using the default
36
+ * execa parser. If `true`, commands are executed using the default OS shell
37
+ * parser. Otherwise the user may provide a specific shell string (e.g.
38
+ * `/bin/bash`)
39
+ */
40
+ shell?: string | boolean;
29
41
  /**
30
42
  * A delimited string of key-value pairs declaratively specifying variables &
31
43
  * values to be loaded in addition to any dotenv files.
package/dist/index.d.mts CHANGED
@@ -1,5 +1,9 @@
1
1
  import { Command } from '@commander-js/extra-typings';
2
2
 
3
+ type Scripts = Record<string, string | {
4
+ cmd: string;
5
+ shell?: string | boolean;
6
+ }>;
3
7
  /**
4
8
  * Options passed programmatically to `getDotenvCli`.
5
9
  */
@@ -23,9 +27,17 @@ interface GetDotenvCliOptions extends Omit<GetDotenvOptions, 'paths' | 'vars'> {
23
27
  */
24
28
  pathsDelimiterPattern?: string;
25
29
  /**
26
- * Shell scripts that can be executed from the CLI, either individually or via the batch subcommand.
30
+ * Scripts that can be executed from the CLI, either individually or via the batch subcommand.
27
31
  */
28
- shellScripts?: Record<string, string>;
32
+ scripts?: Scripts;
33
+ /**
34
+ * Determines how commands and scripts are executed. If `false` or
35
+ * `undefined`, commands are executed as plain Javascript using the default
36
+ * execa parser. If `true`, commands are executed using the default OS shell
37
+ * parser. Otherwise the user may provide a specific shell string (e.g.
38
+ * `/bin/bash`)
39
+ */
40
+ shell?: string | boolean;
29
41
  /**
30
42
  * A delimited string of key-value pairs declaratively specifying variables &
31
43
  * values to be loaded in addition to any dotenv files.
package/dist/index.d.ts CHANGED
@@ -1,5 +1,9 @@
1
1
  import { Command } from '@commander-js/extra-typings';
2
2
 
3
+ type Scripts = Record<string, string | {
4
+ cmd: string;
5
+ shell?: string | boolean;
6
+ }>;
3
7
  /**
4
8
  * Options passed programmatically to `getDotenvCli`.
5
9
  */
@@ -23,9 +27,17 @@ interface GetDotenvCliOptions extends Omit<GetDotenvOptions, 'paths' | 'vars'> {
23
27
  */
24
28
  pathsDelimiterPattern?: string;
25
29
  /**
26
- * Shell scripts that can be executed from the CLI, either individually or via the batch subcommand.
30
+ * Scripts that can be executed from the CLI, either individually or via the batch subcommand.
27
31
  */
28
- shellScripts?: Record<string, string>;
32
+ scripts?: Scripts;
33
+ /**
34
+ * Determines how commands and scripts are executed. If `false` or
35
+ * `undefined`, commands are executed as plain Javascript using the default
36
+ * execa parser. If `true`, commands are executed using the default OS shell
37
+ * parser. Otherwise the user may provide a specific shell string (e.g.
38
+ * `/bin/bash`)
39
+ */
40
+ shell?: string | boolean;
29
41
  /**
30
42
  * A delimited string of key-value pairs declaratively specifying variables &
31
43
  * values to be loaded in addition to any dotenv files.
package/dist/index.mjs CHANGED
@@ -5054,11 +5054,11 @@ function resolveCommandAttempt(parsed, withoutPathExt) {
5054
5054
  return resolved;
5055
5055
  }
5056
5056
 
5057
- function resolveCommand$1(parsed) {
5057
+ function resolveCommand$2(parsed) {
5058
5058
  return resolveCommandAttempt(parsed) || resolveCommandAttempt(parsed, true);
5059
5059
  }
5060
5060
 
5061
- var resolveCommand_1 = resolveCommand$1;
5061
+ var resolveCommand_1 = resolveCommand$2;
5062
5062
 
5063
5063
  var _escape = {};
5064
5064
 
@@ -5150,7 +5150,7 @@ function readShebang$1(command) {
5150
5150
  var readShebang_1 = readShebang$1;
5151
5151
 
5152
5152
  const path$o = require$$0$2;
5153
- const resolveCommand = resolveCommand_1;
5153
+ const resolveCommand$1 = resolveCommand_1;
5154
5154
  const escape = _escape;
5155
5155
  const readShebang = readShebang_1;
5156
5156
 
@@ -5159,7 +5159,7 @@ const isExecutableRegExp = /\.(?:com|exe)$/i;
5159
5159
  const isCmdShimRegExp = /node_modules[\\/].bin[\\/][^\\/]+\.cmd$/i;
5160
5160
 
5161
5161
  function detectShebang(parsed) {
5162
- parsed.file = resolveCommand(parsed);
5162
+ parsed.file = resolveCommand$1(parsed);
5163
5163
 
5164
5164
  const shebang = parsed.file && readShebang(parsed.file);
5165
5165
 
@@ -5167,7 +5167,7 @@ function detectShebang(parsed) {
5167
5167
  parsed.args.unshift(parsed.file);
5168
5168
  parsed.command = shebang;
5169
5169
 
5170
- return resolveCommand(parsed);
5170
+ return resolveCommand$1(parsed);
5171
5171
  }
5172
5172
 
5173
5173
  return parsed.file;
@@ -31148,9 +31148,13 @@ const baseGetDotenvCliOptions = {
31148
31148
  paths: './',
31149
31149
  pathsDelimiter: ' ',
31150
31150
  privateToken: 'local',
31151
- shellScripts: {
31152
- 'git-status': 'git branch --show-current && git status -s -u',
31151
+ scripts: {
31152
+ 'git-status': {
31153
+ cmd: 'git branch --show-current && git status -s -u',
31154
+ shell: true,
31155
+ },
31153
31156
  },
31157
+ shell: true,
31154
31158
  vars: '',
31155
31159
  varsAssignor: '=',
31156
31160
  varsDelimiter: ' ',
@@ -31165,7 +31169,7 @@ const getDotenvOptionsFilename = 'getdotenv.config.json';
31165
31169
  * @returns `getDotenv` options.
31166
31170
  */
31167
31171
  const getDotenvCliOptions2Options = ({ paths, pathsDelimiter, pathsDelimiterPattern, vars, varsAssignor, varsAssignorPattern, varsDelimiter, varsDelimiterPattern, ...rest }) => ({
31168
- ..._.omit(rest, ['debug', 'shellScripts']),
31172
+ ..._.omit(rest, ['debug', 'scripts']),
31169
31173
  paths: paths?.split(pathsDelimiterPattern
31170
31174
  ? RegExp(pathsDelimiterPattern)
31171
31175
  : pathsDelimiter ?? ' ') ?? [],
@@ -31737,6 +31741,13 @@ const getDotenv = async (options = {}) => {
31737
31741
  return dotenv;
31738
31742
  };
31739
31743
 
31744
+ const resolveCommand = (scripts, command) => (scripts && _.isObject(scripts[command])
31745
+ ? scripts[command].cmd
31746
+ : scripts?.[command] ?? command);
31747
+ const resolveShell = (scripts, command, shell) => scripts && _.isObject(scripts[command])
31748
+ ? scripts[command].shell
31749
+ : shell;
31750
+
31740
31751
  /*
31741
31752
  * merge2
31742
31753
  * https://github.com/teambition/merge2
@@ -39478,7 +39489,7 @@ const globPaths = async ({ globs, logger, pkgCwd, rootPath, }) => {
39478
39489
  }
39479
39490
  return { absRootPath, paths };
39480
39491
  };
39481
- const execShellCommandBatch = async ({ command, globs, ignoreErrors, list, logger, pkgCwd, rootPath, }) => {
39492
+ const execShellCommandBatch = async ({ command, getDotenvCliOptions, globs, ignoreErrors, list, logger, pkgCwd, rootPath, shell, }) => {
39482
39493
  if (!command) {
39483
39494
  logger.error(`No command provided.`);
39484
39495
  process.exit(0);
@@ -39490,8 +39501,8 @@ const execShellCommandBatch = async ({ command, globs, ignoreErrors, list, logge
39490
39501
  rootPath,
39491
39502
  });
39492
39503
  const headerTitle = list
39493
- ? 'Listing shell command working directories...'
39494
- : 'Executing shell command batch...';
39504
+ ? 'Listing working directories...'
39505
+ : 'Executing command batch...';
39495
39506
  logger.info('');
39496
39507
  const headerRootPath = `ROOT: ${absRootPath}`;
39497
39508
  const headerGlobs = `GLOBS: ${globs}`;
@@ -39513,12 +39524,17 @@ const execShellCommandBatch = async ({ command, globs, ignoreErrors, list, logge
39513
39524
  logger.info('*'.repeat(pathLabel.length));
39514
39525
  logger.info(pathLabel);
39515
39526
  logger.info(headerCommand);
39516
- // Execute shell command.
39527
+ // Execute command.
39517
39528
  try {
39518
39529
  await execaCommand(command, {
39519
39530
  cwd: path,
39531
+ env: {
39532
+ getDotenvCliOptions: getDotenvCliOptions
39533
+ ? JSON.stringify(getDotenvCliOptions)
39534
+ : undefined,
39535
+ },
39520
39536
  stdio: 'inherit',
39521
- shell: true,
39537
+ shell,
39522
39538
  });
39523
39539
  }
39524
39540
  catch (error) {
@@ -39532,7 +39548,7 @@ const execShellCommandBatch = async ({ command, globs, ignoreErrors, list, logge
39532
39548
 
39533
39549
  const cmdCommand$1 = new Command()
39534
39550
  .name('cmd')
39535
- .description('execute shell command, conflicts with --command option (default command)')
39551
+ .description('execute command, conflicts with --command option (default subcommand)')
39536
39552
  .enablePositionalOptions()
39537
39553
  .passThroughOptions()
39538
39554
  .action(async (options, thisCommand) => {
@@ -39540,60 +39556,64 @@ const cmdCommand$1 = new Command()
39540
39556
  throw new Error(`unable to resolve parent command`);
39541
39557
  if (!thisCommand.parent.parent)
39542
39558
  throw new Error(`unable to resolve root command`);
39543
- const { getDotenvCliOptions: { logger = console, shellScripts }, } = thisCommand.parent.parent;
39559
+ const { getDotenvCliOptions: { logger = console, ...getDotenvCliOptions }, } = thisCommand.parent.parent;
39544
39560
  const { ignoreErrors, globs, list, pkgCwd, rootPath } = thisCommand.parent.opts();
39545
- // Execute shell command.
39561
+ // Execute command.
39546
39562
  {
39547
39563
  const command = thisCommand.args.join(' ');
39548
39564
  await execShellCommandBatch({
39549
- command: shellScripts?.[command] ?? command,
39565
+ command: resolveCommand(getDotenvCliOptions.scripts, command),
39566
+ getDotenvCliOptions,
39550
39567
  globs,
39551
39568
  ignoreErrors,
39552
39569
  list,
39553
39570
  logger,
39554
39571
  pkgCwd,
39555
39572
  rootPath,
39573
+ shell: resolveShell(getDotenvCliOptions.scripts, command, getDotenvCliOptions.shell),
39556
39574
  });
39557
39575
  }
39558
39576
  });
39559
39577
 
39560
39578
  const batchCommand = new Command()
39561
39579
  .name('batch')
39562
- .description('Batch shell commands across multiple working directories.')
39580
+ .description('Batch command execution across multiple working directories.')
39563
39581
  .enablePositionalOptions()
39564
39582
  .passThroughOptions()
39565
39583
  .option('-p, --pkg-cwd', 'use nearest package directory as current working directory')
39566
39584
  .option('-r, --root-path <string>', 'path to batch root directory from current working directory', './')
39567
39585
  .option('-g, --globs <string>', 'space-delimited globs from root path', '*')
39568
- .option('-c, --command <string>', 'shell command string, conflicts with cmd subcommand (dotenv-expanded)', dotenvExpandFromProcessEnv)
39586
+ .option('-c, --command <string>', 'command executed according to the base --shell option, conflicts with cmd subcommand (dotenv-expanded)', dotenvExpandFromProcessEnv)
39569
39587
  .option('-l, --list', 'list working directories without executing command')
39570
39588
  .option('-e, --ignore-errors', 'ignore errors and continue with next path')
39571
39589
  .hook('preSubcommand', async (thisCommand) => {
39572
39590
  if (!thisCommand.parent)
39573
39591
  throw new Error(`unable to resolve root command`);
39574
- const { getDotenvCliOptions: { logger = console, shellScripts }, } = thisCommand.parent;
39592
+ const { getDotenvCliOptions: { logger = console, ...getDotenvCliOptions }, } = thisCommand.parent;
39575
39593
  const { command, ignoreErrors, globs, list, pkgCwd, rootPath } = thisCommand.opts();
39576
39594
  if (command && thisCommand.args.length) {
39577
39595
  logger.error(`--command option conflicts with cmd subcommand.`);
39578
39596
  process.exit(0);
39579
39597
  }
39580
- // Execute shell command.
39598
+ // Execute command.
39581
39599
  if (command)
39582
39600
  await execShellCommandBatch({
39583
- command: shellScripts?.[command] ?? command,
39601
+ command: resolveCommand(getDotenvCliOptions.scripts, command),
39602
+ getDotenvCliOptions,
39584
39603
  globs,
39585
39604
  ignoreErrors,
39586
39605
  list,
39587
39606
  logger,
39588
39607
  pkgCwd,
39589
39608
  rootPath,
39609
+ shell: resolveShell(getDotenvCliOptions.scripts, command, getDotenvCliOptions.shell),
39590
39610
  });
39591
39611
  })
39592
39612
  .addCommand(cmdCommand$1, { isDefault: true });
39593
39613
 
39594
39614
  const cmdCommand = new Command()
39595
39615
  .name('cmd')
39596
- .description('batch execute shell command, conflicts with --command option (default command)')
39616
+ .description('batch execute command string according to the --shell option, conflicts with --command option (default subcommand)')
39597
39617
  .configureHelp({ showGlobalOptions: true })
39598
39618
  .enablePositionalOptions()
39599
39619
  .passThroughOptions()
@@ -39602,13 +39622,14 @@ const cmdCommand = new Command()
39602
39622
  return;
39603
39623
  if (!thisCommand.parent)
39604
39624
  throw new Error('parent command not found');
39605
- const { getDotenvCliOptions: { debug, logger = console, shellScripts }, } = thisCommand.parent;
39625
+ const { getDotenvCliOptions: { logger = console, ...getDotenvCliOptions }, } = thisCommand.parent;
39606
39626
  const command = thisCommand.args.join(' ');
39607
- const shellCommand = shellScripts?.[command] ?? command;
39608
- if (debug)
39609
- logger.log('\n*** shell command ***\n', `'${shellCommand}'`);
39610
- await execaCommand(shellCommand, {
39611
- shell: true,
39627
+ const cmd = resolveCommand(getDotenvCliOptions.scripts, command);
39628
+ if (getDotenvCliOptions.debug)
39629
+ logger.log('\n*** command ***\n', `'${cmd}'`);
39630
+ await execaCommand(cmd, {
39631
+ env: { getDotenvCliOptions: JSON.stringify(getDotenvCliOptions) },
39632
+ shell: resolveShell(getDotenvCliOptions.scripts, command, getDotenvCliOptions.shell),
39612
39633
  stdio: 'inherit',
39613
39634
  });
39614
39635
  });
@@ -39657,7 +39678,7 @@ const resolveExclusionAll = (exclude, excludeOff, defaultValue, excludeAll, excl
39657
39678
  * Generate a Commander CLI Command for get-dotenv.
39658
39679
  */
39659
39680
  const generateGetDotenvCli = async (customOptions) => {
39660
- const { alias, debug, defaultEnv, description, dotenvToken, dynamicPath, env, excludeDynamic, excludeEnv, excludeGlobal, excludePrivate, excludePublic, loadProcess, log, logger, outputPath, paths, pathsDelimiter, pathsDelimiterPattern, postHook, preHook, privateToken, shellScripts, varsAssignor, varsAssignorPattern, varsDelimiter, varsDelimiterPattern, } = await resolveGetDotenvCliGenerateOptions(customOptions);
39681
+ const { alias, debug, defaultEnv, description, dotenvToken, dynamicPath, env, excludeDynamic, excludeEnv, excludeGlobal, excludePrivate, excludePublic, loadProcess, log, logger, outputPath, paths, pathsDelimiter, pathsDelimiterPattern, postHook, preHook, privateToken, scripts, shell, varsAssignor, varsAssignorPattern, varsDelimiter, varsDelimiterPattern, } = await resolveGetDotenvCliGenerateOptions(customOptions);
39661
39682
  const excludeAll = !!excludeDynamic &&
39662
39683
  ((!!excludeEnv && !!excludeGlobal) ||
39663
39684
  (!!excludePrivate && !!excludePublic));
@@ -39673,8 +39694,10 @@ const generateGetDotenvCli = async (customOptions) => {
39673
39694
  ]
39674
39695
  .map((v) => v.join(varsAssignor))
39675
39696
  .join(varsDelimiter)}`, dotenvExpandFromProcessEnv)
39676
- .option('-c, --command <string>', 'shell command string, conflicts with cmd subcommand (dotenv-expanded)', dotenvExpandFromProcessEnv)
39697
+ .option('-c, --command <string>', 'command executed according to the --shell option, conflicts with cmd subcommand (dotenv-expanded)', dotenvExpandFromProcessEnv)
39677
39698
  .option('-o, --output-path <string>', 'consolidated output file (dotenv-expanded)', dotenvExpandFromProcessEnv, outputPath)
39699
+ .addOption(new Option('-s, --shell [string]', `command execution shell, no argument for default OS shell or provide shell string${shell ? ` (default ${_.isBoolean(shell) ? 'OS shell' : shell})` : ''}`).conflicts('shellOff'))
39700
+ .addOption(new Option('-S, --shell-off', `command execution shell OFF${!shell ? ' (default)' : ''}`).conflicts('shell'))
39678
39701
  .addOption(new Option('-p, --load-process', `load variables to process.env ON${loadProcess ? ' (default)' : ''}`).conflicts('loadProcessOff'))
39679
39702
  .addOption(new Option('-P, --load-process-off', `load variables to process.env OFF${!loadProcess ? ' (default)' : ''}`).conflicts('loadProcess'))
39680
39703
  .addOption(new Option('-a, --exclude-all', `exclude all dotenv variables from loading ON${excludeAll ? ' (default)' : ''}`).conflicts('excludeAllOff'))
@@ -39704,8 +39727,8 @@ const generateGetDotenvCli = async (customOptions) => {
39704
39727
  .option('--vars-delimiter-pattern <string>', 'vars delimiter regex pattern', varsDelimiterPattern)
39705
39728
  .option('--vars-assignor <string>', 'vars assignment operator string', varsAssignor)
39706
39729
  .option('--vars-assignor-pattern <string>', 'vars assignment operator regex pattern', varsAssignorPattern)
39707
- .addOption(new Option('--shell-scripts <string>')
39708
- .default(JSON.stringify(shellScripts))
39730
+ .addOption(new Option('--scripts <string>')
39731
+ .default(JSON.stringify(scripts))
39709
39732
  .hideHelp())
39710
39733
  .addCommand(batchCommand)
39711
39734
  .addCommand(cmdCommand, { isDefault: true })
@@ -39717,10 +39740,10 @@ const generateGetDotenvCli = async (customOptions) => {
39717
39740
  // Get raw CLI options from commander.
39718
39741
  const rawCliOptions = thisCommand.opts();
39719
39742
  // Extract current GetDotenvCliOptions from raw CLI options.
39720
- const { command, debugOff, excludeAll, excludeAllOff, excludeDynamicOff, excludeEnvOff, excludeGlobalOff, excludePrivateOff, excludePublicOff, loadProcessOff, logOff, shellScripts, ...rawCliOptionsRest } = rawCliOptions;
39743
+ const { command, debugOff, excludeAll, excludeAllOff, excludeDynamicOff, excludeEnvOff, excludeGlobalOff, excludePrivateOff, excludePublicOff, loadProcessOff, logOff, scripts, shellOff, ...rawCliOptionsRest } = rawCliOptions;
39721
39744
  const currentGetDotenvCliOptions = rawCliOptionsRest;
39722
- if (shellScripts)
39723
- currentGetDotenvCliOptions.shellScripts = JSON.parse(shellScripts);
39745
+ if (scripts)
39746
+ currentGetDotenvCliOptions.scripts = JSON.parse(scripts);
39724
39747
  // Merge current & parent GetDotenvCliOptions.
39725
39748
  const mergedGetDotenvCliOptions = _.defaultsDeep(currentGetDotenvCliOptions, parentGetDotenvCliOptions ?? {});
39726
39749
  // Resolve flags.
@@ -39732,12 +39755,17 @@ const generateGetDotenvCli = async (customOptions) => {
39732
39755
  mergedGetDotenvCliOptions.excludePublic = resolveExclusionAll(mergedGetDotenvCliOptions.excludePublic, excludePublicOff, excludePublic, excludeAll, excludeAllOff);
39733
39756
  mergedGetDotenvCliOptions.log = resolveExclusion(mergedGetDotenvCliOptions.log, logOff, log);
39734
39757
  mergedGetDotenvCliOptions.loadProcess = resolveExclusion(mergedGetDotenvCliOptions.loadProcess, loadProcessOff, loadProcess);
39758
+ mergedGetDotenvCliOptions.shell = shellOff
39759
+ ? false
39760
+ : !_.isUndefined(mergedGetDotenvCliOptions.shell)
39761
+ ? mergedGetDotenvCliOptions.shell
39762
+ : !_.isUndefined(shell)
39763
+ ? shell
39764
+ : false;
39735
39765
  if (mergedGetDotenvCliOptions.debug && parentGetDotenvCliOptions)
39736
39766
  logger.debug('\n*** parent command GetDotenvCliOptions ***\n', parentGetDotenvCliOptions);
39737
39767
  if (mergedGetDotenvCliOptions.debug)
39738
39768
  logger.debug('\n*** current command raw options ***\n', rawCliOptions);
39739
- if (mergedGetDotenvCliOptions.debug)
39740
- logger.debug('\n*** current command GetDotenvCliOptions ***\n', currentGetDotenvCliOptions);
39741
39769
  if (mergedGetDotenvCliOptions.debug)
39742
39770
  logger.debug('\n*** merged GetDotenvCliOptions ***\n', {
39743
39771
  mergedGetDotenvCliOptions,
@@ -39757,21 +39785,21 @@ const generateGetDotenvCli = async (customOptions) => {
39757
39785
  // Execute post-hook.
39758
39786
  if (postHook)
39759
39787
  await postHook(dotenv);
39760
- // Execute shell command.
39788
+ // Execute command.
39761
39789
  if (command && thisCommand.args.length) {
39762
39790
  logger.error(`--command option conflicts with cmd subcommand.`);
39763
39791
  process.exit(0);
39764
39792
  }
39765
39793
  if (command) {
39766
- const shellCommand = mergedGetDotenvCliOptions.shellScripts?.[command] ?? command;
39794
+ const cmd = resolveCommand(mergedGetDotenvCliOptions.scripts, command);
39767
39795
  if (mergedGetDotenvCliOptions.debug)
39768
- logger.debug('\n*** shell command ***\n', shellCommand);
39769
- await execaCommand(shellCommand, {
39796
+ logger.debug('\n*** command ***\n', cmd);
39797
+ await execaCommand(cmd, {
39770
39798
  env: {
39771
39799
  ...process.env,
39772
39800
  getDotenvCliOptions: JSON.stringify(_.omit(mergedGetDotenvCliOptions, ['logger'])),
39773
39801
  },
39774
- shell: true,
39802
+ shell: resolveShell(mergedGetDotenvCliOptions.scripts, command, mergedGetDotenvCliOptions.shell),
39775
39803
  stdio: 'inherit',
39776
39804
  });
39777
39805
  }
package/package.json CHANGED
@@ -22,7 +22,7 @@
22
22
  "@commander-js/extra-typings": "^12.1.0",
23
23
  "@eslint/js": "^9.3.0",
24
24
  "@rollup/plugin-alias": "^5.1.0",
25
- "@rollup/plugin-commonjs": "^25.0.7",
25
+ "@rollup/plugin-commonjs": "^25.0.8",
26
26
  "@rollup/plugin-json": "^6.1.0",
27
27
  "@rollup/plugin-node-resolve": "^15.2.3",
28
28
  "@rollup/plugin-terser": "^0.4.4",
@@ -34,7 +34,7 @@
34
34
  "@types/fs-extra": "^11.0.4",
35
35
  "@types/lodash": "^4.17.4",
36
36
  "@types/mocha": "^10.0.6",
37
- "@types/node": "^20.12.12",
37
+ "@types/node": "^20.12.13",
38
38
  "auto-changelog": "^2.4.0",
39
39
  "chai": "^5.1.1",
40
40
  "cross-env": "^7.0.3",
@@ -45,7 +45,7 @@
45
45
  "eslint-plugin-tsdoc": "^0.2.17",
46
46
  "fs-extra": "^11.2.0",
47
47
  "jsdom-global": "^3.0.2",
48
- "lefthook": "^1.6.12",
48
+ "lefthook": "^1.6.14",
49
49
  "mocha": "^10.4.0",
50
50
  "nyc": "^15.1.0",
51
51
  "prettier": "^3.2.5",
@@ -57,7 +57,7 @@
57
57
  "ts-node": "^10.9.2",
58
58
  "tslib": "^2.6.2",
59
59
  "typescript": "^5.4.5",
60
- "typescript-eslint": "^7.10.0"
60
+ "typescript-eslint": "^7.11.0"
61
61
  },
62
62
  "exports": {
63
63
  ".": {
@@ -124,6 +124,7 @@
124
124
  },
125
125
  "scripts": {
126
126
  "build": "rimraf dist && rollup --config rollup.config.ts --configPlugin @rollup/plugin-typescript",
127
+ "cli": "node dist/getdotenv.cli.mjs",
127
128
  "lint": "eslint src/** && prettier -c src",
128
129
  "lint:fix": "eslint --fix src/** && prettier --write src",
129
130
  "release": "release-it",
@@ -131,5 +132,5 @@
131
132
  },
132
133
  "type": "module",
133
134
  "types": "dist/index.d.ts",
134
- "version": "4.3.2"
135
+ "version": "4.4.1"
135
136
  }