@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.
- package/dist/index.d.ts +113 -10
- package/dist/index.js +241 -59
- 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
|
-
|
|
11
|
-
|
|
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
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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
|
-
|
|
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 = {
|
|
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
|
-
|
|
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.
|
|
68
|
+
this._getGroups = _getGroups;
|
|
69
|
+
this._getExamples = _getExamples;
|
|
28
70
|
this._notes = _notes;
|
|
29
|
-
|
|
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
|
-
|
|
87
|
+
const sections = [
|
|
33
88
|
this.renderHeader(),
|
|
34
89
|
this.renderUsage(),
|
|
35
|
-
this.
|
|
36
|
-
this.renderGlobalFlags(),
|
|
90
|
+
this.renderParameters(),
|
|
37
91
|
this.renderCommandFlags(),
|
|
38
|
-
this.
|
|
39
|
-
this.
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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
|
|
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
|
-
|
|
61
|
-
if (command.parameters) usage += ` ${command.parameters.join(" ")}`;
|
|
62
|
-
}
|
|
63
|
-
if (command?.flags && !objectIsEmpty(command.flags) || !objectIsEmpty(this._globalFlags)) usage += " [
|
|
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
|
-
|
|
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
|
|
74
|
-
body
|
|
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
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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:
|
|
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:
|
|
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
|
-
|
|
270
|
+
const examples = this._getExamples();
|
|
271
|
+
if (!examples?.length) return;
|
|
119
272
|
return {
|
|
120
273
|
title: "Examples",
|
|
121
|
-
body: splitTable(
|
|
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
|
|
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
|
|
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:
|
|
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
|
-
|
|
168
|
-
return;
|
|
340
|
+
const parentCommandName = commandName.join(" ");
|
|
341
|
+
if (tryPrintSubcommandsHelp(parentCommandName)) return;
|
|
342
|
+
throw new NoSuchCommandError(parentCommandName);
|
|
169
343
|
}
|
|
170
344
|
}
|
|
171
|
-
|
|
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: "
|
|
354
|
+
enforce: "post",
|
|
180
355
|
handler: async (ctx, next) => {
|
|
181
|
-
if (ctx.flags.help)
|
|
182
|
-
|
|
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.
|
|
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
|
-
"
|
|
56
|
-
"@clerc/
|
|
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
|
}
|