diff --git a/README.md b/README.md index b350c84..05831a1 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # qo -A task runner for insect people. And for people who use [pogoscript](http://pogoscript.org/). +A task runner for insect people. ## install @@ -8,63 +8,92 @@ A task runner for insect people. And for people who use [pogoscript](http://pogo ## how to use -Write a file `qo.pogo`: +Write a file `qo.js`: - task 'hi' - console.log 'hi!' +```js +task('hi', function () { + console.log('hi!'); +}); +``` Then - # qo hi - hi! +```bash +# qo hi +hi! +``` ### named arguments - task 'hi' @(args, name: nil) - console.log "hi #(name)" +```js +task('hi', function (args, options) { + console.log("hi " + options.name)" +}); +``` Then - # qo hi --name jack - hi jack +```bash +# qo hi --name jack +hi jack +``` ### lists of arguments - task 'hi' @(args) - console.log "hi #(args.join ', ')" +```js +task('hi', function (args) { + console.log("hi " + args.join(', ')); +}); +``` Then - # qo hi jack jill jesse - hi jack, jill, jesse +```bash +# qo hi jack jill jesse +hi jack, jill, jesse +``` -### asynchrony +### promises -Inevitably your tasks will need to be asynchronous. +If you return a promise, and it's rejected, then `qo` will print the error exit with `1`. ncp = require 'ncp' - task 'copy' - ncp 'original.txt' 'copy.txt' ^! - console.log 'all done' +```js +var fs = require('fs-promise'); + +task('print', function (args) { + return fs.readFile(args[0], 'utf-8').then(function (contents) { + console.log(contents); + }); +}); +``` Then - # qo copy - all done +```bash +# qo print some-file.txt +Error: ENOENT, open 'asdf' + at Error (native) +``` ### task descriptions - task 'hi' (desc: 'says hi') - console.log 'hi' +```js +task('hi', {desc: 'says hi'}, function () { + console.log('hi'); +}); +``` Then - # qo - tasks: +```bash +# qo +tasks: - hi, says hi + hi, says hi +``` # pogoscript -`qo` uses pogoscript because it does asynchronous stuff very nicely. Learn more about pogoscript from pogoscript's [cheatsheet](http://pogoscript.org/cheatsheet.html). +you can write a `qo.pogo` file too diff --git a/index.js b/index.js index cdc2a93..f726115 100644 --- a/index.js +++ b/index.js @@ -1,173 +1,152 @@ -(function() { - var Promise = require("bluebird"); - var self = this; - var fs, path, pogo, argv, util, findParentDirectoryWhere, findQo, defineTasks, isFunctionAsynchronous, parseArgs, runTaskFromWithArgs, displayTasks; - fs = require("fs"); - path = require("path"); - pogo = require("pogo"); - argv = require("optimist").argv; - util = require("util"); - findParentDirectoryWhere = function(predicate) { - var findParentWhere, gen1_asyncResult; - return new Promise(function(gen2_onFulfilled) { - findParentWhere = function(dir, p) { - var gen3_asyncResult, gen4_asyncResult; - return new Promise(function(gen2_onFulfilled) { - gen2_onFulfilled(Promise.resolve(p(dir)).then(function(gen4_asyncResult) { - return Promise.resolve(function() { - if (!gen4_asyncResult) { - return new Promise(function(gen2_onFulfilled) { - newDir = path.normalize(path.join(dir, "..")); - gen2_onFulfilled(Promise.resolve(function() { - if (dir !== "/") { - return new Promise(function(gen2_onFulfilled) { - gen2_onFulfilled(Promise.resolve(findParentWhere(newDir, p))); - }); - } - }())); - }); - } else { - return dir; - } - }()); - })); - }); - }; - gen2_onFulfilled(Promise.resolve(findParentWhere(process.cwd(), predicate))); - }); - }; - findQo = function() { - var gen5_asyncResult, foundDir; - return new Promise(function(gen2_onFulfilled) { - gen2_onFulfilled(Promise.resolve(findParentDirectoryWhere(function(dir) { - return new Promise(function(onSuccess) { - return fs.exists(path.join(dir, "qo.pogo"), function(e) { - return onSuccess(e); - }); - }); - })).then(function(gen5_asyncResult) { - foundDir = gen5_asyncResult; - if (foundDir) { - return path.join(foundDir, "qo.pogo"); - } - })); - }); - }; - defineTasks = function() { - var tasks; - tasks = {}; - global.task = function(name, $function, gen6_options) { - var self = this; - var description, desc; - description = gen6_options !== void 0 && Object.prototype.hasOwnProperty.call(gen6_options, "description") && gen6_options.description !== void 0 ? gen6_options.description : void 0; - desc = gen6_options !== void 0 && Object.prototype.hasOwnProperty.call(gen6_options, "desc") && gen6_options.desc !== void 0 ? gen6_options.desc : void 0; - return tasks[name] = { - "function": $function, - description: desc || description, - name: name - }; - }; - return tasks; - }; - isFunctionAsynchronous = function(f) { - return /function(.*continuation)/.test(f.toString()); - }; - parseArgs = function() { - var args, opts, s; - args = argv._.slice(0); - opts = {}; - for (s in argv) { - (function(s) { - if (argv.hasOwnProperty(s) && s !== "_" && s !== "$0") { - opts[s] = argv[s]; - } - })(s); +var Promise = require("bluebird"); +var fs = require("fs"); +var path = require("path"); +var argv = require("optimist").argv; +var util = require("util"); +var glob = require('glob'); + +function findFirstParentDirectory(predicate) { + function findParentWhere(dir, p) { + return p(dir).then(function(predicateResult) { + if (!predicateResult) { + var newDir = path.normalize(path.join(dir, "..")); + if (dir !== "/") { + return findParentWhere(newDir, p); } - return { - arguments: args, - options: opts - }; - }; - runTaskFromWithArgs = function(name, tasks, args) { - var task, gen7_asyncResult; - return new Promise(function(gen2_onFulfilled) { - task = tasks[name]; - gen2_onFulfilled(Promise.resolve(function() { - if (task) { - return new Promise(function(gen2_onFulfilled) { - gen2_onFulfilled(new Promise(function(gen2_onFulfilled) { - result = task.function(args.arguments, args.options); - gen2_onFulfilled(Promise.resolve(function() { - if (result && result.then instanceof Function) { - return new Promise(function(gen2_onFulfilled) { - gen2_onFulfilled(Promise.resolve(result)); - }); - } - }())); - }).then(void 0, function(e) { - process.stderr.write(util.inspect(e)); - return process.exit(1); - })); - }); - } else { - process.stderr.write("could not find task `" + name + "'"); - return process.exit(1); - } - }())); - }); - }; - displayTasks = function(tasks) { - var tn; - console.log("tasks:"); - console.log(); - for (tn in tasks) { - (function(tn) { - var task, description; - if (tasks.hasOwnProperty(tn)) { - task = tasks[tn]; - description = function() { - if (task.description) { - return ", " + task.description; - } else { - return ""; - } - }(); - console.log(" " + task.name + "" + description); - console.log(); - } - })(tn); + } else { + return predicateResult; + } + }); + } + + return findParentWhere(process.cwd(), predicate); +} + +function findQo() { + return findFirstParentDirectory(function(dir) { + return new Promise(function(fulfil, reject) { + glob(dir + '/@(qo|qo.*)', function (err, result) { + if (err) { + reject(err); + } else { + fulfil(result[0]); } - return void 0; - }; - exports.run = function() { - var self = this; - var gen8_asyncResult, qo, gen9_asyncResult; - return new Promise(function(gen2_onFulfilled) { - gen2_onFulfilled(Promise.resolve(findQo()).then(function(gen8_asyncResult) { - qo = gen8_asyncResult; - return Promise.resolve(function() { - if (qo) { - return new Promise(function(gen2_onFulfilled) { - tasks = defineTasks(); - process.chdir(path.dirname(qo)); - require(qo); - taskName = argv._.shift(); - gen2_onFulfilled(Promise.resolve(function() { - if (taskName) { - return new Promise(function(gen2_onFulfilled) { - gen2_onFulfilled(Promise.resolve(runTaskFromWithArgs(taskName, tasks, parseArgs()))); - }); - } else { - return displayTasks(tasks); - } - }())); - }); - } else { - process.stderr.write("couldn't find `qo.pogo` in any parent directory"); - return process.exit(1); - } - }()); - })); - }); + }); + }); + }); +} + +function defineTasks() { + var tasks = {}; + + global.task = function(name, fn, options) { + if (typeof options === 'function') { + var tmp = fn; + fn = options; + options = tmp; + } + + var description = options && options.hasOwnProperty('description') && options.description !== undefined? options.description: undefined; + var desc = options && options.hasOwnProperty('desc') && options.desc !== undefined? options.desc: undefined; + + tasks[name] = { + "function": fn, + description: desc || description, + name: name }; -}).call(this); \ No newline at end of file + }; + + return tasks; +} + +function parseArgs() { + var args = argv._.slice(0); + var opts = {}; + + Object.keys(argv).forEach(function (s) { + if (s !== "_" && s !== "$0") { + opts[s] = argv[s]; + } + }); + + return { + arguments: args, + options: opts + }; +} + +function runTaskFromWithArgs(name, tasks, args) { + var task = tasks[name]; + + if (task) { + function handleError(e) { + if (e.stack) { + process.stderr.write(e.stack + '\n'); + } else { + process.stderr.write(util.inspect(e) + '\n'); + } + process.exit(1); + } + + var result; + + try { + result = task.function(args.arguments, args.options); + } catch (e) { + handleError(e); + } + + if (result && typeof result.then === 'function') { + return result.then(undefined, handleError); + } + } else { + process.stderr.write("could not find task `" + name + "'"); + return process.exit(1); + } +} + +function displayTasks(tasks) { + console.log("tasks:"); + console.log(); + + Object.keys(tasks).forEach(function (tn) { + var task = tasks[tn]; + var description = + task.description + ? ', ' + task.description + : ''; + + console.log(" " + task.name + "" + description); + console.log(); + }); +} + +function requireQo(filename) { + var extension = path.extname(filename); + + if (extension && extension != '.js') { + require(extension.substring(1)); + } + + require(filename); +} + +exports.run = function() { + return findQo().then(function(qo) { + if (qo) { + var tasks = defineTasks(); + process.chdir(path.dirname(qo)); + requireQo(qo); + var taskName = argv._.shift(); + + if (taskName) { + return runTaskFromWithArgs(taskName, tasks, parseArgs()); + } else { + displayTasks(tasks); + } + } else { + process.stderr.write("couldn't find `qo.js` or `qo.pogo` in any parent directory"); + process.exit(1); + } + }); +}; diff --git a/index.pogo b/index.pogo deleted file mode 100644 index 9273125..0000000 --- a/index.pogo +++ /dev/null @@ -1,98 +0,0 @@ -fs = require 'fs' -path = require 'path' -pogo = require 'pogo' -argv = require 'optimist'.argv -util = require 'util' - -findParentDirectoryWhere (predicate) = - findParent (dir) where (p)! = - if (@not p (dir)!) - newDir = path.normalize (path.join (dir, '..')) - if (dir != '/') - findParent (newDir) where (p)! - else - dir - - findParent (process.cwd()) where (predicate)! - -findQo()! = - foundDir = findParentDirectoryWhere! @(dir) - @new Promise @(onSuccess) - fs.exists (path.join (dir, 'qo.pogo')) @(e) - onSuccess (e) - - if (foundDir) - path.join (foundDir, 'qo.pogo') - -defineTasks () = - tasks = {} - - global.task (name, function, description: nil, desc: nil) = - tasks.(name) = { - function = function - description = desc @or description - name = name - } - - tasks - -isFunction (f) asynchronous = - r/function(.*continuation)/.test (f.toString ()) - -parseArgs () = - args = argv._.slice 0 - opts = {} - - for @(s) in (argv) - if (argv.hasOwnProperty (s) @and s != '_' @and s != '$0') - opts.(s) = argv.(s) - - { arguments = args, options = opts } - -runTask (name) from (tasks) withArgs (args)! = - task = tasks.(name) - - if (task) - try - result = task.function (args.arguments, args.options) - if (result @and (result.then :: Function)) - result! - catch (e) - process.stderr.write(util.inspect(e)) - process.exit 1 - else - process.stderr.write "could not find task `#(name)'" - process.exit 1 - -displayTasks (tasks) = - console.log "tasks:" - console.log () - for @(tn) in (tasks) - if (tasks.hasOwnProperty (tn)) - task = tasks.(tn) - - description = - if (task.description) - ", #(task.description)" - else - '' - - console.log " #(task.name)#(description)" - console.log () - -exports.run () = - qo = findQo()! - - if (qo) - tasks = defineTasks () - process.chdir (path.dirname (qo)) - require (qo) - - taskName = argv._.shift () - if (taskName) - runTask (taskName) from (tasks) withArgs (parseArgs ())! - else - displayTasks (tasks) - else - process.stderr.write "couldn't find `qo.pogo` in any parent directory" - process.exit 1 diff --git a/package.json b/package.json index 6fb64a7..11ec92e 100644 --- a/package.json +++ b/package.json @@ -5,24 +5,25 @@ "main": "index.js", "bin": "bin/qo", "scripts": { - "test": "mocha test/*Spec.pogo", - "prepublish": "pogo -c index.pogo" + "test": "mocha test/*Spec.pogo" }, "author": "Tim Macfarlane ", "license": "BSD-2-Clause", "dependencies": { + "glob": "5.0.10", "optimist": "~0.6.0" }, "peerDependencies": { "pogo": "~0.8.0 || ~0.9.0" }, "devDependencies": { - "qo-ps": "0.0.4", - "mkdirp": "~0.3.5", - "rimraf": "~2.2.2", + "bluebird": "2.9.26", "chai": "~1.8.1", - "mocha": "~1.18.2", - "pogo": "~0.8.0-beta7" + "mkdirp": "~0.3.5", + "mocha": "2.2.5", + "pogo": "0.9.10", + "qo-ps": "0.0.4", + "rimraf": "~2.2.2" }, "repository": { "type": "git", diff --git a/qo.pogo b/qo.pogo index a8208c6..f42254c 100644 --- a/qo.pogo +++ b/qo.pogo @@ -12,4 +12,5 @@ task 'haha' @(args, opts) console.log (opts) task 'blah' - throw (new (Error 'asdlfksdf')) + promise! @(result, error) + error (new (Error 'asdlfksdf'))