Skip to content

Commit

Permalink
feat: let $.preferLocal accept dir (#886)
Browse files Browse the repository at this point in the history
* feat: provide `$.killSignal` option

* feat: let `$.preferLocal` accept directories
  • Loading branch information
antongolub authored Sep 8, 2024
1 parent 76463de commit fbf2ddf
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 24 deletions.
29 changes: 18 additions & 11 deletions src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ import {
isString,
noop,
parseDuration,
preferNmBin,
preferLocalBin,
quote,
quotePowerShell,
} from './util.js'
Expand All @@ -70,7 +70,7 @@ export interface Options {
signal?: AbortSignal
input?: string | Buffer | Readable | ProcessOutput | ProcessPromise
timeout?: Duration
timeoutSignal?: string
timeoutSignal?: NodeJS.Signals
stdio: StdioOptions
verbose: boolean
sync: boolean
Expand All @@ -82,12 +82,13 @@ export interface Options {
quote?: typeof quote
quiet: boolean
detached: boolean
preferLocal: boolean
preferLocal: boolean | string | string[]
spawn: typeof spawn
spawnSync: typeof spawnSync
store?: TSpawnStore
log: typeof log
kill: typeof kill
killSignal?: NodeJS.Signals
}

const storage = new AsyncLocalStorage<Options>()
Expand Down Expand Up @@ -125,6 +126,8 @@ export const defaults: Options = {
spawnSync,
log,
kill,
killSignal: 'SIGTERM',
timeoutSignal: 'SIGTERM',
}

export function usePowerShell() {
Expand Down Expand Up @@ -168,9 +171,9 @@ export const $: Shell & Options = new Proxy<Shell & Options>(
if (!Array.isArray(pieces)) {
return function (this: any, ...args: any) {
const self = this
return within(() => {
return Object.assign($, snapshot, pieces).apply(self, args)
})
return within(() =>
Object.assign($, snapshot, pieces).apply(self, args)
)
}
}
const from = getCallerLocation()
Expand Down Expand Up @@ -237,7 +240,7 @@ export class ProcessPromise extends Promise<ProcessOutput> {
private _quiet?: boolean
private _verbose?: boolean
private _timeout?: number
private _timeoutSignal = 'SIGTERM'
private _timeoutSignal = $.timeoutSignal
private _resolved = false
private _halted = false
private _piped = false
Expand Down Expand Up @@ -270,7 +273,11 @@ export class ProcessPromise extends Promise<ProcessOutput> {

if (input) this.stdio('pipe')
if ($.timeout) this.timeout($.timeout, $.timeoutSignal)
if ($.preferLocal) $.env = preferNmBin($.env, $.cwd, $[processCwd])
if ($.preferLocal) {
const dirs =
$.preferLocal === true ? [$.cwd, $[processCwd]] : [$.preferLocal].flat()
$.env = preferLocalBin($.env, ...dirs)
}

$.log({
kind: 'cmd',
Expand Down Expand Up @@ -486,7 +493,7 @@ export class ProcessPromise extends Promise<ProcessOutput> {
return this._snapshot.signal || this._snapshot.ac?.signal
}

async kill(signal = 'SIGTERM'): Promise<void> {
async kill(signal = $.killSignal): Promise<void> {
if (!this.child)
throw new Error('Trying to kill a process without creating one.')
if (!this.child.pid) throw new Error('The process pid is undefined.')
Expand Down Expand Up @@ -530,7 +537,7 @@ export class ProcessPromise extends Promise<ProcessOutput> {
return this._nothrow ?? this._snapshot.nothrow
}

timeout(d: Duration, signal = 'SIGTERM'): ProcessPromise {
timeout(d: Duration, signal = $.timeoutSignal): ProcessPromise {
this._timeout = parseDuration(d)
this._timeoutSignal = signal
return this
Expand Down Expand Up @@ -686,7 +693,7 @@ export function cd(dir: string | ProcessOutput) {
$[processCwd] = process.cwd()
}

export async function kill(pid: number, signal?: string) {
export async function kill(pid: number, signal = $.killSignal) {
let children = await ps.tree({ pid, recursive: true })
for (const p of children) {
try {
Expand Down
5 changes: 2 additions & 3 deletions src/goods.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,13 +182,12 @@ export async function spinner<T>(
return within(async () => {
$.verbose = false
const id = setInterval(spin, 100)
let result: T

try {
result = await callback!()
return await callback!()
} finally {
clearInterval(id as NodeJS.Timeout)
process.stderr.write(' '.repeat((process.stdout.columns || 1) - 1) + '\r')
}
return result
})
}
11 changes: 9 additions & 2 deletions src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export function isString(obj: any) {

const pad = (v: string) => (v === ' ' ? ' ' : '')

export function preferNmBin(
export function preferLocalBin(
env: NodeJS.ProcessEnv,
...dirs: (string | undefined)[]
) {
Expand All @@ -58,7 +58,14 @@ export function preferNmBin(
.find((key) => key.toUpperCase() === 'PATH') || 'Path'
: 'PATH'
const pathValue = dirs
.map((c) => c && path.resolve(c as string, 'node_modules', '.bin'))
.map(
(c) =>
c && [
path.resolve(c as string, 'node_modules', '.bin'),
path.resolve(c as string),
]
)
.flat()
.concat(env[pathKey])
.filter(Boolean)
.join(path.delimiter)
Expand Down
20 changes: 16 additions & 4 deletions test/core.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -220,10 +220,22 @@ describe('core', () => {
})

test('`preferLocal` preserves env', async () => {
const path = await $({
preferLocal: true,
})`echo $PATH`
assert(path.stdout.startsWith(`${process.cwd()}/node_modules/.bin:`))
const cases = [
[true, `${process.cwd()}/node_modules/.bin:${process.cwd()}:`],
['/foo', `/foo/node_modules/.bin:/foo:`],
[
['/bar', '/baz'],
`/bar/node_modules/.bin:/bar:/baz/node_modules/.bin:/baz`,
],
]

for (const [preferLocal, expected] of cases) {
const path = await $({
preferLocal,
env: { PATH: process.env.PATH },
})`echo $PATH`
assert(path.stdout.startsWith(expected))
}
})

test('supports custom intermediate store', async () => {
Expand Down
11 changes: 7 additions & 4 deletions test/util.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import {
getCallerLocationFromString,
tempdir,
tempfile,
preferNmBin,
preferLocalBin,
} from '../build/util.js'

describe('util', () => {
Expand Down Expand Up @@ -169,10 +169,13 @@ test('tempfile() creates temporary files', () => {
assert.equal(fs.readFileSync(tf, 'utf-8'), 'bar')
})

test('preferNmBin()', () => {
test('preferLocalBin()', () => {
const env = {
PATH: '/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/usr/local/sbin',
}
const _env = preferNmBin(env, process.cwd())
assert.equal(_env.PATH, `${process.cwd()}/node_modules/.bin:${env.PATH}`)
const _env = preferLocalBin(env, process.cwd())
assert.equal(
_env.PATH,
`${process.cwd()}/node_modules/.bin:${process.cwd()}:${env.PATH}`
)
})

0 comments on commit fbf2ddf

Please sign in to comment.