From cd5ac50c6aee292cf2f7739e6aed1ba8c192ee8b Mon Sep 17 00:00:00 2001 From: Joel Kemp Date: Sun, 21 Dec 2014 01:24:22 -0500 Subject: [PATCH] Use the cli's new flags. Fixes #86 --- node_dependents.py | 24 +- node_modules/dependents/README.md | 24 +- .../dependents/bin/dependencyLookup.js | 13 +- node_modules/dependents/bin/dependents.js | 47 +- node_modules/dependents/index.js | 63 +- node_modules/dependents/lib/util.js | 4 +- .../node_modules/commander/Readme.md | 256 +++++ .../node_modules/commander/index.js | 985 ++++++++++++++++++ .../node_modules/commander/package.json | 70 ++ node_modules/dependents/package.json | 17 +- node_modules/dependents/test/test.js | 91 +- package.json | 2 +- 12 files changed, 1485 insertions(+), 111 deletions(-) create mode 100644 node_modules/dependents/node_modules/commander/Readme.md create mode 100644 node_modules/dependents/node_modules/commander/index.js create mode 100644 node_modules/dependents/node_modules/commander/package.json diff --git a/node_dependents.py b/node_dependents.py index 904f89e3..7bd116e0 100644 --- a/node_dependents.py +++ b/node_dependents.py @@ -10,32 +10,40 @@ def get_dependents(options): try: - args = [options['filename'], options['root']] + args = ['--directory=' + options['root']] except: t('Misc_Error', { - "type": "filename or root not supplied" + "type": "root not supplied" }) try: if options['config']: - args.append(options['config']) + args.append('--config=' + options['config']) except: p('Dependents: Config not supplied') try: if options['exclude']: - args.append(options['exclude']) + args.append('--exclude=' + options['exclude']) except: p('Dependents: exclude list not supplied') + args.append(options['filename']) + return exec_script('dependents.js', args).split('\n') def alias_lookup(options): - try: - args = [options['config'], options['module']] - except: - args['', options['module']] + args = [] + + # if 'config' not in options: + # show_error('An error occurred. Please file an issue with the following:\n\n' + str(e), True) + + if 'config' in options: + args.append('--config=' + options['config']) + + if 'module' in options: + args.append(options['module']) return exec_script('dependencyLookup.js', args).strip() diff --git a/node_modules/dependents/README.md b/node_modules/dependents/README.md index 4030a821..68262a88 100644 --- a/node_modules/dependents/README.md +++ b/node_modules/dependents/README.md @@ -11,58 +11,62 @@ ### Usage -JS Example: +##### JS Example: ```javascript var dependents = require('dependents'); // Find all modules that require (depend on) ./a.js -dependents.for({ +dependents({ filename: './a.js', directory: './', config: 'path/to/my/config.js' // optional exclude: ['my_vendor_files'], // optional - success: function (dependents) { + success: function (err, dependents) { console.log(dependents); } }); ``` -SASS Example: +##### SASS Example: ```javascript var dependents = require('dependents'); // Find all sass files that import (depend on) _myPartial.scss -dependents.for({ +dependents({ filename: '_myPartial.scss', directory: 'path/to/my/sass', - success: function (dependents) { + success: function (err, dependents) { console.log(dependents); } }); ``` -Required options: +### Options + +##### Required options: * `filename`: the module that you want to get the dependents of * `directory`: the directory to search for dependents (also known as the "root") * `success`: a function that should accept a list of filenames representing modules that depend on the module in `filename` -Optional: +##### Optional: * `files`: list of files to search through (if you want to control the files processed). Useful in clustering. * `config`: path to your requirejs config. Used to look up path aliases. * `exclude`: a list of files and/or folders to exclude from the search. * The following 3rd party modules are excluded from the following folders by default: `node_modules`, `bower_components`, `vendor` -Or via a shell command: +### CLI Requires `npm install -g dependents` ```bash -dependents filename directory [config] +dependents --directory=path/to/my/js [options] filename ``` +* You can see all the cli options via `dependents --help` + The shell command will utilize multi-core processing if the `directory` contains more than 500 modules. That number was deduced from testing. \ No newline at end of file diff --git a/node_modules/dependents/bin/dependencyLookup.js b/node_modules/dependents/bin/dependencyLookup.js index 7ebbc959..51ffd0f1 100755 --- a/node_modules/dependents/bin/dependencyLookup.js +++ b/node_modules/dependents/bin/dependencyLookup.js @@ -3,7 +3,16 @@ 'use strict'; var lookup = require('module-lookup-amd'); -var config = process.argv[2]; -var path = process.argv[3]; + +var program = require('commander'); + +program + .version(require('../package.json').version) + .usage('[options] ') + .option('-c, --config [path]', 'location of a RequireJS config file for AMD') + .parse(process.argv); + +var config = program.config; +var path = program.args[0]; console.log(lookup(config, path)); diff --git a/node_modules/dependents/bin/dependents.js b/node_modules/dependents/bin/dependents.js index d5e06d28..7245d615 100755 --- a/node_modules/dependents/bin/dependents.js +++ b/node_modules/dependents/bin/dependents.js @@ -5,11 +5,6 @@ var dependents = require('../'); var util = require('../lib/util'); -var filename = process.argv[2]; -var directory = process.argv[3]; -var config = process.argv[4]; -var exclude = process.argv[5]; - var getJSFiles = require('get-all-js-files'); var cluster = require('cluster'); var q = require('q'); @@ -17,6 +12,22 @@ var dir = require('node-dir'); var path = require('path'); var ConfigFile = require('requirejs-config-file').ConfigFile; +var program = require('commander'); + +program + .version(require('../package.json').version) + .usage('[options] ') + .option('-d, --directory ', 'location of JS files') + .option('-c, --config [path]', 'location of a RequireJS config file for AMD') + .option('-e, --exclude [path,...]', + 'comma-separated list of files and folder names to exclude') + .parse(process.argv); + +var directory = program.directory; +var config = program.config; +var exclude = program.exclude; +var filename = program.args[0]; + /** * Uniquely aggregates the dependents across forks (if used). * This being an object allows us to avoid duplicate dependents. @@ -82,8 +93,9 @@ function spawnWorkers(filename, files, cb) { delegateWork(worker); - worker.on('message', function(deps) { - deps.forEach(function(depFilename) { + worker.on('message', function(data) { + if (data.err) { throw data.err; } + data.deps.forEach(function(depFilename) { _dependents[depFilename] = 1; }); @@ -97,7 +109,7 @@ function spawnWorkers(filename, files, cb) { throw new Error('missed some files'); } - cb(Object.keys(_dependents)); + cb(null, Object.keys(_dependents)); }); } @@ -120,8 +132,11 @@ function getMoreFiles(files, chunkSize) { return _files; } -/** @param {Array} dependents */ -function printDependents(dependents) { +/** + * @param {Object} err + * @param {String[]} dependents + */ +function printDependents(err, dependents) { dependents.forEach(function(dependent) { console.log(dependent); }); @@ -136,7 +151,7 @@ function filesCb(files) { spawnWorkers(filename, files, printDependents); } else { - dependents.for({ + dependents({ filename: filename, directory: directory, files: files, @@ -152,6 +167,7 @@ if (cluster.isMaster) { exclude = exclude.split(','); } + exclude = util.DEFAULT_EXCLUDE_DIR.concat(exclude); var exclusions = util.processExcludes(exclude, directory); // Convert once and reuse across processes @@ -183,14 +199,17 @@ if (cluster.isWorker) { var files = args.files; var config = args.config; - dependents.for({ + dependents({ filename: filename, directory: directory, files: files, config: config, exclusions: exclude, - success: function(deps) { - process.send(deps); + success: function(err, deps) { + process.send({ + err: err, + deps: deps + }); } }); }); diff --git a/node_modules/dependents/index.js b/node_modules/dependents/index.js index d472f96f..0f216b29 100644 --- a/node_modules/dependents/index.js +++ b/node_modules/dependents/index.js @@ -18,11 +18,14 @@ var util = require('./lib/util'); * @param {String} [options.config] - Path to the shim config * @param {String[]} [options.exclusions] - List of files and directories to exclude */ -module.exports.for = function(options) { +module.exports = function dependents(options) { if (!options || !options.filename) { - throw new Error('expected filename whose dependents to compute'); + throw new Error('expected a filename'); } + if (!options.success) { throw new Error('expected success callback'); } + if (!options.directory) { throw new Error('expected directory name'); } + options.filename = path.resolve(options.filename); options.exclusions = options.exclusions || []; @@ -32,7 +35,7 @@ module.exports.for = function(options) { options.dependents = {}; if (options.config && typeof options.config !== 'object') { - options.config = this._readConfig(options.config); + options.config = dependents._readConfig(options.config); } processFiles(options); @@ -56,38 +59,34 @@ function processFiles(options) { var files = options.files; var cb = options.success; - if (!cb) { throw new Error('expected callback'); } - if (!directory) { throw new Error('expected directory name'); } - var done = function() { - cb(Object.keys(options.dependents[filename] || {})); + cb(null, Object.keys(options.dependents[filename] || {})); }; + var _processDeps = function(file, content) { + processDependents({ + filename: file, + content: content, + directory: directory, + dependents: options.dependents, + config: options.config + }); + } + var _excludes = util.DEFAULT_EXCLUDE_DIR.concat(options.exclusions); var exclusions = util.processExcludes(_excludes, directory); - var fileOptions; + var fileOptions = { + directory: directory, + dirOptions: { + excludeDir: exclusions.directories, + exclude: exclusions.files + }, + contentCb: _processDeps, + filesCb: done + }; if (!files) { - fileOptions = { - directory: directory, - dirOptions: { - excludeDir: exclusions.directories, - exclude: exclusions.files - }, - contentCb: function(file, content) { - processDependents({ - filename: file, - content: content, - directory: directory, - dependents: options.dependents, - config: options.config - }); - }, - // When all files have been processed - filesCb: done - }; - if (util.isSassFile(filename)) { util.getSassFiles(fileOptions); } else { @@ -97,15 +96,9 @@ function processFiles(options) { } else { files.forEach(function(filename) { try { - processDependents({ - filename: filename, - content: fs.readFileSync(filename, 'utf8'), - directory: directory, - dependents: options.dependents, - config: options.config - }); + _processDeps(filename, fs.readFileSync(filename, 'utf8')); } catch (e) { - console.log(e); + cb(e, []); } }); diff --git a/node_modules/dependents/lib/util.js b/node_modules/dependents/lib/util.js index e7f3b307..0a18b05f 100644 --- a/node_modules/dependents/lib/util.js +++ b/node_modules/dependents/lib/util.js @@ -75,9 +75,8 @@ module.exports.processExcludes = function(excludes, directory) { if (!excludes) { return results; } excludes.forEach(function(exclude) { - var resolved = path.resolve(directory, exclude); - try { + var resolved = path.resolve(directory, exclude); var stats = fs.lstatSync(resolved); if (stats.isDirectory()) { @@ -86,6 +85,7 @@ module.exports.processExcludes = function(excludes, directory) { exclude = path.basename(exclude, path.extname(exclude)); files.push(exclude); } + // Ignore files that don't exist } catch (e) {} }); diff --git a/node_modules/dependents/node_modules/commander/Readme.md b/node_modules/dependents/node_modules/commander/Readme.md new file mode 100644 index 00000000..c48624c9 --- /dev/null +++ b/node_modules/dependents/node_modules/commander/Readme.md @@ -0,0 +1,256 @@ +# Commander.js + + [![Build Status](https://api.travis-ci.org/tj/commander.js.svg)](http://travis-ci.org/tj/commander.js) +[![NPM Version](http://img.shields.io/npm/v/commander.svg?style=flat)](https://www.npmjs.org/package/commander) +[![NPM Downloads](https://img.shields.io/npm/dm/commander.svg?style=flat)](https://www.npmjs.org/package/commander) + + The complete solution for [node.js](http://nodejs.org) command-line interfaces, inspired by Ruby's [commander](https://github.com/tj/commander). + API documentation: [http://tj.github.com/commander.js/](http://tj.github.com/commander.js/) + + +## Installation + + $ npm install commander + +## Option parsing + + Options with commander are defined with the `.option()` method, also serving as documentation for the options. The example below parses args and options from `process.argv`, leaving remaining args as the `program.args` array which were not consumed by options. + +```js +#!/usr/bin/env node + +/** + * Module dependencies. + */ + +var program = require('commander'); + +program + .version('0.0.1') + .option('-p, --peppers', 'Add peppers') + .option('-P, --pineapple', 'Add pineapple') + .option('-b, --bbq', 'Add bbq sauce') + .option('-c, --cheese [type]', 'Add the specified type of cheese [marble]', 'marble') + .parse(process.argv); + +console.log('you ordered a pizza with:'); +if (program.peppers) console.log(' - peppers'); +if (program.pineapple) console.log(' - pineapple'); +if (program.bbq) console.log(' - bbq'); +console.log(' - %s cheese', program.cheese); +``` + + Short flags may be passed as a single arg, for example `-abc` is equivalent to `-a -b -c`. Multi-word options such as "--template-engine" are camel-cased, becoming `program.templateEngine` etc. + +## Variadic arguments + + The last argument of a command can be variadic, and only the last argument. To make an argument variadic you have to + append `...` to the argument name. Here is an example: + +```js +#!/usr/bin/env node + +/** + * Module dependencies. + */ + +var program = require('commander'); + +program + .version('0.0.1') + .command('rmdir [otherDirs...]') + .action(function (dir, otherDirs) { + console.log('rmdir %s', dir); + if (otherDirs) { + otherDirs.forEach(function (oDir) { + console.log('rmdir %s', oDir); + }); + } + }); + +program.parse(process.argv); +``` + + An `Array` is used for the value of a variadic argument. This applies to `program.args` as well as the argument passed + to your action as demonstrated above. + +## Git-style sub-commands + +```js +// file: ./examples/pm +var program = require('..'); + +program + .version('0.0.1') + .command('install [name]', 'install one or more packages') + .command('search [query]', 'search with optional query') + .command('list', 'list packages installed') + .parse(process.argv); +``` + +When `.command()` is invoked with a description argument, no `.action(callback)` should be called to handle sub-commands, otherwise there will be an error. This tells commander that you're going to use separate executables for sub-commands, much like `git(1)` and other popular tools. +The commander will try to find the executable script in __current directory__ with the name `scriptBasename-subcommand`, like `pm-install`, `pm-search`. + +## Automated --help + + The help information is auto-generated based on the information commander already knows about your program, so the following `--help` info is for free: + +``` + $ ./examples/pizza --help + + Usage: pizza [options] + + Options: + + -V, --version output the version number + -p, --peppers Add peppers + -P, --pineapple Add pineapple + -b, --bbq Add bbq sauce + -c, --cheese Add the specified type of cheese [marble] + -h, --help output usage information + +``` + +## Coercion + +```js +function range(val) { + return val.split('..').map(Number); +} + +function list(val) { + return val.split(','); +} + +function collect(val, memo) { + memo.push(val); + return memo; +} + +function increaseVerbosity(v, total) { + return total + 1; +} + +program + .version('0.0.1') + .usage('[options] ') + .option('-i, --integer ', 'An integer argument', parseInt) + .option('-f, --float ', 'A float argument', parseFloat) + .option('-r, --range ..', 'A range', range) + .option('-l, --list ', 'A list', list) + .option('-o, --optional [value]', 'An optional value') + .option('-c, --collect [value]', 'A repeatable value', collect, []) + .option('-v, --verbose', 'A value that can be increased', increaseVerbosity, 0) + .parse(process.argv); + +console.log(' int: %j', program.integer); +console.log(' float: %j', program.float); +console.log(' optional: %j', program.optional); +program.range = program.range || []; +console.log(' range: %j..%j', program.range[0], program.range[1]); +console.log(' list: %j', program.list); +console.log(' collect: %j', program.collect); +console.log(' verbosity: %j', program.verbose); +console.log(' args: %j', program.args); +``` + +## Custom help + + You can display arbitrary `-h, --help` information + by listening for "--help". Commander will automatically + exit once you are done so that the remainder of your program + does not execute causing undesired behaviours, for example + in the following executable "stuff" will not output when + `--help` is used. + +```js +#!/usr/bin/env node + +/** + * Module dependencies. + */ + +var program = require('commander'); + +program + .version('0.0.1') + .option('-f, --foo', 'enable some foo') + .option('-b, --bar', 'enable some bar') + .option('-B, --baz', 'enable some baz'); + +// must be before .parse() since +// node's emit() is immediate + +program.on('--help', function(){ + console.log(' Examples:'); + console.log(''); + console.log(' $ custom-help --help'); + console.log(' $ custom-help -h'); + console.log(''); +}); + +program.parse(process.argv); + +console.log('stuff'); +``` + +Yields the following help output when `node script-name.js -h` or `node script-name.js --help` are run: + +``` + +Usage: custom-help [options] + +Options: + + -h, --help output usage information + -V, --version output the version number + -f, --foo enable some foo + -b, --bar enable some bar + -B, --baz enable some baz + +Examples: + + $ custom-help --help + $ custom-help -h + +``` + +## .outputHelp() + + Output help information without exiting. + +## .help() + + Output help information and exit immediately. + +## Links + + - [ascii tables](https://github.com/LearnBoost/cli-table) + - [progress bars](https://github.com/tj/node-progress) + - [more progress bars](https://github.com/substack/node-multimeter) + - [examples](https://github.com/tj/commander.js/tree/master/examples) + +## License + +(The MIT License) + +Copyright (c) 2011 TJ Holowaychuk <tj@vision-media.ca> + +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. diff --git a/node_modules/dependents/node_modules/commander/index.js b/node_modules/dependents/node_modules/commander/index.js new file mode 100644 index 00000000..2d3d12a7 --- /dev/null +++ b/node_modules/dependents/node_modules/commander/index.js @@ -0,0 +1,985 @@ + +/** + * Module dependencies. + */ + +var EventEmitter = require('events').EventEmitter; +var spawn = require('child_process').spawn; +var path = require('path'); +var dirname = path.dirname; +var basename = path.basename; + +/** + * Expose the root command. + */ + +exports = module.exports = new Command(); + +/** + * Expose `Command`. + */ + +exports.Command = Command; + +/** + * Expose `Option`. + */ + +exports.Option = Option; + +/** + * Initialize a new `Option` with the given `flags` and `description`. + * + * @param {String} flags + * @param {String} description + * @api public + */ + +function Option(flags, description) { + this.flags = flags; + this.required = ~flags.indexOf('<'); + this.optional = ~flags.indexOf('['); + this.bool = !~flags.indexOf('-no-'); + flags = flags.split(/[ ,|]+/); + if (flags.length > 1 && !/^[[<]/.test(flags[1])) this.short = flags.shift(); + this.long = flags.shift(); + this.description = description || ''; +} + +/** + * Return option name. + * + * @return {String} + * @api private + */ + +Option.prototype.name = function() { + return this.long + .replace('--', '') + .replace('no-', ''); +}; + +/** + * Check if `arg` matches the short or long flag. + * + * @param {String} arg + * @return {Boolean} + * @api private + */ + +Option.prototype.is = function(arg) { + return arg == this.short || arg == this.long; +}; + +/** + * Initialize a new `Command`. + * + * @param {String} name + * @api public + */ + +function Command(name) { + this.commands = []; + this.options = []; + this._execs = []; + this._args = []; + this._name = name; +} + +/** + * Inherit from `EventEmitter.prototype`. + */ + +Command.prototype.__proto__ = EventEmitter.prototype; + +/** + * Add command `name`. + * + * The `.action()` callback is invoked when the + * command `name` is specified via __ARGV__, + * and the remaining arguments are applied to the + * function for access. + * + * When the `name` is "*" an un-matched command + * will be passed as the first arg, followed by + * the rest of __ARGV__ remaining. + * + * Examples: + * + * program + * .version('0.0.1') + * .option('-C, --chdir ', 'change the working directory') + * .option('-c, --config ', 'set config path. defaults to ./deploy.conf') + * .option('-T, --no-tests', 'ignore test hook') + * + * program + * .command('setup') + * .description('run remote setup commands') + * .action(function() { + * console.log('setup'); + * }); + * + * program + * .command('exec ') + * .description('run the given remote command') + * .action(function(cmd) { + * console.log('exec "%s"', cmd); + * }); + * + * program + * .command('teardown [otherDirs...]') + * .description('run teardown commands') + * .action(function(dir, otherDirs) { + * console.log('dir "%s"', dir); + * if (otherDirs) { + * otherDirs.forEach(function (oDir) { + * console.log('dir "%s"', oDir); + * }); + * } + * }); + * + * program + * .command('*') + * .description('deploy the given env') + * .action(function(env) { + * console.log('deploying "%s"', env); + * }); + * + * program.parse(process.argv); + * + * @param {String} name + * @param {String} [desc] for git-style sub-commands + * @return {Command} the new command + * @api public + */ + +Command.prototype.command = function(name, desc) { + var args = name.split(/ +/); + var cmd = new Command(args.shift()); + + if (desc) { + cmd.description(desc); + this.executables = true; + this._execs[cmd._name] = true; + } + + this.commands.push(cmd); + cmd.parseExpectedArgs(args); + cmd.parent = this; + + if (desc) return this; + return cmd; +}; + +/** + * Add an implicit `help [cmd]` subcommand + * which invokes `--help` for the given command. + * + * @api private + */ + +Command.prototype.addImplicitHelpCommand = function() { + this.command('help [cmd]', 'display help for [cmd]'); +}; + +/** + * Parse expected `args`. + * + * For example `["[type]"]` becomes `[{ required: false, name: 'type' }]`. + * + * @param {Array} args + * @return {Command} for chaining + * @api public + */ + +Command.prototype.parseExpectedArgs = function(args) { + if (!args.length) return; + var self = this; + args.forEach(function(arg) { + var argDetails = { + required: false, + name: '', + variadic: false + }; + + switch (arg[0]) { + case '<': + argDetails.required = true; + argDetails.name = arg.slice(1, -1); + break; + case '[': + argDetails.name = arg.slice(1, -1); + break; + } + + if (argDetails.name.length > 3 && argDetails.name.slice(-3) === '...') { + argDetails.variadic = true; + argDetails.name = argDetails.name.slice(0, -3); + } + if (argDetails.name) { + self._args.push(argDetails); + } + }); + return this; +}; + +/** + * Register callback `fn` for the command. + * + * Examples: + * + * program + * .command('help') + * .description('display verbose help') + * .action(function() { + * // output help here + * }); + * + * @param {Function} fn + * @return {Command} for chaining + * @api public + */ + +Command.prototype.action = function(fn) { + var self = this; + var listener = function(args, unknown) { + // Parse any so-far unknown options + args = args || []; + unknown = unknown || []; + + var parsed = self.parseOptions(unknown); + + // Output help if necessary + outputHelpIfNecessary(self, parsed.unknown); + + // If there are still any unknown options, then we simply + // die, unless someone asked for help, in which case we give it + // to them, and then we die. + if (parsed.unknown.length > 0) { + self.unknownOption(parsed.unknown[0]); + } + + // Leftover arguments need to be pushed back. Fixes issue #56 + if (parsed.args.length) args = parsed.args.concat(args); + + self._args.forEach(function(arg, i) { + if (arg.required && null == args[i]) { + self.missingArgument(arg.name); + } else if (arg.variadic) { + if (i !== self._args.length - 1) { + self.variadicArgNotLast(arg.name); + } + + args[i] = args.splice(i); + } + }); + + // Always append ourselves to the end of the arguments, + // to make sure we match the number of arguments the user + // expects + if (self._args.length) { + args[self._args.length] = self; + } else { + args.push(self); + } + + fn.apply(self, args); + }; + this.parent.on(this._name, listener); + if (this._alias) this.parent.on(this._alias, listener); + return this; +}; + +/** + * Define option with `flags`, `description` and optional + * coercion `fn`. + * + * The `flags` string should contain both the short and long flags, + * separated by comma, a pipe or space. The following are all valid + * all will output this way when `--help` is used. + * + * "-p, --pepper" + * "-p|--pepper" + * "-p --pepper" + * + * Examples: + * + * // simple boolean defaulting to false + * program.option('-p, --pepper', 'add pepper'); + * + * --pepper + * program.pepper + * // => Boolean + * + * // simple boolean defaulting to true + * program.option('-C, --no-cheese', 'remove cheese'); + * + * program.cheese + * // => true + * + * --no-cheese + * program.cheese + * // => false + * + * // required argument + * program.option('-C, --chdir ', 'change the working directory'); + * + * --chdir /tmp + * program.chdir + * // => "/tmp" + * + * // optional argument + * program.option('-c, --cheese [type]', 'add cheese [marble]'); + * + * @param {String} flags + * @param {String} description + * @param {Function|Mixed} fn or default + * @param {Mixed} defaultValue + * @return {Command} for chaining + * @api public + */ + +Command.prototype.option = function(flags, description, fn, defaultValue) { + var self = this + , option = new Option(flags, description) + , oname = option.name() + , name = camelcase(oname); + + // default as 3rd arg + if (typeof fn != 'function') { + defaultValue = fn; + fn = null; + } + + // preassign default value only for --no-*, [optional], or + if (false == option.bool || option.optional || option.required) { + // when --no-* we make sure default is true + if (false == option.bool) defaultValue = true; + // preassign only if we have a default + if (undefined !== defaultValue) self[name] = defaultValue; + } + + // register the option + this.options.push(option); + + // when it's passed assign the value + // and conditionally invoke the callback + this.on(oname, function(val) { + // coercion + if (null !== val && fn) val = fn(val, undefined === self[name] + ? defaultValue + : self[name]); + + // unassigned or bool + if ('boolean' == typeof self[name] || 'undefined' == typeof self[name]) { + // if no value, bool true, and we have a default, then use it! + if (null == val) { + self[name] = option.bool + ? defaultValue || true + : false; + } else { + self[name] = val; + } + } else if (null !== val) { + // reassign + self[name] = val; + } + }); + + return this; +}; + +/** + * Parse `argv`, settings options and invoking commands when defined. + * + * @param {Array} argv + * @return {Command} for chaining + * @api public + */ + +Command.prototype.parse = function(argv) { + // implicit help + if (this.executables) this.addImplicitHelpCommand(); + + // store raw args + this.rawArgs = argv; + + // guess name + this._name = this._name || basename(argv[1], '.js'); + + // process argv + var parsed = this.parseOptions(this.normalize(argv.slice(2))); + var args = this.args = parsed.args; + + var result = this.parseArgs(this.args, parsed.unknown); + + // executable sub-commands + var name = result.args[0]; + if (this._execs[name] && typeof this._execs[name] != "function") { + return this.executeSubCommand(argv, args, parsed.unknown); + } + + return result; +}; + +/** + * Execute a sub-command executable. + * + * @param {Array} argv + * @param {Array} args + * @param {Array} unknown + * @api private + */ + +Command.prototype.executeSubCommand = function(argv, args, unknown) { + args = args.concat(unknown); + + if (!args.length) this.help(); + if ('help' == args[0] && 1 == args.length) this.help(); + + // --help + if ('help' == args[0]) { + args[0] = args[1]; + args[1] = '--help'; + } + + // executable + var dir = dirname(argv[1]); + var bin = basename(argv[1], '.js') + '-' + args[0]; + + // check for ./ first + var local = path.join(dir, bin); + + // run it + args = args.slice(1); + args.unshift(local); + var proc = spawn('node', args, { stdio: 'inherit', customFds: [0, 1, 2] }); + proc.on('error', function(err) { + if (err.code == "ENOENT") { + console.error('\n %s(1) does not exist, try --help\n', bin); + } else if (err.code == "EACCES") { + console.error('\n %s(1) not executable. try chmod or run with root\n', bin); + } + }); + + this.runningCommand = proc; +}; + +/** + * Normalize `args`, splitting joined short flags. For example + * the arg "-abc" is equivalent to "-a -b -c". + * This also normalizes equal sign and splits "--abc=def" into "--abc def". + * + * @param {Array} args + * @return {Array} + * @api private + */ + +Command.prototype.normalize = function(args) { + var ret = [] + , arg + , lastOpt + , index; + + for (var i = 0, len = args.length; i < len; ++i) { + arg = args[i]; + if (i > 0) { + lastOpt = this.optionFor(args[i-1]); + } + + if (arg === '--') { + // Honor option terminator + ret = ret.concat(args.slice(i)); + break; + } else if (lastOpt && lastOpt.required) { + ret.push(arg); + } else if (arg.length > 1 && '-' == arg[0] && '-' != arg[1]) { + arg.slice(1).split('').forEach(function(c) { + ret.push('-' + c); + }); + } else if (/^--/.test(arg) && ~(index = arg.indexOf('='))) { + ret.push(arg.slice(0, index), arg.slice(index + 1)); + } else { + ret.push(arg); + } + } + + return ret; +}; + +/** + * Parse command `args`. + * + * When listener(s) are available those + * callbacks are invoked, otherwise the "*" + * event is emitted and those actions are invoked. + * + * @param {Array} args + * @return {Command} for chaining + * @api private + */ + +Command.prototype.parseArgs = function(args, unknown) { + var name; + + if (args.length) { + name = args[0]; + if (this.listeners(name).length) { + this.emit(args.shift(), args, unknown); + } else { + this.emit('*', args); + } + } else { + outputHelpIfNecessary(this, unknown); + + // If there were no args and we have unknown options, + // then they are extraneous and we need to error. + if (unknown.length > 0) { + this.unknownOption(unknown[0]); + } + } + + return this; +}; + +/** + * Return an option matching `arg` if any. + * + * @param {String} arg + * @return {Option} + * @api private + */ + +Command.prototype.optionFor = function(arg) { + for (var i = 0, len = this.options.length; i < len; ++i) { + if (this.options[i].is(arg)) { + return this.options[i]; + } + } +}; + +/** + * Parse options from `argv` returning `argv` + * void of these options. + * + * @param {Array} argv + * @return {Array} + * @api public + */ + +Command.prototype.parseOptions = function(argv) { + var args = [] + , len = argv.length + , literal + , option + , arg; + + var unknownOptions = []; + + // parse options + for (var i = 0; i < len; ++i) { + arg = argv[i]; + + // literal args after -- + if ('--' == arg) { + literal = true; + continue; + } + + if (literal) { + args.push(arg); + continue; + } + + // find matching Option + option = this.optionFor(arg); + + // option is defined + if (option) { + // requires arg + if (option.required) { + arg = argv[++i]; + if (null == arg) return this.optionMissingArgument(option); + this.emit(option.name(), arg); + // optional arg + } else if (option.optional) { + arg = argv[i+1]; + if (null == arg || ('-' == arg[0] && '-' != arg)) { + arg = null; + } else { + ++i; + } + this.emit(option.name(), arg); + // bool + } else { + this.emit(option.name()); + } + continue; + } + + // looks like an option + if (arg.length > 1 && '-' == arg[0]) { + unknownOptions.push(arg); + + // If the next argument looks like it might be + // an argument for this option, we pass it on. + // If it isn't, then it'll simply be ignored + if (argv[i+1] && '-' != argv[i+1][0]) { + unknownOptions.push(argv[++i]); + } + continue; + } + + // arg + args.push(arg); + } + + return { args: args, unknown: unknownOptions }; +}; + +/** + * Return an object containing options as key-value pairs + * + * @return {Object} + * @api public + */ +Command.prototype.opts = function() { + var result = {} + , len = this.options.length; + + for (var i = 0 ; i < len; i++) { + var key = this.options[i].name(); + result[key] = key === 'version' ? this._version : this[key]; + } + return result; +}; + +/** + * Argument `name` is missing. + * + * @param {String} name + * @api private + */ + +Command.prototype.missingArgument = function(name) { + console.error(); + console.error(" error: missing required argument `%s'", name); + console.error(); + process.exit(1); +}; + +/** + * `Option` is missing an argument, but received `flag` or nothing. + * + * @param {String} option + * @param {String} flag + * @api private + */ + +Command.prototype.optionMissingArgument = function(option, flag) { + console.error(); + if (flag) { + console.error(" error: option `%s' argument missing, got `%s'", option.flags, flag); + } else { + console.error(" error: option `%s' argument missing", option.flags); + } + console.error(); + process.exit(1); +}; + +/** + * Unknown option `flag`. + * + * @param {String} flag + * @api private + */ + +Command.prototype.unknownOption = function(flag) { + console.error(); + console.error(" error: unknown option `%s'", flag); + console.error(); + process.exit(1); +}; + +/** + * Variadic argument with `name` is not the last argument as required. + * + * @param {String} name + * @api private + */ + +Command.prototype.variadicArgNotLast = function(name) { + console.error(); + console.error(" error: variadic arguments must be last `%s'", name); + console.error(); + process.exit(1); +}; + +/** + * Set the program version to `str`. + * + * This method auto-registers the "-V, --version" flag + * which will print the version number when passed. + * + * @param {String} str + * @param {String} flags + * @return {Command} for chaining + * @api public + */ + +Command.prototype.version = function(str, flags) { + if (0 == arguments.length) return this._version; + this._version = str; + flags = flags || '-V, --version'; + this.option(flags, 'output the version number'); + this.on('version', function() { + process.stdout.write(str + '\n'); + process.exit(0); + }); + return this; +}; + +/** + * Set the description to `str`. + * + * @param {String} str + * @return {String|Command} + * @api public + */ + +Command.prototype.description = function(str) { + if (0 == arguments.length) return this._description; + this._description = str; + return this; +}; + +/** + * Set an alias for the command + * + * @param {String} alias + * @return {String|Command} + * @api public + */ + +Command.prototype.alias = function(alias) { + if (0 == arguments.length) return this._alias; + this._alias = alias; + return this; +}; + +/** + * Set / get the command usage `str`. + * + * @param {String} str + * @return {String|Command} + * @api public + */ + +Command.prototype.usage = function(str) { + var args = this._args.map(function(arg) { + return humanReadableArgName(arg); + }); + + var usage = '[options]' + + (this.commands.length ? ' [command]' : '') + + (this._args.length ? ' ' + args.join(' ') : ''); + + if (0 == arguments.length) return this._usage || usage; + this._usage = str; + + return this; +}; + +/** + * Get the name of the command + * + * @param {String} name + * @return {String|Command} + * @api public + */ + +Command.prototype.name = function(name) { + return this._name; +}; + +/** + * Return the largest option length. + * + * @return {Number} + * @api private + */ + +Command.prototype.largestOptionLength = function() { + return this.options.reduce(function(max, option) { + return Math.max(max, option.flags.length); + }, 0); +}; + +/** + * Return help for options. + * + * @return {String} + * @api private + */ + +Command.prototype.optionHelp = function() { + var width = this.largestOptionLength(); + + // Prepend the help information + return [pad('-h, --help', width) + ' ' + 'output usage information'] + .concat(this.options.map(function(option) { + return pad(option.flags, width) + ' ' + option.description; + })) + .join('\n'); +}; + +/** + * Return command help documentation. + * + * @return {String} + * @api private + */ + +Command.prototype.commandHelp = function() { + if (!this.commands.length) return ''; + + var commands = this.commands.map(function(cmd) { + var args = cmd._args.map(function(arg) { + return humanReadableArgName(arg); + }).join(' '); + + return [ + cmd._name + + (cmd._alias + ? '|' + cmd._alias + : '') + + (cmd.options.length + ? ' [options]' + : '') + + ' ' + args + , cmd.description() + ]; + }); + + var width = commands.reduce(function(max, command) { + return Math.max(max, command[0].length); + }, 0); + + return [ + '' + , ' Commands:' + , '' + , commands.map(function(cmd) { + return pad(cmd[0], width) + ' ' + cmd[1]; + }).join('\n').replace(/^/gm, ' ') + , '' + ].join('\n'); +}; + +/** + * Return program help documentation. + * + * @return {String} + * @api private + */ + +Command.prototype.helpInformation = function() { + return [ + '' + , ' Usage: ' + this._name + + (this._alias + ? '|' + this._alias + : '') + + ' ' + this.usage() + , '' + this.commandHelp() + , ' Options:' + , '' + , '' + this.optionHelp().replace(/^/gm, ' ') + , '' + , '' + ].join('\n'); +}; + +/** + * Output help information for this command + * + * @api public + */ + +Command.prototype.outputHelp = function() { + process.stdout.write(this.helpInformation()); + this.emit('--help'); +}; + +/** + * Output help information and exit. + * + * @api public + */ + +Command.prototype.help = function() { + this.outputHelp(); + process.exit(); +}; + +/** + * Camel-case the given `flag` + * + * @param {String} flag + * @return {String} + * @api private + */ + +function camelcase(flag) { + return flag.split('-').reduce(function(str, word) { + return str + word[0].toUpperCase() + word.slice(1); + }); +} + +/** + * Pad `str` to `width`. + * + * @param {String} str + * @param {Number} width + * @return {String} + * @api private + */ + +function pad(str, width) { + var len = Math.max(0, width - str.length); + return str + Array(len + 1).join(' '); +} + +/** + * Output help information if necessary + * + * @param {Command} command to output help for + * @param {Array} array of options to search for -h or --help + * @api private + */ + +function outputHelpIfNecessary(cmd, options) { + options = options || []; + for (var i = 0; i < options.length; i++) { + if (options[i] == '--help' || options[i] == '-h') { + cmd.outputHelp(); + process.exit(0); + } + } +} + +/** + * Takes an argument an returns its human readable equivalent for help usage. + * + * @param {Object} arg + * @return {String} + * @api private + */ + +function humanReadableArgName(arg) { + var nameOutput = arg.name + (arg.variadic === true ? '...' : ''); + + return arg.required + ? '<' + nameOutput + '>' + : '[' + nameOutput + ']' +} diff --git a/node_modules/dependents/node_modules/commander/package.json b/node_modules/dependents/node_modules/commander/package.json new file mode 100644 index 00000000..43ce0311 --- /dev/null +++ b/node_modules/dependents/node_modules/commander/package.json @@ -0,0 +1,70 @@ +{ + "name": "commander", + "version": "2.5.1", + "description": "the complete solution for node.js command-line programs", + "keywords": [ + "command", + "option", + "parser", + "prompt" + ], + "author": { + "name": "TJ Holowaychuk", + "email": "tj@vision-media.ca" + }, + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/tj/commander.js.git" + }, + "devDependencies": { + "should": ">= 0.0.1" + }, + "scripts": { + "test": "make test" + }, + "main": "index", + "engines": { + "node": ">= 0.6.x" + }, + "files": [ + "index.js" + ], + "bugs": { + "url": "https://github.com/tj/commander.js/issues" + }, + "homepage": "https://github.com/tj/commander.js", + "_id": "commander@2.5.1", + "_shasum": "23c61f6e47be143cc02e7ad4bb1c47f5cd5a2883", + "_from": "commander@~2.5.1", + "_npmVersion": "1.4.9", + "_npmUser": { + "name": "zhiyelee", + "email": "zhiyelee@gmail.com" + }, + "maintainers": [ + { + "name": "tjholowaychuk", + "email": "tj@vision-media.ca" + }, + { + "name": "somekittens", + "email": "rkoutnik@gmail.com" + }, + { + "name": "zhiyelee", + "email": "zhiyelee@gmail.com" + }, + { + "name": "thethomaseffect", + "email": "thethomaseffect@gmail.com" + } + ], + "dist": { + "shasum": "23c61f6e47be143cc02e7ad4bb1c47f5cd5a2883", + "tarball": "http://registry.npmjs.org/commander/-/commander-2.5.1.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/commander/-/commander-2.5.1.tgz", + "readme": "ERROR: No README data found!" +} diff --git a/node_modules/dependents/package.json b/node_modules/dependents/package.json index 57549130..b20dece1 100644 --- a/node_modules/dependents/package.json +++ b/node_modules/dependents/package.json @@ -1,6 +1,6 @@ { "name": "dependents", - "version": "1.8.1", + "version": "2.0.3", "description": "Get the files that depend on a given JS/SASS file", "main": "index.js", "bin": { @@ -27,6 +27,7 @@ }, "homepage": "https://github.com/mrjoelkemp/node-dependents", "dependencies": { + "commander": "~2.5.1", "get-all-js-files": "~1.2.0", "module-lookup-amd": "~1.0.0", "node-dir": "~0.1.5", @@ -40,10 +41,10 @@ "mocha": "~1.21.4", "sinon": "~1.12.2" }, - "gitHead": "e25302301d7213fbe9800be1855e2728349b8749", - "_id": "dependents@1.8.1", - "_shasum": "f8478a6a2bcce3df4464fd7022f5337baa901916", - "_from": "dependents@1.8.1", + "gitHead": "cc7576a216d154688a5a3df86dfcd71c6aedb9b4", + "_id": "dependents@2.0.3", + "_shasum": "682004f00d0c3bb176c201a80435895746cdceb3", + "_from": "dependents@2.0.3", "_npmVersion": "1.4.28", "_npmUser": { "name": "mrjoelkemp", @@ -56,9 +57,9 @@ } ], "dist": { - "shasum": "f8478a6a2bcce3df4464fd7022f5337baa901916", - "tarball": "http://registry.npmjs.org/dependents/-/dependents-1.8.1.tgz" + "shasum": "682004f00d0c3bb176c201a80435895746cdceb3", + "tarball": "http://registry.npmjs.org/dependents/-/dependents-2.0.3.tgz" }, "directories": {}, - "_resolved": "https://registry.npmjs.org/dependents/-/dependents-1.8.1.tgz" + "_resolved": "https://registry.npmjs.org/dependents/-/dependents-2.0.3.tgz" } diff --git a/node_modules/dependents/test/test.js b/node_modules/dependents/test/test.js index 429ecd0e..f770b390 100644 --- a/node_modules/dependents/test/test.js +++ b/node_modules/dependents/test/test.js @@ -4,38 +4,67 @@ var defaultExclusions = require('../lib/util').DEFAULT_EXCLUDE_DIR; var sinon = require('sinon'); describe('dependents', function() { - it('does not throw on esprima errors', function(done) { - dependents.for({ - filename: __dirname + '/example/error.js', - directory: __dirname + '/example', - success: function(dependents) { - assert(!dependents.length); - done(); - } - }); - }); - it('reuses a given configuration object config', function(done) { var config = dependents._readConfig(__dirname + '/example/amd/config.json'); var spy = sinon.spy(dependents, '_readConfig'); - dependents.for({ + dependents({ filename: __dirname + '/example/error.js', directory: __dirname + '/example', config: config, - success: function(dependents) { + success: function(err, dependents) { assert(!spy.called); done(); } }); }); + describe('exceptions', function() { + it('throws if a success callback was not supplied', function() { + assert.throws(function() { + dependents({ + filename: __dirname + '/example/error.js', + directory: __dirname + '/example' + }); + }); + }); + + it('throws if a filename was not supplied', function() { + assert.throws(function() { + dependents({ + directory: __dirname + '/example' + }); + }); + }); + + it('throws if a directory was not supplied', function() { + assert.throws(function() { + dependents({ + filename: __dirname + '/example/error.js', + success: function(err, dependents) {} + }); + }); + }); + + it('does not throw on esprima errors', function(done) { + dependents({ + filename: __dirname + '/example/error.js', + directory: __dirname + '/example', + success: function(err, dependents) { + assert(!err); + assert(!dependents.length); + done(); + } + }); + }); + }); + describe('exclusions', function() { it('excludes common 3rd party folders by default', function(done) { - dependents.for({ + dependents({ filename: __dirname + '/example/exclusions/a.js', directory: __dirname + '/example/exclusions', - success: function(dependents) { + success: function(err, dependents) { assert(!dependents.some(function(dependent) { return defaultExclusions.indexOf(dependents) !== -1; })); @@ -45,11 +74,11 @@ describe('dependents', function() { }); it('excludes custom folders', function(done) { - dependents.for({ + dependents({ filename: __dirname + '/example/exclusions/a.js', directory: __dirname + '/example/exclusions', exclusions: ['customExclude'], - success: function(dependents) { + success: function(err, dependents) { assert(!dependents.some(function(dependent) { return dependent.indexOf('customExclude') !== -1; })); @@ -60,11 +89,11 @@ describe('dependents', function() { it('cannot exclude particular subdirectories', function(done) { // node-dir looks at a directory name at a time, not partial paths - dependents.for({ + dependents({ filename: __dirname + '/example/exclusions/a.js', directory: __dirname + '/example/exclusions', exclusions: ['customExclude/subdir'], - success: function(dependents) { + success: function(err, dependents) { assert(dependents.some(function(dependent) { return dependent.indexOf('customExclude/subdir') !== -1; })); @@ -76,10 +105,10 @@ describe('dependents', function() { describe('amd', function() { it('returns the (non-aliased) dependents', function(done) { - dependents.for({ + dependents({ filename: __dirname + '/example/amd/b.js', directory: __dirname + '/example/amd', - success: function(dependents) { + success: function(err, dependents) { assert(dependents.length === 1); assert(dependents[0].indexOf('a.js') !== -1); done(); @@ -88,11 +117,11 @@ describe('dependents', function() { }); it('resolves aliased modules if given a requirejs config', function(done) { - dependents.for({ + dependents({ filename: __dirname + '/example/amd/b.js', directory: __dirname + '/example/amd', config: __dirname + '/example/amd/config.json', - success: function(dependents) { + success: function(err, dependents) { assert(dependents.length === 2); assert(dependents[0].indexOf('a.js') !== -1); assert(dependents[1].indexOf('c.js') !== -1); @@ -104,10 +133,10 @@ describe('dependents', function() { describe('commonjs', function() { it('finds the dependents of commonjs modules', function(done) { - dependents.for({ + dependents({ filename: __dirname + '/example/commonjs/b.js', directory: __dirname + '/example/commonjs', - success: function(dependents) { + success: function(err, dependents) { assert(dependents.length); done(); } @@ -115,10 +144,10 @@ describe('dependents', function() { }); it('handles relative dependencies', function(done) { - dependents.for({ + dependents({ filename: __dirname + '/example/commonjs/b.js', directory: __dirname + '/example/commonjs', - success: function(dependents) { + success: function(err, dependents) { assert(dependents.some(function(d) { return d.indexOf('c.js') !== -1; })); @@ -130,10 +159,10 @@ describe('dependents', function() { describe('es6', function() { it('finds the dependents of es6 modules', function(done) { - dependents.for({ + dependents({ filename: __dirname + '/example/es6/b.js', directory: __dirname + '/example/es6', - success: function(dependents) { + success: function(err, dependents) { assert(dependents.length); done(); } @@ -143,10 +172,10 @@ describe('dependents', function() { describe('sass', function() { it('finds the dependents of sass files', function(done) { - dependents.for({ + dependents({ filename: __dirname + '/example/sass/_foo.scss', directory: __dirname + '/example/sass', - success: function(dependents) { + success: function(err, dependents) { assert(dependents.length === 2); done(); } diff --git a/package.json b/package.json index cece7ad0..9099085b 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "Sublime Text plugin to navigate JS codebases", "main": "index.js", "dependencies": { - "dependents": "~1.8.1" + "dependents": "~2.0.3" }, "private": true, "devDependencies": {},