Skip to content
This repository was archived by the owner on Jan 6, 2021. It is now read-only.

Commit 50299d9

Browse files
DanReyLopKent C. Dodds
authored andcommitted
fix: remove env variables that don't exist from the converted commands in Windows. (#149)
* Remove trailing comma on arguments list (unsupported in Node v6 LTS) * Remove non-defined env variables from the command (and command args), since those have a different behaviour in Windows. * Fixed tests * Added new test cases * Fixed lint error
1 parent 36b009e commit 50299d9

File tree

4 files changed

+42
-15
lines changed

4 files changed

+42
-15
lines changed

jest.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
const jestConfig = require('kcd-scripts/config').jest
22

33
jestConfig.coveragePathIgnorePatterns = jestConfig.coveragePathIgnorePatterns.concat(
4-
['/bin/'],
4+
['/bin/']
55
)
66
module.exports = jestConfig

src/__tests__/command.js

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,74 @@
11
import isWindowsMock from 'is-windows'
22
import commandConvert from '../command'
33

4+
const env = {
5+
test: 'a',
6+
test1: 'b',
7+
test2: 'c',
8+
test3: 'd',
9+
'empty_var': ''
10+
}
11+
412
beforeEach(() => {
513
isWindowsMock.__mock.reset()
614
})
715

816
test(`converts unix-style env variable usage for windows`, () => {
917
isWindowsMock.__mock.returnValue = true
10-
expect(commandConvert('$test')).toBe('%test%')
18+
expect(commandConvert('$test', env)).toBe('%test%')
1119
})
1220

1321
test(`leaves command unchanged when not a variable`, () => {
14-
expect(commandConvert('test')).toBe('test')
22+
expect(commandConvert('test', env)).toBe('test')
1523
})
1624

1725
test(`doesn't convert windows-style env variable`, () => {
1826
isWindowsMock.__mock.returnValue = false
19-
expect(commandConvert('%test%')).toBe('%test%')
27+
expect(commandConvert('%test%', env)).toBe('%test%')
2028
})
2129

2230
test(`leaves variable unchanged when using correct operating system`, () => {
2331
isWindowsMock.__mock.returnValue = false
24-
expect(commandConvert('$test')).toBe('$test')
32+
expect(commandConvert('$test', env)).toBe('$test')
2533
})
2634

2735
test(`is stateless`, () => {
2836
// this test prevents falling into regexp traps like this:
2937
// http://stackoverflow.com/a/1520853/971592
3038
isWindowsMock.__mock.returnValue = true
31-
expect(commandConvert('$test')).toBe(commandConvert('$test'))
39+
expect(commandConvert('$test', env)).toBe(commandConvert('$test', env))
3240
})
3341

3442
test(`converts embedded unix-style env variables usage for windows`, () => {
3543
isWindowsMock.__mock.returnValue = true
36-
expect(commandConvert('$test1/$test2/$test3')).toBe('%test1%/%test2%/%test3%')
44+
expect(commandConvert('$test1/$test2/$test3', env)).toBe('%test1%/%test2%/%test3%')
3745
})
3846

3947
// eslint-disable-next-line max-len
4048
test(`leaves embedded variables unchanged when using correct operating system`, () => {
4149
isWindowsMock.__mock.returnValue = false
42-
expect(commandConvert('$test1/$test2/$test3')).toBe('$test1/$test2/$test3')
50+
expect(commandConvert('$test1/$test2/$test3', env)).toBe('$test1/$test2/$test3')
4351
})
4452

4553
test(`converts braced unix-style env variable usage for windows`, () => {
4654
isWindowsMock.__mock.returnValue = true
4755
// eslint-disable-next-line no-template-curly-in-string
48-
expect(commandConvert('${test}')).toBe('%test%')
56+
expect(commandConvert('${test}', env)).toBe('%test%')
57+
})
58+
59+
test(`removes non-existent variables from the converted command`, () => {
60+
isWindowsMock.__mock.returnValue = true
61+
expect(commandConvert('$test1/$foo/$test2', env)).toBe('%test1%//%test2%')
62+
})
63+
64+
test(`removes empty variables from the converted command`, () => {
65+
isWindowsMock.__mock.returnValue = true
66+
expect(commandConvert('$foo/$test/$empty_var', env)).toBe('/%test%/')
4967
})
5068

5169
test(`normalizes command on windows`, () => {
5270
isWindowsMock.__mock.returnValue = true
5371
// index.js calls `commandConvert` with `normalize` param
5472
// as `true` for command only
55-
expect(commandConvert('./cmd.bat', true)).toBe('cmd.bat')
73+
expect(commandConvert('./cmd.bat', env, true)).toBe('cmd.bat')
5674
})

src/command.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,24 @@ export default commandConvert
66
/**
77
* Converts an environment variable usage to be appropriate for the current OS
88
* @param {String} command Command to convert
9+
* @param {Object} env Map of the current environment variable names and their values
910
* @param {boolean} normalize If the command should be normalized using `path`
1011
* after converting
1112
* @returns {String} Converted command
1213
*/
13-
function commandConvert(command, normalize = false) {
14+
function commandConvert(command, env, normalize = false) {
1415
if (!isWindows()) {
1516
return command
1617
}
1718
const envUnixRegex = /\$(\w+)|\${(\w+)}/g // $my_var or ${my_var}
18-
const convertedCmd = command.replace(envUnixRegex, '%$1$2%')
19+
const convertedCmd = command.replace(envUnixRegex, (match, $1, $2) => {
20+
const varName = $1 || $2
21+
// In Windows, non-existent variables are not replaced by the shell,
22+
// so for example "echo %FOO%" will literally print the string "%FOO%", as
23+
// opposed to printing an empty string in UNIX. See kentcdodds/cross-env#145
24+
// If the env variable isn't defined at runtime, just strip it from the command entirely
25+
return env[varName] ? `%${varName}%` : ''
26+
})
1927
// Normalization is required for commands with relative paths
2028
// For example, `./cmd.bat`. See kentcdodds/cross-env#127
2129
// However, it should not be done for command arguments.

src/index.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,17 @@ const envSetterRegex = /(\w+)=('(.*)'|"(.*)"|(.*))/
88

99
function crossEnv(args, options = {}) {
1010
const [envSetters, command, commandArgs] = parseCommand(args)
11+
const env = getEnvVars(envSetters)
1112
if (command) {
1213
const proc = spawn(
1314
// run `path.normalize` for command(on windows)
14-
commandConvert(command, true),
15+
commandConvert(command, env, true),
1516
// by default normalize is `false`, so not run for cmd args
16-
commandArgs.map(arg => commandConvert(arg)),
17+
commandArgs.map(arg => commandConvert(arg, env)),
1718
{
1819
stdio: 'inherit',
1920
shell: options.shell,
20-
env: getEnvVars(envSetters),
21+
env,
2122
},
2223
)
2324
process.on('SIGTERM', () => proc.kill('SIGTERM'))

0 commit comments

Comments
 (0)