fix(expand-envs): test more edge cases

This commit is contained in:
Todd Bluhm 2025-08-05 23:56:08 -08:00
parent 18e8a28148
commit 0846e5d6a5
No known key found for this signature in database
GPG Key ID: 9CF312607477B8AB
7 changed files with 25 additions and 23 deletions

View File

@ -57,17 +57,18 @@ To use a custom env filename or path, pass the `-f` flag. This is a major breaki
Usage: env-cmd [options] -- <command> [...args] Usage: env-cmd [options] -- <command> [...args]
Options: Options:
-v, --version output the version number -v, --version output the version number
-e, --environments [env1,env2,...] The rc file environment(s) to use -e, --environments [envs...] The rc file environment(s) to use
-f, --file [path] Custom env file path (default path: ./.env) -f, --file [path] Custom env file path or .rc file path if '-e' used (default path: ./.env or ./.env-cmdrc.(js|cjs|mjs|json))
-x, --expand-envs Replace $var and ${var} in args and command with environment variables -x, --expand-envs Replace $var and ${var} in args and command with environment variables
--fallback Fallback to default env file path, if custom env file path not found --recursive Replace $var and ${var} in env file with the referenced environment variable
--no-override Do not override existing environment variables --fallback Fallback to default env file path, if custom env file path not found
--silent Ignore any env-cmd errors and only fail on executed program failure. --no-override Do not override existing environment variables
--use-shell Execute the command in a new shell with the given environment --silent Ignore any env-cmd errors and only fail on executed program failure.
--verbose Print helpful debugging information --use-shell Execute the command in a new shell with the given environment
--recursive Replace $var and ${var} in env file with the referenced environment variable --verbose Print helpful debugging information
-h, --help output usage information -h, --help display help for command
``` ```
## 🔬 Advanced Usage ## 🔬 Advanced Usage

View File

@ -1,6 +1,6 @@
import type { Environment } from './types.ts'; import type { Environment } from './types.ts';
/** /**
* expandEnvs Replaces $var and ${var} in args and command with environment variables * expandEnvs Replaces $var and ${var} in args and command with environment variables
* the environment variable doesn't exist, it leaves it as is. * if the environment variable doesn't exist, it leaves it as is.
*/ */
export declare function expandEnvs(str: string, envs: Environment): string; export declare function expandEnvs(str: string, envs: Environment): string;

4
dist/expand-envs.js vendored
View File

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

5
dist/parse-args.js vendored
View File

@ -88,18 +88,17 @@ export function parseArgsUsingCommander(args) {
.usage('[options] -- <command> [...args]') .usage('[options] -- <command> [...args]')
.option('-e, --environments [envs...]', 'The rc file environment(s) to use', parseArgList) .option('-e, --environments [envs...]', 'The rc file environment(s) to use', parseArgList)
.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))') .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))')
.option('-x, --expand-envs', 'Replace $var and $\\{var\\} in args and command with environment variables') .option('-x, --expand-envs', 'Replace $var and ${var} in args and command with environment variables')
.option('--recursive', 'Replace $var and ${var} in env file with the referenced environment variable')
.option('--fallback', 'Fallback to default env file path, if custom env file path not found') .option('--fallback', 'Fallback to default env file path, if custom env file path not found')
.option('--no-override', 'Do not override existing environment variables') .option('--no-override', 'Do not override existing environment variables')
.option('--silent', 'Ignore any env-cmd errors and only fail on executed program failure.') .option('--silent', 'Ignore any env-cmd errors and only fail on executed program failure.')
.option('--use-shell', 'Execute the command in a new shell with the given environment') .option('--use-shell', 'Execute the command in a new shell with the given environment')
.option('--verbose', 'Print helpful debugging information') .option('--verbose', 'Print helpful debugging information')
.option('--recursive', 'Replace $var and $\\{var\\} in env file with the referenced environment variable')
// TODO: Remove -r deprecation error on version >= v12 // TODO: Remove -r deprecation error on version >= v12
.addOption(new Option('-r, --rc-file [path]', 'Deprecated Option') .addOption(new Option('-r, --rc-file [path]', 'Deprecated Option')
.hideHelp() .hideHelp()
.argParser(() => { throw new CommanderError(1, 'deprecated-option', 'The -r flag has been deprecated, use the -f flag instead.'); })) .argParser(() => { throw new CommanderError(1, 'deprecated-option', 'The -r flag has been deprecated, use the -f flag instead.'); }))
// ENDTODO
.allowUnknownOption(true) .allowUnknownOption(true)
.allowExcessArguments(true) .allowExcessArguments(true)
.parse(['_', '_', ...args], { from: 'node' }); .parse(['_', '_', ...args], { from: 'node' });

