diff --git a/.changeset/migrate-execa-to-tinyexec.md b/.changeset/migrate-execa-to-tinyexec.md new file mode 100644 index 0000000..f00d438 --- /dev/null +++ b/.changeset/migrate-execa-to-tinyexec.md @@ -0,0 +1,5 @@ +--- +'prool': patch +--- + +Migrated the internal process backend from `execa` to `tinyexec`. The public callback shape `($) => $\`cmd ${args}\`` is unchanged, including the callable form `$({ env })\`...\``. Removed 16 transitive dependencies. diff --git a/package.json b/package.json index 8faa7a1..882a2ef 100644 --- a/package.json +++ b/package.json @@ -53,10 +53,10 @@ "dependencies": { "change-case": "5.4.4", "eventemitter3": "^5.0.1", - "execa": "^9.1.0", "get-port": "^7.1.0", "http-proxy": "^1.18.1", - "tar": "7.2.0" + "tar": "7.2.0", + "tinyexec": "^1.1.2" }, "peerDependencies": { "@pimlico/alto": "*", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0373e3b..f44f3e9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,9 +14,6 @@ importers: eventemitter3: specifier: ^5.0.1 version: 5.0.1 - execa: - specifier: ^9.1.0 - version: 9.1.0 get-port: specifier: ^7.1.0 version: 7.1.0 @@ -26,6 +23,9 @@ importers: tar: specifier: 7.2.0 version: 7.2.0 + tinyexec: + specifier: ^1.1.2 + version: 1.1.2 devDependencies: '@biomejs/biome': specifier: ^2.3.8 @@ -860,9 +860,6 @@ packages: '@scure/bip39@1.5.4': resolution: {integrity: sha512-TFM4ni0vKvCfBpohoh+/lY05i9gRbSwXWngAsF4CABQxoaOHijxuaZ2R6cStDQ5CHtHO9aGJTr4ksVJASRRyMA==} - '@sec-ant/readable-stream@0.4.1': - resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==} - '@sentry-internal/tracing@7.116.0': resolution: {integrity: sha512-y5ppEmoOlfr77c/HqsEXR72092qmGYS4QE5gSz5UZFn9CiinEwGfEorcg2xIrrCuU7Ry/ZU2VLz9q3xd04drRA==} engines: {node: '>=8'} @@ -887,10 +884,6 @@ packages: resolution: {integrity: sha512-Vn9fcvwTq91wJvCd7WTMWozimqMi+dEZ3ie3EICELC2diONcN16ADFdzn65CQQbYwmUzRjN9EjDN2k41pKZWhQ==} engines: {node: '>=8'} - '@sindresorhus/merge-streams@4.0.0': - resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==} - engines: {node: '>=18'} - '@standard-schema/spec@1.0.0': resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} @@ -1389,10 +1382,6 @@ packages: resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} engines: {node: '>=0.8.x'} - execa@9.1.0: - resolution: {integrity: sha512-lSgHc4Elo2m6bUDhc3Hl/VxvUDJdQWI40RZ4KMY9bKRc+hgMOT7II/JjbNDhI8VnMtrCb7U/fhpJIkLORZozWw==} - engines: {node: '>=18'} - expect-type@1.3.0: resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} engines: {node: '>=12.0.0'} @@ -1453,10 +1442,6 @@ packages: picomatch: optional: true - figures@6.1.0: - resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==} - engines: {node: '>=18'} - fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} @@ -1517,10 +1502,6 @@ packages: resolution: {integrity: sha512-QB9NKEeDg3xxVwCCwJQ9+xycaz6pBB6iQ76wiWMl1927n0Kir6alPiP+yuiICLLU4jpMe08dXfpebuQppFA2zw==} engines: {node: '>=16'} - get-stream@9.0.1: - resolution: {integrity: sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==} - engines: {node: '>=18'} - glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} @@ -1559,10 +1540,6 @@ packages: resolution: {integrity: sha512-tsYlhAYpjCKa//8rXZ9DqKEawhPoSytweBC2eNvcaDK+57RZLHGqNs3PZTQO6yekLFSuvA6AlnAfrw1uBvtb+Q==} hasBin: true - human-signals@7.0.0: - resolution: {integrity: sha512-74kytxOUSvNbjrT9KisAbaTZ/eJwD/LrbM/kh5j0IhPuJzwuA19dWvniFGwBzN9rVjg+O/e+F310PjObDXS+9Q==} - engines: {node: '>=18.18.0'} - iconv-lite@0.7.0: resolution: {integrity: sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==} engines: {node: '>=0.10.0'} @@ -1613,26 +1590,14 @@ packages: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} - is-plain-obj@4.1.0: - resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} - engines: {node: '>=12'} - is-stream@2.0.1: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} - is-stream@4.0.1: - resolution: {integrity: sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==} - engines: {node: '>=18'} - is-subdir@1.2.0: resolution: {integrity: sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==} engines: {node: '>=4'} - is-unicode-supported@2.0.0: - resolution: {integrity: sha512-FRdAyx5lusK1iHG0TWpVtk9+1i+GjrzRffhDg4ovQ7mcidMQ6mj+MhKPmvh7Xwyv5gIS06ns49CA7Sqg7lC22Q==} - engines: {node: '>=18'} - is-windows@1.0.2: resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} engines: {node: '>=0.10.0'} @@ -1837,10 +1802,6 @@ packages: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} - npm-run-path@5.3.0: - resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - obug@2.1.1: resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} @@ -1891,10 +1852,6 @@ packages: package-manager-detector@0.2.11: resolution: {integrity: sha512-BEnLolu+yuz22S56CU1SUKq3XC3PkwD5wv4ikR4MfGvnRVcmzXR9DwSlW2fEamyTPyXHomBJRzgapeuBvRNzJQ==} - parse-ms@4.0.0: - resolution: {integrity: sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==} - engines: {node: '>=18'} - path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -1903,10 +1860,6 @@ packages: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} - path-key@4.0.0: - resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} - engines: {node: '>=12'} - path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} @@ -1973,10 +1926,6 @@ packages: engines: {node: '>=10.13.0'} hasBin: true - pretty-ms@9.0.0: - resolution: {integrity: sha512-E9e9HJ9R9NasGOgPaPE8VMeiPKAyWR5jcFpNnwIejslIhWqdqOrb2wShBsncMPUb+BcCd2OPYfh7p2W6oemTng==} - engines: {node: '>=18'} - process-nextick-args@2.0.1: resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} @@ -2236,10 +2185,6 @@ packages: resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} engines: {node: '>=4'} - strip-final-newline@4.0.0: - resolution: {integrity: sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==} - engines: {node: '>=18'} - strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} @@ -2291,8 +2236,8 @@ packages: tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} - tinyexec@1.0.2: - resolution: {integrity: sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==} + tinyexec@1.1.2: + resolution: {integrity: sha512-dAqSqE/RabpBKI8+h26GfLq6Vb3JVXs30XYQjdMjaj/c2tS8IYYMbIzP599KtRj7c57/wYApb3QjgRgXmrCukA==} engines: {node: '>=18'} tinyglobby@0.2.15: @@ -2525,10 +2470,6 @@ packages: resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} engines: {node: '>=12'} - yoctocolors@2.0.2: - resolution: {integrity: sha512-Ct97huExsu7cWeEjmrXlofevF8CvzUglJ4iGUet5B8xn1oumtAZBpHU4GzYuoE6PVqcZ5hghtBrSlhwHuR1Jmw==} - engines: {node: '>=18'} - zile@0.0.13: resolution: {integrity: sha512-a6F5jiKqSd12kf/n1bVIjLpP5FbKr2g1sgdfysMvT+cpyjz0uN6ChY0HX93+7d0OkFa7UX+eiqBJG4ZPVlcIww==} hasBin: true @@ -3406,8 +3347,6 @@ snapshots: '@noble/hashes': 1.7.1 '@scure/base': 1.2.4 - '@sec-ant/readable-stream@0.4.1': {} - '@sentry-internal/tracing@7.116.0': dependencies: '@sentry/core': 7.116.0 @@ -3440,8 +3379,6 @@ snapshots: dependencies: '@sentry/types': 7.116.0 - '@sindresorhus/merge-streams@4.0.0': {} - '@standard-schema/spec@1.0.0': {} '@types/chai@5.2.3': @@ -3961,21 +3898,6 @@ snapshots: events@3.3.0: {} - execa@9.1.0: - dependencies: - '@sindresorhus/merge-streams': 4.0.0 - cross-spawn: 7.0.6 - figures: 6.1.0 - get-stream: 9.0.1 - human-signals: 7.0.0 - is-plain-obj: 4.1.0 - is-stream: 4.0.1 - npm-run-path: 5.3.0 - pretty-ms: 9.0.0 - signal-exit: 4.1.0 - strip-final-newline: 4.0.0 - yoctocolors: 2.0.2 - expect-type@1.3.0: {} extendable-error@0.1.7: {} @@ -4047,10 +3969,6 @@ snapshots: optionalDependencies: picomatch: 4.0.3 - figures@6.1.0: - dependencies: - is-unicode-supported: 2.0.0 - fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 @@ -4100,11 +4018,6 @@ snapshots: get-port@7.1.0: {} - get-stream@9.0.1: - dependencies: - '@sec-ant/readable-stream': 0.4.1 - is-stream: 4.0.1 - glob-parent@5.1.2: dependencies: is-glob: 4.0.3 @@ -4148,8 +4061,6 @@ snapshots: human-id@4.1.3: {} - human-signals@7.0.0: {} - iconv-lite@0.7.0: dependencies: safer-buffer: 2.1.2 @@ -4206,18 +4117,12 @@ snapshots: is-number@7.0.0: {} - is-plain-obj@4.1.0: {} - is-stream@2.0.1: {} - is-stream@4.0.1: {} - is-subdir@1.2.0: dependencies: better-path-resolve: 1.0.0 - is-unicode-supported@2.0.0: {} - is-windows@1.0.2: {} isarray@1.0.0: {} @@ -4407,10 +4312,6 @@ snapshots: normalize-path@3.0.0: {} - npm-run-path@5.3.0: - dependencies: - path-key: 4.0.0 - obug@2.1.1: {} on-exit-leak-free@2.1.2: {} @@ -4463,14 +4364,10 @@ snapshots: dependencies: quansync: 0.2.11 - parse-ms@4.0.0: {} - path-exists@4.0.0: {} path-key@3.1.1: {} - path-key@4.0.0: {} - path-parse@1.0.7: {} path-scurry@1.11.1: @@ -4561,10 +4458,6 @@ snapshots: prettier@2.8.8: {} - pretty-ms@9.0.0: - dependencies: - parse-ms: 4.0.0 - process-nextick-args@2.0.1: {} process-warning@3.0.0: {} @@ -4861,8 +4754,6 @@ snapshots: strip-bom@3.0.0: {} - strip-final-newline@4.0.0: {} - strip-json-comments@3.1.1: {} supports-color@7.2.0: @@ -4961,7 +4852,7 @@ snapshots: tinybench@2.9.0: {} - tinyexec@1.0.2: {} + tinyexec@1.1.2: {} tinyglobby@0.2.15: dependencies: @@ -5057,7 +4948,7 @@ snapshots: picomatch: 4.0.3 std-env: 3.10.0 tinybench: 2.9.0 - tinyexec: 1.0.2 + tinyexec: 1.1.2 tinyglobby: 0.2.15 tinyrainbow: 3.0.3 vite: 7.2.7(@types/node@24.10.2)(yaml@2.8.2) @@ -5130,8 +5021,6 @@ snapshots: y18n: 5.0.8 yargs-parser: 21.1.1 - yoctocolors@2.0.2: {} - zile@0.0.13(typescript@5.9.3): dependencies: '@clack/prompts': 1.0.0-alpha.8 diff --git a/src/processes/execa.test.ts b/src/processes/execa.test.ts index 9432b06..a47de80 100644 --- a/src/processes/execa.test.ts +++ b/src/processes/execa.test.ts @@ -136,3 +136,61 @@ test('behavior: exit when status is starting', async () => { process._internal.process.kill() await expect(resolvers.exit.promise).resolves.toBeDefined() }) + +test('behavior: array interpolation preserves multi-word elements', async () => { + const emitter = new EventEmitter() + const process = createProcess() + + let stdout = '' + const done = Promise.withResolvers() + emitter.on('stdout', (message) => { + stdout += message + }) + emitter.on('exit', () => done.resolve()) + + const value = 'test test test test test test test test test test test junk' + + await process.start( + ($) => $`node ${['-e', 'process.stdout.write(process.argv[1])', value]}`, + { + emitter, + status: 'idle', + resolver({ resolve }) { + resolve() + }, + }, + ) + + await done.promise + expect(stdout).toBe(value) +}) + +test('behavior: callable form merges env into spawned process', async () => { + const emitter = new EventEmitter() + const process = createProcess() + + let stdout = '' + const done = Promise.withResolvers() + emitter.on('stdout', (message) => { + stdout += message + }) + emitter.on('exit', () => done.resolve()) + + await process.start( + ($) => + $({ env: { PROOL_TEST_VAR: 'hello' } })`node ${[ + '-e', + 'process.stdout.write(process.env.PROOL_TEST_VAR || "missing")', + ]}`, + { + emitter, + status: 'idle', + resolver({ resolve }) { + resolve() + }, + }, + ) + + await done.promise + expect(stdout).toBe('hello') +}) diff --git a/src/processes/execa.ts b/src/processes/execa.ts index 7be5b74..e51906f 100644 --- a/src/processes/execa.ts +++ b/src/processes/execa.ts @@ -1,9 +1,14 @@ +import type { ChildProcess, SpawnOptions } from 'node:child_process' import type { SignalConstants } from 'node:os' -import { execa as exec, type ResultPromise } from 'execa' +import type { Readable } from 'node:stream' +import { x } from 'tinyexec' import type * as Instance from '../Instance.js' import { stripColors } from '../internal/utils.js' -export type Process_internal = ResultPromise<{ cleanup: true; reject: false }> +export type Process_internal = ChildProcess & { + stdout: Readable + stderr: Readable +} export type ExecaStartOptions = Instance.define.InstanceStartOptions_internal & { @@ -14,13 +19,69 @@ export type ExecaStartOptions = }): void } +type TagOptions = { env?: Record } +type Tag = ( + strings: TemplateStringsArray, + ...values: unknown[] +) => Process_internal + +// Assumes `toArgs` produces unquoted `string[]` tokens: array interpolations +// are spread as separate argv items; static segments are whitespace-split. +function buildTag(options: TagOptions = {}): Tag { + return (strings, ...values) => { + const argv: string[] = [] + let buffer = '' + const flush = () => { + const trimmed = buffer.trim() + if (trimmed) for (const token of trimmed.split(/\s+/)) argv.push(token) + buffer = '' + } + for (let i = 0; i < strings.length; i++) { + buffer += strings[i] + if (i < values.length) { + const value = values[i] + if (Array.isArray(value)) { + flush() + for (const element of value) argv.push(String(element)) + } else { + buffer += String(value) + } + } + } + flush() + const [command, ...args] = argv + const nodeOptions: SpawnOptions = options.env + ? { env: { ...process.env, ...options.env } as NodeJS.ProcessEnv } + : {} + // `x()` spawns synchronously, so `.process` is defined here. + return x(command!, args, { nodeOptions }).process as Process_internal + } +} + +type DualShape = { + (options: TagOptions): Tag + (strings: TemplateStringsArray, ...values: unknown[]): Process_internal +} + +function isTemplateStringsArray(value: unknown): value is TemplateStringsArray { + return Array.isArray(value) && 'raw' in value +} + +const $: DualShape = (( + arg: TemplateStringsArray | TagOptions, + ...values: unknown[] +): Process_internal | Tag => { + if (isTemplateStringsArray(arg)) return buildTag()(arg, ...values) + return buildTag(arg) +}) as DualShape + export type Process = { _internal: { process: Process_internal } name: string start( - command: (x: typeof exec) => void, + command: (tag: DualShape) => Process_internal, options: ExecaStartOptions, ): Promise stop(signal?: keyof SignalConstants | number): Promise @@ -30,33 +91,28 @@ export function execa(parameters: execa.Parameters): execa.ReturnType { const { name } = parameters const errorMessages: string[] = [] - let process: Process_internal + let proc: Process_internal async function stop(signal?: keyof SignalConstants | number) { - const killed = process.kill(signal) + const killed = proc.kill(signal) if (!killed) return - return new Promise((resolve) => process.on('close', resolve)) + return new Promise((resolve) => proc.on('close', resolve)) } return { _internal: { get process() { - return process + return proc }, }, name, start(command, { emitter, resolver, status }) { const { promise, resolve, reject } = Promise.withResolvers() - process = command( - exec({ - cleanup: true, - reject: false, - }) as any, - ) as unknown as Process_internal + proc = command($) resolver({ - process, + process: proc, async reject(data) { await stop() reject( @@ -69,12 +125,12 @@ export function execa(parameters: execa.Parameters): execa.ReturnType { }, }) - process.stdout.on('data', (data) => { + proc.stdout.on('data', (data) => { const message = stripColors(data.toString()) emitter.emit('message', message) emitter.emit('stdout', message) }) - process.stderr.on('data', async (data) => { + proc.stderr.on('data', async (data) => { const message = stripColors(data.toString()) errorMessages.push(message) @@ -83,12 +139,12 @@ export function execa(parameters: execa.Parameters): execa.ReturnType { emitter.emit('message', message) emitter.emit('stderr', message) }) - process.on('close', () => process.removeAllListeners()) - process.on('exit', (code, signal) => { + proc.on('close', () => proc.removeAllListeners()) + proc.on('exit', (code, signal) => { emitter.emit('exit', code, signal) if (!code) { - process.removeAllListeners() + proc.removeAllListeners() if (status === 'starting') reject( new Error( @@ -105,7 +161,7 @@ export function execa(parameters: execa.Parameters): execa.ReturnType { return promise }, async stop() { - process.removeAllListeners() + proc.removeAllListeners() await stop() }, }