Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
- supports --debug & --debug-brk for @arrrght :)
- fixes / implements #34 #27 #33 #29 #23 #20 #17
  • Loading branch information
DTrejo committed Dec 25, 2013
1 parent a413749 commit ff76ab4
Show file tree
Hide file tree
Showing 4 changed files with 215 additions and 0 deletions.
6 changes: 6 additions & 0 deletions inside.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports = inside

function inside(str, delim) {
var rest = str.slice(str.indexOf(delim) + 1)
return rest.slice(0, rest.indexOf(delim))
}
38 changes: 38 additions & 0 deletions refwatch.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
var chokidar = require('chokidar')
var watcher = chokidar.watch([], { persistent: true })
var path = require('path')
function l() { console.log.apply(console, arguments) }

module.exports = {
watch: watch,
unwatch: unwatch,
watcher: watcher,
clear: clear
}

var tracked = {}

// assumes that you pass it absolute file paths
function watch(file) {
tracked[file] = tracked[file] || 0
tracked[file]++

// first time seeing it
if (tracked[file] === 1) {
l('watching %s', path.relative(process.cwd(), file))
watcher.add(file)
}
}

function unwatch(file) {
if (tracked[file] > 0) tracked[file]--

if (tracked[file] == 0) {
// chokidar does not yet support unwatching, so do nothing
return l('TODO unwatch %s', file)
}
}

function clear() {
tracked = {}
}
101 changes: 101 additions & 0 deletions run
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
#!/usr/bin/env node
/*
Ideal psuedo-code
if dep changes
- scan file for new deps
- if dep is not installed, install it, restart()
- else restart()
- scan file for removed deps
- unwatch the removed dep (use refcount)
- restart()
- deps that were there last time
- do nothing
if dep is deleted
- unwatch it (automatic)
- unwatch its sub-deps (use refcount)
- restart()
*/
var path = require('path')
var inside = require('./inside')
var required = require('required')
var refwatch = require('./refwatch')
var watcher = refwatch.watcher
var watch = refwatch.watch
var unwatch = refwatch.unwatch

var root = path.resolve(process.argv[2])
var runner = require('./runner')(process.argv.slice(2))

function l() { console.log.apply(console, arguments) }

// TODO make these arguments to refwatch
watcher
.on('change', onChange)
.on('unlink', onUnlink)
.on('error', onWatcherError)
function onChange(path) {
l('File', path, 'has been changed')
// TODO reindex(path)
runner.restart()
}
// This case only matters when we want to restart; it's okay to overwatch
function onUnlink(path) {
l('File', path, 'has been removed')
// TODO unwatch(path)
// TODO unindex(path)
runner.restart()
}
function onWatcherError(error) {
console.error('onWatcherError %s\n', error.stack)
}

watch(root)
return parseAndWatch(root, function() {
runner.start()
})

// if the parse fails because the package is not installed / doesn't exist,
// handleError tries to install it & restart, or just logs the error.
function parseAndWatch(filename, cb) {
// TODO clear listeners, clear refcounts
return required(filename, onDeps)
function onDeps(err, deps) {
if (err) return handleError(err, filename)
watchDepTree(deps)
return cb && cb()
}
}
function handleError(err, root) {
var name = inside(err.message, "'")
if (name) {
var filename = err.message.slice(err.message.lastIndexOf(' ')+1)
l('TODO npm install %s # seen in: %s', name, filename)
// TODO actually install it, not pretend to
return setTimeout(function() {
return parseAndWatch(root)
}, 2000)
}
// it was an unknown kind of error, give up.
l(err.stack)
}

function isNotCore(dep) { return !dep.core }
// function isPath(dep) { return dep.id[0] === '.' || dep.id[0] === '/' }
// function isNotPath(dep) { return !isPath(dep) }

function watchDepTree(deps, indent) {
indent = indent || ''
// console.log('deps', JSON.stringify(deps, null, 2))
var userland = deps.filter(isNotCore)
// l(indent + 'userland', JSON.stringify(userland).slice(0, 40))

userland.map(function(dep) {
watch(dep.filename)
watchDepTree(dep.deps, indent + '\t')
})
// var js = userland.filter(isPath)
// var modules = userland.filter(isNotPath)
}
70 changes: 70 additions & 0 deletions runner.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
module.exports = createRunner
var path = require('path')
var spawn = require('child_process').spawn
function l() { console.log.apply(console, arguments) }
/*
Returns a Runner object with three methods: start, kill, and restart.
Takes process.argv arrays of these forms and turns them into commands to run.
hello.js -> node hello.js
hello.coffee -> coffee hello.coffee
arg1 arg2 -- node hello.js --debug-brk=1337 -> node hello.js --debug-brk=1337
*/
function createRunner(argv) {
return new Runner(argv)
}
function Runner(argv) {
var self = this

// allow typing to reach the child, whichever one is running
process.stdin.resume();
process.stdin.setEncoding('utf8');

var dashdash = argv.indexOf('--')
if (dashdash > -1) {
self.argv = argv.slice(dashdash + 1)

} else if (path.extname(argv[0]) === '.coffee') {
self.argv = [ 'coffee', argv[0] ]

} else {
self.argv = [ 'node', argv[0] ]
}
return self
}
Runner.prototype.start = function() {
var self = this
l('Starting `%s`', self.argv.join(' '))
self.child = spawn(self.argv[0], self.argv.slice(1), { stdio: 'inherit' })
self.child.on('close', function (code) {
l('`%s` exited with code ' + code, self.argv.join(' '))
})

return self
}
Runner.prototype.kill = function() {
var self = this
// usually SIGNAL will be undefined, and node will use SIGTERM.
self.child && self.child.kill(process.env.SIGNAL)
return self
}
Runner.prototype.restart = function() {
var self = this
self.kill()
self.start()
return self
}

if (!module.parent) {
var a = require('assert')
a.deepEqual(createRunner(['hello.js']).argv, 'node hello.js'.split(' '))
a.deepEqual(createRunner(['hello.coffee']).argv, 'coffee hello.coffee'.split(' '))

var input = 'arg1 arg2 -- node hello.js --debug-brk=1337'.split(' ')
var output = 'node hello.js --debug-brk=1337'.split(' ')
a.deepEqual(createRunner(input).argv, output)

createRunner('-- echo TESTOUTPUT'.split(' ')).start()
createRunner('-- sleep 10'.split(' ')).start().kill()
createRunner('-- node hij.js'.split(' ')).start()
}

1 comment on commit ff76ab4

@arrrght
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you ::)))

Please sign in to comment.