Skip to content

Commit

Permalink
feat: let cwdHook be configurable (#765)
Browse files Browse the repository at this point in the history
* feat: let `cwdHook` be configurable

* test: add `$.cwdHook` test

* feat: disable $.cwdHook by default

BREAKING CHANGE: affects legacy cd() flow

* test: fix pkg.test.js cd()

* chore: rename `setupSmth` helpers to `useSmth`
  • Loading branch information
antongolub committed Apr 2, 2024
1 parent b75c71a commit d79a638
Show file tree
Hide file tree
Showing 7 changed files with 35 additions and 18 deletions.
22 changes: 12 additions & 10 deletions src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

import assert from 'node:assert'
import { spawn, spawnSync, StdioNull, StdioPipe } from 'node:child_process'
import { AsyncLocalStorage, createHook } from 'node:async_hooks'
import { AsyncHook, AsyncLocalStorage, createHook } from 'node:async_hooks'
import { Readable, Writable } from 'node:stream'
import { inspect } from 'node:util'
import {
Expand All @@ -38,6 +38,7 @@ import {
parseDuration,
quote,
quotePowerShell,
noquote,
} from './util.js'

export interface Shell {
Expand Down Expand Up @@ -74,14 +75,18 @@ export interface Options {
}

const storage = new AsyncLocalStorage<Options>()
const hook = createHook({
const cwdSyncHook: AsyncHook & { enabled?: boolean } = createHook({
init: syncCwd,
before: syncCwd,
promiseResolve: syncCwd,
after: syncCwd,
destroy: syncCwd,
})
hook.enable()

export function syncProcessCwd(flag: boolean = true) {
if (flag) cwdSyncHook.enable()
else cwdSyncHook.disable()
}

export const defaults: Options = {
[processCwd]: process.cwd(),
Expand All @@ -94,24 +99,22 @@ export const defaults: Options = {
quiet: false,
prefix: '',
postfix: '',
quote: () => {
throw new Error('No quote function is defined: https://ï.at/no-quote-func')
},
quote: noquote,
spawn,
spawnSync,
log,
kill,
}
const isWin = process.platform == 'win32'

export function setupPowerShell() {
export function usePowerShell() {
$.shell = which.sync('powershell.exe')
$.prefix = ''
$.postfix = '; exit $LastExitCode'
$.quote = quotePowerShell
}

export function setupBash() {
export function useBash() {
$.shell = which.sync('bash')
$.prefix = 'set -euo pipefail;'
$.quote = quote
Expand Down Expand Up @@ -184,9 +187,8 @@ export const $: Shell & Options = new Proxy<Shell & Options>(
},
}
)

try {
setupBash()
useBash()
} catch (err) {}

type Resolve = (out: ProcessOutput) => void
Expand Down
3 changes: 2 additions & 1 deletion src/globals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ declare global {
var quote: typeof _.quote
var quotePowerShell: typeof _.quotePowerShell
var retry: typeof _.retry
var setupPowerShell: typeof _.setupPowerShell
var usePowerShell: typeof _.usePowerShell
var useBash: typeof _.useBash
var sleep: typeof _.sleep
var spinner: typeof _.spinner
var stdin: typeof _.stdin
Expand Down
4 changes: 4 additions & 0 deletions src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ export function normalizeMultilinePieces(
)
}

export function noquote(): string {
throw new Error('No quote function is defined: https://ï.at/no-quote-func')
}

export function quote(arg: string) {
if (/^[a-z0-9/_.\-@:=]+$/i.test(arg) || arg === '') {
return arg
Expand Down
10 changes: 6 additions & 4 deletions test/core.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -263,25 +263,26 @@ describe('core', () => {
}
})

test('cd() does affect parallel contexts', async () => {
test('cd() does not affect parallel contexts ($.cwdSyncHook enabled)', async () => {
syncProcessCwd()
const cwd = process.cwd()
try {
fs.mkdirpSync('/tmp/zx-cd-parallel/one/two')
await Promise.all([
within(async () => {
assert.equal(process.cwd(), cwd)
await sleep(1)
cd('/tmp/zx-cd-parallel/one')
await sleep(Math.random() * 15)
assert.ok(process.cwd().endsWith('/tmp/zx-cd-parallel/one'))
}),
within(async () => {
assert.equal(process.cwd(), cwd)
await sleep(2)
await sleep(Math.random() * 15)
assert.equal(process.cwd(), cwd)
}),
within(async () => {
assert.equal(process.cwd(), cwd)
await sleep(3)
await sleep(Math.random() * 15)
$.cwd = '/tmp/zx-cd-parallel/one/two'
assert.equal(process.cwd(), cwd)
assert.ok(
Expand All @@ -297,6 +298,7 @@ describe('core', () => {
} finally {
fs.rmSync('/tmp/zx-cd-parallel', { recursive: true })
cd(cwd)
syncProcessCwd(false)
}
})

Expand Down
6 changes: 6 additions & 0 deletions test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ import {
$,
log,
cd,
syncProcessCwd,
usePowerShell,
useBash,
kill,
ProcessOutput,
ProcessPromise,
Expand Down Expand Up @@ -60,10 +63,13 @@ describe('index', () => {
assert(ProcessOutput)
assert(ProcessPromise)
assert(cd)
assert(syncProcessCwd)
assert(log)
assert(kill)
assert(defaults)
assert(within)
assert(usePowerShell)
assert(useBash)

// goods
assert(argv)
Expand Down
4 changes: 3 additions & 1 deletion test/package.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@
// limitations under the License.

import assert from 'node:assert'
import { test, describe, beforeEach } from 'node:test'
import { test, describe, beforeEach, before, after } from 'node:test'
import '../build/globals.js'

describe('package', () => {
before(() => syncProcessCwd())
after(() => syncProcessCwd(false))
beforeEach(async () => {
const pack = await $`npm pack`
await $`tar xf ${pack}`
Expand Down
4 changes: 2 additions & 2 deletions test/smoke/win32.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ _describe('win32', () => {
assert.match(p.stdout, /bash/)

await within(async () => {
setupPowerShell()
usePowerShell()
assert.match($.shell, /powershell/i)
const p = await $`get-host`
assert.match(p.stdout, /PowerShell/)
Expand All @@ -33,7 +33,7 @@ _describe('win32', () => {

test('quotePowerShell works', async () => {
await within(async () => {
setupPowerShell()
usePowerShell()
const p = await $`echo ${`Windows 'rulez!'`}`
assert.match(p.stdout, /Windows 'rulez!'/)
})
Expand Down

0 comments on commit d79a638

Please sign in to comment.