Skip to content

Commit

Permalink
fix: Resolve value of env variables before invoking cross-spawn
Browse files Browse the repository at this point in the history
  • Loading branch information
Daniel Rey Lopez committed Mar 26, 2017
1 parent 91cb2be commit 928dbc4
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 7 deletions.
4 changes: 2 additions & 2 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ function crossEnv(args) {

function getCommandArgsAndEnvVars(args) {
const envVars = getEnvVars()
const commandArgs = args.map(commandConvert)
const commandArgs = args.slice()
const command = getCommand(commandArgs, envVars)
return [command, commandArgs, envVars]
return [commandConvert(command), commandArgs.map(commandConvert), envVars]
}

function getCommand(commandArgs, envVars) {
Expand Down
38 changes: 33 additions & 5 deletions src/variable.js
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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))
}
28 changes: 28 additions & 0 deletions src/variable.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down Expand Up @@ -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-')
})

0 comments on commit 928dbc4

Please sign in to comment.