Added logging, changed some directory structure

This commit is contained in:
2018-01-13 21:33:40 -05:00
parent f079a5f067
commit 8e72ffb917
73656 changed files with 35284 additions and 53718 deletions

View File

@@ -0,0 +1,38 @@
## 1.2.3 (September 20, 2017)
- Rolled back passing params to `args()` back as array
## 1.2.2 (September 18, 2017)
- Fixed context passed to `Command#args()`, now it's a command as expected (#10)
- Fixed consuming of literal arguments that wrongly concating with other arguments (i.e. anything going after `--` concats with arguments before `--`)
## 1.2.1 (September 18, 2017)
- Fixed multi value option processing (@tyanas & @smelukov, #9)
## 1.2.0 (June 13, 2017)
- Improved multi value option processing (@smelukov, #7)
## 1.1.3 (March 16, 2017)
- Fixed `Command#normalize()` issue when set a value for option with argument and no default value
## 1.1.2 (December 3, 2016)
- Fix exception on `Command#normalize()`
## 1.1.1 (May 10, 2016)
- Fix `chalk` version
## 1.1.0 (March 19, 2016)
- `Command#extend()` accepts parameters for passed function now
- Implement `Command#end()` method to return to parent command definition
- Fix suggestion bugs and add tests
## 1.0.0 (Oct 12, 2014)
- Initial release

View File

@@ -0,0 +1,19 @@
Copyright (C) 2014-2016 by Roman Dvornov <rdvornov@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -0,0 +1,11 @@
[![NPM version](https://img.shields.io/npm/v/clap.svg)](https://www.npmjs.com/package/clap)
[![Dependency Status](https://img.shields.io/david/lahmatiy/clap.svg)](https://david-dm.org/lahmatiy/clap)
[![Build Status](https://travis-ci.org/lahmatiy/clap.svg?branch=master)](https://travis-ci.org/lahmatiy/clap)
# Clap.js
Argument parser for command-line interfaces. It primary target to large tool sets that provides a lot of subcommands. Support for argument coercion and completion makes task run much easer, even if you doesn't use CLI.
Inspired by TJ Holowaychuk [Commander](https://github.com/visionmedia/commander.js).
[TODO: Complete readme]

View File

@@ -0,0 +1,950 @@
var MAX_LINE_WIDTH = process.stdout.columns || 200;
var MIN_OFFSET = 25;
var errorHandler;
var commandsPath;
var reAstral = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g;
var ansiRegex = /\x1B\[([0-9]{1,3}(;[0-9]{1,3})*)?[m|K]/g;
var hasOwnProperty = Object.prototype.hasOwnProperty;
function stringLength(str){
return str
.replace(ansiRegex, '')
.replace(reAstral, ' ')
.length;
}
function camelize(name){
return name.replace(/-(.)/g, function(m, ch){
return ch.toUpperCase();
});
}
function assign(dest, source){
for (var key in source)
if (hasOwnProperty.call(source, key))
dest[key] = source[key];
return dest;
}
function returnFirstArg(value){
return value;
}
function pad(width, str){
return str + Array(Math.max(0, width - stringLength(str)) + 1).join(' ');
}
function noop(){
// nothing todo
}
function parseParams(str){
// params [..<required>] [..[optional]]
// <foo> - require
// [foo] - optional
var tmp;
var left = str.trim();
var result = {
minArgsCount: 0,
maxArgsCount: 0,
args: []
};
do {
tmp = left;
left = left.replace(/^<([a-zA-Z][a-zA-Z0-9\-\_]*)>\s*/, function(m, name){
result.args.push(new Argument(name, true));
result.minArgsCount++;
result.maxArgsCount++;
return '';
});
}
while (tmp != left);
do {
tmp = left;
left = left.replace(/^\[([a-zA-Z][a-zA-Z0-9\-\_]*)\]\s*/, function(m, name){
result.args.push(new Argument(name, false));
result.maxArgsCount++;
return '';
});
}
while (tmp != left);
if (left)
throw new SyntaxError('Bad parameter description: ' + str);
return result.args.length ? result : false;
}
/**
* @class
*/
var SyntaxError = function(message){
this.message = message;
};
SyntaxError.prototype = Object.create(Error.prototype);
SyntaxError.prototype.name = 'SyntaxError';
SyntaxError.prototype.clap = true;
/**
* @class
*/
var Argument = function(name, required){
this.name = name;
this.required = required;
};
Argument.prototype = {
required: false,
name: '',
normalize: returnFirstArg,
suggest: function(){
return [];
}
};
/**
* @class
* @param {string} usage
* @param {string} description
*/
var Option = function(usage, description){
var self = this;
var params;
var left = usage.trim()
// short usage
// -x
.replace(/^-([a-zA-Z])(?:\s*,\s*|\s+)/, function(m, name){
self.short = name;
return '';
})
// long usage
// --flag
// --no-flag - invert value if flag is boolean
.replace(/^--([a-zA-Z][a-zA-Z0-9\-\_]+)\s*/, function(m, name){
self.long = name;
self.name = name.replace(/(^|-)no-/, '$1');
self.defValue = self.name != self.long;
return '';
});
if (!this.long)
throw new SyntaxError('Usage has no long name: ' + usage);
try {
params = parseParams(left);
} catch(e) {
throw new SyntaxError('Bad paramenter description in usage for option: ' + usage, e);
}
if (params)
{
left = '';
this.name = this.long;
this.defValue = undefined;
assign(this, params);
}
if (left)
throw new SyntaxError('Bad usage description for option: ' + usage);
if (!this.name)
this.name = this.long;
this.description = description || '';
this.usage = usage.trim();
this.camelName = camelize(this.name);
};
Option.prototype = {
name: '',
description: '',
short: '',
long: '',
beforeInit: false,
required: false,
minArgsCount: 0,
maxArgsCount: 0,
args: null,
defValue: undefined,
normalize: returnFirstArg
};
//
// Command
//
function createOption(usage, description, opt_1, opt_2){
var option = new Option(usage, description);
// if (option.bool && arguments.length > 2)
// throw new SyntaxError('bool flags can\'t has default value or validator');
if (arguments.length == 3)
{
if (opt_1 && opt_1.constructor === Object)
{
for (var key in opt_1)
if (key == 'normalize' ||
key == 'defValue' ||
key == 'beforeInit')
option[key] = opt_1[key];
// old name for `beforeInit` setting is `hot`
if (opt_1.hot)
option.beforeInit = true;
}
else
{
if (typeof opt_1 == 'function')
option.normalize = opt_1;
else
option.defValue = opt_1;
}
}
if (arguments.length == 4)
{
if (typeof opt_1 == 'function')
option.normalize = opt_1;
option.defValue = opt_2;
}
return option;
}
function addOptionToCommand(command, option){
var commandOption;
// short
if (option.short)
{
commandOption = command.short[option.short];
if (commandOption)
throw new SyntaxError('Short option name -' + option.short + ' already in use by ' + commandOption.usage + ' ' + commandOption.description);
command.short[option.short] = option;
}
// long
commandOption = command.long[option.long];
if (commandOption)
throw new SyntaxError('Long option --' + option.long + ' already in use by ' + commandOption.usage + ' ' + commandOption.description);
command.long[option.long] = option;
// camel
commandOption = command.options[option.camelName];
if (commandOption)
throw new SyntaxError('Name option ' + option.camelName + ' already in use by ' + commandOption.usage + ' ' + commandOption.description);
command.options[option.camelName] = option;
// set default value
if (typeof option.defValue != 'undefined')
command.setOption(option.camelName, option.defValue, true);
// add to suggestions
command.suggestions.push('--' + option.long);
return option;
}
function findVariants(obj, entry){
return obj.suggestions.filter(function(item){
return item.substr(0, entry.length) == entry;
});
}
function processArgs(command, args, suggest){
function processOption(option, command){
var params = [];
if (option.maxArgsCount)
{
for (var j = 0; j < option.maxArgsCount; j++)
{
var suggestPoint = suggest && i + 1 + j >= args.length - 1;
var nextToken = args[i + 1];
// TODO: suggestions for options
if (suggestPoint)
{
// search for suggest
noSuggestions = true;
i = args.length;
return;
}
if (!nextToken || nextToken[0] == '-')
break;
params.push(args[++i]);
}
if (params.length < option.minArgsCount)
throw new SyntaxError('Option ' + token + ' should be used with at least ' + option.minArgsCount + ' argument(s)\nUsage: ' + option.usage);
if (option.maxArgsCount == 1)
params = params[0];
}
else
{
params = !option.defValue;
}
//command.values[option.camelName] = newValue;
resultToken.options.push({
option: option,
value: params
});
}
var resultToken = {
command: command,
args: [],
literalArgs: [],
options: []
};
var result = [resultToken];
var suggestStartsWith = '';
var noSuggestions = false;
var collectArgs = false;
var commandArgs = [];
var noOptionsYet = true;
var option;
commandsPath = [command.name];
for (var i = 0; i < args.length; i++)
{
var suggestPoint = suggest && i == args.length - 1;
var token = args[i];
if (collectArgs)
{
commandArgs.push(token);
continue;
}
if (suggestPoint && (token == '--' || token == '-' || token[0] != '-'))
{
suggestStartsWith = token;
break; // returns long option & command list outside the loop
}
if (token == '--')
{
resultToken.args = commandArgs;
commandArgs = [];
noOptionsYet = false;
collectArgs = true;
continue;
}
if (token[0] == '-')
{
noOptionsYet = false;
if (commandArgs.length)
{
//command.args_.apply(command, commandArgs);
resultToken.args = commandArgs;
commandArgs = [];
}
if (token[1] == '-')
{
// long option
option = command.long[token.substr(2)];
if (!option)
{
// option doesn't exist
if (suggestPoint)
return findVariants(command, token);
else
throw new SyntaxError('Unknown option: ' + token);
}
// process option
processOption(option, command);
}
else
{
// short flags sequence
if (!/^-[a-zA-Z]+$/.test(token))
throw new SyntaxError('Wrong short option sequence: ' + token);
if (token.length == 2)
{
option = command.short[token[1]];
if (!option)
throw new SyntaxError('Unknown short option name: -' + token[1]);
// single option
processOption(option, command);
}
else
{
// short options sequence
for (var j = 1; j < token.length; j++)
{
option = command.short[token[j]];
if (!option)
throw new SyntaxError('Unknown short option name: -' + token[j]);
if (option.maxArgsCount)
throw new SyntaxError('Non-boolean option -' + token[j] + ' can\'t be used in short option sequence: ' + token);
processOption(option, command);
}
}
}
}
else
{
if (command.commands[token] && (!command.params || commandArgs.length >= command.params.minArgsCount))
{
if (noOptionsYet)
{
resultToken.args = commandArgs;
commandArgs = [];
}
if (command.params && resultToken.args.length < command.params.minArgsCount)
throw new SyntaxError('Missed required argument(s) for command `' + command.name + '`');
// switch control to another command
command = command.commands[token];
noOptionsYet = true;
commandsPath.push(command.name);
resultToken = {
command: command,
args: [],
literalArgs: [],
options: []
};
result.push(resultToken);
}
else
{
if (noOptionsYet && command.params && commandArgs.length < command.params.maxArgsCount)
{
commandArgs.push(token);
continue;
}
if (suggestPoint)
return findVariants(command, token);
else
throw new SyntaxError('Unknown command: ' + token);
}
}
}
if (suggest)
{
if (collectArgs || noSuggestions)
return [];
return findVariants(command, suggestStartsWith);
}
else
{
if (!noOptionsYet)
resultToken.literalArgs = commandArgs;
else
resultToken.args = commandArgs;
if (command.params && resultToken.args.length < command.params.minArgsCount)
throw new SyntaxError('Missed required argument(s) for command `' + command.name + '`');
}
return result;
}
function setFunctionFactory(name){
return function(fn){
var property = name + '_';
if (this[property] !== noop)
throw new SyntaxError('Method `' + name + '` could be invoked only once');
if (typeof fn != 'function')
throw new SyntaxError('Value for `' + name + '` method should be a function');
this[property] = fn;
return this;
}
}
/**
* @class
*/
var Command = function(name, params){
this.name = name;
this.params = false;
try {
if (params)
this.params = parseParams(params);
} catch(e) {
throw new SyntaxError('Bad paramenter description in command definition: ' + this.name + ' ' + params);
}
this.commands = {};
this.options = {};
this.short = {};
this.long = {};
this.values = {};
this.defaults_ = {};
this.suggestions = [];
this.option('-h, --help', 'Output usage information', function(){
this.showHelp();
process.exit(0);
}, undefined);
};
Command.prototype = {
params: null,
commands: null,
options: null,
short: null,
long: null,
values: null,
defaults_: null,
suggestions: null,
description_: '',
version_: '',
initContext_: noop,
init_: noop,
delegate_: noop,
action_: noop,
args_: noop,
end_: null,
option: function(usage, description, opt_1, opt_2){
addOptionToCommand(this, createOption.apply(null, arguments));
return this;
},
shortcut: function(usage, description, fn, opt_1, opt_2){
if (typeof fn != 'function')
throw new SyntaxError('fn should be a function');
var command = this;
var option = addOptionToCommand(this, createOption(usage, description, opt_1, opt_2));
var normalize = option.normalize;
option.normalize = function(value){
var values;
value = normalize.call(command, value);
values = fn(value);
for (var name in values)
if (hasOwnProperty.call(values, name))
if (hasOwnProperty.call(command.options, name))
command.setOption(name, values[name]);
else
command.values[name] = values[name];
command.values[option.name] = value;
return value;
};
return this;
},
hasOption: function(name){
return hasOwnProperty.call(this.options, name);
},
hasOptions: function(){
return Object.keys(this.options).length > 0;
},
setOption: function(name, value, isDefault){
if (!this.hasOption(name))
throw new SyntaxError('Option `' + name + '` is not defined');
var option = this.options[name];
var oldValue = this.values[name];
var newValue = option.normalize.call(this, value, oldValue);
this.values[name] = option.maxArgsCount ? newValue : value;
if (isDefault && !hasOwnProperty.call(this.defaults_, name))
this.defaults_[name] = this.values[name];
},
setOptions: function(values){
for (var name in values)
if (hasOwnProperty.call(values, name) && this.hasOption(name))
this.setOption(name, values[name]);
},
reset: function(){
this.values = {};
assign(this.values, this.defaults_);
},
command: function(nameOrCommand, params){
var name;
var command;
if (nameOrCommand instanceof Command)
{
command = nameOrCommand;
name = command.name;
}
else
{
name = nameOrCommand;
if (!/^[a-zA-Z][a-zA-Z0-9\-\_]*$/.test(name))
throw new SyntaxError('Wrong command name: ' + name);
}
// search for existing one
var subcommand = this.commands[name];
if (!subcommand)
{
// create new one if not exists
subcommand = command || new Command(name, params);
subcommand.end_ = this;
this.commands[name] = subcommand;
this.suggestions.push(name);
}
return subcommand;
},
end: function() {
return this.end_;
},
hasCommands: function(){
return Object.keys(this.commands).length > 0;
},
version: function(version, usage, description){
if (this.version_)
throw new SyntaxError('Version for command could be set only once');
this.version_ = version;
this.option(
usage || '-v, --version',
description || 'Output version',
function(){
console.log(this.version_);
process.exit(0);
},
undefined
);
return this;
},
description: function(description){
if (this.description_)
throw new SyntaxError('Description for command could be set only once');
this.description_ = description;
return this;
},
init: setFunctionFactory('init'),
initContext: setFunctionFactory('initContext'),
args: setFunctionFactory('args'),
delegate: setFunctionFactory('delegate'),
action: setFunctionFactory('action'),
extend: function(fn){
fn.apply(null, [this].concat(Array.prototype.slice.call(arguments, 1)));
return this;
},
parse: function(args, suggest){
if (!args)
args = process.argv.slice(2);
if (!errorHandler)
return processArgs(this, args, suggest);
else
try {
return processArgs(this, args, suggest);
} catch(e) {
errorHandler(e.message || e);
}
},
run: function(args, context){
var commands = this.parse(args);
if (!commands)
return;
var prevCommand;
var context = assign({}, context || this.initContext_());
for (var i = 0; i < commands.length; i++)
{
var item = commands[i];
var command = item.command;
// reset command values
command.reset();
command.context = context;
command.root = this;
if (prevCommand)
prevCommand.delegate_(command);
// apply beforeInit options
item.options.forEach(function(entry){
if (entry.option.beforeInit)
command.setOption(entry.option.camelName, entry.value);
});
command.init_(item.args.slice()); // use slice to avoid args mutation in handler
if (item.args.length)
command.args_(item.args.slice()); // use slice to avoid args mutation in handler
// apply regular options
item.options.forEach(function(entry){
if (!entry.option.beforeInit)
command.setOption(entry.option.camelName, entry.value);
});
prevCommand = command;
}
// return last command action result
if (command)
return command.action_(item.args, item.literalArgs);
},
normalize: function(values){
var result = {};
if (!values)
values = {};
for (var name in this.values)
if (hasOwnProperty.call(this.values, name))
result[name] = hasOwnProperty.call(values, name) && hasOwnProperty.call(this.options, name)
? this.options[name].normalize.call(this, values[name])
: this.values[name];
for (var name in values)
if (hasOwnProperty.call(values, name) && !hasOwnProperty.call(result, name))
result[name] = values[name];
return result;
},
showHelp: function(){
console.log(showCommandHelp(this));
}
};
//
// help
//
/**
* Return program help documentation.
*
* @return {String}
* @api private
*/
function showCommandHelp(command){
function breakByLines(str, offset){
var words = str.split(' ');
var maxWidth = MAX_LINE_WIDTH - offset || 0;
var lines = [];
var line = '';
while (words.length)
{
var word = words.shift();
if (!line || (line.length + word.length + 1) < maxWidth)
{
line += (line ? ' ' : '') + word;
}
else
{
lines.push(line);
words.unshift(word);
line = '';
}
}
lines.push(line);
return lines.map(function(line, idx){
return (idx && offset ? pad(offset, '') : '') + line;
}).join('\n');
}
function args(command){
return command.params.args.map(function(arg){
return arg.required
? '<' + arg.name + '>'
: '[' + arg.name + ']';
}).join(' ');
}
function commandsHelp(){
if (!command.hasCommands())
return '';
var maxNameLength = MIN_OFFSET - 2;
var lines = Object.keys(command.commands).sort().map(function(name){
var subcommand = command.commands[name];
var line = {
name: chalk.green(name) + chalk.gray(
(subcommand.params ? ' ' + args(subcommand) : '')
// (subcommand.hasOptions() ? ' [options]' : '')
),
description: subcommand.description_ || ''
};
maxNameLength = Math.max(maxNameLength, stringLength(line.name));
return line;
});
return [
'',
'Commands:',
'',
lines.map(function(line){
return ' ' + pad(maxNameLength, line.name) + ' ' + breakByLines(line.description, maxNameLength + 4);
}).join('\n'),
''
].join('\n');
}
function optionsHelp(){
if (!command.hasOptions())
return '';
var hasShortOptions = Object.keys(command.short).length > 0;
var maxNameLength = MIN_OFFSET - 2;
var lines = Object.keys(command.long).sort().map(function(name){
var option = command.long[name];
var line = {
name: option.usage
.replace(/^(?:-., |)/, function(m){
return m || (hasShortOptions ? ' ' : '');
})
.replace(/(^|\s)(-[^\s,]+)/ig, function(m, p, flag){
return p + chalk.yellow(flag);
}),
description: option.description
};
maxNameLength = Math.max(maxNameLength, stringLength(line.name));
return line;
});
// Prepend the help information
return [
'',
'Options:',
'',
lines.map(function(line){
return ' ' + pad(maxNameLength, line.name) + ' ' + breakByLines(line.description, maxNameLength + 4);
}).join('\n'),
''
].join('\n');
}
var output = [];
var chalk = require('chalk');
chalk.enabled = module.exports.color && process.stdout.isTTY;
if (command.description_)
output.push(command.description_ + '\n');
output.push(
'Usage:\n\n ' +
chalk.cyan(commandsPath ? commandsPath.join(' ') : command.name) +
(command.params ? ' ' + chalk.magenta(args(command)) : '') +
(command.hasOptions() ? ' [' + chalk.yellow('options') + ']' : '') +
(command.hasCommands() ? ' [' + chalk.green('command') + ']' : ''),
commandsHelp() +
optionsHelp()
);
return output.join('\n');
};
//
// export
//
module.exports = {
color: true,
Error: SyntaxError,
Argument: Argument,
Command: Command,
Option: Option,
error: function(fn){
if (errorHandler)
throw new SyntaxError('Error handler should be set only once');
if (typeof fn != 'function')
throw new SyntaxError('Error handler should be a function');
errorHandler = fn;
return this;
},
create: function(name, params){
return new Command(name || require('path').basename(process.argv[1]) || 'cli', params);
},
confirm: function(message, fn){
process.stdout.write(message);
process.stdin.setEncoding('utf8');
process.stdin.once('data', function(val){
process.stdin.pause();
fn(/^y|yes|ok|true$/i.test(val.trim()));
});
process.stdin.resume();
}
};

View File

@@ -0,0 +1,73 @@
{
"_args": [
[
"clap@1.2.3",
"C:\\Users\\deranjer\\go\\src\\github.com\\deranjer\\goTorrent\\torrent-project"
]
],
"_from": "clap@1.2.3",
"_id": "clap@1.2.3",
"_inBundle": false,
"_integrity": "sha512-4CoL/A3hf90V3VIEjeuhSvlGFEHKzOz+Wfc2IVZc+FaUgU0ZQafJTP49fvnULipOPcAfqhyI2duwQyns6xqjYA==",
"_location": "/react-scripts/clap",
"_phantomChildren": {},
"_requested": {
"type": "version",
"registry": true,
"raw": "clap@1.2.3",
"name": "clap",
"escapedName": "clap",
"rawSpec": "1.2.3",
"saveSpec": null,
"fetchSpec": "1.2.3"
},
"_requiredBy": [
"/react-scripts/csso"
],
"_resolved": "https://registry.npmjs.org/clap/-/clap-1.2.3.tgz",
"_spec": "1.2.3",
"_where": "C:\\Users\\deranjer\\go\\src\\github.com\\deranjer\\goTorrent\\torrent-project",
"author": {
"name": "Roman Dvornov",
"email": "rdvornov@gmail.com"
},
"bugs": {
"url": "https://github.com/lahmatiy/clap/issues"
},
"dependencies": {
"chalk": "^1.1.3"
},
"description": "Command line argument parser",
"devDependencies": {
"mocha": "^2.4.5"
},
"engines": {
"node": ">=0.10.0"
},
"files": [
"index.js",
"HISTORY.md",
"LICENSE",
"README.md"
],
"homepage": "https://github.com/lahmatiy/clap",
"keywords": [
"cli",
"command",
"option",
"argument",
"completion"
],
"license": "MIT",
"main": "index.js",
"name": "clap",
"repository": {
"type": "git",
"url": "git+https://github.com/lahmatiy/clap.git"
},
"scripts": {
"test": "mocha test -R spec"
},
"title": "Command line argument parser",
"version": "1.2.3"
}