From 7b11072c00981f2b77331b2c2ad8067156769746 Mon Sep 17 00:00:00 2001 From: Daniel Rey Lopez Date: Sat, 25 Mar 2017 22:42:53 +0000 Subject: [PATCH] fix: Resolve value of env variables before invoking cross-spawn #90 --- src/index.js | 6 +++--- src/variable.js | 38 +++++++++++++++++++++++++++++++++----- src/variable.test.js | 28 ++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 8 deletions(-) diff --git a/src/index.js b/src/index.js index e7734ef..992007d 100644 --- a/src/index.js +++ b/src/index.js @@ -26,9 +26,9 @@ function crossEnv(args) { function getCommandArgsAndEnvVars(args) { const envVars = getEnvVars() - const commandArgs = args.map(commandConvert) - const command = getCommand(commandArgs, envVars) - return [command, commandArgs, envVars] + const commandArgs = args.slice() + const command = commandConvert(getCommand(commandArgs, envVars)) + return [commandConvert(command), commandArgs.map(commandConvert), envVars] } function getCommand(commandArgs, envVars) { diff --git a/src/variable.js b/src/variable.js index 4012cc4..92d66a7 100644 --- a/src/variable.js +++ b/src/variable.js @@ -1,16 +1,15 @@ import isWindows from 'is-windows' /** - * Converts an environment variable value to be appropriate for the current OS. - * It will transform UNIX-style list values to Windows-style. + * This will transform UNIX-style list values to Windows-style. * For example, the value of the $PATH variable "/usr/bin:/usr/local/bin:." * will become "/usr/bin;/usr/local/bin;." on Windows. - * @param {String} originalValue Original value of the env variable + * @param {String} varValue Original value of the env variable * @returns {String} Converted value */ -export default function varValueConvert(originalValue) { +function replaceListDelimiters(varValue) { const targetSeparator = isWindows() ? ';' : ':' - return originalValue.replace(/(\\*):/g, (match, backslashes) => { + return varValue.replace(/(\\*):/g, (match, backslashes) => { if (backslashes.length % 2) { // Odd number of backslashes preceding it means it's escaped, // remove 1 backslash and return the rest as-is @@ -19,3 +18,32 @@ export default function varValueConvert(originalValue) { return backslashes + targetSeparator }) } + +/** + * This will attempt to resolve the value of any env variables that are inside + * this string. For example, it will transform this: + * cross-env FOO=$NODE_ENV echo $FOO + * Into this: + * FOO=development echo $FOO + * (Or whatever value the variable NODE_ENV has) + * Note that this function is only called with the right-side portion of the + * env var assignment, so in that example, this function would transform + * the string "$NODE_ENV" into "development" + * @param {String} varValue Original value of the env variable + * @returns {String} Converted value + */ +function resolveEnvVars(varValue) { + const envUnixRegex = /\$(\w+)|\${(\w+)}/g // $my_var or ${my_var} + return varValue.replace(envUnixRegex, (_, varName, altVarName) => { + return process.env[varName || altVarName] || '' + }) +} + +/** + * Converts an environment variable value to be appropriate for the current OS. + * @param {String} originalValue Original value of the env variable + * @returns {String} Converted value + */ +export default function varValueConvert(originalValue) { + return resolveEnvVars(replaceListDelimiters(originalValue)) +} diff --git a/src/variable.test.js b/src/variable.test.js index 41c8602..b6fa3a4 100644 --- a/src/variable.test.js +++ b/src/variable.test.js @@ -2,9 +2,16 @@ import isWindowsMock from 'is-windows' import varValueConvert from './variable' beforeEach(() => { + process.env.VAR1 = 'value1' + process.env.VAR2 = 'value2' isWindowsMock.__mock.reset() }) +afterEach(() => { + delete process.env.VAR1 + delete process.env.VAR2 +}) + test(`doesn't affect simple variable values`, () => { isWindowsMock.__mock.returnValue = true expect(varValueConvert('foo')).toBe('foo') @@ -44,3 +51,24 @@ test(`converts multiple separators`, () => { isWindowsMock.__mock.returnValue = true expect(varValueConvert('foo:bar:baz')).toBe('foo;bar;baz') }) + +test(`resolves an env variable value`, () => { + isWindowsMock.__mock.returnValue = true + expect(varValueConvert('foo-$VAR1')).toBe('foo-value1') +}) + +test(`resolves an env variable value with curly syntax`, () => { + isWindowsMock.__mock.returnValue = true + // eslint-disable-next-line no-template-curly-in-string + expect(varValueConvert('foo-${VAR1}')).toBe('foo-value1') +}) + +test(`resolves multiple env variable values`, () => { + isWindowsMock.__mock.returnValue = true + expect(varValueConvert('foo-$VAR1-$VAR2')).toBe('foo-value1-value2') +}) + +test(`resolves an env variable value for non-existant variable`, () => { + isWindowsMock.__mock.returnValue = true + expect(varValueConvert('foo-$VAR_POTATO')).toBe('foo-') +})