@clerc/plugin-help 1.0.0-beta.2 → 1.0.0-beta.21

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 +113 -10
  2. package/dist/index.js +241 -59
  3. package/package.json +6 -9
package/dist/index.d.ts CHANGED
@@ -1,41 +1,143 @@
1
1
  import { Plugin } from "@clerc/core";
2
2
 
3
3
  //#region ../parser/src/types.d.ts
4
-
4
+ interface FlagDefaultValueFunction<T> {
5
+ (): T;
6
+ display?: string;
7
+ }
8
+ type FlagDefaultValue<T = unknown> = T | FlagDefaultValueFunction<T>;
5
9
  /**
6
10
  * Defines how a string input is converted to the target type T.
7
11
  *
8
12
  * @template T The target type.
9
13
  */
10
- type FlagTypeFunction<T = unknown> = (value: string) => T;
11
- type FlagType<T = unknown> = FlagTypeFunction<T> | readonly [FlagTypeFunction<T>];
14
+ interface TypeFunction<T = unknown> {
15
+ (value: string): T;
16
+ /**
17
+ * Optional display name for the type, useful in help output.
18
+ * If provided, this will be shown instead of the function name.
19
+ */
20
+ display?: string;
21
+ }
22
+ type FlagType<T = unknown> = TypeFunction<T> | readonly [TypeFunction<T>];
12
23
  //#endregion
13
24
  //#region src/types.d.ts
14
25
  interface Formatters {
15
26
  formatFlagType: (type: FlagType) => string;
27
+ formatFlagDefault: <T>(value: FlagDefaultValue<T>) => string;
28
+ }
29
+ /**
30
+ * A group definition as a tuple of [key, displayName].
31
+ * The key is used in help options to assign items to groups.
32
+ * The displayName is shown in the help output.
33
+ */
34
+ type GroupDefinition = [key: string, name: string];
35
+ /**
36
+ * Options for defining groups in help output.
37
+ */
38
+ interface GroupsOptions {
39
+ /**
40
+ * Groups for commands.
41
+ * Each group is defined as `[key, name]`.
42
+ */
43
+ commands?: GroupDefinition[];
44
+ /**
45
+ * Groups for command-specific flags.
46
+ * Each group is defined as `[key, name]`.
47
+ */
48
+ flags?: GroupDefinition[];
49
+ /**
50
+ * Groups for global flags.
51
+ * Each group is defined as `[key, name]`.
52
+ */
53
+ globalFlags?: GroupDefinition[];
16
54
  }
17
55
  //#endregion
18
56
  //#region src/formatters.d.ts
19
57
  declare const defaultFormatters: Formatters;
20
58
  //#endregion
21
59
  //#region src/index.d.ts
60
+ interface HelpOptions {
61
+ /**
62
+ * The group this item belongs to.
63
+ * The group must be defined in the `groups` option of `helpPlugin()`.
64
+ */
65
+ group?: string;
66
+ }
67
+ interface CommandHelpOptions extends HelpOptions {
68
+ /**
69
+ * Whether to show the command in help output.
70
+ *
71
+ * @default true
72
+ */
73
+ show?: boolean;
74
+ /**
75
+ * Notes to show in the help output.
76
+ */
77
+ notes?: string[];
78
+ /**
79
+ * Examples to show in the help output.
80
+ * Each example is a tuple of `[command, description]`.
81
+ */
82
+ examples?: [string, string][];
83
+ }
22
84
  declare module "@clerc/core" {
23
85
  interface CommandCustomOptions {
24
- help?: {
25
- showInHelp?: boolean;
26
- notes?: string[];
27
- examples?: [string, string][];
28
- };
86
+ /**
87
+ * Help options for the command.
88
+ */
89
+ help?: CommandHelpOptions;
90
+ }
91
+ interface FlagCustomOptions {
92
+ /**
93
+ * Help options for the flag.
94
+ */
95
+ help?: HelpOptions;
29
96
  }
30
97
  }
