Skip to content

Commit 13f5517

Browse files
ExE-Bosszkochan
authored andcommitted
feat: add proper support for PowerShell
* Add proper support for PowerShell 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. * Add option to allow disabling PowerShell shim script generation BREAKING CHANGE: Make Node 6 the minimum supported version, same as pnpm@2 PR #5
1 parent 006f380 commit 13f5517

File tree

5 files changed

+173
-7
lines changed

5 files changed

+173
-7
lines changed

.travis.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
language: node_js
22
node_js:
3-
- 4
43
- 6
54
- 7
65
before_install:

appveyor.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
environment:
22
matrix:
3-
- nodejs_version: 4
43
- nodejs_version: 6
54
- nodejs_version: 7
65
install:

index.js

Lines changed: 60 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,12 @@ function cmdShim (src, to, opts) {
4242
}
4343

4444
function cmdShim_ (src, to, opts) {
45-
return Promise.all([rm(to), opts.createCmdFile && rm(`${to}.cmd`)])
46-
.then(() => writeShim(src, to, opts))
45+
return Promise.all([
46+
rm(to),
47+
rm(`${to}.ps1`),
48+
opts.createCmdFile && rm(`${to}.cmd`)
49+
])
50+
.then(() => writeShim(src, to, opts))
4751
}
4852

4953
function writeShim (src, to, opts) {
@@ -70,23 +74,29 @@ function writeShim (src, to, opts) {
7074

7175
function writeShim_ (src, to, opts) {
7276
opts = opts || {}
77+
// Create PowerShell file by default if the option hasn't been specified
78+
opts.createPwshFile = (typeof opts.createPwshFile !== 'undefined' ? opts.createPwshFile : true)
7379
let shTarget = path.relative(path.dirname(to), src)
7480
let target = shTarget.split('/').join('\\')
7581
let longProg
7682
let prog = opts.prog
7783
let shProg = prog && prog.split('\\').join('/')
7884
let shLongProg
85+
let pwshProg = shProg && `"${shProg}$exe"`
86+
let pwshLongProg
7987
shTarget = shTarget.split('\\').join('/')
8088
let args = opts.args || ''
8189
if (!prog) {
8290
prog = `"%~dp0\\${target}"`
8391
shProg = `"$basedir/${shTarget}"`
92+
pwshProg = shProg
8493
args = ''
8594
target = ''
8695
shTarget = ''
8796
} else {
8897
longProg = `"%~dp0\\${prog}.exe"`
8998
shLongProg = '"$basedir/' + prog + '"'
99+
pwshLongProg = `"$basedir/${prog}$exe"`
90100
target = `"%~dp0\\${target}"`
91101
shTarget = `"$basedir/${shTarget}"`
92102
}
@@ -155,16 +165,62 @@ function writeShim_ (src, to, opts) {
155165
'exit $?\n'
156166
}
157167

168+
// #!/usr/bin/env pwsh
169+
// $basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
170+
//
171+
// $ret=0
172+
// $exe = ""
173+
// if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
174+
// # Fix case when both the Windows and Linux builds of Node
175+
// # are installed in the same directory
176+
// $exe = ".exe"
177+
// }
178+
// if (Test-Path "$basedir/node") {
179+
// & "$basedir/node$exe" "$basedir/node_modules/npm/bin/npm-cli.js" $args
180+
// $ret=$LASTEXITCODE
181+
// } else {
182+
// & "node$exe" "$basedir/node_modules/npm/bin/npm-cli.js" $args
183+
// $ret=$LASTEXITCODE
184+
// }
185+
// exit $ret
186+
let pwsh = '#!/usr/bin/env pwsh\n' +
187+
'$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent\n' +
188+
'\n' +
189+
'$exe=""\n' +
190+
'if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {\n' +
191+
' # Fix case when both the Windows and Linux builds of Node\n' +
192+
' # are installed in the same directory\n' +
193+
' $exe=".exe"\n' +
194+
'}\n'
195+
if (shLongProg) {
196+
pwsh = pwsh +
197+
'$ret=0\n' +
198+
`if (Test-Path ${pwshLongProg}) {\n` +
199+
` & ${pwshLongProg} ${args} ${shTarget} $args\n` +
200+
' $ret=$LASTEXITCODE\n' +
201+
'} else {\n' +
202+
` & ${pwshProg} ${args} ${shTarget} $args\n` +
203+
' $ret=$LASTEXITCODE\n' +
204+
'}\n' +
205+
'exit $ret\n'
206+
} else {
207+
pwsh = pwsh +
208+
`& ${pwshProg} ${args} ${shTarget} $args\n` +
209+
'exit $LASTEXITCODE\n'
210+
}
211+
158212
return Promise.all([
159213
opts.createCmdFile && fs.writeFile(to + '.cmd', cmd, 'utf8'),
214+
opts.createPwshFile && fs.writeFile(`${to}.ps1`, pwsh, 'utf8'),
160215
fs.writeFile(to, sh, 'utf8')
161216
])
162-
.then(() => chmodShim(to, opts.createCmdFile))
217+
.then(() => chmodShim(to, opts))
163218
}
164219

165-
function chmodShim (to, createCmdFile) {
220+
function chmodShim (to, {createCmdFile, createPwshFile}) {
166221
return Promise.all([
167222
fs.chmod(to, 0o755),
223+
createPwshFile && fs.chmod(`${to}.ps1`, 0o755),
168224
createCmdFile && fs.chmod(`${to}.cmd`, 0o755)
169225
])
170226
}

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
"tape-promise": "^2.0.0"
3535
},
3636
"engines": {
37-
"node": ">=4"
37+
"node": ">=6"
3838
},
3939
"mos": {
4040
"plugins": [

test/basic.js

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,19 @@ test('no cmd file', function (t) {
2323
'\n\n' +
2424
'"$basedir/src.exe" "$@"\nexit $?\n')
2525
t.throws(() => fs.readFileSync(to + '.cmd', 'utf8'), 'cmd file not created')
26+
t.equal(fs.readFileSync(`${to}.ps1`, 'utf8'),
27+
'#!/usr/bin/env pwsh' +
28+
'\n$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent' +
29+
'\n' +
30+
'\n$exe=""' +
31+
'\nif ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {' +
32+
'\n # Fix case when both the Windows and Linux builds of Node' +
33+
'\n # are installed in the same directory' +
34+
'\n $exe=".exe"' +
35+
'\n}' +
36+
'\n& "$basedir/src.exe" $args' +
37+
'\nexit $LASTEXITCODE' +
38+
'\n')
2639
})
2740
})
2841

@@ -42,6 +55,19 @@ test('no shebang', function (t) {
4255
'"$basedir/src.exe" "$@"\nexit $?\n')
4356
t.equal(fs.readFileSync(to + '.cmd', 'utf8'),
4457
'@"%~dp0\\src.exe" %*\r\n')
58+
t.equal(fs.readFileSync(`${to}.ps1`, 'utf8'),
59+
'#!/usr/bin/env pwsh' +
60+
'\n$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent' +
61+
'\n' +
62+
'\n$exe=""' +
63+
'\nif ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {' +
64+
'\n # Fix case when both the Windows and Linux builds of Node' +
65+
'\n # are installed in the same directory' +
66+
'\n $exe=".exe"' +
67+
'\n}' +
68+
'\n& "$basedir/src.exe" $args' +
69+
'\nexit $LASTEXITCODE' +
70+
'\n')
4571
})
4672
})
4773

