-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
215 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 = {} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() | ||
} |
ff76ab4
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you ::)))