Skip to content

Commit 3db3c8f

Browse files
authored
Merge pull request #410 from toddbluhm/feat-recursive-var-expansion
feat: Add --recursive flag to enable env-var nesting
2 parents 6eacc1a + 0846e5d commit 3db3c8f

15 files changed

Lines changed: 108 additions & 28 deletions

CHANGELOG.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
# Changelog
22

3-
## 10.1.1 - In Development
3+
## Landed in master
44

5-
- **Upgrade**: Upgraded dependency `commander` to `5.x`
6-
- **Upgrade**: Upgraded devDependencies `ts-standard`, `sinon`
5+
- **Upgrade**: Upgraded dependency `commander` to `13.x`
6+
- **Upgrade**: Upgraded dependency `cross-spawn` to `7.x`
7+
- **Upgrade**: Upgraded all devDependencies `ts-standard`, `sinon`
8+
- **Feature**: support both `$var` and `${var}` when expanding vars
9+
- **Feature**: Added support for nested env variables with the `--recursive` flag
710

811
## 10.1.0
912

1013
- **Feature**: Added support for expanding vars using the `-x` flag.
1114
Note: only supports `$var` syntax
12-
- **Feature**: Added support for `--silent` flag that ignores env-cmd errors and missing files and
15+
- **Feature**: Added support for `--silent` flag that ignores env-cmd errors and missing files and
1316
only terminates on caught signals
1417
- **Feature**: Added a new `--verbose` flag that prints additional debugging info to `console.info`
1518
- **Upgrade**: Upgraded dependency `commander` to `4.x`

README.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -59,15 +59,16 @@ Usage: env-cmd [options] -- <command> [...args]
5959
Options:
6060
-v, --version output the version number
6161
-e, --environments [envs...] The rc file environment(s) to use
62-
-f, --file [path] Custom env file path or .rc file path if '-e' used (default path: ./.env or
63-
./.env-cmdrc.(js|cjs|mjs|json))
64-
-x, --expand-envs Replace $var in args and command with environment variables
62+
-f, --file [path] Custom env file path or .rc file path if '-e' used (default path: ./.env or ./.env-cmdrc.(js|cjs|mjs|json))
63+
-x, --expand-envs Replace $var and ${var} in args and command with environment variables
64+
--recursive Replace $var and ${var} in env file with the referenced environment variable
6565
--fallback Fallback to default env file path, if custom env file path not found
6666
--no-override Do not override existing environment variables
6767
--silent Ignore any env-cmd errors and only fail on executed program failure.
6868
--use-shell Execute the command in a new shell with the given environment
6969
--verbose Print helpful debugging information
7070
-h, --help display help for command
71+
7172
```
7273

7374
## 🔬 Advanced Usage
@@ -129,14 +130,14 @@ commands together that share the same environment variables.
129130
```
130131

131132
### Asynchronous env file support
132-
133+
133134
EnvCmd supports reading from asynchronous `.env` files. Instead of using a `.env` file, pass in a `.js`
134135
file that exports either an object or a `Promise` resolving to an object (`{ ENV_VAR_NAME: value, ... }`). Asynchronous `.rc`
135136
files are also supported using `.js` file extension and resolving to an object with top level environment
136137
names (`{ production: { ENV_VAR_NAME: value, ... } }`).
137-
138+
138139
**Terminal**
139-
140+
140141
```sh
141142
./node_modules/.bin/env-cmd -f ./async-file.js -- node index.js
142143
```

