From 9a1c83057393e8e374580697b7a4ec1034a6104a Mon Sep 17 00:00:00 2001 From: Hugo Wood Date: Wed, 19 Apr 2017 00:19:03 +0200 Subject: [PATCH] feat(bin): two bins (shell, non-shell) (#104) Revert the default bin (cross-env) to its v3 behavior (not using the shell option). Add a new (cross-env-shell) which uses the shell option. BREAKING CHANGE: Scripts using quotes or escape sequences will see a difference in behavior. Switching to the second bin should resolve any issue. Closes #99. --- README.md | 25 ++++++++++++++++++++----- src/bin/cross-env-shell.js | 5 +++++ src/index.js | 4 ++-- src/index.test.js | 6 ++++-- 4 files changed, 31 insertions(+), 9 deletions(-) create mode 100644 src/bin/cross-env-shell.js diff --git a/README.md b/README.md index c29569d..8db6372 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,8 @@ I use this in my npm scripts: } ``` -Ultimately, the command that is executed (using `cross-spawn`) is: +Ultimately, the command that is executed (using [`cross-spawn`][cross-spawn]) +is: ``` webpack --config build/webpack.config.js @@ -84,19 +85,32 @@ the parent. This is quite useful for launching the same command with different env variables or when the environment variables are too long to have everything in one line. -## Gotchas +## `cross-env` vs `cross-env-shell` -If you want to have the environment variable apply to several commands in series -then you will need to wrap those in quotes in your script. For example: +The `cross-env` module exposes two bins: `cross-env` and `cross-env-shell`. The +first one executes commands using [`cross-spawn`][cross-spawn], while the +second one uses the `shell` option from Node's `spawn`. + +The main use case for `cross-env-shell` is when your need an environment +variable to be set across an entire inline shell script, rather than just one +command. + +For example, if you want to have the environment variable apply to several +commands in series then you will need to wrap those in quotes and use +`cross-env-shell` instead of `cross-env`. ```json { "scripts": { - "greet": "cross-env GREETING=Hi NAME=Joe \"echo $GREETING && echo $NAME\"" + "greet": "cross-env-shell GREETING=Hi NAME=Joe \"echo $GREETING && echo $NAME\"" } } ``` +The rule of thumb is: if you want to pass to `cross-env` a command that +contains special shell characters *that you want interpreted*, then use +`cross-env-shell`. Otherwise stick to `cross-env`. + ## Inspiration I originally created this to solve a problem I was having with my npm scripts in @@ -164,3 +178,4 @@ MIT [all-contributors]: https://github.com/kentcdodds/all-contributors [win-bash]: https://msdn.microsoft.com/en-us/commandline/wsl/about [angular-formly]: https://github.com/formly-js/angular-formly +[cross-spawn]: https://www.npmjs.com/package/cross-spawn diff --git a/src/bin/cross-env-shell.js b/src/bin/cross-env-shell.js new file mode 100644 index 0000000..588034a --- /dev/null +++ b/src/bin/cross-env-shell.js @@ -0,0 +1,5 @@ +#!/usr/bin/env node + +const crossEnv = require('..') + +crossEnv(process.argv.slice(2), {shell: true}) diff --git a/src/index.js b/src/index.js index 130f388..3f884a4 100644 --- a/src/index.js +++ b/src/index.js @@ -6,7 +6,7 @@ module.exports = crossEnv const envSetterRegex = /(\w+)=('(.+)'|"(.+)"|(.+))/ -function crossEnv(args) { +function crossEnv(args, options = {}) { const [envSetters, command, commandArgs] = parseCommand(args) if (command) { const proc = spawn( @@ -14,7 +14,7 @@ function crossEnv(args) { commandArgs.map(commandConvert), { stdio: 'inherit', - shell: true, + shell: options.shell, env: getEnvVars(envSetters), }, ) diff --git a/src/index.test.js b/src/index.test.js index f63b647..aaf07a3 100644 --- a/src/index.test.js +++ b/src/index.test.js @@ -44,7 +44,9 @@ it(`should handle equality signs in quoted strings`, () => { }) it(`should handle quoted scripts`, () => { - crossEnv(['GREETING=Hi', 'NAME=Joe', 'echo $GREETING && echo $NAME']) + crossEnv(['GREETING=Hi', 'NAME=Joe', 'echo $GREETING && echo $NAME'], { + shell: true, + }) expect( crossSpawnMock.spawn, ).toHaveBeenCalledWith('echo $GREETING && echo $NAME', [], { @@ -94,7 +96,7 @@ function testEnvSetting(expected, ...envSettings) { expect(crossSpawnMock.spawn).toHaveBeenCalledTimes(1) expect(crossSpawnMock.spawn).toHaveBeenCalledWith('echo', ['hello world'], { stdio: 'inherit', - shell: true, + shell: undefined, env: Object.assign({}, process.env, env), })