@@ -52,6 +78,7 @@ test('env shebang', function (t) {
5278
.then(() => {
5379
console.error('%j', fs.readFileSync(to, 'utf8'))
5480
console.error('%j', fs.readFileSync(to + '.cmd', 'utf8'))
81+
console.error('%j', fs.readFileSync(`${to}.ps1`, 'utf8'))
5582

5683
t.equal(fs.readFileSync(to, 'utf8'),
5784
'#!/bin/sh' +
@@ -78,6 +105,26 @@ test('env shebang', function (t) {
78105
'\n @SET PATHEXT=%PATHEXT:;.JS;=;%\r' +
79106
'\n node "%~dp0\\src.env" %*\r' +
80107
'\n)')
108+
t.equal(fs.readFileSync(`${to}.ps1`, 'utf8'),
109+
'#!/usr/bin/env pwsh' +
110+
'\n$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent' +
111+
'\n' +
112+
'\n$exe=""' +
113+
'\nif ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {' +
114+
'\n # Fix case when both the Windows and Linux builds of Node' +
115+
'\n # are installed in the same directory' +
116+
'\n $exe=".exe"' +
117+
'\n}' +
118+
'\n$ret=0' +
119+
'\nif (Test-Path "$basedir/node$exe") {' +
120+
'\n & "$basedir/node$exe" "$basedir/src.env" $args' +
121+
'\n $ret=$LASTEXITCODE' +
122+
'\n} else {' +
123+
'\n & "node$exe" "$basedir/src.env" $args' +
124+
'\n $ret=$LASTEXITCODE' +
125+
'\n}' +
126+
'\nexit $ret' +
127+
'\n')
81128
t.end()
82129
})
83130
})
@@ -164,6 +211,7 @@ test('env shebang with args', function (t) {
164211
.then(() => {
165212
console.error('%j', fs.readFileSync(to, 'utf8'))
166213
console.error('%j', fs.readFileSync(to + '.cmd', 'utf8'))
214+
console.error('%j', fs.readFileSync(`${to}.ps1`, 'utf8'))
167215

168216
t.equal(fs.readFileSync(to, 'utf8'),
169217
'#!/bin/sh' +
@@ -190,6 +238,26 @@ test('env shebang with args', function (t) {
190238
'\n @SET PATHEXT=%PATHEXT:;.JS;=;%\r' +
191239
'\n node --expose_gc "%~dp0\\src.env.args" %*\r' +
192240
'\n)')
241+
t.equal(fs.readFileSync(`${to}.ps1`, 'utf8'),
242+
'#!/usr/bin/env pwsh' +
243+
'\n$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent' +
244+
'\n' +
245+
'\n$exe=""' +
246+
'\nif ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {' +
247+
'\n # Fix case when both the Windows and Linux builds of Node' +
248+
'\n # are installed in the same directory' +
249+
'\n $exe=".exe"' +
250+
'\n}' +
251+
'\n$ret=0' +
252+
'\nif (Test-Path "$basedir/node$exe") {' +
253+
'\n & "$basedir/node$exe" --expose_gc "$basedir/src.env.args" $args' +
254+
'\n $ret=$LASTEXITCODE' +
255+
'\n} else {' +
256+
'\n & "node$exe" --expose_gc "$basedir/src.env.args" $args' +
257+
'\n $ret=$LASTEXITCODE' +
258+
'\n}' +
259+
'\nexit $ret' +
260+
'\n')
193261
t.end()
194262
})
195263
})
@@ -201,6 +269,7 @@ test('explicit shebang', function (t) {
201269
.then(() => {
202270
console.error('%j', fs.readFileSync(to, 'utf8'))
203271
console.error('%j', fs.readFileSync(to + '.cmd', 'utf8'))
272+
console.error('%j', fs.readFileSync(`${to}.ps1`, 'utf8'))
204273

205274
t.equal(fs.readFileSync(to, 'utf8'),
206275
'#!/bin/sh' +
@@ -228,6 +297,27 @@ test('explicit shebang', function (t) {
228297
'\n @SET PATHEXT=%PATHEXT:;.JS;=;%\r' +
229298
'\n /usr/bin/sh "%~dp0\\src.sh" %*\r' +
230299
'\n)')
300+
301+
t.equal(fs.readFileSync(`${to}.ps1`, 'utf8'),
302+
'#!/usr/bin/env pwsh' +
303+
'\n$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent' +
304+
'\n' +
305+
'\n$exe=""' +
306+
'\nif ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {' +
307+
'\n # Fix case when both the Windows and Linux builds of Node' +
308+
'\n # are installed in the same directory' +
309+
'\n $exe=".exe"' +
310+
'\n}' +
311+
'\n$ret=0' +
312+
'\nif (Test-Path "$basedir//usr/bin/sh$exe") {' +
313+
'\n & "$basedir//usr/bin/sh$exe" "$basedir/src.sh" $args' +
314+
'\n $ret=$LASTEXITCODE' +
315+
'\n} else {' +
316+
'\n & "/usr/bin/sh$exe" "$basedir/src.sh" $args' +
317+
'\n $ret=$LASTEXITCODE' +
318+
'\n}' +
319+
'\nexit $ret' +
320+
'\n')
231321
t.end()
232322
})
233323
})
@@ -239,6 +329,7 @@ test('explicit shebang with args', function (t) {
239329
.then(() => {
240330
console.error('%j', fs.readFileSync(to, 'utf8'))
241331
console.error('%j', fs.readFileSync(to + '.cmd', 'utf8'))
332+
console.error('%j', fs.readFileSync(`${to}.ps1`, 'utf8'))
242333

243334
t.equal(fs.readFileSync(to, 'utf8'),
244335
'#!/bin/sh' +
@@ -266,6 +357,27 @@ test('explicit shebang with args', function (t) {
266357
'\n @SET PATHEXT=%PATHEXT:;.JS;=;%\r' +
267358
'\n /usr/bin/sh -x "%~dp0\\src.sh.args" %*\r' +
268359
'\n)')
360+
361+
t.equal(fs.readFileSync(`${to}.ps1`, 'utf8'),
362+
'#!/usr/bin/env pwsh' +
363+
'\n$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent' +
364+
'\n' +
365+
'\n$exe=""' +
366+
'\nif ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {' +
367+
'\n # Fix case when both the Windows and Linux builds of Node' +
368+
'\n # are installed in the same directory' +
369+
'\n $exe=".exe"' +
370+
'\n}' +
371+
'\n$ret=0' +
372+
'\nif (Test-Path "$basedir//usr/bin/sh$exe") {' +
373+
'\n & "$basedir//usr/bin/sh$exe" -x "$basedir/src.sh.args" $args' +
374+
'\n $ret=$LASTEXITCODE' +
375+
'\n} else {' +
376+
'\n & "/usr/bin/sh$exe" -x "$basedir/src.sh.args" $args' +
377+
'\n $ret=$LASTEXITCODE' +
378+
'\n}' +
379+
'\nexit $ret' +
380+
'\n')
269381
t.end()
270382
})
271383
})

0 commit comments

Comments
 (0)