Comparing version 6.2.5 to 6.2.6
@@ -9,6 +9,7 @@ "use strict"; | ||
class Command { | ||
constructor(rawName, description, config = {}) { | ||
constructor(rawName, description, config = {}, cli) { | ||
this.rawName = rawName; | ||
this.description = description; | ||
this.config = config; | ||
this.cli = cli; | ||
this.options = []; | ||
@@ -70,2 +71,5 @@ this.aliasNames = []; | ||
} | ||
get isGlobalCommand() { | ||
return this instanceof GlobalCommand; | ||
} | ||
/** | ||
@@ -81,7 +85,8 @@ * Check if an option is registered in this command | ||
} | ||
outputHelp(config) { | ||
const version = this.versionNumber || config.versionNumber; | ||
outputHelp() { | ||
const { bin, commands } = this.cli; | ||
const { versionNumber, options: globalOptions, helpCallback } = this.cli.globalCommand; | ||
const sections = [ | ||
{ | ||
body: `${config.bin}${version ? ` v${version}` : ''}` | ||
body: `${bin}${versionNumber ? ` v${versionNumber}` : ''}` | ||
} | ||
@@ -91,9 +96,10 @@ ]; | ||
title: 'Usage', | ||
body: ` $ ${config.bin} ${this.usageText || this.rawName}` | ||
body: ` $ ${bin} ${this.usageText || this.rawName}` | ||
}); | ||
if (config.subCommands && config.subCommands.length > 0) { | ||
const longestCommandName = utils_1.findLongest(config.subCommands.map(command => command.rawName)); | ||
const showCommands = (this.isGlobalCommand || this.isDefaultCommand) && commands.length > 0; | ||
if (showCommands) { | ||
const longestCommandName = utils_1.findLongest(commands.map(command => command.rawName)); | ||
sections.push({ | ||
title: 'Commands', | ||
body: config.subCommands | ||
body: commands | ||
.map(command => { | ||
@@ -106,8 +112,10 @@ return ` ${utils_1.padRight(command.rawName, longestCommandName.length)} ${command.description}`; | ||
title: `For more info, run any command with the \`--help\` flag`, | ||
body: config.subCommands | ||
.map(command => ` $ ${config.bin}${command.name === '' ? '' : ` ${command.name}`} --help`) | ||
body: commands | ||
.map(command => ` $ ${bin}${command.name === '' ? '' : ` ${command.name}`} --help`) | ||
.join('\n') | ||
}); | ||
} | ||
const options = [...this.options, ...(config.globalOptions || [])]; | ||
const options = this.isGlobalCommand | ||
? globalOptions | ||
: [...this.options, ...(globalOptions || [])]; | ||
if (options.length > 0) { | ||
@@ -132,3 +140,3 @@ const longestOptionName = utils_1.findLongest(options.map(option => option.rawName)); | ||
if (typeof example === 'function') { | ||
return example(config.bin); | ||
return example(bin); | ||
} | ||
@@ -140,4 +148,4 @@ return example; | ||
} | ||
if (this.helpCallback) { | ||
this.helpCallback(sections); | ||
if (helpCallback) { | ||
helpCallback(sections); | ||
} | ||
@@ -153,17 +161,19 @@ console.log(sections | ||
} | ||
outputVersion(bin) { | ||
if (this.versionNumber) { | ||
console.log(`${bin}/${this.versionNumber} ${process.platform}-${process.arch} node-${process.version}`); | ||
process.exit(0); | ||
outputVersion() { | ||
const { bin } = this.cli; | ||
const { versionNumber } = this.cli.globalCommand; | ||
if (versionNumber) { | ||
console.log(`${bin}/${versionNumber} ${process.platform}-${process.arch} node-${process.version}`); | ||
} | ||
process.exit(0); | ||
} | ||
/** | ||
* Check if the parsed options contain any unknown options | ||
* | ||
* Exit and output error when true | ||
* @param options Original options, i.e. not camelCased one | ||
* @param globalCommand | ||
*/ | ||
checkUnknownOptions(options, globalCommand) { | ||
checkUnknownOptions() { | ||
const { rawOptions, globalCommand } = this.cli; | ||
if (!this.config.allowUnknownOptions) { | ||
for (const name of Object.keys(options)) { | ||
for (const name of Object.keys(rawOptions)) { | ||
if (name !== '--' && | ||
@@ -180,9 +190,8 @@ !this.hasOption(name) && | ||
* Check if the required string-type options exist | ||
* @param values | ||
* @param globalCommand | ||
*/ | ||
checkRequiredOptions(values, globalCommand) { | ||
checkRequiredOptions() { | ||
const { rawOptions, globalCommand } = this.cli; | ||
const requiredOptions = [...globalCommand.options, ...this.options].filter(option => option.required); | ||
for (const option of requiredOptions) { | ||
const value = values[option.names[0].split('.')[0]]; | ||
const value = rawOptions[option.names[0].split('.')[0]]; | ||
if (typeof value === 'boolean') { | ||
@@ -195,2 +204,8 @@ console.error(`error: option \`${option.rawName}\` argument is missing`); | ||
} | ||
class GlobalCommand extends Command { | ||
constructor(cli) { | ||
super('@@global@@', '', {}, cli); | ||
} | ||
} | ||
exports.GlobalCommand = GlobalCommand; | ||
exports.default = Command; |
@@ -5,174 +5,4 @@ "use strict"; | ||
}; | ||
const events_1 = require("events"); | ||
const path_1 = __importDefault(require("path")); | ||
const mri_1 = __importDefault(require("mri")); | ||
const Command_1 = __importDefault(require("./Command")); | ||
const utils_1 = require("./utils"); | ||
const NAME_OF_GLOBAL_COMMAND = 'this-does-not-matter'; | ||
class CAC extends events_1.EventEmitter { | ||
constructor() { | ||
super(); | ||
this.commands = []; | ||
this.globalCommand = new Command_1.default(NAME_OF_GLOBAL_COMMAND, 'The global command'); | ||
this.globalCommand.usage('<command> [options]'); | ||
} | ||
usage(text) { | ||
this.globalCommand.usage(text); | ||
return this; | ||
} | ||
command(rawName, description, config) { | ||
const command = new Command_1.default(rawName, description, config); | ||
this.commands.push(command); | ||
return command; | ||
} | ||
option(rawName, description, config) { | ||
this.globalCommand.option(rawName, description, config); | ||
return this; | ||
} | ||
help(callback) { | ||
this.globalCommand.option('-h, --help', 'Display this message'); | ||
this.globalCommand.helpCallback = callback; | ||
return this; | ||
} | ||
version(version, customFlags = '-v, --version') { | ||
this.globalCommand.version(version, customFlags); | ||
return this; | ||
} | ||
/** | ||
* Add a global example | ||
* @param example | ||
*/ | ||
example(example) { | ||
this.globalCommand.example(example); | ||
return this; | ||
} | ||
outputHelp(subCommand) { | ||
if (subCommand && this.matchedCommand) { | ||
this.matchedCommand.outputHelp({ | ||
bin: this.bin, | ||
subCommands: this.matchedCommand.name === '' ? this.commands : undefined, | ||
versionNumber: this.globalCommand.versionNumber, | ||
globalOptions: this.globalCommand.options | ||
}); | ||
} | ||
else { | ||
this.globalCommand.outputHelp({ | ||
bin: this.bin, | ||
subCommands: this.commands | ||
}); | ||
} | ||
return this; | ||
} | ||
outputVersion() { | ||
this.globalCommand.outputVersion(this.bin); | ||
return this; | ||
} | ||
parse(argv = process.argv) { | ||
this.rawArgs = argv; | ||
this.bin = argv[1] ? path_1.default.basename(argv[1]) : 'cli'; | ||
// Search sub-commands | ||
for (const command of this.commands) { | ||
const mriOptions = utils_1.getMriOptions(this.globalCommand, command); | ||
const { args, options, originalOptions } = this.mri(argv.slice(2), mriOptions); | ||
const commandName = args[0]; | ||
if (command.isMatched(commandName)) { | ||
this.matchedCommand = command; | ||
this.args = args.slice(1); | ||
this.options = options; | ||
this.emit(`command:${commandName}`, command); | ||
this.runCommandAction(command, this.globalCommand, { | ||
args: this.args, | ||
options, | ||
originalOptions | ||
}); | ||
return { args: this.args, options }; | ||
} | ||
} | ||
// Search the default command | ||
for (const command of this.commands) { | ||
if (command.name === '') { | ||
const mriOptions = utils_1.getMriOptions(this.globalCommand, command); | ||
const { args, options, originalOptions } = this.mri(argv.slice(2), mriOptions); | ||
this.matchedCommand = command; | ||
this.args = args; | ||
this.options = options; | ||
this.emit(`command:!`, command); | ||
this.runCommandAction(command, this.globalCommand, { | ||
args, | ||
options, | ||
originalOptions | ||
}); | ||
return { args, options }; | ||
} | ||
} | ||
const globalMriOptions = utils_1.getMriOptions(this.globalCommand); | ||
const { args, options } = this.mri(argv.slice(2), globalMriOptions); | ||
this.args = args; | ||
this.options = options; | ||
if (options.help && this.globalCommand.hasOption('help')) { | ||
this.outputHelp(); | ||
} | ||
if (options.version && | ||
this.globalCommand.hasOption('version') && | ||
this.globalCommand.versionNumber) { | ||
this.outputVersion(); | ||
} | ||
this.emit('command:*'); | ||
return { args, options }; | ||
} | ||
mri(argv, mriOptions) { | ||
let argsAfterDoubleDashes = []; | ||
const doubleDashesIndex = argv.indexOf('--'); | ||
if (doubleDashesIndex > -1) { | ||
argsAfterDoubleDashes = argv.slice(0, doubleDashesIndex); | ||
argv = argv.slice(doubleDashesIndex + 1); | ||
} | ||
const parsed = mri_1.default(argv, mriOptions); | ||
const args = parsed._; | ||
delete parsed._; | ||
const options = { | ||
'--': argsAfterDoubleDashes | ||
}; | ||
for (const key of Object.keys(parsed)) { | ||
const keys = key.split('.').map((v, i) => { | ||
return i === 0 ? utils_1.camelcase(v) : v; | ||
}); | ||
utils_1.setDotProp(options, keys, parsed[key]); | ||
} | ||
return { | ||
args, | ||
options, | ||
originalOptions: parsed | ||
}; | ||
} | ||
runCommandAction(command, globalCommand, { args, options, originalOptions }) { | ||
if (options.help && globalCommand.hasOption('help')) { | ||
return this.outputHelp(true); | ||
} | ||
if (options.version && globalCommand.hasOption('version')) { | ||
return this.outputVersion(); | ||
} | ||
if (!command.commandAction) | ||
return; | ||
command.checkUnknownOptions(originalOptions, globalCommand); | ||
command.checkRequiredOptions(originalOptions, globalCommand); | ||
const minimalArgsCount = command.args.filter(arg => arg.required).length; | ||
if (args.length < minimalArgsCount) { | ||
console.error(`error: missing required args for command \`${command.rawName}\``); | ||
process.exit(1); | ||
} | ||
const actionArgs = []; | ||
command.args.forEach((arg, index) => { | ||
if (arg.variadic) { | ||
actionArgs.push(args.slice(index)); | ||
} | ||
else { | ||
actionArgs.push(args[index]); | ||
} | ||
}); | ||
actionArgs.push(options); | ||
return command.commandAction.apply(this, actionArgs); | ||
} | ||
} | ||
const cac = () => new CAC(); | ||
const CAC_1 = __importDefault(require("./CAC")); | ||
const cac = () => new CAC_1.default(); | ||
module.exports = cac; |
{ | ||
"name": "cac", | ||
"version": "6.2.5", | ||
"version": "6.2.6", | ||
"description": "Simple yet powerful framework for building command-line apps.", | ||
@@ -5,0 +5,0 @@ "repository": { |
@@ -0,1 +1,2 @@ | ||
import CAC from './CAC'; | ||
import Option, { OptionConfig } from './Option'; | ||
@@ -7,8 +8,2 @@ interface CommandArg { | ||
} | ||
interface HelpConfig { | ||
bin: string; | ||
subCommands?: Command[]; | ||
versionNumber?: string; | ||
globalOptions?: Option[]; | ||
} | ||
interface HelpSection { | ||
@@ -24,6 +19,7 @@ title?: string; | ||
declare type CommandExample = ((bin: string) => string) | string; | ||
export default class Command { | ||
declare class Command { | ||
rawName: string; | ||
description: string; | ||
config: CommandConfig; | ||
cli: CAC; | ||
options: Option[]; | ||
@@ -38,3 +34,4 @@ aliasNames: string[]; | ||
helpCallback?: HelpCallback; | ||
constructor(rawName: string, description: string, config?: CommandConfig); | ||
globalCommand?: GlobalCommand; | ||
constructor(rawName: string, description: string, config: CommandConfig, cli: CAC); | ||
usage(text: string): this; | ||
@@ -60,2 +57,3 @@ allowUnknownOptions(): this; | ||
readonly isDefaultCommand: boolean; | ||
readonly isGlobalCommand: boolean; | ||
/** | ||
@@ -66,22 +64,19 @@ * Check if an option is registered in this command | ||
hasOption(name: string): Option | undefined; | ||
outputHelp(config: HelpConfig): void; | ||
outputVersion(bin: string): void; | ||
outputHelp(): void; | ||
outputVersion(): void; | ||
/** | ||
* Check if the parsed options contain any unknown options | ||
* | ||
* Exit and output error when true | ||
* @param options Original options, i.e. not camelCased one | ||
* @param globalCommand | ||
*/ | ||
checkUnknownOptions(options: { | ||
[k: string]: any; | ||
}, globalCommand: Command): void; | ||
checkUnknownOptions(): void; | ||
/** | ||
* Check if the required string-type options exist | ||
* @param values | ||
* @param globalCommand | ||
*/ | ||
checkRequiredOptions(values: { | ||
[k: string]: any; | ||
}, globalCommand: Command): void; | ||
checkRequiredOptions(): void; | ||
} | ||
export { HelpCallback, CommandExample, CommandConfig }; | ||
declare class GlobalCommand extends Command { | ||
constructor(cli: CAC); | ||
} | ||
export { HelpCallback, CommandExample, CommandConfig, GlobalCommand }; | ||
export default Command; |
@@ -1,48 +0,3 @@ | ||
/// <reference types="node" /> | ||
import { EventEmitter } from 'events'; | ||
import Command, { CommandConfig, HelpCallback, CommandExample } from './Command'; | ||
import { OptionConfig } from './Option'; | ||
interface ParsedArgv { | ||
args: ReadonlyArray<string>; | ||
options: { | ||
[k: string]: any; | ||
}; | ||
} | ||
declare class CAC extends EventEmitter { | ||
bin: string; | ||
commands: Command[]; | ||
globalCommand: Command; | ||
matchedCommand: Command; | ||
/** | ||
* Raw CLI arguments | ||
*/ | ||
rawArgs: string[]; | ||
/** | ||
* Parsed CLI arguments | ||
*/ | ||
args: ReadonlyArray<string>; | ||
/** | ||
* Parsed CLI options, camelCased | ||
*/ | ||
options: { | ||
[k: string]: any; | ||
}; | ||
constructor(); | ||
usage(text: string): this; | ||
command(rawName: string, description: string, config?: CommandConfig): Command; | ||
option(rawName: string, description: string, config?: OptionConfig): this; | ||
help(callback?: HelpCallback): this; | ||
version(version: string, customFlags?: string): this; | ||
/** | ||
* Add a global example | ||
* @param example | ||
*/ | ||
example(example: CommandExample): this; | ||
outputHelp(subCommand?: boolean): this; | ||
outputVersion(): this; | ||
parse(argv?: string[]): ParsedArgv; | ||
private mri; | ||
private runCommandAction; | ||
} | ||
import CAC from './CAC'; | ||
declare const cac: () => CAC; | ||
export = cac; |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
37618
13
741