31
98
  interface HelpPluginOptions {
99
+ /**
100
+ * Whether to register the `help` command.
101
+ *
102
+ * @default true
103
+ */
32
104
  command?: boolean;
105
+ /**
106
+ * Whether to register the `--help` global flag.
107
+ *
108
+ * @default true
109
+ */
33
110
  flag?: boolean;
111
+ /**
112
+ * Whether to show help when no command is specified.
113
+ *
114
+ * @default true
115
+ */
34
116
  showHelpWhenNoCommandSpecified?: boolean;
117
+ /**
118
+ * Notes to show in the help output.
119
+ */
35
120
  notes?: string[];
121
+ /**
122
+ * Examples to show in the help output.
123
+ * Each example is a tuple of `[command, description]`.
124
+ */
36
125
  examples?: [string, string][];
126
+ /**
127
+ * A banner to show before the help output.
128
+ */
37
129
  banner?: string;
130
+ /**
131
+ * Custom formatters for rendering help.
132
+ */
38
133
  formatters?: Partial<Formatters>;
134
+ /**
135
+ * Group definitions for commands and flags.
136
+ * Groups allow organizing commands and flags into logical sections in help output.
137
+ * Each group is defined as `[key, name]` where `key` is the identifier used in help options
138
+ * and `name` is the display name shown in help output.
139
+ */
140
+ groups?: GroupsOptions;
39
141
  }
