Skip to content

Commit

Permalink
Add proper support for PowerShell
Browse files Browse the repository at this point in the history
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: #34
Credit: @ExE-Boss
Close: #34
Reviewed-by: @isaacs
  • Loading branch information
ExE-Boss authored and isaacs committed Aug 12, 2019
1 parent 49f0c13 commit 51a8af3
Show file tree
Hide file tree
Showing 2 changed files with 146 additions and 1 deletion.
52 changes: 51 additions & 1 deletion index.js
Expand Up @@ -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 + "\""
}
Expand Down Expand Up @@ -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 () {
Expand All @@ -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) {
Expand Down
95 changes: 95 additions & 0 deletions test/basic.js
Expand Up @@ -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()
})
})
Expand Down Expand Up @@ -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()
})
})
Expand Down Expand Up @@ -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()
})
})
Expand Down Expand Up @@ -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()
})
})
Expand Down Expand Up @@ -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()
})
})

0 comments on commit 51a8af3

Please sign in to comment.