Skip to content

Commit

Permalink
Enable support for --outpipe (#44)
Browse files Browse the repository at this point in the history
  • Loading branch information
blakeembrey committed Dec 2, 2016
1 parent 9c67f10 commit 069a7ce
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 32 deletions.
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,16 @@ Use polling to monitor for changes. Omitting the interval will default to 100ms.
onchange '**/*.js' -p -- npm test
```

### Outpipe (`-o`, `--outpipe`)

Shell command to execute every change:

```sh
onchange 'src/**/*.js' -o '> .change' -- echo '{{event}} to {{changed}}'
```

**P.S.** When a command is used with `--outpipe`, the `stdout` from the command will be piped into `outpipe`.

## TypeScript

Includes [types](index.d.ts) for TypeScript users.
Expand Down
10 changes: 5 additions & 5 deletions cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ var argv = require('minimist')(process.argv.slice(2), {
wait: ['w'],
cwd: ['c'],
delay: ['d'],
poll: ['p']
poll: ['p'],
outpipe: ['o']
},
default: {
exclude: '**/node_modules/**'
Expand Down Expand Up @@ -46,12 +47,11 @@ var options = {
cwd: argv.cwd,
delay: argv.delay,
poll: argv.poll,
killSignal: argv.killSignal
killSignal: argv.killSignal,
outpipe: argv.outpipe
}

console.log(options)

if (!command) {
if (!command && !options.outpipe) {
console.error('Remember to pass the command after "--":')
console.error(' onchange \'**/*.js\' -- echo \'{{changed}}\'')
process.exit(1)
Expand Down
3 changes: 3 additions & 0 deletions echo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/usr/bin/env node

process.stdin.pipe(process.stdout)
1 change: 1 addition & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ declare namespace onchange {
delay?: number;
stdout?: any;
stderr?: any;
outpipe?: string;
}
}

Expand Down
104 changes: 77 additions & 27 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
var kill = require('tree-kill')
var resolve = require('path').resolve
var exec = require('child_process').exec
var spawn = require('cross-spawn').spawn
var chokidar = require('chokidar')
var arrify = require('arrify')
var echo = process.execPath + ' ' + resolve(__dirname, 'echo.js')

module.exports = function (match, command, args, opts) {
opts = opts || {}
Expand All @@ -13,8 +15,14 @@ module.exports = function (match, command, args, opts) {
var stderr = opts.stderr || process.stderr
var delay = Number(opts.delay) || 0
var killSignal = opts.killSignal || 'SIGTERM'
var outpipe = typeof opts.outpipe === 'string' ? outpipetmpl(opts.outpipe) : undefined

var child
if (!command && !outpipe) {
throw new TypeError('Expected "command" and/or "outpipe" to be specified')
}

var childOutpipe
var childCommand
var pendingOpts
var pendingTimeout
var pendingExit = false
Expand All @@ -37,7 +45,10 @@ module.exports = function (match, command, args, opts) {
* Run when the script exits.
*/
function onexit () {
child = null
if (childOutpipe || childCommand) {
return
}

pendingExit = false

if (pendingOpts) {
Expand Down Expand Up @@ -70,15 +81,22 @@ module.exports = function (match, command, args, opts) {
*/
function start (opts) {
// Set pending options for next execution.
if (child) {
if (childOutpipe || childCommand) {
pendingOpts = opts

if (!pendingExit) {
if (opts.wait) {
log('waiting for process and restarting')
} else {
log('killing process ' + child.pid + ' and restarting')
kill(child.pid, killSignal)
if (childCommand) {
log('killing command ' + childCommand.pid + ' and restarting')
kill(childCommand.pid, killSignal)
}

if (childOutpipe) {
log('killing outpipe ' + childOutpipe.pid + ' and restarting')
kill(childOutpipe.pid, killSignal)
}
}

pendingExit = true
Expand All @@ -89,27 +107,52 @@ module.exports = function (match, command, args, opts) {
return
}

// Generate argument strings from templates.
var filtered = tmpls.map(function (tmpl) {
return tmpl(opts)
})
if (outpipe) {
var filtered = outpipe(opts)

log('executing "' + [command].concat(filtered).join(' ') + '"')
log('executing outpipe "' + filtered + '"')

child = spawn(command, filtered, {
cwd: cwd,
stdio: ['ignore', stdout, stderr]
})
childOutpipe = exec(filtered, {
cwd: cwd
})

child.on('exit', function (code, signal) {
if (code == null) {
log('process exited with ' + signal)
} else {
log('process completed with code ' + code)
}
// Must pipe stdout and stderr.
childOutpipe.stdout.pipe(stdout)
childOutpipe.stderr.pipe(stderr)

return onexit()
})
childOutpipe.on('exit', function (code, signal) {
log('outpipe ' + (code == null ? 'exited with ' + signal : 'completed with code ' + code))

childOutpipe = null

return onexit()
})
}

if (command) {
// Generate argument strings from templates.
var filtered = tmpls.map(function (tmpl) {
return tmpl(opts)
})

log('executing command "' + [command].concat(filtered).join(' ') + '"')

childCommand = spawn(command, filtered, {
cwd: cwd,
stdio: ['ignore', childOutpipe ? childOutpipe.stdin : stdout, stderr]
})

childCommand.on('exit', function (code, signal) {
log('command ' + (code == null ? 'exited with ' + signal : 'completed with code ' + code))

childCommand = null

return onexit()
})
} else {
// No data to write to `outpipe`.
childOutpipe.stdin.end()
}
}

watcher.on('ready', function () {
Expand All @@ -135,15 +178,22 @@ module.exports = function (match, command, args, opts) {
})
}

//
// Helpers
//

// Double mustache template generator
// Double mustache template generator.
function tmpl (str) {
return function (data) {
return str.replace(/{{([^{}]+)}}/g, function (_, key) {
return data[key]
})
}
}

// Template generator for `outpipe` option.
function outpipetmpl (str) {
var value = str.trim()

if (value.charAt(0) === '|' || value.charAt(0) === '>') {
return tmpl(echo + ' ' + value)
}

return tmpl(value)
}

0 comments on commit 069a7ce

Please sign in to comment.