From 17be24d8c5bbb2eb3c3faedc1f441e219997210a Mon Sep 17 00:00:00 2001 From: MrBear Date: Thu, 9 Apr 2020 17:19:46 +0800 Subject: [PATCH] feat(devkit): support plugin/devkit sub command help (#207) * feat: help command in plugin * feat: add option description to plugin-template * test: lerna run test * feat(action): test workflow * feat: github action publish * feat(actions): add feflowbot account * feat: merge help command * feat: combine plugin and devkit help command * feat: remove workflow files Co-authored-by: bethonxyfu --- .../feflow-cli/src/core/commander/index.ts | 3 +- .../src/core/devkit/commandOptions.ts | 34 +++++++++++ .../feflow-cli/src/core/devkit/loadDevkits.ts | 11 ++-- packages/feflow-cli/src/core/index.ts | 56 +++++++++++++------ .../templates/plugin-template/lib/index.js | 12 +++- 5 files changed, 92 insertions(+), 24 deletions(-) create mode 100644 packages/feflow-cli/src/core/devkit/commandOptions.ts diff --git a/packages/feflow-cli/src/core/commander/index.ts b/packages/feflow-cli/src/core/commander/index.ts index 29cb2f81..9665bff4 100644 --- a/packages/feflow-cli/src/core/commander/index.ts +++ b/packages/feflow-cli/src/core/commander/index.ts @@ -23,9 +23,10 @@ export default class Commander { return this.store; } - register(name: string, desc: string, fn: Function) { + register(name: string, desc: string, fn: Function, options?: Array) { this.store[name.toLowerCase()] = fn; this.store[name.toLowerCase()].desc = desc; + this.store[name.toLowerCase()].options = options; this.alias = abbrev(Object.keys(this.store)); } }; diff --git a/packages/feflow-cli/src/core/devkit/commandOptions.ts b/packages/feflow-cli/src/core/devkit/commandOptions.ts new file mode 100644 index 00000000..b3cae099 --- /dev/null +++ b/packages/feflow-cli/src/core/devkit/commandOptions.ts @@ -0,0 +1,34 @@ +const getOptionFromCommand = (optionsDescription: object): Object[] => { + const options: Object[] = []; + + const optionDescritions = Object.keys(optionsDescription); + if (!optionDescritions.length) return options; + + optionDescritions.forEach(option => { + let optionItemConfig = optionsDescription[option]; + const optionDescritionItem = getOptionItem(optionItemConfig, option); + options.push(optionDescritionItem); + }); + + return options; +}; + +const getOptionItem = (optionItemConfig: any, option: any): object => { + let optionDescritionItem: any = {}; + if (typeof optionItemConfig == 'string') { + optionDescritionItem = { + name: option, + description: optionItemConfig, + }; + } else { + if (!optionItemConfig.name) { + optionItemConfig.name = option; + } + + optionDescritionItem = optionItemConfig; + optionDescritionItem.type = String; + } + return optionDescritionItem; +}; + +export default getOptionFromCommand; diff --git a/packages/feflow-cli/src/core/devkit/loadDevkits.ts b/packages/feflow-cli/src/core/devkit/loadDevkits.ts index 638f5491..3c0c5bca 100644 --- a/packages/feflow-cli/src/core/devkit/loadDevkits.ts +++ b/packages/feflow-cli/src/core/devkit/loadDevkits.ts @@ -1,5 +1,6 @@ import path from 'path'; import Config from './config'; +import getOptionFromCommand from "./commandOptions"; const registerDevkitCommand = (command: any, commandConfig: any, directoryPath: any, ctx: any) => { const builder = commandConfig.builder; @@ -8,19 +9,21 @@ const registerDevkitCommand = (command: any, commandConfig: any, directoryPath: const pkgPath = path.join(directoryPath, 'node_modules', packageName); try { const devkitConfig = config.loadDevkitConfig(pkgPath); - const { implementation, description } = devkitConfig.builders[command]; + const { implementation, description, optionsDescription = {} } = devkitConfig.builders[command]; + + const options = getOptionFromCommand(optionsDescription); if (Array.isArray(implementation)) { ctx.commander.register(command, description, async () => { for (let i = 0; i < implementation.length; i ++) { const action = path.join(pkgPath, implementation[i]); await require(action)(ctx); } - }); + }, options); } else { const action = path.join(pkgPath, implementation); ctx.commander.register(command, description, () => { require(action)(ctx); - }); + }, options); } } catch (e) { ctx.logger.debug(`${ pkgPath } not found!`); @@ -50,4 +53,4 @@ export default function loadDevkits(ctx: any): Promise { } resolve(); }); -} \ No newline at end of file +} diff --git a/packages/feflow-cli/src/core/index.ts b/packages/feflow-cli/src/core/index.ts index 0c2754bc..3bea0b49 100644 --- a/packages/feflow-cli/src/core/index.ts +++ b/packages/feflow-cli/src/core/index.ts @@ -15,6 +15,7 @@ import packageJson from '../shared/packageJson'; import { getRegistryUrl, install } from '../shared/npm'; import chalk from 'chalk'; import semver from 'semver'; +import commandLineUsage from 'command-line-usage'; const pkg = require('../../package.json'); export default class Feflow { @@ -285,6 +286,10 @@ export default class Feflow { call(name: any, ctx: any) { + const args = ctx.args; + if(args.h || args.help) { + return this.showCommandOptionDescription(name, ctx); + } return new Promise((resolve, reject) => { const cmd = this.commander.get(name); if (cmd) { @@ -353,23 +358,38 @@ export default class Feflow { } } - getOptionItem(optionItemConfig: any, option: any): object { - let optionDescritionItem = {}; - if (typeof optionItemConfig == 'string') { - optionDescritionItem = { - name: option, - description: optionItemConfig, - }; - } else { - const { name, description, alias, type, typeLabel } = optionItemConfig; - optionDescritionItem = { - name, - description, - alias, - typeLabel, - type: /boolean/i.test(type) ? Boolean : String, - }; + async showCommandOptionDescription(cmd: any, ctx: any): Promise { + let cmdDescription; + + let optionDescrition: any = { + header: 'Options', + optionList: [], + }; + + const registriedCommand = ctx.commander.get(cmd); + + if (registriedCommand && registriedCommand.options) { + cmdDescription = registriedCommand.desc; + optionDescrition.optionList = registriedCommand.options; + } + + if(optionDescrition.optionList.length == 0) { + return this.call("help", ctx) } - return optionDescritionItem; - }; + + const sections = []; + sections.push({ + header: `fef ${cmd}`, + content: cmdDescription + }) + sections.push({ + header: 'Usage', + content: `$ fef ${cmd} [options]` + }) + sections.push(optionDescrition); + const usage = commandLineUsage(sections); + + console.log(usage); + } + } \ No newline at end of file diff --git a/packages/feflow-plugin-devtool/templates/plugin-template/lib/index.js b/packages/feflow-plugin-devtool/templates/plugin-template/lib/index.js index 2199f11c..9bb9fdbd 100644 --- a/packages/feflow-plugin-devtool/templates/plugin-template/lib/index.js +++ b/packages/feflow-plugin-devtool/templates/plugin-template/lib/index.js @@ -11,7 +11,17 @@ module.exports = (context) => { // 再比如 `feflow add -x 1 -y 2 --z-value 3 4 5 6`,args 就是 { _: [ 4, 5, 6 ], x: 1, y: 2, 'z-value': 3 } // 调用主要的逻辑 return calculator.add(args._); - }); + }, + // 传入插件的参数描述,用于说明参数如何使用 + // 字段参考文档(关注文档中的optionList即可): https://github.com/75lb/command-line-usage/blob/master/doc/api.md + [ + { + name: 'help', + description: 'Display this usage guide.', + alias: 'h', + type: Boolean + } + ]); // 注册乘、除、减三个命令 context.commander.register('multiply', '乘法运算器', () => calculator.multiply(args._));