Skip to content

Commit

Permalink
fix: add .finally method
Browse files Browse the repository at this point in the history
fix #120
  • Loading branch information
jedwards1211 committed May 8, 2020
1 parent 3c6ebaa commit e44cc4d
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 6 deletions.
2 changes: 1 addition & 1 deletion README.md
Expand Up @@ -14,7 +14,7 @@ but more thorough, because that package doesn't seem very actively maintained.
`promisify-child-process` provides a **drop-in replacement** for the
original `child_process` functions, not just duplicate methods that
return a `Promise`. So when you call `exec(...)` we still return a
`ChildProcess` instance, just with `.then()` and `.catch()` added to
`ChildProcess` instance, just with `.then()`, `.catch()`, and `.finally()` added to
make it promise-friendly.

## Install and Set-up
Expand Down
3 changes: 2 additions & 1 deletion package.json
Expand Up @@ -134,7 +134,8 @@
"prettier-eslint": "^8.8.2",
"rimraf": "^2.6.0",
"semantic-release": "^15.13.31",
"typescript": "^3.7.3"
"typescript": "^3.7.3",
"waait": "^1.0.5"
},
"dependencies": {},
"renovate": {
Expand Down
19 changes: 19 additions & 0 deletions src/index.js
Expand Up @@ -26,6 +26,21 @@ type PromisifyChildProcessBaseOpts = {
export type SpawnOpts = child_process$spawnOpts & PromisifyChildProcessBaseOpts
export type ForkOpts = child_process$forkOpts & PromisifyChildProcessBaseOpts

const bindFinally = <T>(promise: Promise<T>) => (
handler: () => mixed
): Promise<T> =>
// don't assume we're running in an environment with Promise.finally
promise.then(
async (value: any): any => {
await handler()
return value
},
async (reason: any): any => {
await handler()
throw reason
}
)

function joinChunks(
chunks: $ReadOnlyArray<string | Buffer>,
encoding: ?string
Expand Down Expand Up @@ -139,6 +154,9 @@ export function promisifyChildProcess(
return (Object.create(child, {
then: { value: _promise.then.bind(_promise) },
catch: { value: _promise.catch.bind(_promise) },
finally: {
value: bindFinally(_promise),
},
}): any)
}

Expand Down Expand Up @@ -196,6 +214,7 @@ function promisifyExecMethod(method: any): any {
return (Object.create((child: any), {
then: { value: _promise.then.bind(_promise) },
catch: { value: _promise.catch.bind(_promise) },
finally: { value: bindFinally(_promise) },
}): any)
}
}
Expand Down
33 changes: 29 additions & 4 deletions test/index.js
Expand Up @@ -5,6 +5,7 @@ import { describe, it, before, after } from 'mocha'
import { expect } from 'chai'
import path from 'path'
import fs from 'fs-extra'
import delay from 'waait'

before(() =>
Promise.all([
Expand Down Expand Up @@ -50,11 +51,16 @@ describe('spawn', function() {
this.timeout(30000)

it('resolves with process output', async () => {
let finallyDone = false
const { stdout, stderr } = await spawn(
process.execPath,
[require.resolve('./resolvesWithProcessOutput')],
{ maxBuffer: 200 * 1024 }
)
).finally(async () => {
await delay(50)
finallyDone = true
})
expect(finallyDone, 'finally handler finished').to.be.true
if (stdout == null || stderr == null) throw new Error('missing output')
if (!(stdout instanceof Buffer))
throw new Error('expected stdout to be a buffer')
Expand Down Expand Up @@ -94,10 +100,17 @@ describe('spawn', function() {
expect(stderr).to.equal('world')
})
it('rejects with exit code', async () => {
let finallyDone = false
let error
await spawn(process.execPath, [require.resolve('./rejectsWithExitCode')], {
maxBuffer: 200 * 1024,
}).catch(err => (error = err))
})
.finally(async () => {
await delay(50)
finallyDone = true
})
.catch(err => (error = err))
expect(finallyDone, 'finally handler finished').to.be.true
if (error == null) throw new Error('missing error')
const { code, message, stdout, stderr } = error
expect(message).to.equal('Process exited with code 2')
Expand Down Expand Up @@ -157,10 +170,15 @@ describe('fork', function() {
this.timeout(30000)

it('resolves with process output', async () => {
let finallyDone = false
const { stdout, stderr } = await fork(
require.resolve('./resolvesWithProcessOutput'),
{ silent: true, maxBuffer: 200 * 1024 }
)
).finally(async () => {
await delay(50)
finallyDone = true
})
expect(finallyDone, 'finally handler finished').to.be.true
if (stdout == null || stderr == null) throw new Error('missing output')
if (!(stdout instanceof Buffer))
throw new Error('expected stdout to be a buffer')
Expand Down Expand Up @@ -198,11 +216,18 @@ describe('fork', function() {
expect(stderr).to.equal('world')
})
it('rejects with exit code', async () => {
let finallyDone = false
let error
await fork(require.resolve('./rejectsWithExitCode'), {
silent: true,
maxBuffer: 200 * 1024,
}).catch(err => (error = err))
})
.finally(async () => {
await delay(50)
finallyDone = true
})
.catch(err => (error = err))
expect(finallyDone, 'finally handler finished').to.be.true
if (error == null) throw new Error('missing error')
const { code, message, stdout, stderr } = error
expect(message).to.equal('Process exited with code 2')
Expand Down
5 changes: 5 additions & 0 deletions yarn.lock
Expand Up @@ -8313,6 +8313,11 @@ vue-eslint-parser@^2.0.2:
esquery "^1.0.0"
lodash "^4.17.4"

waait@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/waait/-/waait-1.0.5.tgz#6a3c7aaa88bd0a1a545e9d47890b9595bebf9aa7"
integrity sha512-wp+unA4CpqxvBUKHHv8D86fK4jWByHAWyhEXXVHfVUZfK+16ylpj7hjQ58Z8j9ntu8XNukRQT8Fi5qbyJ8rkyw==

wcwidth@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8"
Expand Down

0 comments on commit e44cc4d

Please sign in to comment.