From ea0ac4bd048733d14a260538dd0c377478ddc2cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Rey=20L=C3=B3pez?= Date: Thu, 23 Mar 2017 15:52:58 +0000 Subject: [PATCH] feat: Convert list delimiters for PATH-style env variables (#93) * feat: Convert list delimiters for PATH-style env variables In Windows, env variables that represent a list (such as PATH or NODE_PATH) separate their elements using a semicolon(;), but in UNIX they're separated using a colon (:). This commit adds a conversion layer, so regardless of how the delimiter is written when calling cross-env, it will be converted to the correct platform delimiter at runtime. To interpret a colon/semicolon literally instead, preced it with a backslash, like this: "cross-env VAR=semi\\;colon\\:" BREAKING CHANGE: If an env variable has : or ; in its value, it will be converted to : on UNIX systems or ; on Windows systems. To keep the old functionality, you will need to escape those characters with a backslash. #80 * chore: Add myself (DanReyLop) to the contributors list * Simplified logic. Now only : (UNIX-style) are converted to ; (Windows-style), not the other way around BREAKING CHANGE: You now must escape `:` to use it in a value of you don't want it to be swapped with `;` on Windows --- .all-contributorsrc | 10 ++++++++++ README.md | 4 ++-- src/index.js | 3 ++- src/variable.js | 21 ++++++++++++++++++++ src/variable.test.js | 46 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 81 insertions(+), 3 deletions(-) create mode 100644 src/variable.js create mode 100644 src/variable.test.js diff --git a/.all-contributorsrc b/.all-contributorsrc index 0b0e61a..af5881b 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -92,6 +92,16 @@ "code", "test" ] + }, + { + "login": "DanReyLop", + "name": "Daniel Rey LΓ³pez", + "avatar_url": "https://avatars1.githubusercontent.com/u/1715800?v=3", + "profile": "https://daniel.blog", + "contributions": [ + "code", + "test" + ] } ] } diff --git a/README.md b/README.md index 74d32c0..531a97a 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Run scripts that set and use environment variables across platforms [![downloads][downloads-badge]][npm-stat] [![MIT License][license-badge]][LICENSE] -[![All Contributors](https://img.shields.io/badge/all_contributors-8-orange.svg?style=flat-square)](#contributors) +[![All Contributors](https://img.shields.io/badge/all_contributors-9-orange.svg?style=flat-square)](#contributors) [![PRs Welcome][prs-badge]][prs] [![Donate][donate-badge]][donate] [![Code of Conduct][coc-badge]][coc] @@ -114,7 +114,7 @@ Thanks goes to these people ([emoji key][emojis]): | [
Kent C. Dodds](https://kentcdodds.com)
[πŸ’»](https://github.com/kentcdodds/cross-env/commits?author=kentcdodds) [πŸ“–](https://github.com/kentcdodds/cross-env/commits?author=kentcdodds) πŸš‡ [⚠️](https://github.com/kentcdodds/cross-env/commits?author=kentcdodds) | [
Ya Zhuang ](https://zhuangya.me)
πŸ”Œ [πŸ“–](https://github.com/kentcdodds/cross-env/commits?author=zhuangya) | [
James Harris](https://wopian.me)
[πŸ“–](https://github.com/kentcdodds/cross-env/commits?author=wopian) | [
compumike08](https://github.com/compumike08)
[πŸ›](https://github.com/kentcdodds/cross-env/issues?q=author%3Acompumike08) [πŸ“–](https://github.com/kentcdodds/cross-env/commits?author=compumike08) [⚠️](https://github.com/kentcdodds/cross-env/commits?author=compumike08) | [
Daniel RodrΓ­guez Rivero](https://github.com/danielo515)
[πŸ›](https://github.com/kentcdodds/cross-env/issues?q=author%3Adanielo515) [πŸ’»](https://github.com/kentcdodds/cross-env/commits?author=danielo515) [πŸ“–](https://github.com/kentcdodds/cross-env/commits?author=danielo515) | [
Jonas Keinholz](https://github.com/inyono)
[πŸ›](https://github.com/kentcdodds/cross-env/issues?q=author%3Ainyono) [πŸ’»](https://github.com/kentcdodds/cross-env/commits?author=inyono) [⚠️](https://github.com/kentcdodds/cross-env/commits?author=inyono) | [
Hugo Wood](https://github.com/hgwood)
[πŸ›](https://github.com/kentcdodds/cross-env/issues?q=author%3Ahgwood) [πŸ’»](https://github.com/kentcdodds/cross-env/commits?author=hgwood) [⚠️](https://github.com/kentcdodds/cross-env/commits?author=hgwood) | | :---: | :---: | :---: | :---: | :---: | :---: | :---: | -| [
Thiebaud Thomas](https://github.com/thomasthiebaud)
| +| [
Thiebaud Thomas](https://github.com/thomasthiebaud)
[πŸ›](https://github.com/kentcdodds/cross-env/issues?q=author%3Athomasthiebaud) [πŸ’»](https://github.com/kentcdodds/cross-env/commits?author=thomasthiebaud) [⚠️](https://github.com/kentcdodds/cross-env/commits?author=thomasthiebaud) | [
Daniel Rey LΓ³pez](https://daniel.blog)
[πŸ’»](https://github.com/kentcdodds/cross-env/commits?author=DanReyLop) [⚠️](https://github.com/kentcdodds/cross-env/commits?author=DanReyLop) | This project follows the [all-contributors][all-contributors] specification. Contributions of any kind welcome! diff --git a/src/index.js b/src/index.js index 9a4a7a7..e7734ef 100644 --- a/src/index.js +++ b/src/index.js @@ -1,5 +1,6 @@ import {spawn} from 'cross-spawn' import commandConvert from './command' +import varValueConvert from './variable' module.exports = crossEnv @@ -35,7 +36,7 @@ function getCommand(commandArgs, envVars) { const shifted = commandArgs.shift() const match = envSetterRegex.exec(shifted) if (match) { - envVars[match[1]] = match[3] || match[4] || match[5] + envVars[match[1]] = varValueConvert(match[3] || match[4] || match[5]) } else { return shifted } diff --git a/src/variable.js b/src/variable.js new file mode 100644 index 0000000..4012cc4 --- /dev/null +++ b/src/variable.js @@ -0,0 +1,21 @@ +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. + * 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 + * @returns {String} Converted value + */ +export default function varValueConvert(originalValue) { + const targetSeparator = isWindows() ? ';' : ':' + return originalValue.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 + return match.substr(1) + } + return backslashes + targetSeparator + }) +} diff --git a/src/variable.test.js b/src/variable.test.js new file mode 100644 index 0000000..41c8602 --- /dev/null +++ b/src/variable.test.js @@ -0,0 +1,46 @@ +import isWindowsMock from 'is-windows' +import varValueConvert from './variable' + +beforeEach(() => { + isWindowsMock.__mock.reset() +}) + +test(`doesn't affect simple variable values`, () => { + isWindowsMock.__mock.returnValue = true + expect(varValueConvert('foo')).toBe('foo') +}) + +test(`doesn't convert a ; into a : on UNIX`, () => { + isWindowsMock.__mock.returnValue = false + expect(varValueConvert('foo;bar')).toBe('foo;bar') +}) + +test(`converts a : into a ; on Windows`, () => { + isWindowsMock.__mock.returnValue = true + expect(varValueConvert('foo:bar')).toBe('foo;bar') +}) + +test(`doesn't convert already valid separators`, () => { + isWindowsMock.__mock.returnValue = false + expect(varValueConvert('foo:bar')).toBe('foo:bar') +}) + +test(`doesn't convert escaped separators on Windows`, () => { + isWindowsMock.__mock.returnValue = true + expect(varValueConvert('foo\\:bar')).toBe('foo:bar') +}) + +test(`doesn't convert escaped separators on UNIX`, () => { + isWindowsMock.__mock.returnValue = false + expect(varValueConvert('foo\\:bar')).toBe('foo:bar') +}) + +test(`converts a separator even if preceded by an escaped backslash`, () => { + isWindowsMock.__mock.returnValue = true + expect(varValueConvert('foo\\\\:bar')).toBe('foo\\\\;bar') +}) + +test(`converts multiple separators`, () => { + isWindowsMock.__mock.returnValue = true + expect(varValueConvert('foo:bar:baz')).toBe('foo;bar;baz') +})