From b255c51134b5e6988b33ce6fb3e8bb39b9cfd2d4 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. --- index.js | 52 ++++++++++++++++++++++++++- test/basic.js | 99 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 150 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index 9f22e10..8e5d98b 100644 --- a/index.js +++ b/index.js @@ -77,17 +77,21 @@ function writeShim_ (from, to, prog, args, cb) { , longProg , shProg = prog && prog.split("\\").join("/") , shLongProg + , pwshProg = shProg && "\"" + shProg + "$exe\"" + , pwshLongProg shTarget = shTarget.split("\\").join("/") args = args || "" 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 + "\"" } @@ -153,7 +157,52 @@ function writeShim_ (from, to, prog, args, 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 () { @@ -165,6 +214,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 0982315..6812d2a 100755 --- a/test/basic.js +++ b/test/basic.js @@ -16,6 +16,19 @@ test('no shebang', function (t) { "\"$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() }) }) @@ -28,6 +41,7 @@ test('env shebang', function (t) { throw er console.error('%j', fs.readFileSync(to, 'utf8')) console.error('%j', fs.readFileSync(to + '.cmd', 'utf8')) + console.error('%j', fs.readFileSync(to + '.ps1', 'utf8')) t.equal(fs.readFileSync(to, 'utf8'), "#!/bin/sh"+ @@ -54,6 +68,26 @@ test('env shebang', function (t) { "\n @SET PATHEXT=%PATHEXT:;.JS;=;%\r"+ "\n node \"%~dp0\\from.env\" %*\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" $args'+ + '\n $ret=$LASTEXITCODE'+ + '\n} else {'+ + '\n & "node$exe" "$basedir/from.env" $args'+ + '\n $ret=$LASTEXITCODE'+ + '\n}'+ + '\nexit $ret'+ + '\n') t.end() }) }) @@ -66,6 +100,7 @@ test('env shebang with args', function (t) { throw er console.error('%j', fs.readFileSync(to, 'utf8')) console.error('%j', fs.readFileSync(to + '.cmd', 'utf8')) + console.error('%j', fs.readFileSync(to + '.ps1', 'utf8')) t.equal(fs.readFileSync(to, 'utf8'), "#!/bin/sh"+ @@ -92,6 +127,26 @@ test('env shebang with args', function (t) { "\n @SET PATHEXT=%PATHEXT:;.JS;=;%\r"+ "\n node --expose_gc \"%~dp0\\from.env.args\" %*\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() }) }) @@ -104,6 +159,7 @@ test('explicit shebang', function (t) { throw er console.error('%j', fs.readFileSync(to, 'utf8')) console.error('%j', fs.readFileSync(to + '.cmd', 'utf8')) + console.error('%j', fs.readFileSync(to + '.ps1', 'utf8')) t.equal(fs.readFileSync(to, 'utf8'), "#!/bin/sh" + @@ -131,6 +187,27 @@ test('explicit shebang', function (t) { "\n @SET PATHEXT=%PATHEXT:;.JS;=;%\r"+ "\n /usr/bin/sh \"%~dp0\\from.sh\" %*\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() }) }) @@ -143,6 +220,7 @@ test('explicit shebang with args', function (t) { throw er console.error('%j', fs.readFileSync(to, 'utf8')) console.error('%j', fs.readFileSync(to + '.cmd', 'utf8')) + console.error('%j', fs.readFileSync(to + '.ps1', 'utf8')) t.equal(fs.readFileSync(to, 'utf8'), "#!/bin/sh" + @@ -170,6 +248,27 @@ test('explicit shebang with args', function (t) { "\n @SET PATHEXT=%PATHEXT:;.JS;=;%\r"+ "\n /usr/bin/sh -x \"%~dp0\\from.sh.args\" %*\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() }) })