From 51a8af30990cb072cb30d67fc1b564b14746bba9 Mon Sep 17 00:00:00 2001 From: ExE Boss <3889017+ExE-Boss@users.noreply.github.com> Date: Thu, 24 May 2018 10:00:00 +0200 Subject: [PATCH] Add proper support for PowerShell MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PowerShell can’t properly pass string arguments containing the `&` symbol to Windows Command Prompt scripts, if the string containing the ampersand doesn’t have spaces, due to how the cmd prompt parses the `&` as a command delimiter, even in a string. This patch adds a workaround by generating a third script specifically for PowerShell. PR-URL: https://github.com/npm/cmd-shim/pull/34 Credit: @ExE-Boss Close: #34 Reviewed-by: @isaacs --- index.js | 52 +++++++++++++++++++++++++++- test/basic.js | 95 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 146 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index cfdc45c..722ad91 100644 --- a/index.js +++ b/index.js @@ -80,18 +80,22 @@ function writeShim_ (from, to, prog, args, variables, cb) { , longProg , shProg = prog && prog.split("\\").join("/") , shLongProg + , pwshProg = shProg && "\"" + shProg + "$exe\"" + , pwshLongProg shTarget = shTarget.split("\\").join("/") args = args || "" variables = variables || "" if (!prog) { prog = "\"%~dp0\\" + target + "\"" shProg = "\"$basedir/" + shTarget + "\"" + pwshProg = shProg args = "" target = "" shTarget = "" } else { longProg = "\"%~dp0\\" + prog + ".exe\"" shLongProg = "\"$basedir/" + prog + "\"" + pwshLongProg = "\"$basedir/" + prog + "$exe\"" target = "\"%~dp0\\" + target + "\"" shTarget = "\"$basedir/" + shTarget + "\"" } @@ -170,7 +174,52 @@ function writeShim_ (from, to, prog, args, variables, cb) { + "exit $?\n" } - var then = times(2, next, cb) + // #!/usr/bin/env pwsh + // $basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent + // + // $ret=0 + // $exe = "" + // if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) { + // # Fix case when both the Windows and Linux builds of Node + // # are installed in the same directory + // $exe = ".exe" + // } + // if (Test-Path "$basedir/node") { + // & "$basedir/node$exe" "$basedir/node_modules/npm/bin/npm-cli.js" $args + // $ret=$LASTEXITCODE + // } else { + // & "node$exe" "$basedir/node_modules/npm/bin/npm-cli.js" $args + // $ret=$LASTEXITCODE + // } + // exit $ret + var pwsh = "#!/usr/bin/env pwsh\n" + + "$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent\n" + + "\n" + + "$exe=\"\"\n" + + "if ($PSVersionTable.PSVersion -lt \"6.0\" -or $IsWindows) {\n" + + " # Fix case when both the Windows and Linux builds of Node\n" + + " # are installed in the same directory\n" + + " $exe=\".exe\"\n" + + "}\n" + if (shLongProg) { + pwsh = pwsh + + "$ret=0\n" + + "if (Test-Path " + pwshLongProg + ") {\n" + + " & " + pwshLongProg + " " + args + " " + shTarget + " $args\n" + + " $ret=$LASTEXITCODE\n" + + "} else {\n" + + " & " + pwshProg + " " + args + " " + shTarget + " $args\n" + + " $ret=$LASTEXITCODE\n" + + "}\n" + + "exit $ret\n" + } else { + pwsh = pwsh + + "& " + pwshProg + " " + args + " " + shTarget + " $args\n" + + "exit $LASTEXITCODE\n" + } + + var then = times(3, next, cb) + fs.writeFile(to + ".ps1", pwsh, "utf8", then) fs.writeFile(to + ".cmd", cmd, "utf8", then) fs.writeFile(to, sh, "utf8", then) function next () { @@ -182,6 +231,7 @@ function chmodShim (to, cb) { var then = times(2, cb, cb) fs.chmod(to, "0755", then) fs.chmod(to + ".cmd", "0755", then) + fs.chmod(to + ".ps1", "0755", then) } function times(n, ok, cb) { diff --git a/test/basic.js b/test/basic.js index 001c2d0..4d92557 100755 --- a/test/basic.js +++ b/test/basic.js @@ -23,6 +23,19 @@ test('no shebang', function (t) { "\n\"$basedir/from.exe\" \"$@\"\nexit $?\n") t.equal(fs.readFileSync(to + '.cmd', 'utf8'), "@\"%~dp0\\from.exe\" %*\r\n") + t.equal(fs.readFileSync(to + '.ps1', 'utf8'), + '#!/usr/bin/env pwsh'+ + '\n$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent'+ + '\n'+ + '\n$exe=""'+ + '\nif ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {'+ + '\n # Fix case when both the Windows and Linux builds of Node'+ + '\n # are installed in the same directory'+ + '\n $exe=".exe"'+ + '\n}'+ + '\n& "$basedir/from.exe" $args'+ + '\nexit $LASTEXITCODE'+ + '\n') t.end() }) }) @@ -105,6 +118,26 @@ test('env shebang with args', function (t) { "\n\"%_prog%\" --expose_gc \"%~dp0\\from.env.args\" %*\r" + "\n@ENDLOCAL\r" + "\n") + t.equal(fs.readFileSync(to + '.ps1', 'utf8'), + '#!/usr/bin/env pwsh'+ + '\n$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent'+ + '\n'+ + '\n$exe=""'+ + '\nif ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {'+ + '\n # Fix case when both the Windows and Linux builds of Node'+ + '\n # are installed in the same directory'+ + '\n $exe=".exe"'+ + '\n}'+ + '\n$ret=0'+ + '\nif (Test-Path "$basedir/node$exe") {'+ + '\n & "$basedir/node$exe" --expose_gc "$basedir/from.env.args" $args'+ + '\n $ret=$LASTEXITCODE'+ + '\n} else {'+ + '\n & "node$exe" --expose_gc "$basedir/from.env.args" $args'+ + '\n $ret=$LASTEXITCODE'+ + '\n}'+ + '\nexit $ret'+ + '\n') t.end() }) }) @@ -146,6 +179,26 @@ test('env shebang with variables', function (t) { "\n\r"+ "\n\"%_prog%\" \"%~dp0\\from.env.variables\" %*\r"+ "\n@ENDLOCAL\r\n") + t.equal(fs.readFileSync(to + '.ps1', 'utf8'), + '#!/usr/bin/env pwsh'+ + '\n$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent'+ + '\n'+ + '\n$exe=""'+ + '\nif ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {'+ + '\n # Fix case when both the Windows and Linux builds of Node'+ + '\n # are installed in the same directory'+ + '\n $exe=".exe"'+ + '\n}'+ + '\n$ret=0'+ + '\nif (Test-Path "$basedir/node$exe") {'+ + '\n & "$basedir/node$exe" "$basedir/from.env.variables" $args'+ + '\n $ret=$LASTEXITCODE'+ + '\n} else {'+ + '\n & "node$exe" "$basedir/from.env.variables" $args'+ + '\n $ret=$LASTEXITCODE'+ + '\n}'+ + '\nexit $ret'+ + '\n') t.end() }) }) @@ -188,6 +241,27 @@ test('explicit shebang', function (t) { "\n\"%_prog%\" \"%~dp0\\from.sh\" %*\r" + "\n@ENDLOCAL\r" + "\n") + + t.equal(fs.readFileSync(to + '.ps1', 'utf8'), + '#!/usr/bin/env pwsh'+ + '\n$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent'+ + '\n'+ + '\n$exe=""'+ + '\nif ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {'+ + '\n # Fix case when both the Windows and Linux builds of Node'+ + '\n # are installed in the same directory'+ + '\n $exe=".exe"'+ + '\n}'+ + '\n$ret=0'+ + '\nif (Test-Path "$basedir//usr/bin/sh$exe") {'+ + '\n & "$basedir//usr/bin/sh$exe" "$basedir/from.sh" $args'+ + '\n $ret=$LASTEXITCODE'+ + '\n} else {'+ + '\n & "/usr/bin/sh$exe" "$basedir/from.sh" $args'+ + '\n $ret=$LASTEXITCODE'+ + '\n}'+ + '\nexit $ret'+ + '\n') t.end() }) }) @@ -230,6 +304,27 @@ test('explicit shebang with args', function (t) { "\n\"%_prog%\" -x \"%~dp0\\from.sh.args\" %*\r" + "\n@ENDLOCAL\r" + "\n") + + t.equal(fs.readFileSync(to + '.ps1', 'utf8'), + '#!/usr/bin/env pwsh'+ + '\n$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent'+ + '\n'+ + '\n$exe=""'+ + '\nif ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {'+ + '\n # Fix case when both the Windows and Linux builds of Node'+ + '\n # are installed in the same directory'+ + '\n $exe=".exe"'+ + '\n}'+ + '\n$ret=0'+ + '\nif (Test-Path "$basedir//usr/bin/sh$exe") {'+ + '\n & "$basedir//usr/bin/sh$exe" -x "$basedir/from.sh.args" $args'+ + '\n $ret=$LASTEXITCODE'+ + '\n} else {'+ + '\n & "/usr/bin/sh$exe" -x "$basedir/from.sh.args" $args'+ + '\n $ret=$LASTEXITCODE'+ + '\n}'+ + '\nexit $ret'+ + '\n') t.end() }) })