lib/cli.js (181 lines of code) (raw):

'use strict'; function bold(input) { return '\u001b[1m' + input + '\u001b[22m'; } class Version { constructor(app) { this.app = app; this.name = 'version'; this.description = 'print version'; this.useArgs = '0'; } async run(argv) { console.log(this.app.version); } } class Help { constructor(app) { this.app = app; this.name = 'help'; this.description = 'print help information'; this.arguments = [ { name: 'command', description: 'the sub-command name', required: false } ]; } async run(argv) { const app = this.app; const [subcommand] = argv; if (subcommand) { await app.printUsage(subcommand); return; } console.log(`${app.description} v${app.version}`); console.log(); for (const [name, cmd] of app.commands) { console.log(` ${name}${' '.repeat(18 - name.length)}${cmd.description}`); } console.log(); } } class Completion { constructor(app) { this.app = app; this.name = 'completion'; this.description = 'auto completion'; this.useArgs = '0'; } async run(argv) { const shell = process.env.SHELL; if (shell.endsWith('/zsh')) { let script = `# Installation: ${this.app.name} completion >> ~/.bashrc\n`; script += `# or ${this.app.name} completion >> ~/.bash_profile on OSX.\n`; script += `complete -C ${this.app.name} ${this.app.name}\n`; script += `# end of ${this.app.name} completion`; console.log(script); } else { // } } } class CLI { constructor(name, version, description) { this.name = name; this.version = version; this.description = description; this.commands = new Map(); this.registerCommand(Help); this.registerCommand(Version); this.registerCommand(Completion); } registerCommand(Command) { const cmd = new Command(this); this.commands.set(cmd.name, cmd); } async completion(args) { if (args.length === 0) { for (const [name] of this.commands) { console.log(name); } return; } if (args.length === 1) { const prefix = args[0]; for (const [name] of this.commands) { if (name.startsWith(prefix)) { console.log(name); } } return; } // after --, no more auto completion if (args.indexOf('--') !== -1) { return; } // eslint-disable-next-line no-unused-vars const [name, ...others] = args; const cmd = this.commands.get(name); if (cmd.options) { Object.keys(cmd.options).forEach((item) => { console.log(`--${item}`); }); return; } } async process(argv) { // auto completion if (process.env.COMP_LINE) { await this.completion(process.env.COMP_LINE.split(' ').slice(1)); return; } const [subcommand] = argv; if (this.commands.has(subcommand)) { await this.runCommand(subcommand, argv.slice(1)); return; } if (subcommand) { console.log(`invalid sub-command '${subcommand}'`); await this.runCommand('help', []); process.exit(-1); } await this.runCommand('help', argv); } async runCommand(subcommand, argv) { const cmd = this.commands.get(subcommand); await cmd.run(argv); } async printUsage(subcommand) { const cmd = this.commands.get(subcommand); console.log(bold(`Usage:`)); if (cmd.useArgs === '0-n') { console.log(` ${this.name} ${subcommand}${cmd.supportAfterCommand ? ' -- <command>' : ''}`); console.log(` ${this.name} ${subcommand} [arguments]${cmd.supportAfterCommand ? ' -- <command>' : ''}`); } else if (cmd.useArgs === '1-n') { console.log(` ${this.name} ${subcommand} [options]${cmd.supportAfterCommand ? ' -- <command>' : ''}`); } else if (cmd.useArgs === '0') { console.log(` ${this.name} ${subcommand}${cmd.supportAfterCommand ? ' -- <command>' : ''}`); } if (cmd.arguments && cmd.arguments.length > 0) { let output = ` ${this.name} ${subcommand}`; for (let i = 0; i < cmd.arguments.length; i++) { const arg = cmd.arguments[i]; output += ` [${arg.name}]`; } console.log(`${output}${cmd.supportAfterCommand ? ' -- <command>' : ''}`); } else { console.log(` ${this.name} ${subcommand}${cmd.supportAfterCommand ? ' -- <command>' : ''}`); } if (cmd.description) { console.log(); console.log(bold('Description:')); console.log(` ${cmd.description}`); } if (cmd.options && Object.keys(cmd.options).length > 0) { console.log(); console.log(bold('Options:')); Object.keys(cmd.options).forEach((key) => { const option = cmd.options[key]; console.log(` ${option.short ? `-${option.short}, ` : ''}--${key} [${option.required ? 'Required' : 'Optional'}]`); console.log(` ${option.description}`); console.log(); }); } if (cmd.arguments && cmd.arguments.length > 0) { console.log(); console.log(bold('Arguments:')); for (let i = 0; i < cmd.arguments.length; i++) { const arg = cmd.arguments[i]; console.log(`${arg.name}:`); console.log(` ${arg.description}`); } } } run(argv) { // global handle this.process(argv).then(() => { process.exit(0); }).catch((err) => { console.log(err.stack); process.exit(-1); }); } } module.exports = CLI;