40
142
  declare const helpPlugin: ({
41
143
  command,
@@ -44,7 +146,8 @@ declare const helpPlugin: ({
44
146
  notes,
45
147
  examples,
46
148
  banner,
47
- formatters
149
+ formatters,
150
+ groups
48
151
  }?: HelpPluginOptions) => Plugin;
49
152
  //#endregion
50
- export { HelpPluginOptions, defaultFormatters, helpPlugin };
153
+ export { CommandHelpOptions, type GroupDefinition, type GroupsOptions, HelpOptions, HelpPluginOptions, defaultFormatters, helpPlugin };
package/dist/index.js CHANGED
@@ -1,55 +1,108 @@
1
- import { definePlugin, resolveCommand } from "@clerc/core";
2
- import { formatFlagName, formatVersion, isTruthy, objectIsEmpty, toArray } from "@clerc/utils";
1
+ import { NoSuchCommandError, definePlugin, normalizeFlagValue, resolveCommand } from "@clerc/core";
3
2
  import stringWidth from "string-width";
4
3
  import textTable from "text-table";
5
4
  import * as yc from "yoctocolors";
6
5
 
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
7
15
  //#region src/utils.ts
8
16
  function formatFlagType(type) {
9
- if (typeof type === "function") return type.name;
10
- return `Array<${type[0].name}>`;
17
+ if (typeof type === "function") return type.display ?? type.name;
18
+ const innerType = type[0];
19
+ return `Array<${innerType.displayName ?? innerType.name}>`;
20
+ }
21
+ function formatFlagDefault(value) {
22
+ if (typeof value === "function" && "display" in value && value.display) return value.display;
23
+ return String(value);
24
+ }
25
+ function formatCommandName(name) {
26
+ if (name === "") return "(root)";
27
+ return name;
11
28
  }
12
29
 
13
30
  //#endregion
14
31
  //#region src/formatters.ts
15
- const defaultFormatters = { formatFlagType };
32
+ const defaultFormatters = {
33
+ formatFlagType,
34
+ formatFlagDefault
35
+ };
16
36
 
17
37
  //#endregion
18
38
  //#region src/renderer.ts
39
+ const DEFAULT_GROUP_KEY = "default";
19
40
  const table = (items) => textTable(items, { stringLength: stringWidth });
20
41
  const splitTable = (items) => table(items).split("\n");
21
42
  const DELIMITER = yc.yellow("-");
43
+ const INDENT = " ".repeat(2);
44
+ const withIndent = (str) => `${INDENT}${str}`;
45
+ function groupDefinitionsToMap(definitions) {
46
+ const map = /* @__PURE__ */ new Map();
47
+ if (definitions) for (const [key, name] of definitions) map.set(key, name);
48
+ return map;
49
+ }
50
+ function validateGroup(group, groupMap, itemType, itemName) {
51
+ if (group && group !== DEFAULT_GROUP_KEY && !groupMap.has(group)) throw new Error(`Unknown ${itemType} group "${group}" for "${itemName}". Available groups: ${[...groupMap.keys()].join(", ") || "(none)"}`);
52
+ }
22
53
  var HelpRenderer = class {
23
- constructor(_formatters, _cli, _globalFlags, _command, _notes, _examples) {
54
+ _command;
55
+ get _commandGroups() {
56
+ return groupDefinitionsToMap(this._getGroups().commands);
57
+ }
58
+ get _flagGroups() {
59
+ return groupDefinitionsToMap(this._getGroups().flags);
60
+ }
61
+ get _globalFlagGroups() {
62
+ return groupDefinitionsToMap(this._getGroups().globalFlags);
63
+ }
64
+ constructor(_formatters, _cli, _globalFlags, _getGroups, _getExamples, _notes) {
24
65
  this._formatters = _formatters;
25
66
  this._cli = _cli;
26
67
  this._globalFlags = _globalFlags;
27
- this._command = _command;
68
+ this._getGroups = _getGroups;
69
+ this._getExamples = _getExamples;
28
70
  this._notes = _notes;
29
- this._examples = _examples;
71
+ }
72
+ setCommand(command) {
73
+ if (command) {
74
+ this._command = command;
75
+ this._getExamples = () => command?.help?.examples;
76
+ this._notes = command?.help?.notes;
77
+ }
78
+ }
79
+ renderSections(sections) {
80
+ return sections.filter(isTruthy).map((section) => {
81
+ const body = Array.isArray(section.body) ? section.body.filter((s) => s !== void 0).join("\n") : section.body;
82
+ if (!section.title) return body;
83
+ return `${yc.bold(section.title)}\n${body.split("\n").map(withIndent).join("\n")}`;
84
+ }).join("\n\n");
30
85
  }
31
86
  render() {
32
- return [
87
+ const sections = [
33
88
  this.renderHeader(),
34
89
  this.renderUsage(),
35
- this.renderCommands(),
36
- this.renderGlobalFlags(),
90
+ this.renderParameters(),
37
91
  this.renderCommandFlags(),
38
- this.renderNotes(),
39
- this.renderExamples()
40
- ].filter(isTruthy).filter((section) => section.body.length > 0).map((section) => {
41
- const body = Array.isArray(section.body) ? section.body.filter(Boolean).join("\n") : section.body;
42
- if (!section.title) return body;
43
- return `${yc.bold(section.title)}\n${body.split("\n").map((line) => ` ${line}`).join("\n")}`;
44
- }).join("\n\n");
92
+ this.renderGlobalFlags(),
93
+ this.renderCommands(),
94
+ this.renderExamples(),
95
+ this.renderNotes()
96
+ ];
97
+ return this.renderSections(sections);
45
98
  }
46
99
  renderHeader() {
47
100
  const { _name, _version, _description } = this._cli;
48
101
  const command = this._command;
49
- const description = command?.description ?? _description;
102
+ const description = command ? command.description : _description;
50
103
  const formattedCommandName = command?.name ? ` ${yc.cyan(command.name)}` : "";
51
104
  const headerLine = command ? `${yc.green(_name)}${formattedCommandName}` : `${yc.green(_name)} ${yc.yellow(formatVersion(_version))}`;
52
- const alias = command?.alias ? `Alias${toArray(command.alias).length > 1 ? "es" : ""}: ${toArray(command.alias).map((a) => yc.cyan(a)).join(", ")}` : "";
105
+ const alias = command?.alias ? `Alias${toArray(command.alias).length > 1 ? "es" : ""}: ${toArray(command.alias).map((a) => yc.cyan(a)).join(", ")}` : void 0;
53
106
  return { body: [`${headerLine}${description ? ` ${DELIMITER} ${description}` : ""}`, alias] };
54
107
  }
55
108
  renderUsage() {
@@ -57,54 +110,153 @@ var HelpRenderer = class {
57
110
  const command = this._command;
58
111
  let usage = `$ ${_scriptName}`;
59
112
  if (command) {
60
- usage += command.name ? ` ${command.name}` : "";
61
- if (command.parameters) usage += ` ${command.parameters.join(" ")}`;
62
- }
63
- if (command?.flags && !objectIsEmpty(command.flags) || !objectIsEmpty(this._globalFlags)) usage += " [FLAGS]";
113
+ if (command.name) usage += ` ${command.name}`;
114
+ if (command.parameters) usage += ` ${command.parameters.map((p) => typeof p === "string" ? p : p.key).join(" ")}`;
115
+ } else usage += this._cli._commands.has("") ? " [command]" : " <command>";
116
+ if (command?.flags && !objectIsEmpty(command.flags) || !objectIsEmpty(this._globalFlags)) usage += " [flags]";
64
117
  return {
65
118
  title: "Usage",
66
- body: [usage]
119
+ body: [yc.magenta(usage)]
67
120
  };
68
121
  }
122
+ renderParameters() {
123
+ const command = this._command;
124
+ if (!command?.parameters || command.parameters.length === 0) return;
125
+ return {
126
+ title: "Parameters",
127
+ body: splitTable(command.parameters.map((parameter) => {
128
+ const key = typeof parameter === "string" ? parameter : parameter.key;
129
+ const type = typeof parameter === "string" ? void 0 : parameter.type;
130
+ const description = typeof parameter === "string" ? void 0 : parameter.description;
131
+ return [
132
+ yc.blue(key),
133
+ type?.display ? yc.gray(type.display) : void 0,
134
+ description
135
+ ].filter(isTruthy);
136
+ }))
137
+ };
138
+ }
139
+ getSubcommands(parentCommandName) {
140
+ const subcommands = /* @__PURE__ */ new Map();
141
+ if (parentCommandName === "") return subcommands;
142
+ const prefix = `${parentCommandName} `;
143
+ for (const [name, command] of this._cli._commands) if (name.startsWith(prefix)) {
144
+ const subcommandName = name.slice(prefix.length);
145
+ subcommands.set(subcommandName, command);
146
+ }
147
+ return subcommands;
148
+ }
149
+ buildGroupedCommandsBody(commandsToShow, prefix) {
150
+ const groupedCommands = /* @__PURE__ */ new Map();
151
+ const defaultCommands = [];
152
+ let rootCommand = [];
153
+ for (const command of commandsToShow.values()) {
154
+ if (command.__isAlias || command.help?.show === false) continue;
155
+ const group = command.help?.group;
156
+ validateGroup(group, this._commandGroups, "command", command.name);
157
+ const item = [`${yc.cyan(formatCommandName(command.name.slice(prefix.length)))}${command.alias ? ` (${toArray(command.alias).join(", ")})` : ""}`, command.description].filter(isTruthy);
158
+ if (command.name === "") rootCommand = item;
159
+ else if (group && group !== DEFAULT_GROUP_KEY) {
160
+ const groupItems = groupedCommands.get(group) ?? [];
161
+ groupItems.push(item);
162
+ groupedCommands.set(group, groupItems);
163
+ } else defaultCommands.push(item);
164
+ }
165
+ const body = [];
166
+ const defaultGroup = [];
167
+ if (rootCommand.length > 0) defaultGroup.push(rootCommand);
168
+ if (defaultCommands.length > 0) defaultGroup.push(...defaultCommands);
169
+ if (defaultGroup.length > 0) body.push(...splitTable(defaultGroup));
170
+ for (const [key, name] of this._commandGroups) {
171
+ const items = groupedCommands.get(key);
172
+ if (items && items.length > 0) {
173
+ if (body.length > 0) body.push("");
174
+ body.push(`${yc.dim(name)}`);
175
+ body.push(...splitTable(items).map(withIndent));
176
+ }
177
+ }
178
+ return body;
179
+ }
180
+ renderAvailableSubcommands(parentCommandName) {
181
+ const subcommands = this.getSubcommands(parentCommandName);
182
+ if (subcommands.size === 0) return "";
183
+ const prefix = `${parentCommandName} `;
184
+ const body = this.buildGroupedCommandsBody(subcommands, prefix);
185
+ if (body.length === 0) return "";
186
+ const sections = [{ body: `${yc.green(this._cli._name)} ${yc.cyan(parentCommandName)} not found` }, {
187
+ title: "Available Subcommands",
188
+ body
189
+ }];
190
+ return this.renderSections(sections);
191
+ }
69
192
  renderCommands() {
70
193
  const commands = this._cli._commands;
71
- if (this._command || commands.size === 0) return;
194
+ let commandsToShow;
195
+ let title = "Commands";
196
+ let prefix = "";
197
+ if (this._command) {
198
+ prefix = this._command.name ? `${this._command.name} ` : "";
199
+ title = "Subcommands";
200
+ commandsToShow = this.getSubcommands(this._command.name);
201
+ if (commandsToShow.size === 0) return;
202
+ } else commandsToShow = commands;
203
+ if (commandsToShow.size === 0) return;
204
+ const body = this.buildGroupedCommandsBody(commandsToShow, prefix);
72
205
  return {
73
- title: "Commands",
74
- body: splitTable([...commands.values()].map((command) => {
75
- if (command.__isAlias || command.help?.showInHelp === false) return null;
76
- return [`${yc.cyan(command.name)}${command.alias ? ` (${toArray(command.alias).join(", ")})` : ""}`, command.description];
77
- }).filter(isTruthy))
206
+ title,
207
+ body
78
208
  };
79
209
  }
80
- renderFlags(flags) {
81
- return Object.entries(flags).map(([name, flag]) => {
82
- const flagName = formatFlagName(name);
83
- const aliases = (Array.isArray(flag.alias) ? flag.alias : flag.alias ? [flag.alias] : []).map(formatFlagName).join(", ");
84
- const description = flag.description ?? "";
85
- const type = this._formatters.formatFlagType(flag.type);
86
- const defaultValue = flag.default === void 0 ? "" : `[default: ${String(flag.default)}]`;
87
- return [
88
- yc.blue([flagName, aliases].filter(Boolean).join(", ")),
89
- yc.gray(type),
90
- description,
91
- yc.gray(defaultValue)
92
- ];
93
- });
210
+ renderFlagItem(name, flag) {
211
+ flag = normalizeFlagValue(flag);
212
+ const flagName = formatFlagName(name);
213
+ const aliases = (Array.isArray(flag.alias) ? flag.alias : flag.alias ? [flag.alias] : []).map(formatFlagName).join(", ");
214
+ const type = this._formatters.formatFlagType(flag.type);
215
+ return [
216
+ yc.blue([flagName, aliases].filter(Boolean).join(", ")),
217
+ yc.gray(type),
218
+ flag.description,
219
+ flag.default !== void 0 && yc.gray(`[default: ${this._formatters.formatFlagDefault(flag.default)}]`)
220
+ ].filter(isTruthy);
221
+ }
222
+ renderGroupedFlags(flags, groupMap, itemType) {
223
+ const groupedFlags = /* @__PURE__ */ new Map();
224
+ const defaultFlags = [];
225
+ for (const [name, flag] of Object.entries(flags)) {
226
+ const group = flag.help?.group;
227
+ validateGroup(group, groupMap, itemType, name);
228
+ const item = this.renderFlagItem(name, flag);
229
+ if (group && group !== DEFAULT_GROUP_KEY) {
230
+ const groupItems = groupedFlags.get(group) ?? [];
231
+ groupItems.push(item);
232
+ groupedFlags.set(group, groupItems);
233
+ } else defaultFlags.push(item);
234
+ }
235
+ const body = [];
236
+ if (defaultFlags.length > 0) body.push(...splitTable(defaultFlags));
237
+ for (const [key, name] of groupMap) {
238
+ const items = groupedFlags.get(key);
239
+ if (items && items.length > 0) {
240
+ if (body.length > 0) body.push("");
241
+ body.push(`${yc.dim(name)}`);
242
+ body.push(...splitTable(items).map(withIndent));
243
+ }
244
+ }
245
+ return body;
94
246
  }
95
247
  renderCommandFlags() {
96
248
  const command = this._command;
97
249
  if (!command?.flags || objectIsEmpty(command.flags)) return;
98
250
  return {
99
251
  title: "Flags",
100
- body: splitTable(this.renderFlags(command.flags))
252
+ body: this.renderGroupedFlags(command.flags, this._flagGroups, "flag")
101
253
  };
102
254
  }
103
255
  renderGlobalFlags() {
104
256
  if (!this._globalFlags || objectIsEmpty(this._globalFlags)) return;
105
257
  return {
106
258
  title: "Global Flags",
107
- body: splitTable(this.renderFlags(this._globalFlags))
259
+ body: this.renderGroupedFlags(this._globalFlags, this._globalFlagGroups, "global flag")
108
260
  };
109
261
  }
110
262
  renderNotes() {
@@ -115,10 +267,11 @@ var HelpRenderer = class {
115
267
  };
116
268
  }
117
269
  renderExamples() {
118
- if (!this._examples?.length) return;
270
+ const examples = this._getExamples();
271
+ if (!examples?.length) return;
119
272
  return {
120
273
  title: "Examples",
121
- body: splitTable(this._examples.map(([command, description]) => {
274
+ body: splitTable(examples.map(([command, description]) => {
122
275
  return [
123
276
  command,
124
277
  DELIMITER,
@@ -129,9 +282,20 @@ var HelpRenderer = class {
129
282
  }
130
283
  };
131
284
 
285
+ //#endregion
286
+ //#region src/store.ts
287
+ function addStoreApi(cli, { groups }) {
288
+ cli.store.help = { addGroup: (options) => {
289
+ if (options.commands) groups.commands = [...groups.commands ?? [], ...options.commands];
290
+ if (options.flags) groups.flags = [...groups.flags ?? [], ...options.flags];
291
+ if (options.globalFlags) groups.globalFlags = [...groups.globalFlags ?? [], ...options.globalFlags];
292
+ } };
293
+ }
294
+
132
295
  //#endregion
133
296
  //#region src/index.ts
134
- const helpPlugin = ({ command = true, flag = true, showHelpWhenNoCommandSpecified = true, notes, examples, banner, formatters } = {}) => definePlugin({ setup: (cli) => {
297
+ const helpPlugin = ({ command = true, flag = true, showHelpWhenNoCommandSpecified = true, notes, examples, banner, formatters, groups = {} } = {}) => definePlugin({ setup: (cli) => {
298
+ addStoreApi(cli, { groups });
135
299
  const mergedFormatters = {
136
300
  ...defaultFormatters,
137
301
  ...formatters
@@ -141,22 +305,31 @@ const helpPlugin = ({ command = true, flag = true, showHelpWhenNoCommandSpecifie
141
305
  "If a command is specified, show help for the command.",
142
306
  flag && "-h is an alias for --help."
143
307
  ].filter(isTruthy);
144
- const generalHelpExamples = [
308
+ const getGeneralHelpExamples = () => [
145
309
  command && [`$ ${cli._scriptName} help`, "Show help"],
146
310
  command && [`$ ${cli._scriptName} help <command>`, "Show help for a specific command"],
147
311
  flag && [`$ ${cli._scriptName} <command> --help`, "Show help for a specific command"]
148
312
  ].filter(isTruthy);
149
313
  const effectiveNotes = notes ?? generalHelpNotes;
150
- const effectiveExamples = examples ?? generalHelpExamples;
314
+ const getEffectiveExamples = () => examples ?? getGeneralHelpExamples();
151
315
  function printHelp(s) {
152
316
  if (banner) console.log(banner);
153
317
  console.log(s);
154
318
  }
319
+ const renderer = new HelpRenderer(mergedFormatters, cli, cli._globalFlags, () => groups, getEffectiveExamples, effectiveNotes);
320
+ function tryPrintSubcommandsHelp(commandName) {
321
+ const subcommandsOutput = renderer.renderAvailableSubcommands(commandName);
322
+ if (subcommandsOutput) {
323
+ printHelp(subcommandsOutput);
324
+ return true;
325
+ }
326
+ return false;
327
+ }
155
328
  if (command) cli.command("help", "Show help", {
156
329
  parameters: ["[command...]"],
157
330
  help: {
158
331
  notes: generalHelpNotes,
159
- examples: generalHelpExamples
332
+ examples: getGeneralHelpExamples()
160
333
  }
161
334
  }).on("help", (ctx) => {
162
335
  const commandName = ctx.parameters.command;
@@ -164,11 +337,13 @@ const helpPlugin = ({ command = true, flag = true, showHelpWhenNoCommandSpecifie
164
337
  if (commandName.length > 0) {
165
338
  [command$1] = resolveCommand(cli._commands, commandName);
166
339
  if (!command$1) {
167
- console.error(`Command "${commandName.join(" ")}" not found.`);
168
- return;
340
+ const parentCommandName = commandName.join(" ");
341
+ if (tryPrintSubcommandsHelp(parentCommandName)) return;
342
+ throw new NoSuchCommandError(parentCommandName);
169
343
  }
170
344
  }
171
- printHelp(new HelpRenderer(mergedFormatters, cli, cli._globalFlags, command$1, command$1 ? command$1.help?.notes : effectiveNotes, command$1 ? command$1.help?.examples : effectiveExamples).render());
345
+ renderer.setCommand(command$1);
346
+ printHelp(renderer.render());
172
347
  });
173
348
  if (flag) cli.globalFlag("help", "Show help", {
174
349
  alias: "h",
@@ -176,10 +351,17 @@ const helpPlugin = ({ command = true, flag = true, showHelpWhenNoCommandSpecifie
176
351
  default: false
177
352
  });
178
353
  cli.interceptor({
179
- enforce: "pre",
354
+ enforce: "post",
180
355
  handler: async (ctx, next) => {
181
- 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).render());
182
- else if (showHelpWhenNoCommandSpecified && !ctx.command && ctx.rawParsed.parameters.length === 0) printHelp(new HelpRenderer(mergedFormatters, cli, cli._globalFlags, void 0, effectiveNotes, effectiveExamples).render());
356
+ if (ctx.flags.help) {
357
+ const command$1 = ctx.command;
358
+ if (!command$1 && ctx.rawParsed.parameters.length > 0) {
359
+ if (tryPrintSubcommandsHelp(ctx.rawParsed.parameters.join(" "))) return;
360
+ await next();
361
+ }
362
+ renderer.setCommand(command$1);
363
+ printHelp(renderer.render());
364
+ } else if (showHelpWhenNoCommandSpecified && !ctx.command && ctx.rawParsed.parameters.length === 0) printHelp(renderer.render());
183
365
  else await next();
184
366
  }
185
367
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clerc/plugin-help",
3
- "version": "1.0.0-beta.2",
3
+ "version": "1.0.0-beta.21",
4
4
  "author": "Ray <i@mk1.io> (https://github.com/so1ve)",
5
5
  "type": "module",
6
6
  "description": "Clerc plugin help",
@@ -48,18 +48,15 @@
48
48
  "@types/text-table": "^0.2.5",
49
49
  "string-width": "^8.1.0",
50
50
  "text-table": "^0.2.0",
51
- "yoctocolors": "^2.1.2",
52
- "@clerc/utils": "1.0.0-beta.2"
51
+ "yoctocolors": "^2.1.2"
53
52
  },
54
53
  "devDependencies": {
55
- "@clerc/parser": "1.0.0-beta.2",
56
- "@clerc/core": "1.0.0-beta.2"
54
+ "kons": "^0.7.1",
55
+ "@clerc/parser": "1.0.0-beta.21",
56
+ "@clerc/utils": "1.0.0-beta.21",
57
+ "@clerc/core": "1.0.0-beta.21"
57
58
  },
58
59
  "peerDependencies": {
59
60
  "@clerc/core": "*"
60
- },
61
- "scripts": {
62
- "build": "tsdown",
63
- "watch": "tsdown --watch"
64
61
  }
65
62
  }