UNPKG

@clerc/plugin-help

Version:
373 lines (366 loc) 11.4 kB
import { Root, formatCommandName, definePlugin, withBrackets, NoSuchCommandError } from '@clerc/core'; import { gracefulFlagName, toArray, resolveCommandStrict } from '@clerc/utils'; import * as yc from 'yoctocolors'; import stringWidth from 'string-width'; import textTable from 'text-table'; const locales = { "en": { "help.name": "Name:", "help.version": "Version:", "help.subcommand": "Subcommand:", "help.commands": "Commands:", "help.globalFlags": "Global Flags:", "help.flags": "Flags:", "help.description": "Description:", "help.usage": "Usage:", "help.examples": "Examples:", "help.notes": "Notes:", "help.noDescription": "(No description)", "help.notes.1": "If no command is specified, show help for the CLI.", "help.notes.2": "If a command is specified, show help for the command.", "help.notes.3": "-h is an alias for --help.", "help.examples.1": "Show help", "help.examples.2": "Show help for a specific command", "help.commandDescription": "Show help", "help.default": "Default: %s" }, "zh-CN": { "help.name": "\u540D\u79F0:", "help.version": "\u7248\u672C:", "help.subcommand": "\u5B50\u547D\u4EE4:", "help.commands": "\u547D\u4EE4:", "help.globalFlags": "\u5168\u5C40\u6807\u5FD7:", "help.flags": "\u6807\u5FD7:", "help.description": "\u63CF\u8FF0:", "help.usage": "\u4F7F\u7528:", "help.examples": "\u793A\u4F8B:", "help.notes": "\u5907\u6CE8:", "help.noDescription": "(\u65E0\u63CF\u8FF0)", "help.notes.1": "\u5982\u679C\u6CA1\u6709\u6307\u5B9A\u5C55\u793A\u54EA\u4E2A\u547D\u4EE4\u7684\u5E2E\u52A9\u4FE1\u606F\uFF0C\u9ED8\u8BA4\u5C55\u793ACLI\u7684\u3002", "help.notes.2": "\u5982\u679C\u6307\u5B9A\u4E86\u5219\u5C55\u793A\u8BE5\u547D\u4EE4\u5E2E\u52A9\u4FE1\u606F\u3002", "help.notes.3": "-h \u662F --help \u7684\u4E00\u4E2A\u522B\u540D\u3002", "help.examples.1": "\u5C55\u793A CLI \u7684\u5E2E\u52A9\u4FE1\u606F", "help.examples.2": "\u5C55\u793A\u6307\u5B9A\u547D\u4EE4\u7684\u5E2E\u52A9\u4FE1\u606F", "help.commandDescription": "\u5C55\u793A\u5E2E\u52A9\u4FE1\u606F", "help.default": "\u9ED8\u8BA4\u503C: %s" } }; const table = (items) => textTable(items, { stringLength: stringWidth }); const splitTable = (items) => table(items).split("\n"); const primitiveMap = /* @__PURE__ */ new Map([ [Boolean, void 0], [String, "string"], [Number, "number"] ]); function stringifyType(type, hasDefault = false) { const res = primitiveMap.has(type) ? primitiveMap.get(type) : "value"; return res ? hasDefault ? `[${res}]` : `<${res}>` : ""; } function sortName(a, b) { if (a === Root) { return -1; } if (b === Root) { return 1; } return a.length - b.length; } const DELIMITER = yc.yellow("-"); function print(s) { process.stdout.write(s); } function generateCliDetail(sections, cli, subcommand) { var _a; const { t } = cli.i18n; const items = [ { title: t("help.name"), body: yc.red(cli._name) }, { title: t("help.version"), body: yc.yellow(cli._version) } ]; if (subcommand) { items.push({ title: t("help.subcommand"), body: yc.green( `${cli._scriptName} ${formatCommandName(subcommand.name)}` ) }); } sections.push({ type: "inline", items }); sections.push({ title: t("help.description"), body: [(_a = subcommand == null ? void 0 : subcommand.description) != null ? _a : cli._description] }); } function generateExamples(sections, examples, t) { const examplesFormatted = examples.map(([command, description]) => [ command, DELIMITER, description ]); sections.push({ title: t("help.examples"), body: splitTable(examplesFormatted) }); } const formatFlags = (flags, t, renderers) => Object.entries(flags).map(([name, flag]) => { const hasDefault = flag.default !== void 0; let flagNameWithAlias = [gracefulFlagName(name)]; if (flag.alias) { flagNameWithAlias.push(gracefulFlagName(flag.alias)); } flagNameWithAlias = flagNameWithAlias.map(renderers.renderFlagName); const items = [ yc.blue(flagNameWithAlias.join(", ")), renderers.renderType(flag.type, hasDefault) ]; items.push(DELIMITER, flag.description || t("help.noDescription")); if (hasDefault) { items.push( `(${t("help.default", renderers.renderDefault(flag.default))})` ); } return items; }); const render = (sections) => { const rendered = []; for (const section of sections) { if (section.type === "block" || !section.type) { const indent = " "; const formattedBody = section.body.map((line) => indent + line); formattedBody.unshift(""); const body = formattedBody.join("\n"); rendered.push(table([[yc.bold(`${section.title}`)], [body]]).toString()); } else if (section.type === "inline") { const formattedBody = section.items.map((item) => [ yc.bold(`${item.title}`), item.body ]); const tableGenerated = table(formattedBody); rendered.push(tableGenerated.toString()); } rendered.push(""); } return rendered.join("\n"); }; const noop = (x) => x; const defaultRenderers = { renderFlagName: noop, renderSections: noop, renderType: stringifyType, renderDefault: JSON.stringify }; function generateHelp(render2, ctx, notes, examples, _renderers) { const { cli } = ctx; const { t } = cli.i18n; let sections = []; const renderers = Object.assign( /* @__PURE__ */ Object.create(null), defaultRenderers, _renderers ); generateCliDetail(sections, cli); sections.push({ title: t("help.usage"), body: [ yc.magenta( `$ ${cli._scriptName} ${withBrackets( "command", ctx.hasRootOrAlias )} [flags]` ) ] }); const commands = [ ...ctx.hasRoot ? [cli._commands[Root]] : [], ...Object.values(cli._commands) ].map((command) => { var _a; const commandNameWithAlias = [ typeof command.name === "symbol" ? "" : command.name, ...toArray((_a = command.alias) != null ? _a : []) ].sort(sortName).map( (n) => n === "" || typeof n === "symbol" ? `${cli._scriptName}` : `${cli._scriptName} ${n}` ).join(", "); return [yc.cyan(commandNameWithAlias), DELIMITER, command.description]; }); if (commands.length > 0) { sections.push({ title: t("help.commands"), body: splitTable(commands) }); } const globalFlags = formatFlags(cli._flags, t, renderers); if (globalFlags.length > 0) { sections.push({ title: t("help.globalFlags"), body: splitTable(globalFlags) }); } if (notes) { sections.push({ title: t("help.notes"), body: notes }); } if (examples) { generateExamples(sections, examples, t); } sections = renderers.renderSections(sections); return render2(sections); } function generateSubcommandHelp(render2, ctx, command) { var _a, _b, _c, _d, _e, _f; const { cli } = ctx; const { t } = cli.i18n; const [subcommand] = resolveCommandStrict(cli._commands, command, t); if (!subcommand) { throw new NoSuchCommandError(formatCommandName(command), t); } const renderers = Object.assign( /* @__PURE__ */ Object.create(null), defaultRenderers, (_a = subcommand.help) == null ? void 0 : _a.renderers ); let sections = []; if (command === Root) { generateCliDetail(sections, cli); } else { generateCliDetail(sections, cli, { ...subcommand, name: formatCommandName(command) }); } const parameters = (_c = (_b = subcommand.parameters) == null ? void 0 : _b.join(" ")) != null ? _c : void 0; const commandName = command === Root ? "" : ` ${formatCommandName(command)}`; const parametersString = parameters ? ` ${parameters}` : ""; const flagsString = subcommand.flags ? " [flags]" : ""; sections.push({ title: t("help.usage"), body: [ yc.magenta( `$ ${cli._scriptName}${commandName}${parametersString}${flagsString}` ) ] }); const globalFlags = formatFlags(cli._flags, t, renderers); if (globalFlags.length > 0) { sections.push({ title: t("help.globalFlags"), body: splitTable(globalFlags) }); } if (subcommand.flags) { sections.push({ title: t("help.flags"), body: splitTable(formatFlags(subcommand.flags, t, renderers)) }); } if ((_d = subcommand == null ? void 0 : subcommand.help) == null ? void 0 : _d.notes) { sections.push({ title: t("help.notes"), body: subcommand.help.notes }); } if ((_e = subcommand == null ? void 0 : subcommand.help) == null ? void 0 : _e.examples) { generateExamples(sections, (_f = subcommand == null ? void 0 : subcommand.help) == null ? void 0 : _f.examples, t); } sections = renderers.renderSections(sections); return render2(sections); } const helpPlugin = ({ command = true, flag = true, showHelpWhenNoCommand = true, notes, examples, banner, renderers } = {}) => definePlugin({ setup: (cli) => { const { add, t } = cli.i18n; add(locales); function printHelp(s) { banner && print(`${banner} `); print(s); } if (command) { cli = cli.command("help", t("help.commandDescription"), { parameters: ["[command...]"], help: { notes: [ t("help.notes.1"), t("help.notes.2"), t("help.notes.3") ], examples: [ [`$ ${cli._scriptName} help`, t("help.examples.1")], [`$ ${cli._scriptName} help <command>`, t("help.examples.2")], [ `$ ${cli._scriptName} <command> --help`, t("help.examples.2") ] ] } }).on("help", (ctx) => { if (ctx.parameters.command.length > 0) { printHelp( generateSubcommandHelp(render, ctx, ctx.parameters.command) ); } else { printHelp(generateHelp(render, ctx, notes, examples, renderers)); } }); } if (flag) { cli = cli.flag("help", t("help.commandDescription"), { alias: "h", type: Boolean, default: false }); } cli.interceptor((ctx, next) => { const shouldShowHelp = ctx.flags.help; if (!ctx.hasRootOrAlias && ctx.raw._.length === 0 && showHelpWhenNoCommand && !shouldShowHelp) { let str = `${t("core.noCommandGiven")} `; str += generateHelp(render, ctx, notes, examples, renderers); str += "\n"; printHelp(str); process.exit(1); } else if (shouldShowHelp) { if (ctx.raw._.length > 0) { if (ctx.called === Root) { printHelp(generateSubcommandHelp(render, ctx, ctx.raw._)); } else { if (ctx.name === Root) { printHelp( generateHelp(render, ctx, notes, examples, renderers) ); } else { printHelp(generateSubcommandHelp(render, ctx, ctx.raw._)); } } } else { if (ctx.hasRootOrAlias) { printHelp(generateSubcommandHelp(render, ctx, Root)); } else { printHelp(generateHelp(render, ctx, notes, examples, renderers)); } } } else { next(); } }); return cli; } }); export { helpPlugin };