Skip to content

Commit

Permalink
fix: expose kill, ref & unref methods (#11)
Browse files Browse the repository at this point in the history
* fix: expose kill, ref & unref methods

closes #10

* ci: remove node16

* ci: avoid npx

it's failing in windows
  • Loading branch information
Kikobeats authored Apr 30, 2024
1 parent a4c572d commit ac424c0
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 29 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/pull_request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: ['16', '18', 'lts/*', 'latest']
node-version: ['18', 'lts/*', 'latest']
steps:
- name: Checkout
uses: actions/checkout@v4
Expand All @@ -32,7 +32,7 @@ jobs:
- name: Test
run: pnpm test
- name: Report
run: npx c8 report --reporter=text-lcov > coverage/lcov.info
run: ./node_modules/.bin/c8 report --reporter=text-lcov > coverage/lcov.info
- name: Coverage
uses: coverallsapp/github-action@main
with:
Expand All @@ -43,7 +43,7 @@ jobs:
runs-on: windows-latest
strategy:
matrix:
node-version: ['16', '18', 'lts/*', 'latest']
node-version: ['18', 'lts/*', 'latest']
steps:
- name: Checkout
uses: actions/checkout@v4
Expand All @@ -61,7 +61,7 @@ jobs:
- name: Test
run: pnpm test
- name: Report
run: npx c8 report --reporter=text-lcov > coverage/lcov.info
run: ./node_modules/.bin/c8 report --reporter=text-lcov > coverage/lcov.info
- name: Coverage
uses: coverallsapp/github-action@main
with:
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
"standard-version": "latest"
},
"engines": {
"node": ">= 16"
"node": ">= 18"
},
"files": [
"src"
Expand Down
67 changes: 43 additions & 24 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,29 @@
const { spawn } = require('child_process')
const { EOL } = require('os')

const EE_PROPS = Object.getOwnPropertyNames(require('events').EventEmitter.prototype).filter(name => !name.startsWith('_'))
const EE_PROPS = Object.getOwnPropertyNames(
require('events').EventEmitter.prototype
)
.filter(name => !name.startsWith('_'))
.concat(['kill', 'ref', 'unref'])

const eos = (stream, listener, buffer = []) => stream[listener].on('data', data => buffer.push(data)) && buffer
const eos = (stream, listener, buffer = []) =>
stream[listener].on('data', data => buffer.push(data)) && buffer

const clean = str => str.trim().replace(/\n$/, '')

const parse = (buffer, { json } = {}) => (encoding, start, end) => {
const data = clean(Buffer.concat(buffer).toString(encoding, start, end))
return json ? JSON.parse(data) : data
}
const parse =
(buffer, { json } = {}) =>
(encoding, start, end) => {
const data = clean(Buffer.concat(buffer).toString(encoding, start, end))
return json ? JSON.parse(data) : data
}

const extend = defaults => (input, args, options) => {
if (!(args instanceof Array)) { options = args; args = [] }
if (!(args instanceof Array)) {
options = args
args = []
}
const [cmd, ...cmdArgs] = input.split(' ').concat(args).filter(Boolean)
let childProcess

Expand All @@ -25,27 +35,36 @@ const extend = defaults => (input, args, options) => {
const stdout = eos(childProcess, 'stdout')
const stderr = eos(childProcess, 'stderr')

childProcess
.on('error', reject)
.on('exit', code => {
Object.defineProperty(childProcess, 'stdout', { get: parse(stdout, opts) })
Object.defineProperty(childProcess, 'stderr', { get: parse(stderr) })
if (code === 0) return resolve(childProcess)
const command = `${cmd} ${cmdArgs.join(' ')}`
let message = `The command spawned as:${EOL}${EOL}`
message += ` ${command}${EOL}${EOL}`
message += `exited with \`{ code: ${code} }\` and the following trace:${EOL}${EOL}`
message += String(stderr).split(EOL).map(line => ` ${line}`).join(EOL)
const error = new Error(message)
error.command = command
error.name = 'ChildProcessError'
Object.keys(childProcess).forEach(key => { error[key] = childProcess[key] })
reject(error)
childProcess.on('error', reject).on('exit', code => {
Object.defineProperty(childProcess, 'stdout', {
get: parse(stdout, opts)
})
Object.defineProperty(childProcess, 'stderr', { get: parse(stderr) })
if (code === 0) return resolve(childProcess)
const command = `${cmd} ${cmdArgs.join(' ')}`
let message = `The command spawned as:${EOL}${EOL}`
message += ` ${command}${EOL}${EOL}`
message += `exited with \`{ code: ${code} }\` and the following trace:${EOL}${EOL}`
message += String(stderr)
.split(EOL)
.map(line => ` ${line}`)
.join(EOL)
const error = new Error(message)
error.command = command
error.name = 'ChildProcessError'
Object.keys(childProcess).forEach(key => {
error[key] = childProcess[key]
})
reject(error)
})
})

const subprocess = Object.assign(promise, childProcess)
if (childProcess) EE_PROPS.forEach(name => (subprocess[name] = childProcess[name].bind(childProcess)))
if (childProcess) {
EE_PROPS.forEach(
name => (subprocess[name] = childProcess[name].bind(childProcess))
)
}
return subprocess
}

Expand Down
10 changes: 10 additions & 0 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ test('piping subprocess', async t => {

test('passing timeout', async t => {
const result = await $('sleep 3 && echo OK', {
shell: true,
timeout: 1,
killSignal: 'SIGKILL'
}).catch(err => err)
Expand Down Expand Up @@ -159,3 +160,12 @@ test('event emitter properties are availables', async t => {
'eventNames'
].forEach(name => t.truthy(subprocess[name]))
})

test('child process properties are available', async t => {
const subprocess = $('echo 1234567890')
await new Promise(resolve => subprocess.once('spawn', resolve))
const result = await subprocess
t.is(result.stdout, '1234567890')
t.is(result.exitCode, 0)
;['kill', 'ref', 'unref'].forEach(name => t.truthy(subprocess[name]))
})

0 comments on commit ac424c0

Please sign in to comment.