@clerc/plugin-help
Version:
Clerc plugin help
373 lines (366 loc) • 11.4 kB
JavaScript
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 };