Comparing version 1.2.0 to 1.3.2
{ | ||
"name": "commop", | ||
"version": "1.2.0", | ||
"version": "1.3.2", | ||
"description": "cli interface for nodejs apps", | ||
@@ -25,4 +25,6 @@ "main": "index.js", | ||
"devDependencies": { | ||
"mocha": "*" | ||
"mocha": "^3.4.1", | ||
"bluebird": "^3.5.0", | ||
"object-assign": "^4.1.1" | ||
} | ||
} |
@@ -36,3 +36,4 @@ var util = require ("util"); | ||
optionImplied: "option '%s' implies option '%s' to be defined", | ||
commandScriptPlatformError: "command script configuration is object, but current platform name or 'default' not found" | ||
commandScriptPlatformError: "command script configuration is object, but current platform name or 'default' not found", | ||
functionNotFound: "function '%s' not found in origin" | ||
}; | ||
@@ -759,3 +760,14 @@ | ||
var child = exec (scriptName, {env: cmd.options}, function (error, stdout, stderr) { | ||
var child; | ||
var killScript = function () { | ||
if (!child) | ||
return; | ||
child.kill (); | ||
process.exit (); | ||
}; | ||
process.on ('SIGINT', killScript); | ||
child = exec (scriptName, {env: cmd.options}, function (error, stdout, stderr) { | ||
// The callback gets the arguments (error, stdout, stderr). | ||
@@ -779,5 +791,9 @@ // On success, error will be null. On error, error will be an instance | ||
next (); | ||
next (null, data); | ||
}.bind (this)); | ||
child.on ('exit', function () { | ||
child = undefined; | ||
}); | ||
} | ||
@@ -792,8 +808,6 @@ | ||
if (!cb) cb = function () {} | ||
if ("usage" in cmd) { | ||
var showUsage = cb (cmd); | ||
if (showUsage || showUsage === undefined) | ||
if (this.showUsage || this.showUsage === undefined) | ||
console.log (cmd.usage); | ||
cb && cb (cmd); | ||
return; | ||
@@ -803,9 +817,9 @@ } | ||
if (cmd.errors) { | ||
var showErrors = cb (cmd); | ||
if (showErrors || showErrors === undefined) { | ||
if (this.showErrors || this.showErrors === undefined) { | ||
cmd.errors.forEach (function (err) { | ||
console.error (this.errorPresenter (pathChunk)); | ||
console.error (this.errorPresenter (err)); | ||
}.bind (this)); | ||
return; | ||
} | ||
cb && cb (cmd); | ||
return; | ||
} | ||
@@ -818,3 +832,3 @@ | ||
var data = {}; | ||
var _data = {}; | ||
@@ -830,27 +844,81 @@ // TODO: add support for flow/script | ||
var launchIdx = -1; | ||
var launchNext = function (err) { | ||
// probably need to stop on error? | ||
if (err) | ||
this.appendError ( | ||
this.l10nMessage ("taskError"), | ||
this.helpNamePresenter (methodNames[launchIdx]), | ||
err | ||
); | ||
launchIdx ++; | ||
var methodName = methodNames[launchIdx]; | ||
if (typeof methodName === "function") { | ||
methodName (cmd, data, launchNext); | ||
} else if (methodName) { | ||
origin[methodName] (cmd, data, launchNext); | ||
} else { | ||
cb (cmd, data); | ||
} | ||
}.bind(this); | ||
var _err; | ||
launchNext (null); | ||
var p = new Promise (function (resolve, reject) { | ||
var launchNext = function (err, data) { | ||
// probably need to stop on error? | ||
if (err) { | ||
_err = err; | ||
this.appendError ( | ||
this.l10nMessage ("taskError"), | ||
this.helpNamePresenter (methodNames[launchIdx]), | ||
err | ||
); | ||
} | ||
launchIdx ++; | ||
var methodName = methodNames[launchIdx]; | ||
var cmdResult; | ||
// methodName is actually a function | ||
if (typeof methodName === "function") { | ||
cmdResult = methodName (cmd, data, launchNext); | ||
} else if (methodName) { | ||
if (methodName in origin) { | ||
if (typeof origin[methodName] === 'function') { | ||
cmdResult = origin[methodName] (cmd, data, launchNext); | ||
} else if (origin[methodName].then) { | ||
cmdResult = origin[methodName]; | ||
} else { | ||
this.appendError ( | ||
this.l10nMessage ("functionNotFound"), | ||
this.helpNamePresenter (methodName) | ||
); | ||
return launchNext (null); | ||
} | ||
} else { | ||
this.appendError ( | ||
this.l10nMessage ("functionNotFound"), | ||
this.helpNamePresenter (methodName) | ||
); | ||
return launchNext (null); | ||
} | ||
} else { | ||
if (cb) { | ||
cb (cmd, data); | ||
} else { | ||
if (this.errors && this.errors.length) { | ||
reject (new Error (this.errors[0])); | ||
} else { | ||
resolve ({ | ||
cmd: cmd, | ||
data: data | ||
}); | ||
} | ||
} | ||
} | ||
if (cmdResult && cmdResult.then) { // thenable | ||
cmdResult.then (function (data) { | ||
launchNext (null, data); | ||
}, function (err) { | ||
launchNext (err, data); | ||
}); | ||
} | ||
}.bind (this); | ||
launchNext (null, _data); | ||
}.bind (this)); | ||
if (!cb) | ||
return p; | ||
p.then (function () {}); | ||
} | ||
module.exports = ArgvParser; |
@@ -69,3 +69,3 @@ # CommOp | ||
I had to create cli for arduino supporting ommands like `compile`, `upload` and `platform`. | ||
I had to create cli for arduino supporting commands like `compile`, `upload` and `platform`. | ||
Each task should have `verbose` and `arduino` options. | ||
@@ -135,3 +135,4 @@ And `compile` and `upload` commands should have `sketch` and `board` options. | ||
Each command must have a handler, described by `run` key in [command configuration](#configuration-for-command). | ||
If handler is an array, then tasks launched one after one. | ||
If handler is an array, then tasks launched one after one. Task laso can be a promise or function, | ||
which return a promise. | ||
@@ -155,2 +156,6 @@ ```javascript | ||
Data should be modified and returned via `next` callback or promise's resolve. | ||
Next task will be launched regardless of return status of previous task. | ||
If your task rely on data from previous command, assert data section. | ||
Those tasks can be object methods, you just have to provide an origin as parameter to the `start` call. | ||
@@ -231,2 +236,5 @@ If origin is not provided, `require.main` is used instead (it is your main module exports). | ||
// usage is displayed automatically unless showUsage is false | ||
launcher.showUsage = false; | ||
// usage will be displayed automatically if there is no commands in argv | ||
@@ -236,4 +244,6 @@ launcher.start ([], null, function (cmd, data) { | ||
if ("usage" in cmd) { | ||
// you can return false to skip usage output | ||
return false; | ||
// do something like this: | ||
if (cmd.branch[0] === 'xxx') { | ||
launcher.helpForCommand (["xxx"]); | ||
} | ||
} | ||
@@ -259,4 +269,2 @@ | ||
if ("usage" in cmd) { | ||
// you can return false to skip usage output | ||
return false; | ||
} | ||
@@ -263,0 +271,0 @@ }); |
@@ -5,2 +5,4 @@ var path = require ('path'); | ||
require ('./promise-shim'); | ||
var globalVerbose = process.env.VERBOSE || false; | ||
@@ -7,0 +9,0 @@ |
@@ -5,2 +5,4 @@ var path = require ('path'); | ||
require ('./promise-shim'); | ||
var parserModule = process.env.COMMOP_PARSER ? "../parser/" + process.env.COMMOP_PARSER : "../"; | ||
@@ -7,0 +9,0 @@ |
@@ -5,2 +5,4 @@ var path = require ('path'); | ||
require ('./promise-shim'); | ||
var parserModule = process.env.COMMOP_PARSER ? "../parser/" + process.env.COMMOP_PARSER : "../"; | ||
@@ -25,3 +27,3 @@ | ||
data.arduino = true; | ||
next (); | ||
next (null, data); | ||
}, 10); | ||
@@ -33,3 +35,3 @@ } | ||
data.aaa = "bbb"; | ||
next (); | ||
next (null, data); | ||
}, 10); | ||
@@ -41,14 +43,33 @@ } | ||
data.bbb = "ccc"; | ||
next (); | ||
next (null, data); | ||
}, 10); | ||
} | ||
require.main.compile = function (cmdConf, data) { | ||
return new Promise (function (resolve, reject) { | ||
setTimeout (function () { | ||
data.ccc = "ddd"; | ||
resolve (data); | ||
}, 10); | ||
}) | ||
} | ||
require.main.upload = function (cmdConf, data) { | ||
return new Promise (function (resolve, reject) { | ||
setTimeout (function () { | ||
data.ddd = "eee"; | ||
resolve (data); | ||
}, 10); | ||
}) | ||
} | ||
describe (baseName+" execute command with", function () { | ||
it ("no handler", function (done) { | ||
optParser.showUsage = false; | ||
var cmd = optParser.start ([], require.main, function (cmd, data) { | ||
assert ("usage" in cmd, "no handler for no command, can display usage"); | ||
optParser.showUsage = true; | ||
done(); | ||
return false; | ||
}); | ||
@@ -67,10 +88,90 @@ }); | ||
assert ("aaa" in data, "task stored 'aaa' key in data"); | ||
assert ("aaa" in data, "task stored 'bbb' key in data"); | ||
assert ("bbb" in data, "task stored 'bbb' key in data"); | ||
done(); | ||
}); | ||
}); | ||
it ("multihandler with promises", function (done) { | ||
var cmd = optParser.start (["upload"], require.main, function (cmd, data) { | ||
assert ("aaa" in data, "task stored 'aaa' key in data"); | ||
assert ("ccc" in data, "task stored 'ccc' key in data"); | ||
assert ("ddd" in data, "task stored 'ddd' key in data"); | ||
done(); | ||
}); | ||
}); | ||
it ("multihandler with promises, returning promise", function () { | ||
return optParser.start (["upload"], require.main).then (function (result) { | ||
var cmd = result.cmd; | ||
var data = result.data; | ||
assert ("aaa" in data, "task stored 'aaa' key in data"); | ||
assert ("ccc" in data, "task stored 'ccc' key in data"); | ||
assert ("ddd" in data, "task stored 'ddd' key in data"); | ||
return Promise.resolve (); | ||
}); | ||
}); | ||
}); | ||
// TODO: test flow key | ||
describe (baseName+" launching command using shell expansion", function () { | ||
it ("have config", function (done) { | ||
// TODO: test script and flow keys | ||
var nodePath = process.argv[0]; | ||
var testConfig2 = { | ||
options: { | ||
xxx: {type: "boolean"}, | ||
BBB: {type: "string"}, | ||
DDD: {type: "string"} | ||
}, | ||
commands: { | ||
node: { | ||
script: nodePath + " -e 'console.log (process.argv)' AAA=${BBB} CCC=${DDD}", | ||
options: ["BBB", "DDD"] | ||
} | ||
} | ||
}; | ||
testConfig2.ignoreUnknownCommands = true; | ||
var commop = new OptionParser (testConfig2); | ||
commop.start (["node", "--BBB", "123", "--DDD", "asdfg"], null, function (cmd, data) { | ||
assert (data.scriptStdout.match (/AAA=123/), "shell expansion ok"); | ||
assert (data.scriptStdout.match (/CCC=asdfg/), "shell expansion 2 ok"); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
describe (baseName+" launching command using promise", function () { | ||
it ("have config", function () { | ||
var nodePath = process.argv[0]; | ||
require.main.exports._p1 = Promise.resolve ({a: 123}); | ||
require.main.exports._p2 = function (cmd, data) { | ||
return Promise.resolve (Object.assign (data, {b: 456})); | ||
} | ||
// require.main.exports._p2 = Promise.reject (new Error ("456")); | ||
var testConfig2 = { | ||
options: {}, | ||
commands: { | ||
node: { | ||
run: [ | ||
"_p1", | ||
"_p2" | ||
], | ||
// options: ["BBB", "DDD"] | ||
} | ||
} | ||
}; | ||
testConfig2.ignoreUnknownCommands = true; | ||
var commop = new OptionParser (testConfig2); | ||
return commop.start (["node"], null) | ||
}); | ||
}); |
@@ -6,2 +6,4 @@ var path = require ('path'); | ||
require ('./promise-shim'); | ||
var parserModule = process.env.COMMOP_PARSER ? "../parser/" + process.env.COMMOP_PARSER : "../"; | ||
@@ -8,0 +10,0 @@ |
@@ -5,2 +5,4 @@ var path = require ('path'); | ||
require ('./promise-shim'); | ||
var parserModule = process.env.COMMOP_PARSER ? "../parser/" + process.env.COMMOP_PARSER : "../"; | ||
@@ -71,29 +73,1 @@ | ||
describe (baseName+" launching command using shell expansion", function () { | ||
it ("have config", function (done) { | ||
var nodePath = process.argv[0]; | ||
var testConfig2 = { | ||
options: { | ||
xxx: {type: "boolean"}, | ||
BBB: {type: "string"}, | ||
DDD: {type: "string"} | ||
}, | ||
commands: { | ||
node: { | ||
script: nodePath + " -e 'console.log (process.argv)' AAA=${BBB} CCC=${DDD}", | ||
options: ["BBB", "DDD"] | ||
} | ||
} | ||
}; | ||
testConfig2.ignoreUnknownCommands = true; | ||
var commop = new OptionParser (testConfig2); | ||
commop.start (["node", "--BBB", "123", "--DDD", "asdfg"], null, function (cmd, data) { | ||
assert (data.scriptStdout.match (/AAA=123/), "shell expansion ok"); | ||
assert (data.scriptStdout.match (/CCC=asdfg/), "shell expansion 2 ok"); | ||
done(); | ||
}); | ||
}); | ||
}); |
Sorry, the diff of this file is not supported yet
76078
20
2036
378
3