mirror of
https://github.com/atom/atom.git
synced 2026-01-22 21:38:10 -05:00
100 lines
3.4 KiB
CoffeeScript
100 lines
3.4 KiB
CoffeeScript
ChildProcess = require 'child_process'
|
|
|
|
# Public: A wrapper which provides standard error/output line buffering for
|
|
# Node's ChildProcess.
|
|
#
|
|
# ## Requiring in packages
|
|
#
|
|
# ```coffee
|
|
# {BufferedProcess} = require 'atom'
|
|
#
|
|
# command = 'ps'
|
|
# args = ['-ef']
|
|
# stdout = (output) -> console.log(output)
|
|
# exit = (code) -> console.log("ps -ef exited with #{code}")
|
|
# process = new BufferredProcess({command, args, stdout, exit})
|
|
# ```
|
|
module.exports =
|
|
class BufferedProcess
|
|
# Public: Runs the given command by spawning a new child process.
|
|
#
|
|
# options - An {Object} with the following keys:
|
|
# :command - The {String} command to execute.
|
|
# :args - The {Array} of arguments to pass to the command (optional).
|
|
# :options - The options {Object} to pass to Node's `ChildProcess.spawn`
|
|
# method (optional).
|
|
# :stdout - The callback {Function} that receives a single argument which
|
|
# contains the standard output from the command. The callback is
|
|
# called as data is received but it's buffered to ensure only
|
|
# complete lines are passed until the source stream closes. After
|
|
# the source stream has closed all remaining data is sent in a
|
|
# final call (optional).
|
|
# :stderr - The callback {Function} that receives a single argument which
|
|
# contains the standard error output from the command. The
|
|
# callback is called as data is received but it's buffered to
|
|
# ensure only complete lines are passed until the source stream
|
|
# closes. After the source stream has closed all remaining data
|
|
# is sent in a final call (optional).
|
|
# :exit - The callback {Function} which receives a single argument
|
|
# containing the exit status (optional).
|
|
constructor: ({command, args, options, stdout, stderr, exit}={}) ->
|
|
options ?= {}
|
|
@process = ChildProcess.spawn(command, args, options)
|
|
@killed = false
|
|
|
|
stdoutClosed = true
|
|
stderrClosed = true
|
|
processExited = true
|
|
exitCode = 0
|
|
triggerExitCallback = ->
|
|
return if @killed
|
|
if stdoutClosed and stderrClosed and processExited
|
|
exit?(exitCode)
|
|
|
|
if stdout
|
|
stdoutClosed = false
|
|
@bufferStream @process.stdout, stdout, ->
|
|
stdoutClosed = true
|
|
triggerExitCallback()
|
|
|
|
if stderr
|
|
stderrClosed = false
|
|
@bufferStream @process.stderr, stderr, ->
|
|
stderrClosed = true
|
|
triggerExitCallback()
|
|
|
|
if exit
|
|
processExited = false
|
|
@process.on 'exit', (code) ->
|
|
exitCode = code
|
|
processExited = true
|
|
triggerExitCallback()
|
|
|
|
# Helper method to pass data line by line.
|
|
#
|
|
# stream - The Stream to read from.
|
|
# onLines - The callback to call with each line of data.
|
|
# onDone - The callback to call when the stream has closed.
|
|
bufferStream: (stream, onLines, onDone) ->
|
|
stream.setEncoding('utf8')
|
|
buffered = ''
|
|
|
|
stream.on 'data', (data) =>
|
|
return if @killed
|
|
buffered += data
|
|
lastNewlineIndex = buffered.lastIndexOf('\n')
|
|
if lastNewlineIndex isnt -1
|
|
onLines(buffered.substring(0, lastNewlineIndex + 1))
|
|
buffered = buffered.substring(lastNewlineIndex + 1)
|
|
|
|
stream.on 'close', =>
|
|
return if @killed
|
|
onLines(buffered) if buffered.length > 0
|
|
onDone()
|
|
|
|
# Public: Terminate the process.
|
|
kill: ->
|
|
@killed = true
|
|
@process.kill()
|
|
@process = null
|