@clerc/plugin-help 0.44.0 → 1.0.0-beta.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/index.d.ts +139 -62
  2. package/dist/index.js +254 -362
  3. package/package.json +12 -15
package/dist/index.d.ts CHANGED
@@ -1,70 +1,147 @@
1
- import * as _clerc_core from '@clerc/core';
1
+ import { Plugin } from "@clerc/core";
2
2
 
3
- interface BlockSection {
4
- type?: "block";
5
- title: string;
6
- body: string[];
3
+ //#region ../parser/src/types.d.ts
4
+
5
+ /**
6
+ * Defines how a string input is converted to the target type T.
7
+ *
8
+ * @template T The target type.
9
+ */
10
+ type FlagTypeFunction<T = unknown> = ((value: string) => T) & {
11
+ /**
12
+ * Optional display name for the type, useful in help output.
13
+ * If provided, this will be shown instead of the function name.
14
+ */
15
+ displayName?: string;
16
+ };
17
+ type FlagType<T = unknown> = FlagTypeFunction<T> | readonly [FlagTypeFunction<T>];
18
+ //#endregion
19
+ //#region src/types.d.ts
20
+ interface Formatters {
21
+ formatFlagType: (type: FlagType) => string;
7
22
  }
8
- interface InlineSection {
9
- type: "inline";
10
- items: {
11
- title: string;
12
- body: string;
13
- }[];
23
+ /**
24
+ * A group definition as a tuple of [key, displayName].
25
+ * The key is used in help options to assign items to groups.
26
+ * The displayName is shown in the help output.
27
+ */
28
+ type GroupDefinition = [key: string, name: string];
29
+ /**
30
+ * Options for defining groups in help output.
31
+ */
32
+ interface GroupsOptions {
33
+ /**
34
+ * Groups for commands.
35
+ * Each group is defined as `[key, name]`.
36
+ */
37
+ commands?: GroupDefinition[];
38
+ /**
39
+ * Groups for command-specific flags.
40
+ * Each group is defined as `[key, name]`.
41
+ */
42
+ flags?: GroupDefinition[];
43
+ /**
44
+ * Groups for global flags.
45
+ * Each group is defined as `[key, name]`.
46
+ */
47
+ globalFlags?: GroupDefinition[];
14
48
  }
15
- type Section = BlockSection | InlineSection;
16
- interface Renderers {
17
- renderSections?: (sections: Section[]) => Section[];
18
- renderFlagName?: (name: string) => string;
19
- renderType?: (type: any, hasDefault: boolean) => string;
20
- renderDefault?: (default_: any) => string;
49
+ //#endregion
50
+ //#region src/formatters.d.ts
51
+ declare const defaultFormatters: Formatters;
52
+ //#endregion
53
+ //#region src/index.d.ts
54
+ interface HelpOptions {
55
+ /**
56
+ * The group this item belongs to.
57
+ * The group must be defined in the `groups` option of `helpPlugin()`.
58
+ */
59
+ group?: string;
21
60
  }
22
-
23
- declare module "@clerc/core" {
24
- interface CommandCustomProperties {
25
- help?: {
26
- renderers?: Renderers;
27
- examples?: [string, string][];
28
- notes?: string[];
29
- };
30
- }
61
+ interface CommandHelpOptions extends HelpOptions {
62
+ /**
63
+ * Whether to show the command in help output.
64
+ *
65
+ * @default true
66
+ */
67
+ show?: boolean;
68
+ /**
69
+ * Notes to show in the help output.
70
+ */
71
+ notes?: string[];
72
+ /**
73
+ * Examples to show in the help output.
74
+ * Each example is a tuple of `[command, description]`.
75
+ */
76
+ examples?: [string, string][];
31
77
  }
32
- interface HelpPluginOptions {
33
- /**
34
- * Whether to register the help command.
35
- *
36
- * @default true
37
- */
38
- command?: boolean;
39
- /**
40
- * Whether to register the global help flag.
41
- *
42
- * @default true
43
- */
44
- flag?: boolean;
45
- /**
46
- * Whether to show help when no command is specified.
47
- *
48
- * @default true
49
- */
50
- showHelpWhenNoCommand?: boolean;
51
- /**
52
- * Global notes.
53
- */
54
- notes?: string[];
55
- /**
56
- * Global examples.
57
- */
58
- examples?: [string, string][];
78
+ declare module "@clerc/core" {
79
+ interface CommandCustomOptions {
59
80
  /**
60
- * Banner.
61
- */
62
- banner?: string;
81
+ * Help options for the command.
82
+ */
83
+ help?: CommandHelpOptions;
84
+ }
85
+ interface FlagCustomOptions {
63
86
  /**
64
- * Renderers.
65
- */
66
- renderers?: Renderers;
87
+ * Help options for the flag.
88
+ */
89
+ help?: HelpOptions;
90
+ }
67
91
  }
68
- declare const helpPlugin: ({ command, flag, showHelpWhenNoCommand, notes, examples, banner, renderers, }?: HelpPluginOptions) => _clerc_core.Plugin<_clerc_core.Clerc<{}, {}>, _clerc_core.Clerc<{}, {}>>;
69
-
70
- export { HelpPluginOptions, helpPlugin };
92
+ interface HelpPluginOptions {
93
+ /**
94
+ * Whether to register the `help` command.
95
+ *
96
+ * @default true
97
+ */
98
+ command?: boolean;
99
+ /**
100
+ * Whether to register the `--help` global flag.
101
+ *
102
+ * @default true
103
+ */
104
+ flag?: boolean;
105
+ /**
106
+ * Whether to show help when no command is specified.
107
+ *
108
+ * @default true
109
+ */
110
+ showHelpWhenNoCommandSpecified?: boolean;
111
+ /**
112
+ * Notes to show in the help output.
113
+ */
114
+ notes?: string[];
115
+ /**
116
+ * Examples to show in the help output.
117
+ * Each example is a tuple of `[command, description]`.
118
+ */
119
+ examples?: [string, string][];
120
+ /**
121
+ * A banner to show before the help output.
122
+ */
123
+ banner?: string;
124
+ /**
125
+ * Custom formatters for rendering help.
126
+ */
127
+ formatters?: Partial<Formatters>;
128
+ /**
129
+ * Group definitions for commands and flags.
130
+ * Groups allow organizing commands and flags into logical sections in help output.
131
+ * Each group is defined as `[key, name]` where `key` is the identifier used in help options
132
+ * and `name` is the display name shown in help output.
133
+ */
134
+ groups?: GroupsOptions;
135
+ }
136
+ declare const helpPlugin: ({
137
+ command,
138
+ flag,
139
+ showHelpWhenNoCommandSpecified,
140
+ notes,
141
+ examples,
142
+ banner,
143
+ formatters,
144
+ groups
145
+ }?: HelpPluginOptions) => Plugin;
146
+ //#endregion
147
+ export { CommandHelpOptions, type GroupDefinition, type GroupsOptions, HelpOptions, HelpPluginOptions, defaultFormatters, helpPlugin };
package/dist/index.js CHANGED
@@ -1,372 +1,264 @@
1
- import { Root, formatCommandName, definePlugin, withBrackets, NoSuchCommandError } from '@clerc/core';
2
- import { gracefulFlagName, toArray, resolveCommandStrict } from '@clerc/utils';
3
- import * as yc from 'yoctocolors';
4
- import stringWidth from 'string-width';
5
- import textTable from 'text-table';
1
+ import { definePlugin, resolveCommand } from "@clerc/core";
2
+ import stringWidth from "string-width";
3
+ import textTable from "text-table";
4
+ import * as yc from "yoctocolors";
6
5
 
7
- const locales = {
8
- "en": {
9
- "help.name": "Name:",
10
- "help.version": "Version:",
11
- "help.subcommand": "Subcommand:",
12
- "help.commands": "Commands:",
13
- "help.globalFlags": "Global Flags:",
14
- "help.flags": "Flags:",
15
- "help.description": "Description:",
16
- "help.usage": "Usage:",
17
- "help.examples": "Examples:",
18
- "help.notes": "Notes:",
19
- "help.noDescription": "(No description)",
20
- "help.notes.1": "If no command is specified, show help for the CLI.",
21
- "help.notes.2": "If a command is specified, show help for the command.",
22
- "help.notes.3": "-h is an alias for --help.",
23
- "help.examples.1": "Show help",
24
- "help.examples.2": "Show help for a specific command",
25
- "help.commandDescription": "Show help",
26
- "help.default": "Default: %s"
27
- },
28
- "zh-CN": {
29
- "help.name": "\u540D\u79F0:",
30
- "help.version": "\u7248\u672C:",
31
- "help.subcommand": "\u5B50\u547D\u4EE4:",
32
- "help.commands": "\u547D\u4EE4:",
33
- "help.globalFlags": "\u5168\u5C40\u6807\u5FD7:",
34
- "help.flags": "\u6807\u5FD7:",
35
- "help.description": "\u63CF\u8FF0:",
36
- "help.usage": "\u4F7F\u7528:",
37
- "help.examples": "\u793A\u4F8B:",
38
- "help.notes": "\u5907\u6CE8:",
39
- "help.noDescription": "(\u65E0\u63CF\u8FF0)",
40
- "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",
41
- "help.notes.2": "\u5982\u679C\u6307\u5B9A\u4E86\u5219\u5C55\u793A\u8BE5\u547D\u4EE4\u5E2E\u52A9\u4FE1\u606F\u3002",
42
- "help.notes.3": "-h \u662F --help \u7684\u4E00\u4E2A\u522B\u540D\u3002",
43
- "help.examples.1": "\u5C55\u793A CLI \u7684\u5E2E\u52A9\u4FE1\u606F",
44
- "help.examples.2": "\u5C55\u793A\u6307\u5B9A\u547D\u4EE4\u7684\u5E2E\u52A9\u4FE1\u606F",
45
- "help.commandDescription": "\u5C55\u793A\u5E2E\u52A9\u4FE1\u606F",
46
- "help.default": "\u9ED8\u8BA4\u503C: %s"
47
- }
48
- };
6
+ //#region ../utils/src/index.ts
7
+ const toArray = (a) => Array.isArray(a) ? a : [a];
8
+ const kebabCase = (s) => s.replace(/([A-Z])/g, (_, c) => `-${c.toLowerCase()}`);
9
+ const formatFlagName = (n) => n.length <= 1 ? `-${n}` : `--${kebabCase(n)}`;
10
+ const formatVersion = (v) => v.length === 0 ? "" : v.startsWith("v") ? v : `v${v}`;
11
+ const isTruthy = Boolean;
12
+ const objectIsEmpty = (obj) => Object.keys(obj).length === 0;
13
+
14
+ //#endregion
15
+ //#region src/utils.ts
16
+ function formatFlagType(type) {
17
+ if (typeof type === "function") return type.displayName ?? type.name;
18
+ const innerType = type[0];
19
+ return `Array<${innerType.displayName ?? innerType.name}>`;
20
+ }
49
21
 
22
+ //#endregion
23
+ //#region src/formatters.ts
24
+ const defaultFormatters = { formatFlagType };
25
+
26
+ //#endregion
27
+ //#region src/renderer.ts
28
+ const DEFAULT_GROUP_KEY = "default";
50
29
  const table = (items) => textTable(items, { stringLength: stringWidth });
51
30
  const splitTable = (items) => table(items).split("\n");
52
- const primitiveMap = /* @__PURE__ */ new Map([
53
- [Boolean, void 0],
54
- [String, "string"],
55
- [Number, "number"]
56
- ]);
57
- function stringifyType(type, hasDefault = false) {
58
- const res = primitiveMap.has(type) ? primitiveMap.get(type) : "value";
59
- return res ? hasDefault ? `[${res}]` : `<${res}>` : "";
60
- }
61
- function sortName(a, b) {
62
- if (a === Root) {
63
- return -1;
64
- }
65
- if (b === Root) {
66
- return 1;
67
- }
68
- return a.length - b.length;
69
- }
70
31
  const DELIMITER = yc.yellow("-");
71
- function print(s) {
72
- process.stdout.write(s);
32
+ const INDENT = " ".repeat(2);
33
+ const withIndent = (str) => `${INDENT}${str}`;
34
+ function groupDefinitionsToMap(definitions) {
35
+ const map = /* @__PURE__ */ new Map();
36
+ if (definitions) for (const [key, name] of definitions) map.set(key, name);
37
+ return map;
73
38
  }
74
- function generateCliDetail(sections, cli, subcommand) {
75
- var _a;
76
- const { t } = cli.i18n;
77
- const items = [
78
- {
79
- title: t("help.name"),
80
- body: yc.red(cli._name)
81
- },
82
- {
83
- title: t("help.version"),
84
- body: yc.yellow(cli._version)
85
- }
86
- ];
87
- if (subcommand) {
88
- items.push({
89
- title: t("help.subcommand"),
90
- body: yc.green(
91
- `${cli._scriptName} ${formatCommandName(subcommand.name)}`
92
- )
93
- });
94
- }
95
- sections.push({
96
- type: "inline",
97
- items
98
- });
99
- sections.push({
100
- title: t("help.description"),
101
- body: [(_a = subcommand == null ? void 0 : subcommand.description) != null ? _a : cli._description]
102
- });
39
+ function validateGroup(group, groupMap, itemType, itemName) {
40
+ if (group && group !== DEFAULT_GROUP_KEY && !groupMap.has(group)) throw new Error(`Unknown ${itemType} group "${group}" for "${itemName}". Available groups: ${[...groupMap.keys()].join(", ") || "(none)"}`);
103
41
  }
104
- function generateExamples(sections, examples, t) {
105
- const examplesFormatted = examples.map(([command, description]) => [
106
- command,
107
- DELIMITER,
108
- description
109
- ]);
110
- sections.push({
111
- title: t("help.examples"),
112
- body: splitTable(examplesFormatted)
113
- });
114
- }
115
- const formatFlags = (flags, t, renderers) => Object.entries(flags).map(([name, flag]) => {
116
- const hasDefault = flag.default !== void 0;
117
- let flagNameWithAlias = [gracefulFlagName(name)];
118
- if (flag.alias) {
119
- flagNameWithAlias.push(gracefulFlagName(flag.alias));
120
- }
121
- flagNameWithAlias = flagNameWithAlias.map(renderers.renderFlagName);
122
- const items = [
123
- yc.blue(flagNameWithAlias.join(", ")),
124
- renderers.renderType(flag.type, hasDefault)
125
- ];
126
- items.push(DELIMITER, flag.description || t("help.noDescription"));
127
- if (hasDefault) {
128
- items.push(
129
- `(${t("help.default", renderers.renderDefault(flag.default))})`
130
- );
131
- }
132
- return items;
133
- });
134
-
135
- const render = (sections) => {
136
- const rendered = [];
137
- for (const section of sections) {
138
- if (section.type === "block" || !section.type) {
139
- const indent = " ";
140
- const formattedBody = section.body.map((line) => indent + line);
141
- formattedBody.unshift("");
142
- const body = formattedBody.join("\n");
143
- rendered.push(table([[yc.bold(`${section.title}`)], [body]]).toString());
144
- } else if (section.type === "inline") {
145
- const formattedBody = section.items.map((item) => [
146
- yc.bold(`${item.title}`),
147
- item.body
148
- ]);
149
- const tableGenerated = table(formattedBody);
150
- rendered.push(tableGenerated.toString());
151
- }
152
- rendered.push("");
153
- }
154
- return rendered.join("\n");
155
- };
156
- const noop = (x) => x;
157
- const defaultRenderers = {
158
- renderFlagName: noop,
159
- renderSections: noop,
160
- renderType: stringifyType,
161
- renderDefault: JSON.stringify
42
+ var HelpRenderer = class {
43
+ _commandGroups;
44
+ _flagGroups;
45
+ _globalFlagGroups;
46
+ constructor(_formatters, _cli, _globalFlags, _command, _notes, _examples, groups) {
47
+ this._formatters = _formatters;
48
+ this._cli = _cli;
49
+ this._globalFlags = _globalFlags;
50
+ this._command = _command;
51
+ this._notes = _notes;
52
+ this._examples = _examples;
53
+ this._commandGroups = groupDefinitionsToMap(groups?.commands);
54
+ this._flagGroups = groupDefinitionsToMap(groups?.flags);
55
+ this._globalFlagGroups = groupDefinitionsToMap(groups?.globalFlags);
56
+ }
57
+ render() {
58
+ return [
59
+ this.renderHeader(),
60
+ this.renderUsage(),
61
+ this.renderCommands(),
62
+ this.renderGlobalFlags(),
63
+ this.renderCommandFlags(),
64
+ this.renderNotes(),
65
+ this.renderExamples()
66
+ ].filter(isTruthy).filter((section) => section.body.length > 0).map((section) => {
67
+ const body = Array.isArray(section.body) ? section.body.filter((s) => s !== void 0).join("\n") : section.body;
68
+ if (!section.title) return body;
69
+ return `${yc.bold(section.title)}\n${body.split("\n").map(withIndent).join("\n")}`;
70
+ }).join("\n\n");
71
+ }
72
+ renderHeader() {
73
+ const { _name, _version, _description } = this._cli;
74
+ const command = this._command;
75
+ const description = command?.description ?? _description;
76
+ const formattedCommandName = command?.name ? ` ${yc.cyan(command.name)}` : "";
77
+ const headerLine = command ? `${yc.green(_name)}${formattedCommandName}` : `${yc.green(_name)} ${yc.yellow(formatVersion(_version))}`;
78
+ const alias = command?.alias ? `Alias${toArray(command.alias).length > 1 ? "es" : ""}: ${toArray(command.alias).map((a) => yc.cyan(a)).join(", ")}` : void 0;
79
+ return { body: [`${headerLine}${description ? ` ${DELIMITER} ${description}` : ""}`, alias] };
80
+ }
81
+ renderUsage() {
82
+ const { _scriptName } = this._cli;
83
+ const command = this._command;
84
+ let usage = `$ ${_scriptName}`;
85
+ if (command) {
86
+ if (command.name) usage += ` ${command.name}`;
87
+ if (command.parameters) usage += ` ${command.parameters.join(" ")}`;
88
+ } else usage += this._cli._commands.has("") ? " [command]" : " <command>";
89
+ if (command?.flags && !objectIsEmpty(command.flags) || !objectIsEmpty(this._globalFlags)) usage += " [flags]";
90
+ return {
91
+ title: "Usage",
92
+ body: [yc.magenta(usage)]
93
+ };
94
+ }
95
+ renderCommands() {
96
+ const commands = this._cli._commands;
97
+ if (this._command || commands.size === 0) return;
98
+ const groupedCommands = /* @__PURE__ */ new Map();
99
+ const defaultCommands = [];
100
+ for (const command of commands.values()) {
101
+ if (command.__isAlias || command.help?.show === false) continue;
102
+ const group = command.help?.group;
103
+ validateGroup(group, this._commandGroups, "command", command.name);
104
+ const item = [`${yc.cyan(command.name)}${command.alias ? ` (${toArray(command.alias).join(", ")})` : ""}`, command.description];
105
+ if (group && group !== DEFAULT_GROUP_KEY) {
106
+ const groupItems = groupedCommands.get(group) ?? [];
107
+ groupItems.push(item);
108
+ groupedCommands.set(group, groupItems);
109
+ } else defaultCommands.push(item);
110
+ }
111
+ const body = [];
112
+ for (const [key, name] of this._commandGroups) {
113
+ const items = groupedCommands.get(key);
114
+ if (items && items.length > 0) {
115
+ if (body.length > 0) body.push("");
116
+ body.push(`${yc.dim(name)}`);
117
+ body.push(...splitTable(items).map(withIndent));
118
+ }
119
+ }
120
+ if (defaultCommands.length > 0) if (body.length > 0) {
121
+ body.push("");
122
+ body.push(`${yc.dim("Other")}`);
123
+ body.push(...splitTable(defaultCommands).map(withIndent));
124
+ } else body.push(...splitTable(defaultCommands));
125
+ return {
126
+ title: "Commands",
127
+ body
128
+ };
129
+ }
130
+ renderFlagItem(name, flag) {
131
+ const flagName = formatFlagName(name);
132
+ const aliases = (Array.isArray(flag.alias) ? flag.alias : flag.alias ? [flag.alias] : []).map(formatFlagName).join(", ");
133
+ const type = this._formatters.formatFlagType(flag.type);
134
+ return [
135
+ yc.blue([flagName, aliases].filter(Boolean).join(", ")),
136
+ yc.gray(type),
137
+ flag.description,
138
+ flag.default !== void 0 && yc.gray(`[default: ${String(flag.default)}]`)
139
+ ].filter(isTruthy);
140
+ }
141
+ renderGroupedFlags(flags, groupMap, itemType) {
142
+ const groupedFlags = /* @__PURE__ */ new Map();
143
+ const defaultFlags = [];
144
+ for (const [name, flag] of Object.entries(flags)) {
145
+ const group = flag.help?.group;
146
+ validateGroup(group, groupMap, itemType, name);
147
+ const item = this.renderFlagItem(name, flag);
148
+ if (group && group !== DEFAULT_GROUP_KEY) {
149
+ const groupItems = groupedFlags.get(group) ?? [];
150
+ groupItems.push(item);
151
+ groupedFlags.set(group, groupItems);
152
+ } else defaultFlags.push(item);
153
+ }
154
+ const body = [];
155
+ for (const [key, name] of groupMap) {
156
+ const items = groupedFlags.get(key);
157
+ if (items && items.length > 0) {
158
+ if (body.length > 0) body.push("");
159
+ body.push(`${yc.dim(name)}`);
160
+ body.push(...splitTable(items).map(withIndent));
161
+ }
162
+ }
163
+ if (defaultFlags.length > 0) if (body.length > 0) {
164
+ body.push("");
165
+ body.push(`${yc.dim("Other")}`);
166
+ body.push(...splitTable(defaultFlags).map(withIndent));
167
+ } else body.push(...splitTable(defaultFlags));
168
+ return body;
169
+ }
170
+ renderCommandFlags() {
171
+ const command = this._command;
172
+ if (!command?.flags || objectIsEmpty(command.flags)) return;
173
+ return {
174
+ title: "Flags",
175
+ body: this.renderGroupedFlags(command.flags, this._flagGroups, "flag")
176
+ };
177
+ }
178
+ renderGlobalFlags() {
179
+ if (!this._globalFlags || objectIsEmpty(this._globalFlags)) return;
180
+ return {
181
+ title: "Global Flags",
182
+ body: this.renderGroupedFlags(this._globalFlags, this._globalFlagGroups, "global flag")
183
+ };
184
+ }
185
+ renderNotes() {
186
+ if (!this._notes?.length) return;
187
+ return {
188
+ title: "Notes",
189
+ body: this._notes
190
+ };
191
+ }
192
+ renderExamples() {
193
+ if (!this._examples?.length) return;
194
+ return {
195
+ title: "Examples",
196
+ body: splitTable(this._examples.map(([command, description]) => {
197
+ return [
198
+ command,
199
+ DELIMITER,
200
+ description
201
+ ];
202
+ }))
203
+ };
204
+ }
162
205
  };
163
206
 
164
- function generateHelp(render2, ctx, notes, examples, _renderers) {
165
- const { cli } = ctx;
166
- const { t } = cli.i18n;
167
- let sections = [];
168
- const renderers = Object.assign(
169
- /* @__PURE__ */ Object.create(null),
170
- defaultRenderers,
171
- _renderers
172
- );
173
- generateCliDetail(sections, cli);
174
- sections.push({
175
- title: t("help.usage"),
176
- body: [
177
- yc.magenta(
178
- `$ ${cli._scriptName} ${withBrackets(
179
- "command",
180
- ctx.hasRootOrAlias
181
- )} [flags]`
182
- )
183
- ]
184
- });
185
- const commands = [
186
- ...ctx.hasRoot ? [cli._commands[Root]] : [],
187
- ...Object.values(cli._commands)
188
- ].map((command) => {
189
- var _a;
190
- const commandNameWithAlias = [
191
- typeof command.name === "symbol" ? "" : command.name,
192
- ...toArray((_a = command.alias) != null ? _a : [])
193
- ].sort(sortName).map(
194
- (n) => n === "" || typeof n === "symbol" ? `${cli._scriptName}` : `${cli._scriptName} ${n}`
195
- ).join(", ");
196
- return [yc.cyan(commandNameWithAlias), DELIMITER, command.description];
197
- });
198
- if (commands.length > 0) {
199
- sections.push({
200
- title: t("help.commands"),
201
- body: splitTable(commands)
202
- });
203
- }
204
- const globalFlags = formatFlags(cli._flags, t, renderers);
205
- if (globalFlags.length > 0) {
206
- sections.push({
207
- title: t("help.globalFlags"),
208
- body: splitTable(globalFlags)
209
- });
210
- }
211
- if (notes) {
212
- sections.push({
213
- title: t("help.notes"),
214
- body: notes
215
- });
216
- }
217
- if (examples) {
218
- generateExamples(sections, examples, t);
219
- }
220
- sections = renderers.renderSections(sections);
221
- return render2(sections);
222
- }
223
- function generateSubcommandHelp(render2, ctx, command) {
224
- var _a, _b, _c, _d, _e, _f;
225
- const { cli } = ctx;
226
- const { t } = cli.i18n;
227
- const [subcommand] = resolveCommandStrict(cli._commands, command, t);
228
- if (!subcommand) {
229
- throw new NoSuchCommandError(formatCommandName(command), t);
230
- }
231
- const renderers = Object.assign(
232
- /* @__PURE__ */ Object.create(null),
233
- defaultRenderers,
234
- (_a = subcommand.help) == null ? void 0 : _a.renderers
235
- );
236
- let sections = [];
237
- if (command === Root) {
238
- generateCliDetail(sections, cli);
239
- } else {
240
- generateCliDetail(sections, cli, {
241
- ...subcommand,
242
- name: formatCommandName(command)
243
- });
244
- }
245
- const parameters = (_c = (_b = subcommand.parameters) == null ? void 0 : _b.join(" ")) != null ? _c : void 0;
246
- const commandName = command === Root ? "" : ` ${formatCommandName(command)}`;
247
- const parametersString = parameters ? ` ${parameters}` : "";
248
- const flagsString = subcommand.flags ? " [flags]" : "";
249
- sections.push({
250
- title: t("help.usage"),
251
- body: [
252
- yc.magenta(
253
- `$ ${cli._scriptName}${commandName}${parametersString}${flagsString}`
254
- )
255
- ]
256
- });
257
- const globalFlags = formatFlags(cli._flags, t, renderers);
258
- if (globalFlags.length > 0) {
259
- sections.push({
260
- title: t("help.globalFlags"),
261
- body: splitTable(globalFlags)
262
- });
263
- }
264
- if (subcommand.flags) {
265
- sections.push({
266
- title: t("help.flags"),
267
- body: splitTable(formatFlags(subcommand.flags, t, renderers))
268
- });
269
- }
270
- if ((_d = subcommand == null ? void 0 : subcommand.help) == null ? void 0 : _d.notes) {
271
- sections.push({
272
- title: t("help.notes"),
273
- body: subcommand.help.notes
274
- });
275
- }
276
- if ((_e = subcommand == null ? void 0 : subcommand.help) == null ? void 0 : _e.examples) {
277
- generateExamples(sections, (_f = subcommand == null ? void 0 : subcommand.help) == null ? void 0 : _f.examples, t);
278
- }
279
- sections = renderers.renderSections(sections);
280
- return render2(sections);
281
- }
282
- const helpPlugin = ({
283
- command = true,
284
- flag = true,
285
- showHelpWhenNoCommand = true,
286
- notes,
287
- examples,
288
- banner,
289
- renderers
290
- } = {}) => definePlugin({
291
- setup: (cli) => {
292
- const { add, t } = cli.i18n;
293
- add(locales);
294
- function printHelp(s) {
295
- banner && print(`${banner}
296
- `);
297
- print(s);
298
- }
299
- if (command) {
300
- cli = cli.command("help", t("help.commandDescription"), {
301
- parameters: ["[command...]"],
302
- help: {
303
- notes: [
304
- t("help.notes.1"),
305
- t("help.notes.2"),
306
- t("help.notes.3")
307
- ],
308
- examples: [
309
- [`$ ${cli._scriptName} help`, t("help.examples.1")],
310
- [`$ ${cli._scriptName} help <command>`, t("help.examples.2")],
311
- [
312
- `$ ${cli._scriptName} <command> --help`,
313
- t("help.examples.2")
314
- ]
315
- ]
316
- }
317
- }).on("help", (ctx) => {
318
- if (ctx.parameters.command.length > 0) {
319
- printHelp(
320
- generateSubcommandHelp(render, ctx, ctx.parameters.command)
321
- );
322
- } else {
323
- printHelp(generateHelp(render, ctx, notes, examples, renderers));
324
- }
325
- });
326
- }
327
- if (flag) {
328
- cli = cli.flag("help", t("help.commandDescription"), {
329
- alias: "h",
330
- type: Boolean,
331
- default: false
332
- });
333
- }
334
- cli.interceptor((ctx, next) => {
335
- const shouldShowHelp = ctx.flags.help;
336
- if (!ctx.hasRootOrAlias && ctx.raw._.length === 0 && showHelpWhenNoCommand && !shouldShowHelp) {
337
- let str = `${t("core.noCommandGiven")}
338
-
339
- `;
340
- str += generateHelp(render, ctx, notes, examples, renderers);
341
- str += "\n";
342
- printHelp(str);
343
- process.exit(1);
344
- } else if (shouldShowHelp) {
345
- if (ctx.raw._.length > 0) {
346
- if (ctx.called === Root) {
347
- printHelp(generateSubcommandHelp(render, ctx, ctx.raw._));
348
- } else {
349
- if (ctx.name === Root) {
350
- printHelp(
351
- generateHelp(render, ctx, notes, examples, renderers)
352
- );
353
- } else {
354
- printHelp(generateSubcommandHelp(render, ctx, ctx.raw._));
355
- }
356
- }
357
- } else {
358
- if (ctx.hasRootOrAlias) {
359
- printHelp(generateSubcommandHelp(render, ctx, Root));
360
- } else {
361
- printHelp(generateHelp(render, ctx, notes, examples, renderers));
362
- }
363
- }
364
- } else {
365
- next();
366
- }
367
- });
368
- return cli;
369
- }
370
- });
207
+ //#endregion
208
+ //#region src/index.ts
209
+ const helpPlugin = ({ command = true, flag = true, showHelpWhenNoCommandSpecified = true, notes, examples, banner, formatters, groups } = {}) => definePlugin({ setup: (cli) => {
210
+ const mergedFormatters = {
211
+ ...defaultFormatters,
212
+ ...formatters
213
+ };
214
+ const generalHelpNotes = [
215
+ "If no command is specified, show help for the CLI.",
216
+ "If a command is specified, show help for the command.",
217
+ flag && "-h is an alias for --help."
218
+ ].filter(isTruthy);
219
+ const generalHelpExamples = [
220
+ command && [`$ ${cli._scriptName} help`, "Show help"],
221
+ command && [`$ ${cli._scriptName} help <command>`, "Show help for a specific command"],
222
+ flag && [`$ ${cli._scriptName} <command> --help`, "Show help for a specific command"]
223
+ ].filter(isTruthy);
224
+ const effectiveNotes = notes ?? generalHelpNotes;
225
+ const effectiveExamples = examples ?? generalHelpExamples;
226
+ function printHelp(s) {
227
+ if (banner) console.log(banner);
228
+ console.log(s);
229
+ }
230
+ if (command) cli.command("help", "Show help", {
231
+ parameters: ["[command...]"],
232
+ help: {
233
+ notes: generalHelpNotes,
234
+ examples: generalHelpExamples
235
+ }
236
+ }).on("help", (ctx) => {
237
+ const commandName = ctx.parameters.command;
238
+ let command$1;
239
+ if (commandName.length > 0) {
240
+ [command$1] = resolveCommand(cli._commands, commandName);
241
+ if (!command$1) {
242
+ console.error(`Command "${commandName.join(" ")}" not found.`);
243
+ return;
244
+ }
245
+ }
246
+ printHelp(new HelpRenderer(mergedFormatters, cli, cli._globalFlags, command$1, command$1 ? command$1.help?.notes : effectiveNotes, command$1 ? command$1.help?.examples : effectiveExamples, groups).render());
247
+ });
248
+ if (flag) cli.globalFlag("help", "Show help", {
249
+ alias: "h",
250
+ type: Boolean,
251
+ default: false
252
+ });
253
+ cli.interceptor({
254
+ enforce: "pre",
255
+ handler: async (ctx, next) => {
256
+ if (ctx.flags.help) printHelp(new HelpRenderer(mergedFormatters, cli, cli._globalFlags, ctx.command, ctx.command ? ctx.command.help?.notes : effectiveNotes, ctx.command ? ctx.command.help?.examples : effectiveExamples, groups).render());
257
+ else if (showHelpWhenNoCommandSpecified && !ctx.command && ctx.rawParsed.parameters.length === 0) printHelp(new HelpRenderer(mergedFormatters, cli, cli._globalFlags, void 0, effectiveNotes, effectiveExamples, groups).render());
258
+ else await next();
259
+ }
260
+ });
261
+ } });
371
262
 
372
- export { helpPlugin };
263
+ //#endregion
264
+ export { defaultFormatters, helpPlugin };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clerc/plugin-help",
3
- "version": "0.44.0",
3
+ "version": "1.0.0-beta.10",
4
4
  "author": "Ray <i@mk1.io> (https://github.com/so1ve)",
5
5
  "type": "module",
6
6
  "description": "Clerc plugin help",
@@ -25,13 +25,10 @@
25
25
  "license": "MIT",
26
26
  "sideEffects": false,
27
27
  "exports": {
28
- ".": {
29
- "types": "./dist/index.d.ts",
30
- "import": "./dist/index.js"
31
- }
28
+ ".": "./dist/index.js"
32
29
  },
33
- "main": "dist/index.js",
34
- "module": "dist/index.js",
30
+ "main": "./dist/index.js",
31
+ "module": "./dist/index.js",
35
32
  "types": "dist/index.d.ts",
36
33
  "typesVersions": {
37
34
  "*": {
@@ -48,17 +45,17 @@
48
45
  "access": "public"
49
46
  },
50
47
  "dependencies": {
51
- "@types/text-table": "^0.2.3",
52
- "string-width": "^6.1.0",
48
+ "@types/text-table": "^0.2.5",
49
+ "string-width": "^8.1.0",
53
50
  "text-table": "^0.2.0",
54
- "yoctocolors": "^1.0.0",
55
- "@clerc/utils": "0.44.0"
51
+ "yoctocolors": "^2.1.2"
52
+ },
53
+ "devDependencies": {
54
+ "@clerc/core": "1.0.0-beta.10",
55
+ "@clerc/utils": "1.0.0-beta.10",
56
+ "@clerc/parser": "1.0.0-beta.10"
56
57
  },
57
58
  "peerDependencies": {
58
59
  "@clerc/core": "*"
59
- },
60
- "scripts": {
61
- "build": "pkgroll",
62
- "watch": "pkgroll --watch"
63
60
  }
64
61
  }