dist/env-cmd.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,13 @@ export async function EnvCmd({ command, commandArgs, envFile, rc, options = {},
2929
// Add in the system environment variables to our environment list
3030
env = Object.assign({}, processLib.env, env);
3131
}
32+
if (options.recursive === true) {
33+
for (const key of Object.keys(env)) {
34+
if (env[key] !== undefined) {
35+
env[key] = expandEnvs(env[key], env);
36+
}
37+
}
38+
}
3239
if (options.expandEnvs === true) {
3340
command = expandEnvs(command, env);
3441
commandArgs = commandArgs.map(arg => expandEnvs(arg, env));

dist/expand-envs.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { Environment } from './types.ts';
22
/**
3-
* expandEnvs Replaces $var in args and command with environment variables
3+
* expandEnvs Replaces $var and ${var} in args and command with environment variables
44
* if the environment variable doesn't exist, it leaves it as is.
55
*/
66
export declare function expandEnvs(str: string, envs: Environment): string;

dist/expand-envs.js

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
/**
2-
* expandEnvs Replaces $var in args and command with environment variables
2+
* expandEnvs Replaces $var and ${var} in args and command with environment variables
33
* if the environment variable doesn't exist, it leaves it as is.
44
*/
55
export function expandEnvs(str, envs) {
6-
return str.replace(/(?<!\\)\$[a-zA-Z0-9_]+/g, (varName) => {
7-
const varValue = envs[varName.slice(1)];
8-
// const test = 42;
6+
return str.replace(/(?<!\\)\$(\{\w+\}|\w+)?/g, (varName) => {
7+
const varValue = envs[varName.startsWith('${') ? varName.slice(2, varName.length - 1) : varName.slice(1)];
98
return varValue ?? varName;
109
});
1110
}

dist/parse-args.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ export function parseArgs(args) {
2828
if (parsedCmdOptions.expandEnvs === true) {
2929
expandEnvs = true;
3030
}
31+
let recursive = false;
32+
if (parsedCmdOptions.recursive === true) {
33+
recursive = true;
34+
}
3135
let verbose = false;
3236
if (parsedCmdOptions.verbose === true) {
3337
verbose = true;
@@ -65,6 +69,7 @@ export function parseArgs(args) {
6569
rc,
6670
options: {
6771
expandEnvs,
72+
recursive,
6873
noOverride,
6974
silent,
7075
useShell,
@@ -83,7 +88,8 @@ export function parseArgsUsingCommander(args) {
8388
.usage('[options] -- <command> [...args]')
8489
.option('-e, --environments [envs...]', 'The rc file environment(s) to use', parseArgList)
8590
.option('-f, --file [path]', 'Custom env file path or .rc file path if \'-e\' used (default path: ./.env or ./.env-cmdrc.(js|cjs|mjs|json))')
86-
.option('-x, --expand-envs', 'Replace $var in args and command with environment variables')
91+
.option('-x, --expand-envs', 'Replace $var and ${var} in args and command with environment variables')
92+
.option('--recursive', 'Replace $var and ${var} in env file with the referenced environment variable')
8793
.option('--fallback', 'Fallback to default env file path, if custom env file path not found')
8894
.option('--no-override', 'Do not override existing environment variables')
8995
.option('--silent', 'Ignore any env-cmd errors and only fail on executed program failure.')

dist/types.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ export type RCEnvironment = Partial<Record<string, Environment>>;
44
export type CommanderOptions = Command<[], {
55
environments?: true | string[];
66
expandEnvs?: boolean;
7+
recursive?: boolean;
78
fallback?: boolean;
89
file?: true | string;
910
override?: boolean;
@@ -29,6 +30,7 @@ export interface EnvCmdOptions extends GetEnvVarOptions {
2930
commandArgs: string[];
3031
options?: {
3132
expandEnvs?: boolean;
33+
recursive?: boolean;
3234
noOverride?: boolean;
3335
silent?: boolean;
3436
useShell?: boolean;

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,12 @@
3838
],
3939
"author": "Todd Bluhm",
4040
"contributors": [
41+
"Anton Versal <ant.ver@gmail.com>",
4142
"Eric Lanehart <eric@pushred.co>",
4243
"Jon Scheiding <jonscheiding@gmail.com>",
43-
"serapath (Alexander Praetorius) <dev@serapath.de>",
4444
"Kyle Hensel <me@kyle.kiwi>",
45-
"Anton Versal <ant.ver@gmail.com>"
45+
"Nicholas Krul <nicholas.krul@gmail.com>",
46+
"serapath (Alexander Praetorius) <dev@serapath.de>"
4647
],
4748
"license": "MIT",
4849
"bugs": {

src/env-cmd.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,14 @@ export async function EnvCmd(
4040
env = Object.assign({}, processLib.env, env)
4141
}
4242

43+
if (options.recursive === true) {
44+
for (const key of Object.keys(env)) {
45+
if (env[key] !== undefined) {
46+
env[key] = expandEnvs(env[key], env)
47+
}
48+
}
49+
}
50+
4351
if (options.expandEnvs === true) {
4452
command = expandEnvs(command, env)
4553
commandArgs = commandArgs.map(arg => expandEnvs(arg, env))

src/expand-envs.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
import type { Environment } from './types.ts'
22

33
/**
4-
* expandEnvs Replaces $var in args and command with environment variables
4+
* expandEnvs Replaces $var and ${var} in args and command with environment variables
55
* if the environment variable doesn't exist, it leaves it as is.
66
*/
7-
export function expandEnvs(str: string, envs: Environment): string {
8-
return str.replace(/(?<!\\)\$[a-zA-Z0-9_]+/g, (varName) => {
9-
const varValue = envs[varName.slice(1)]
10-
// const test = 42;
7+
export function expandEnvs (str: string, envs: Environment): string {
8+
return str.replace(/(?<!\\)\$(\{\w+\}|\w+)?/g, (varName) => {
9+
const varValue = envs[varName.startsWith('${') ? varName.slice(2, varName.length - 1) : varName.slice(1)]
1110
return varValue ?? varName
1211
})
1312
}

0 commit comments

Comments
 (0)