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 1/3] 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() }) }) From 70c74dd0f904610e7be852486acf38b63b74a698 Mon Sep 17 00:00:00 2001 From: ExE Boss <3889017+ExE-Boss@users.noreply.github.com> Date: Thu, 24 May 2018 11:11:11 +0200 Subject: [PATCH 2/3] Make Node 6 the minimum supported version, same as pnpm@2 --- .travis.yml | 1 - appveyor.yml | 1 - package.json | 2 +- 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index c720c99..4e847ee 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,5 @@ language: node_js node_js: - - 4 - 6 - 7 before_install: diff --git a/appveyor.yml b/appveyor.yml index 57e6953..05781fb 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,6 +1,5 @@ environment: matrix: - - nodejs_version: 4 - nodejs_version: 6 - nodejs_version: 7 install: diff --git a/package.json b/package.json index d249ea5..f2962ab 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "tape-promise": "^2.0.0" }, "engines": { - "node": ">=4" + "node": ">=6" }, "mos": { "plugins": [ From b4075311a503ffcd89d80066c028fbdb351f0af6 Mon Sep 17 00:00:00 2001 From: ExE Boss <3889017+ExE-Boss@users.noreply.github.com> Date: Mon, 9 Jul 2018 01:40:00 +0200 Subject: [PATCH 3/3] Add option to allow disabling PowerShell shim script generation --- index.js | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/index.js b/index.js index eda5e26..93a1cab 100644 --- a/index.js +++ b/index.js @@ -42,8 +42,12 @@ function cmdShim (src, to, opts) { } function cmdShim_ (src, to, opts) { - return Promise.all([rm(to), opts.createCmdFile && rm(`${to}.cmd`)]) - .then(() => writeShim(src, to, opts)) + return Promise.all([ + rm(to), + rm(`${to}.ps1`), + opts.createCmdFile && rm(`${to}.cmd`) + ]) + .then(() => writeShim(src, to, opts)) } function writeShim (src, to, opts) { @@ -70,6 +74,8 @@ function writeShim (src, to, opts) { function writeShim_ (src, to, opts) { opts = opts || {} + // Create PowerShell file by default if the option hasn't been specified + opts.createPwshFile = (typeof opts.createPwshFile !== 'undefined' ? opts.createPwshFile : true) let shTarget = path.relative(path.dirname(to), src) let target = shTarget.split('/').join('\\') let longProg @@ -205,16 +211,16 @@ function writeShim_ (src, to, opts) { return Promise.all([ opts.createCmdFile && fs.writeFile(to + '.cmd', cmd, 'utf8'), - fs.writeFile(`${to}.ps1`, pwsh, 'utf8'), + opts.createPwshFile && fs.writeFile(`${to}.ps1`, pwsh, 'utf8'), fs.writeFile(to, sh, 'utf8') ]) - .then(() => chmodShim(to, opts.createCmdFile)) + .then(() => chmodShim(to, opts)) } -function chmodShim (to, createCmdFile) { +function chmodShim (to, {createCmdFile, createPwshFile}) { return Promise.all([ fs.chmod(to, 0o755), - fs.chmod(`${to}.ps1`, 0o755), + createPwshFile && fs.chmod(`${to}.ps1`, 0o755), createCmdFile && fs.chmod(`${to}.cmd`, 0o755) ]) }