View File

@ -2,10 +2,10 @@ import type { Environment } from './types.ts'
/** /**
* expandEnvs Replaces $var and ${var} in args and command with environment variables * expandEnvs Replaces $var and ${var} in args and command with environment variables
* the environment variable doesn't exist, it leaves it as is. * if the environment variable doesn't exist, it leaves it as is.
*/ */
export function expandEnvs (str: string, envs: Environment): string { export function expandEnvs (str: string, envs: Environment): string {
return str.replace(/(?<!\\)\$\{?[a-zA-Z0-9_]+\}?/g, (varName) => { return str.replace(/(?<!\\)\$(\{\w+\}|\w+)?/g, (varName) => {
const varValue = envs[varName.startsWith('${') ? varName.slice(2, varName.length - 1) : varName.slice(1)] const varValue = envs[varName.startsWith('${') ? varName.slice(2, varName.length - 1) : varName.slice(1)]
return varValue ?? varName return varValue ?? varName
}) })

View File

@ -100,8 +100,8 @@ export function parseArgsUsingCommander(args: string[]): CommanderOptions {
.usage('[options] -- <command> [...args]') .usage('[options] -- <command> [...args]')
.option('-e, --environments [envs...]', 'The rc file environment(s) to use', parseArgList) .option('-e, --environments [envs...]', 'The rc file environment(s) to use', parseArgList)
.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))') .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))')
.option('-x, --expand-envs', 'Replace $var in args and command with environment variables') .option('-x, --expand-envs', 'Replace $var and ${var} in args and command with environment variables')
.option('--recursive', 'Replace $var and $\\{var\\} in env file with the referenced environment variable') .option('--recursive', 'Replace $var and ${var} in env file with the referenced environment variable')
.option('--fallback', 'Fallback to default env file path, if custom env file path not found') .option('--fallback', 'Fallback to default env file path, if custom env file path not found')
.option('--no-override', 'Do not override existing environment variables') .option('--no-override', 'Do not override existing environment variables')
.option('--silent', 'Ignore any env-cmd errors and only fail on executed program failure.') .option('--silent', 'Ignore any env-cmd errors and only fail on executed program failure.')

View File

@ -15,12 +15,14 @@ describe('expandEnvs', (): void => {
const args = [ const args = [
'notvar', '$dollar', '\\$notvar', '-4', 'notvar', '$dollar', '\\$notvar', '-4',
'$PING', '$IP1', '\\$IP1', '$NONEXIST', '$PING', '$IP1', '\\$IP1', '$NONEXIST',
'${PING}', '${NONEXIST}' '${PING}', '${NONEXIST}', '\\${PING}',
'$PING}', '${PING2'
] ]
const argsExpanded = [ const argsExpanded = [
'notvar', 'money', '\\$notvar', '-4', 'notvar', 'money', '\\$notvar', '-4',
'PONG', '127.0.0.1', '\\$IP1', '$NONEXIST', 'PONG', '127.0.0.1', '\\$IP1', '$NONEXIST',
'PONG', '${NONEXIST}' 'PONG', '${NONEXIST}', '\\${PING}',
'PONG}', '${PING2'
] ]
it('should replace environment variables in args', (): void => { it('should replace environment variables in args', (): void => {