Commit 0c6c4e5

Anton Medvedev <anton@medv.io>
2022-10-04 23:19:03
Switch to powershell.exe
1 parent cfa0fba
src/core.ts
@@ -29,6 +29,7 @@ import {
   parseDuration,
   psTree,
   quote,
+  quotePowerShell,
 } from './util.js'
 
 export type Shell = (
@@ -75,7 +76,8 @@ export const defaults: Options = {
 
 try {
   if (process.platform == 'win32') {
-    defaults.shell = true
+    defaults.shell = which.sync('powershell.exe')
+    defaults.quote = quotePowerShell
   } else {
     defaults.shell = which.sync('bash')
     defaults.prefix = 'set -euo pipefail;'
src/util.ts
@@ -47,6 +47,20 @@ export function quote(arg: string) {
   )
 }
 
+export function quoteWindows(arg: string) {
+  if (/^[a-z0-9/_.\-@:=]+$/i.test(arg) || arg === '') {
+    return arg
+  }
+  return `"` + arg.replace(/"/g, '\\"').replace(/\n/g, '^\n') + `"`
+}
+
+export function quotePowerShell(arg: string) {
+  if (/^[a-z0-9/_.\-]+$/i.test(arg) || arg === '') {
+    return arg
+  }
+  return `'` + arg.replace(/'/g, "''") + `'`
+}
+
 export function exitCodeInfo(exitCode: number | null): string | undefined {
   return {
     2: 'Misuse of shell builtins',
test/util.test.js
@@ -22,6 +22,7 @@ import {
   noop,
   parseDuration,
   quote,
+  quotePowerShell,
   randomId,
 } from '../build/util.js'
 
@@ -58,6 +59,11 @@ test('quote()', () => {
   assert.ok(quote(`'\f\n\r\t\v\0`) === `$'\\'\\f\\n\\r\\t\\v\\0'`)
 })
 
+test('quotePowerShgell()', () => {
+  assert.is(quotePowerShell('string'), 'string')
+  assert.is(quotePowerShell(`'`), `''`)
+})
+
 test('duration parsing works', () => {
   assert.is(parseDuration(1000), 1000)
   assert.is(parseDuration('2s'), 2000)
test/win32.test.js
@@ -25,6 +25,11 @@ if (process.platform === 'win32') {
     const p = await $`ver`
     assert.match(p.stdout, /Windows/)
   })
+
+  test('quotePowerShell works', async () => {
+    const p = await $`echo ${`Windows 'rulez!'`}`
+    assert.match(p.stdout, /Windows 'rulez!'/)
+  })
 }
 
 test.run()