Skip to content

Commit 9f2babb

Browse files
committed
Add promise-fork util
1 parent a1fec7e commit 9f2babb

File tree

2 files changed

+96
-10
lines changed

2 files changed

+96
-10
lines changed

src/utils/promise-fork.ts

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
// An implementation of https://socket.dev/npm/package/@npmcli/promise-spawn/overview/8.0.2
2+
// that uses child_process.fork instead of child_process.spawn.
3+
// ISC License
4+
// Copyright (c) npm, Inc.
5+
import { fork as builtinFork } from 'node:child_process'
6+
7+
import type { ForkOptions as BuiltinForkOptions } from 'node:child_process'
8+
9+
type BuiltinForkResult = ReturnType<typeof builtinFork>
10+
11+
export type ForkOptions = {
12+
cwd?: string
13+
encoding?: BufferEncoding
14+
stdioString?: boolean
15+
} & BuiltinForkOptions
16+
17+
export type ForkResult<Output, Extra> = Promise<
18+
{
19+
cmd: string
20+
args: string[]
21+
code: number
22+
signal: NodeJS.Signals | null
23+
stdout: Output
24+
stderr: Output
25+
} & Extra
26+
> & { process: BuiltinForkResult; stdio: BuiltinForkResult['stdio'] }
27+
28+
export function fork<O extends ForkOptions>(
29+
cmd: string,
30+
args: string[],
31+
opts?: O,
32+
extra?: Record<any, any>
33+
): ForkResult<
34+
O extends { stdioString: false } ? Buffer : string,
35+
typeof extra
36+
> {
37+
const { encoding = 'utf8', ...builtinForkOptions } = {
38+
__proto__: null,
39+
...opts
40+
}
41+
42+
let resolve: ((value: any) => void) | undefined
43+
let reject: ((reason?: any) => void) | undefined
44+
const promise = new Promise((res, rej) => {
45+
resolve = res
46+
reject = rej
47+
}) as ForkResult<
48+
O extends { stdioString: false } ? Buffer : string,
49+
typeof extra
50+
>
51+
52+
const closeError = new Error('command failed')
53+
const stdout: Buffer[] = []
54+
const stderr: Buffer[] = []
55+
56+
const getResult = (result: Record<string, any>) => ({
57+
cmd,
58+
args,
59+
...result,
60+
stdout: Buffer.concat(stdout).toString(encoding),
61+
stderr: Buffer.concat(stderr).toString(encoding),
62+
...extra
63+
})
64+
65+
const rejectWithOpts = (er: Error, erOpts: Record<string, any>) => {
66+
const resultError = getResult(erOpts)
67+
reject?.(Object.assign(er, resultError))
68+
}
69+
70+
const proc = builtinFork(cmd, args, builtinForkOptions)
71+
.on('error', error => rejectWithOpts(error, {}))
72+
.on('close', (code, signal) => {
73+
if (code !== 0 || signal) {
74+
rejectWithOpts(closeError, { code, signal })
75+
} else {
76+
resolve?.(getResult({ code, signal }))
77+
}
78+
})
79+
proc.stdout
80+
?.on('data', chunk => stdout.push(chunk))
81+
.on('error', error => rejectWithOpts(error, {}))
82+
proc.stderr
83+
?.on('data', chunk => stderr.push(chunk))
84+
.on('error', error => rejectWithOpts(error, {}))
85+
;(promise as any).stdin = proc.stdin
86+
;(promise as any).process = proc
87+
return promise
88+
}

src/utils/shadow-npm.ts

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,23 @@
11
import path from 'node:path'
22

3-
import spawn from '@npmcli/promise-spawn'
4-
3+
import { fork } from './promise-fork'
54
import constants from '../constants'
65

6+
import type { ForkOptions, ForkResult } from './promise-fork'
7+
78
const { abortSignal } = constants
89

9-
type ShadowNpmInstallOptions = Exclude<
10-
Parameters<typeof spawn>[2],
11-
undefined
12-
> & {
10+
type ShadowNpmInstallOptions = ForkOptions & {
1311
flags?: string[]
1412
}
1513

16-
export async function shadowNpmInstall(
14+
export function shadowNpmInstall<O extends ShadowNpmInstallOptions>(
1715
opts?: ShadowNpmInstallOptions
18-
): Promise<Awaited<ReturnType<typeof spawn>>> {
16+
) {
1917
const { flags = [], ...spawnOptions } = { __proto__: null, ...opts }
2018
// Lazily access constants.ENV.
2119
const { SOCKET_CLI_DEBUG } = constants.ENV
22-
return await spawn(
20+
return fork(
2321
// Lazily access constants.execPath.
2422
constants.execPath,
2523
[
@@ -44,5 +42,5 @@ export async function shadowNpmInstall(
4442
...spawnOptions.env
4543
}
4644
}
47-
)
45+
) as ForkResult<O extends { stdioString: false } ? Buffer : string, undefined>
4846
}

0 commit comments

Comments
 (0)