@flowscripter/dynamic-cli-framework 1.0.0
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/.github/workflows/check-bun-dependencies.yml +9 -0
- package/.github/workflows/lint-pr-message.yml +11 -0
- package/.github/workflows/release-bun-library.yml +8 -0
- package/.github/workflows/validate-bun-library-pr.yml +8 -0
- package/LICENSE +21 -0
- package/README.md +1475 -0
- package/bun.lock +63 -0
- package/index.ts +89 -0
- package/package.json +23 -0
- package/src/api/CLI.ts +17 -0
- package/src/api/CLIConfig.ts +19 -0
- package/src/api/Context.ts +25 -0
- package/src/api/RunResult.ts +142 -0
- package/src/api/argument/Argument.ts +36 -0
- package/src/api/argument/ArgumentValueTypes.ts +87 -0
- package/src/api/argument/ComplexOption.ts +31 -0
- package/src/api/argument/GlobalCommandArgument.ts +17 -0
- package/src/api/argument/Option.ts +32 -0
- package/src/api/argument/Positional.ts +27 -0
- package/src/api/argument/SubCommandArgument.ts +20 -0
- package/src/api/command/Command.ts +22 -0
- package/src/api/command/GlobalCommand.ts +33 -0
- package/src/api/command/GlobalModifierCommand.ts +12 -0
- package/src/api/command/GroupCommand.ts +20 -0
- package/src/api/command/SubCommand.ts +41 -0
- package/src/api/command/UsageExample.ts +21 -0
- package/src/api/service/ServiceProvider.ts +52 -0
- package/src/api/service/core/AsciiBannerGeneratorService.ts +35 -0
- package/src/api/service/core/KeyValueService.ts +28 -0
- package/src/api/service/core/PrinterService.ts +266 -0
- package/src/api/service/core/ShutdownService.ts +12 -0
- package/src/api/service/core/SyntaxHighlighterService.ts +42 -0
- package/src/cli/BaseCLI.ts +312 -0
- package/src/cli/DefaultRuntimeCLI.ts +54 -0
- package/src/command/MultiCommandCliHelpCommand.ts +394 -0
- package/src/command/SingleCommandCliHelpCommand.ts +183 -0
- package/src/command/UsageCommand.ts +35 -0
- package/src/command/VersionCommand.ts +22 -0
- package/src/launcher.ts +127 -0
- package/src/runtime/DefaultContext.ts +32 -0
- package/src/runtime/argument/ArgumentTypeGuards.ts +8 -0
- package/src/runtime/command/CommandTypeGuards.ts +30 -0
- package/src/runtime/command/CommandValidator.ts +549 -0
- package/src/runtime/parser.ts +219 -0
- package/src/runtime/registry/CommandRegistry.ts +121 -0
- package/src/runtime/registry/DefaultCommandRegistry.ts +318 -0
- package/src/runtime/registry/DefaultServiceProviderRegistry.ts +26 -0
- package/src/runtime/registry/ServiceProviderRegistry.ts +11 -0
- package/src/runtime/runner.ts +697 -0
- package/src/runtime/scanner.ts +417 -0
- package/src/runtime/values/ValuePopulationResult.ts +41 -0
- package/src/runtime/values/argumentValueMerge.ts +201 -0
- package/src/runtime/values/argumentValueValidation.ts +610 -0
- package/src/runtime/values/globalCommandValuePopulation.ts +96 -0
- package/src/runtime/values/subCommandValuePopulation.ts +680 -0
- package/src/service/asciiBannerGenerator/AsciiBannerGeneratorServiceProvider.ts +45 -0
- package/src/service/asciiBannerGenerator/DefaultAsciiBannerGeneratorService.ts +38 -0
- package/src/service/asciiBannerGenerator/standard.flf.json +3 -0
- package/src/service/banner/BannerServiceProvider.ts +99 -0
- package/src/service/banner/command/NoBannerCommand.ts +42 -0
- package/src/service/configuration/ConfigurationServiceProvider.ts +571 -0
- package/src/service/configuration/DefaultKeyValueService.ts +60 -0
- package/src/service/configuration/command/ConfigCommand.ts +43 -0
- package/src/service/configuration/command/DumpConfigCommand.ts +47 -0
- package/src/service/printer/DefaultPrinterService.ts +256 -0
- package/src/service/printer/PrinterServiceProvider.ts +61 -0
- package/src/service/printer/command/DarkModeCommand.ts +43 -0
- package/src/service/printer/command/LogLevelCommand.ts +68 -0
- package/src/service/printer/command/NoColorCommand.ts +43 -0
- package/src/service/printer/terminal/Ansi.ts +10 -0
- package/src/service/printer/terminal/Color.ts +14 -0
- package/src/service/printer/terminal/Progress.ts +373 -0
- package/src/service/printer/terminal/Spinner.ts +100 -0
- package/src/service/printer/terminal/Styler.ts +5 -0
- package/src/service/printer/terminal/Terminal.ts +8 -0
- package/src/service/printer/terminal/Theme.ts +34 -0
- package/src/service/printer/terminal/TtyStyler.ts +39 -0
- package/src/service/printer/terminal/TtyTerminal.ts +65 -0
- package/src/service/shutdown/DefaultShutdownService.ts +9 -0
- package/src/service/shutdown/ShutdownServiceProvider.ts +55 -0
- package/src/service/syntaxHighlighter/DefaultSyntaxHighlighterService.ts +52 -0
- package/src/service/syntaxHighlighter/SyntaxHighlighterServiceProvider.ts +51 -0
- package/src/util/configHelper.ts +95 -0
- package/src/util/envVarHelper.ts +338 -0
- package/src/util/helpHelper.ts +751 -0
- package/src/util/logger.ts +84 -0
- package/src/util/runnerHelper.ts +112 -0
- package/tests/cli/BaseCLI.test.ts +344 -0
- package/tests/command/MultiCommandCliHelpCommand.test.ts +650 -0
- package/tests/command/SingleCommandCliHelpCommand.test.ts +384 -0
- package/tests/command/UsageCommand.test.ts +53 -0
- package/tests/command/VersionCommand.test.ts +36 -0
- package/tests/fixtures/CLIConfig.ts +9 -0
- package/tests/fixtures/Command.ts +229 -0
- package/tests/fixtures/CommandRegistry.ts +8 -0
- package/tests/fixtures/ConfigurationServiceProvider.ts +28 -0
- package/tests/fixtures/Context.ts +33 -0
- package/tests/fixtures/ServiceProviderRegistry.ts +16 -0
- package/tests/fixtures/StreamString.ts +37 -0
- package/tests/fixtures/util.ts +41 -0
- package/tests/runtime/DefaultContext.test.ts +20 -0
- package/tests/runtime/argument/ArgumentTypeGuards.test.ts +49 -0
- package/tests/runtime/command/CommandTypeGuards.test.ts +43 -0
- package/tests/runtime/command/CommandValidator.test.ts +816 -0
- package/tests/runtime/parser.test.ts +766 -0
- package/tests/runtime/registry/DefaultCommandRegistry.test.ts +166 -0
- package/tests/runtime/registry/DefaultServiceProviderRegistry.test.ts +42 -0
- package/tests/runtime/runner.test.ts +1298 -0
- package/tests/runtime/scanner.test.ts +1113 -0
- package/tests/runtime/values/argumentValueMerge.test.ts +314 -0
- package/tests/runtime/values/argumentValueValidation.test.ts +1020 -0
- package/tests/runtime/values/globalCommandValuePopulation.test.ts +662 -0
- package/tests/runtime/values/subCommandValuePopulation.test.ts +2658 -0
- package/tests/service/asciiBannerGenerator/AsciiBannerGeneratorServiceProvider.test.ts +22 -0
- package/tests/service/asciiBannerGenerator/DefaultAsciiBannerGeneratorService.test.ts +70 -0
- package/tests/service/asciiBannerGenerator/__snapshots__/DefaultAsciiBannerGeneratorService.test.ts.snap +22 -0
- package/tests/service/asciiBannerGenerator/small.flf.json +3 -0
- package/tests/service/configuration/ConfigurationServiceProvider.test.ts +249 -0
- package/tests/service/configuration/DefaultKeyValueService.test.ts +65 -0
- package/tests/service/launchBanner/BannerServiceProvider.test.ts +49 -0
- package/tests/service/launchBanner/__snapshots__/BannerServiceProvider.test.ts.snap +3 -0
- package/tests/service/launchBanner/command/NoBannerCommand.test.ts +21 -0
- package/tests/service/printer/DefaultPrinterService.test.ts +409 -0
- package/tests/service/printer/PrinterServiceProvider.test.ts +29 -0
- package/tests/service/printer/command/LogLevelCommand.test.ts +58 -0
- package/tests/service/printer/terminal/Progress.test.ts +40 -0
- package/tests/service/printer/terminal/Spinner.test.ts +57 -0
- package/tests/service/shutdown/ShutdownServiceProvider.test.ts +13 -0
- package/tests/service/syntaxHighlighter/DefaultSyntaxHighlighterService.test.ts +123 -0
- package/tests/service/syntaxHighlighter/SyntaxHighlighterServiceProvider.test.ts +41 -0
- package/tests/util/configHelper.test.ts +275 -0
- package/tests/util/envVarHelper.test.ts +318 -0
- package/tests/util/logger.test.ts +10 -0
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
import type Positional from "../api/argument/Positional.ts";
|
|
2
|
+
import type Option from "../api/argument/Option.ts";
|
|
3
|
+
import type SubCommand from "../api/command/SubCommand.ts";
|
|
4
|
+
import type GlobalCommand from "../api/command/GlobalCommand.ts";
|
|
5
|
+
import type GroupCommand from "../api/command/GroupCommand.ts";
|
|
6
|
+
import { distance } from "fastest-levenshtein";
|
|
7
|
+
import {
|
|
8
|
+
type ArgumentSingleValueType,
|
|
9
|
+
type ArgumentValues,
|
|
10
|
+
ArgumentValueTypeName,
|
|
11
|
+
} from "../api/argument/ArgumentValueTypes.ts";
|
|
12
|
+
import type Context from "../api/Context.ts";
|
|
13
|
+
import type { HelpSection } from "../util/helpHelper.ts";
|
|
14
|
+
import {
|
|
15
|
+
getCommandArgsHelpSections,
|
|
16
|
+
getCommandExamplesHelpSections,
|
|
17
|
+
getGlobalArgumentHelpEntry,
|
|
18
|
+
getMultiCommandAppSyntax,
|
|
19
|
+
getSubCommandArgumentsSyntax,
|
|
20
|
+
printHelpSections,
|
|
21
|
+
} from "../util/helpHelper.ts";
|
|
22
|
+
import type GlobalModifierCommand from "../api/command/GlobalModifierCommand.ts";
|
|
23
|
+
import type CommandRegistry from "../runtime/registry/CommandRegistry.ts";
|
|
24
|
+
import type PrinterService from "../api/service/core/PrinterService.ts";
|
|
25
|
+
import {
|
|
26
|
+
Icon,
|
|
27
|
+
PRINTER_SERVICE_ID,
|
|
28
|
+
} from "../api/service/core/PrinterService.ts";
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Provides common implementation for both {@link MultiCommandCliHelpGlobalCommand} and {@link MultiCommandCliHelpSubCommand}.
|
|
32
|
+
*/
|
|
33
|
+
abstract class MultiCommandCliAbstractHelpCommand {
|
|
34
|
+
readonly name = "help";
|
|
35
|
+
readonly description = "Display application help";
|
|
36
|
+
readonly #includeEnvVars: boolean;
|
|
37
|
+
readonly #commandRegistry: CommandRegistry;
|
|
38
|
+
|
|
39
|
+
constructor(
|
|
40
|
+
includeEnvVars: boolean,
|
|
41
|
+
commandRegistry: CommandRegistry,
|
|
42
|
+
) {
|
|
43
|
+
this.#includeEnvVars = includeEnvVars;
|
|
44
|
+
this.#commandRegistry = commandRegistry;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
#findPossibleCommandNames(
|
|
48
|
+
commandName: string,
|
|
49
|
+
groupCommands: ReadonlyArray<GroupCommand>,
|
|
50
|
+
subCommands: ReadonlyArray<SubCommand>,
|
|
51
|
+
): string[] {
|
|
52
|
+
const levenCommandArray = new Array<[number, string]>();
|
|
53
|
+
subCommands.forEach((subCommand) => {
|
|
54
|
+
levenCommandArray.push([
|
|
55
|
+
distance(subCommand.name, commandName),
|
|
56
|
+
subCommand.name,
|
|
57
|
+
]);
|
|
58
|
+
});
|
|
59
|
+
groupCommands.forEach((groupCommand) => {
|
|
60
|
+
groupCommand.memberSubCommands.forEach((memberCommand) => {
|
|
61
|
+
const memberName = `${groupCommand.name}:${memberCommand.name}`;
|
|
62
|
+
levenCommandArray.push([
|
|
63
|
+
distance(memberName, commandName),
|
|
64
|
+
memberName,
|
|
65
|
+
]);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
return levenCommandArray
|
|
69
|
+
.sort((a, b) => a[0] - b[0])
|
|
70
|
+
.slice(0, 2)
|
|
71
|
+
.filter((value) => value[0] < 3)
|
|
72
|
+
.map((value) => value[1]);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
#getGlobalCommandsHelpSection(
|
|
76
|
+
context: Context,
|
|
77
|
+
title: string,
|
|
78
|
+
globalCommands: ReadonlyArray<GlobalCommand>,
|
|
79
|
+
): HelpSection {
|
|
80
|
+
const globalCommandsSection: HelpSection = {
|
|
81
|
+
title,
|
|
82
|
+
helpEntries: [],
|
|
83
|
+
};
|
|
84
|
+
globalCommands.forEach((globalCommand) => {
|
|
85
|
+
const { syntax, description } = getGlobalArgumentHelpEntry(
|
|
86
|
+
context.cliConfig,
|
|
87
|
+
this.#includeEnvVars,
|
|
88
|
+
globalCommand,
|
|
89
|
+
);
|
|
90
|
+
globalCommandsSection.helpEntries.push({
|
|
91
|
+
syntax: `--${globalCommand.name} ${syntax}`,
|
|
92
|
+
description,
|
|
93
|
+
});
|
|
94
|
+
if (globalCommand.shortAlias !== undefined) {
|
|
95
|
+
globalCommandsSection.helpEntries.push({
|
|
96
|
+
syntax: `-${globalCommand.shortAlias} ${syntax}`,
|
|
97
|
+
description,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
globalCommandsSection.helpEntries.sort((a, b) =>
|
|
102
|
+
a.syntax.localeCompare(b.syntax)
|
|
103
|
+
);
|
|
104
|
+
return globalCommandsSection;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
#getGenericHelpSections(
|
|
108
|
+
context: Context,
|
|
109
|
+
globalModifierCommands: ReadonlyArray<GlobalModifierCommand>,
|
|
110
|
+
globalCommands: ReadonlyArray<GlobalCommand>,
|
|
111
|
+
groupCommands: ReadonlyArray<GroupCommand>,
|
|
112
|
+
subCommands: ReadonlyArray<SubCommand>,
|
|
113
|
+
): Array<HelpSection> {
|
|
114
|
+
const globalPrefix =
|
|
115
|
+
((groupCommands.length > 0) || (subCommands.length > 0)) ? "Global " : "";
|
|
116
|
+
const helpSections: HelpSection[] = [];
|
|
117
|
+
if (globalModifierCommands.length > 0) {
|
|
118
|
+
helpSections.push(
|
|
119
|
+
this.#getGlobalCommandsHelpSection(
|
|
120
|
+
context,
|
|
121
|
+
`${globalPrefix}Options`,
|
|
122
|
+
globalModifierCommands,
|
|
123
|
+
),
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
if (globalCommands.length > 0) {
|
|
127
|
+
helpSections.push(
|
|
128
|
+
this.#getGlobalCommandsHelpSection(
|
|
129
|
+
context,
|
|
130
|
+
`${globalPrefix}Commands`,
|
|
131
|
+
globalCommands,
|
|
132
|
+
),
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
groupCommands.forEach((groupCommand) => {
|
|
136
|
+
const topicSection: HelpSection = {
|
|
137
|
+
title: `${
|
|
138
|
+
groupCommand.name.charAt(0).toUpperCase() + groupCommand.name.slice(1)
|
|
139
|
+
} Commands`,
|
|
140
|
+
helpEntries: [],
|
|
141
|
+
};
|
|
142
|
+
groupCommand.memberSubCommands.forEach((memberCommand) => {
|
|
143
|
+
topicSection.helpEntries.push({
|
|
144
|
+
syntax: `${groupCommand.name}:${memberCommand.name}`,
|
|
145
|
+
description: memberCommand.description,
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
topicSection.helpEntries.sort((a, b) => a.syntax.localeCompare(b.syntax));
|
|
149
|
+
helpSections.push(topicSection);
|
|
150
|
+
});
|
|
151
|
+
if (subCommands.length > 0) {
|
|
152
|
+
const subCommandsByTopic = new Map<string, SubCommand[]>();
|
|
153
|
+
const otherSubCommands: SubCommand[] = [];
|
|
154
|
+
|
|
155
|
+
subCommands.forEach((subCommand) => {
|
|
156
|
+
if (subCommand.helpTopic === undefined) {
|
|
157
|
+
otherSubCommands.push(subCommand);
|
|
158
|
+
} else {
|
|
159
|
+
const commands = subCommandsByTopic.get(subCommand.helpTopic) || [];
|
|
160
|
+
commands.push(subCommand);
|
|
161
|
+
subCommandsByTopic.set(subCommand.helpTopic, commands);
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
for (const key of subCommandsByTopic.keys()) {
|
|
166
|
+
const topicCommands = subCommandsByTopic.get(key);
|
|
167
|
+
if (topicCommands !== undefined) {
|
|
168
|
+
const topicSection: HelpSection = {
|
|
169
|
+
title: `${key.charAt(0).toUpperCase() + key.slice(1)} Commands`,
|
|
170
|
+
helpEntries: [],
|
|
171
|
+
};
|
|
172
|
+
topicCommands.sort((a, b) => a.name.localeCompare(b.name));
|
|
173
|
+
topicCommands.forEach((subCommand) => {
|
|
174
|
+
topicSection.helpEntries.push({
|
|
175
|
+
syntax: subCommand.name,
|
|
176
|
+
description: subCommand.description,
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
helpSections.push(topicSection);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (otherSubCommands.length > 0) {
|
|
184
|
+
const topicSection: HelpSection = {
|
|
185
|
+
title: (subCommandsByTopic.size > 0 || groupCommands.length > 0)
|
|
186
|
+
? "Other Commands"
|
|
187
|
+
: "Sub-Commands",
|
|
188
|
+
helpEntries: [],
|
|
189
|
+
};
|
|
190
|
+
otherSubCommands.sort((a, b) => a.name.localeCompare(b.name));
|
|
191
|
+
otherSubCommands.forEach((subCommand) => {
|
|
192
|
+
topicSection.helpEntries.push({
|
|
193
|
+
syntax: subCommand.name,
|
|
194
|
+
description: subCommand.description,
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
helpSections.push(topicSection);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return helpSections;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
protected async printGenericHelp(
|
|
204
|
+
context: Context,
|
|
205
|
+
): Promise<void> {
|
|
206
|
+
const printerService = context.getServiceById(
|
|
207
|
+
PRINTER_SERVICE_ID,
|
|
208
|
+
) as PrinterService;
|
|
209
|
+
|
|
210
|
+
const globalModifierCommands = this.#commandRegistry
|
|
211
|
+
.getGlobalModifierCommands();
|
|
212
|
+
const globalCommands = this.#commandRegistry.getGlobalCommands();
|
|
213
|
+
const groupCommands = this.#commandRegistry.getGroupCommands();
|
|
214
|
+
const subCommands = this.#commandRegistry.getSubCommands();
|
|
215
|
+
const helpSections: Array<HelpSection> = [];
|
|
216
|
+
helpSections.push({
|
|
217
|
+
title: "Usage",
|
|
218
|
+
helpEntries: [
|
|
219
|
+
{
|
|
220
|
+
syntax: getMultiCommandAppSyntax(
|
|
221
|
+
context,
|
|
222
|
+
globalModifierCommands,
|
|
223
|
+
globalCommands,
|
|
224
|
+
groupCommands,
|
|
225
|
+
subCommands,
|
|
226
|
+
),
|
|
227
|
+
},
|
|
228
|
+
],
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
helpSections.push(
|
|
232
|
+
...this.#getGenericHelpSections(
|
|
233
|
+
context,
|
|
234
|
+
globalModifierCommands,
|
|
235
|
+
globalCommands,
|
|
236
|
+
groupCommands,
|
|
237
|
+
subCommands,
|
|
238
|
+
),
|
|
239
|
+
);
|
|
240
|
+
|
|
241
|
+
await printHelpSections(printerService, helpSections);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
protected async printUsageHelp(
|
|
245
|
+
context: Context,
|
|
246
|
+
commandName: string,
|
|
247
|
+
): Promise<void> {
|
|
248
|
+
const printerService = context.getServiceById(
|
|
249
|
+
PRINTER_SERVICE_ID,
|
|
250
|
+
) as PrinterService;
|
|
251
|
+
|
|
252
|
+
const groupCommands = this.#commandRegistry.getGroupCommands();
|
|
253
|
+
const subCommands = this.#commandRegistry.getSubCommands();
|
|
254
|
+
|
|
255
|
+
// find sub-command or group sub-command
|
|
256
|
+
let subCommand = this.#commandRegistry.getSubCommandByName(commandName);
|
|
257
|
+
|
|
258
|
+
let groupCommand: GroupCommand | undefined;
|
|
259
|
+
|
|
260
|
+
if (subCommand === undefined) {
|
|
261
|
+
const result = this.#commandRegistry
|
|
262
|
+
.getGroupCommandAndMemberSubCommandByJoinedName(commandName);
|
|
263
|
+
if (result !== undefined) {
|
|
264
|
+
groupCommand = result.groupCommand;
|
|
265
|
+
subCommand = result.command;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
if (subCommand === undefined) {
|
|
270
|
+
await printerService.print(
|
|
271
|
+
`Unknown command: ${printerService.red(commandName)}\n\n`,
|
|
272
|
+
Icon.FAILURE,
|
|
273
|
+
);
|
|
274
|
+
|
|
275
|
+
// look for other possible matches
|
|
276
|
+
const possibleCommandNames = this.#findPossibleCommandNames(
|
|
277
|
+
commandName,
|
|
278
|
+
groupCommands,
|
|
279
|
+
subCommands,
|
|
280
|
+
);
|
|
281
|
+
if (possibleCommandNames.length > 0) {
|
|
282
|
+
await printerService.print(
|
|
283
|
+
`Possible matches: ${possibleCommandNames.join(", ")}\n\n`,
|
|
284
|
+
Icon.INFORMATION,
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
await this.printGenericHelp(context);
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// display command help
|
|
292
|
+
const name = (groupCommand === undefined)
|
|
293
|
+
? subCommand.name
|
|
294
|
+
: `${groupCommand.name}:${subCommand.name}`;
|
|
295
|
+
const helpSections: HelpSection[] = [];
|
|
296
|
+
|
|
297
|
+
if (
|
|
298
|
+
(subCommand.description !== undefined) &&
|
|
299
|
+
(subCommand.description.length > 0)
|
|
300
|
+
) {
|
|
301
|
+
helpSections.push({
|
|
302
|
+
title: "Command",
|
|
303
|
+
helpEntries: [{
|
|
304
|
+
syntax: subCommand.name,
|
|
305
|
+
description: subCommand.description,
|
|
306
|
+
}],
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
helpSections.push({
|
|
310
|
+
title: "Usage",
|
|
311
|
+
helpEntries: [{
|
|
312
|
+
syntax: `${context.cliConfig.name}${name.length > 0 ? ` ${name}` : ""}${
|
|
313
|
+
getSubCommandArgumentsSyntax(subCommand)
|
|
314
|
+
}`,
|
|
315
|
+
}],
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
helpSections.push(
|
|
319
|
+
...getCommandArgsHelpSections(
|
|
320
|
+
context.cliConfig,
|
|
321
|
+
this.#includeEnvVars,
|
|
322
|
+
subCommand,
|
|
323
|
+
false,
|
|
324
|
+
),
|
|
325
|
+
);
|
|
326
|
+
|
|
327
|
+
helpSections.push(
|
|
328
|
+
...getCommandExamplesHelpSections(printerService, context, subCommand),
|
|
329
|
+
);
|
|
330
|
+
|
|
331
|
+
await printHelpSections(printerService, helpSections);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Implementation of multi-command CLI help available via `myCli --help`, `myCli -h`, `myCli --help <command>` or `myCli -h <command>`.
|
|
337
|
+
*/
|
|
338
|
+
export class MultiCommandCliHelpGlobalCommand
|
|
339
|
+
extends MultiCommandCliAbstractHelpCommand
|
|
340
|
+
implements GlobalCommand {
|
|
341
|
+
readonly shortAlias = "h";
|
|
342
|
+
|
|
343
|
+
readonly argument = {
|
|
344
|
+
name: "command",
|
|
345
|
+
type: ArgumentValueTypeName.STRING,
|
|
346
|
+
isOptional: true,
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
public async execute(
|
|
350
|
+
context: Context,
|
|
351
|
+
argumentValue?: ArgumentSingleValueType,
|
|
352
|
+
): Promise<void> {
|
|
353
|
+
const commandName = argumentValue as
|
|
354
|
+
| string
|
|
355
|
+
| undefined;
|
|
356
|
+
if (commandName !== undefined) {
|
|
357
|
+
await this.printUsageHelp(context, commandName);
|
|
358
|
+
} else {
|
|
359
|
+
await this.printGenericHelp(context);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Implementation of multi-command CLI help available via `myCli help` or `myCli help <command>`.
|
|
366
|
+
*/
|
|
367
|
+
export class MultiCommandCliHelpSubCommand
|
|
368
|
+
extends MultiCommandCliAbstractHelpCommand
|
|
369
|
+
implements SubCommand {
|
|
370
|
+
public readonly options: ReadonlyArray<Option> = [];
|
|
371
|
+
|
|
372
|
+
public readonly positionals: ReadonlyArray<Positional> = [
|
|
373
|
+
{
|
|
374
|
+
name: "command",
|
|
375
|
+
type: ArgumentValueTypeName.STRING,
|
|
376
|
+
isVarargOptional: true,
|
|
377
|
+
description: "Display help for the specific <command>",
|
|
378
|
+
},
|
|
379
|
+
];
|
|
380
|
+
|
|
381
|
+
public async execute(
|
|
382
|
+
context: Context,
|
|
383
|
+
argumentValues: ArgumentValues,
|
|
384
|
+
): Promise<void> {
|
|
385
|
+
const commandName = argumentValues["command"] as
|
|
386
|
+
| string
|
|
387
|
+
| undefined;
|
|
388
|
+
if (commandName !== undefined) {
|
|
389
|
+
await this.printUsageHelp(context, commandName);
|
|
390
|
+
} else {
|
|
391
|
+
await this.printGenericHelp(context);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import type GlobalCommand from "../api/command/GlobalCommand.ts";
|
|
2
|
+
import type SubCommand from "../api/command/SubCommand.ts";
|
|
3
|
+
import type Option from "../api/argument/Option.ts";
|
|
4
|
+
import type Positional from "../api/argument/Positional.ts";
|
|
5
|
+
import type Context from "../api/Context.ts";
|
|
6
|
+
import {
|
|
7
|
+
getCommandArgsHelpSections,
|
|
8
|
+
getCommandExamplesHelpSections,
|
|
9
|
+
getGlobalArgumentHelpEntry,
|
|
10
|
+
getSubCommandArgumentsSyntax,
|
|
11
|
+
type HelpEntry,
|
|
12
|
+
type HelpSection,
|
|
13
|
+
printHelpSections,
|
|
14
|
+
} from "../util/helpHelper.ts";
|
|
15
|
+
import type GlobalModifierCommand from "../api/command/GlobalModifierCommand.ts";
|
|
16
|
+
import type GroupCommand from "../api/command/GroupCommand.ts";
|
|
17
|
+
import type CommandRegistry from "../runtime/registry/CommandRegistry.ts";
|
|
18
|
+
import type PrinterService from "../api/service/core/PrinterService.ts";
|
|
19
|
+
import { PRINTER_SERVICE_ID } from "../api/service/core/PrinterService.ts";
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Provides common implementation for both {@link SingleCommandCliHelpGlobalCommand} and {@link SingleCommandCliHelpSubCommand}.
|
|
23
|
+
*/
|
|
24
|
+
abstract class SingleCommandCliAbstractHelpCommand {
|
|
25
|
+
readonly #includeEnvVars: boolean;
|
|
26
|
+
readonly #defaultCommand: SubCommand;
|
|
27
|
+
readonly #commandRegistry: CommandRegistry;
|
|
28
|
+
|
|
29
|
+
constructor(
|
|
30
|
+
includeEnvVars: boolean,
|
|
31
|
+
defaultCommand: SubCommand,
|
|
32
|
+
commandRegistry: CommandRegistry,
|
|
33
|
+
) {
|
|
34
|
+
this.#includeEnvVars = includeEnvVars;
|
|
35
|
+
this.#defaultCommand = defaultCommand;
|
|
36
|
+
this.#commandRegistry = commandRegistry;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
readonly name = "help";
|
|
40
|
+
|
|
41
|
+
readonly description = "Display application help";
|
|
42
|
+
|
|
43
|
+
#getAmalgamatedGenericHelpSection(
|
|
44
|
+
context: Context,
|
|
45
|
+
globalModifierCommands: ReadonlyArray<GlobalModifierCommand>,
|
|
46
|
+
globalCommands: ReadonlyArray<GlobalCommand>,
|
|
47
|
+
groupCommands: ReadonlyArray<GroupCommand>,
|
|
48
|
+
subCommands: ReadonlyArray<SubCommand>,
|
|
49
|
+
): HelpSection {
|
|
50
|
+
const helpSection: HelpSection = {
|
|
51
|
+
title: "Other Arguments",
|
|
52
|
+
helpEntries: [],
|
|
53
|
+
};
|
|
54
|
+
if (globalModifierCommands.length > 0) {
|
|
55
|
+
globalModifierCommands.forEach((globalModifierCommand) => {
|
|
56
|
+
const { syntax, description } = getGlobalArgumentHelpEntry(
|
|
57
|
+
context.cliConfig,
|
|
58
|
+
this.#includeEnvVars,
|
|
59
|
+
globalModifierCommand,
|
|
60
|
+
);
|
|
61
|
+
helpSection.helpEntries.push({
|
|
62
|
+
syntax: `--${globalModifierCommand.name} ${syntax}`,
|
|
63
|
+
description,
|
|
64
|
+
});
|
|
65
|
+
if (globalModifierCommand.shortAlias !== undefined) {
|
|
66
|
+
helpSection.helpEntries.push({
|
|
67
|
+
syntax: `-${globalModifierCommand.shortAlias} ${syntax}`,
|
|
68
|
+
description,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
if (globalCommands.length > 0) {
|
|
74
|
+
globalCommands.forEach((globalCommand) => {
|
|
75
|
+
const { syntax, description } = getGlobalArgumentHelpEntry(
|
|
76
|
+
context.cliConfig,
|
|
77
|
+
this.#includeEnvVars,
|
|
78
|
+
globalCommand,
|
|
79
|
+
);
|
|
80
|
+
helpSection.helpEntries.push({
|
|
81
|
+
syntax: `--${globalCommand.name} ${syntax}`,
|
|
82
|
+
description,
|
|
83
|
+
});
|
|
84
|
+
if (globalCommand.shortAlias !== undefined) {
|
|
85
|
+
helpSection.helpEntries.push({
|
|
86
|
+
syntax: `-${globalCommand.shortAlias} ${syntax}`,
|
|
87
|
+
description,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
groupCommands.forEach((groupCommand) => {
|
|
93
|
+
groupCommand.memberSubCommands.forEach((memberCommand: SubCommand) => {
|
|
94
|
+
helpSection.helpEntries.push({
|
|
95
|
+
syntax: `${groupCommand.name}:${memberCommand.name}`,
|
|
96
|
+
description: memberCommand.description,
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
if (subCommands.length > 0) {
|
|
101
|
+
subCommands.forEach((subCommand) => {
|
|
102
|
+
helpSection.helpEntries.push({
|
|
103
|
+
syntax: subCommand.name,
|
|
104
|
+
description: subCommand.description,
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
helpSection.helpEntries.sort((a: HelpEntry, b: HelpEntry) =>
|
|
109
|
+
a.syntax.localeCompare(b.syntax)
|
|
110
|
+
);
|
|
111
|
+
return helpSection;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
public async execute(
|
|
115
|
+
context: Context,
|
|
116
|
+
): Promise<void> {
|
|
117
|
+
const helpSections: Array<HelpSection> = [];
|
|
118
|
+
helpSections.push({
|
|
119
|
+
title: "Usage",
|
|
120
|
+
helpEntries: [{
|
|
121
|
+
syntax: `${context.cliConfig.name || ""}${
|
|
122
|
+
getSubCommandArgumentsSyntax(this.#defaultCommand)
|
|
123
|
+
}`,
|
|
124
|
+
}],
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
helpSections.push(
|
|
128
|
+
...getCommandArgsHelpSections(
|
|
129
|
+
context.cliConfig,
|
|
130
|
+
this.#includeEnvVars,
|
|
131
|
+
this.#defaultCommand,
|
|
132
|
+
true,
|
|
133
|
+
),
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
const globalModifierCommands = this.#commandRegistry
|
|
137
|
+
.getGlobalModifierCommands();
|
|
138
|
+
const globalCommands = this.#commandRegistry.getGlobalCommands();
|
|
139
|
+
helpSections.push(
|
|
140
|
+
this.#getAmalgamatedGenericHelpSection(
|
|
141
|
+
context,
|
|
142
|
+
globalModifierCommands,
|
|
143
|
+
globalCommands,
|
|
144
|
+
[],
|
|
145
|
+
[],
|
|
146
|
+
),
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
const printerService = context.getServiceById(
|
|
150
|
+
PRINTER_SERVICE_ID,
|
|
151
|
+
) as PrinterService;
|
|
152
|
+
|
|
153
|
+
helpSections.push(
|
|
154
|
+
...getCommandExamplesHelpSections(
|
|
155
|
+
printerService,
|
|
156
|
+
context,
|
|
157
|
+
this.#defaultCommand,
|
|
158
|
+
),
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
await printHelpSections(printerService, helpSections);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Implementation of default command CLI help available via `myCli --help` or `myCli -h`.
|
|
167
|
+
*/
|
|
168
|
+
export class SingleCommandCliHelpGlobalCommand
|
|
169
|
+
extends SingleCommandCliAbstractHelpCommand
|
|
170
|
+
implements GlobalCommand {
|
|
171
|
+
readonly shortAlias = "h";
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Implementation of default-command CLI help available via `myCli help`.
|
|
176
|
+
*/
|
|
177
|
+
export class SingleCommandCliHelpSubCommand
|
|
178
|
+
extends SingleCommandCliAbstractHelpCommand
|
|
179
|
+
implements SubCommand {
|
|
180
|
+
public readonly options: ReadonlyArray<Option> = [];
|
|
181
|
+
|
|
182
|
+
public readonly positionals: ReadonlyArray<Positional> = [];
|
|
183
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type Context from "../api/Context.ts";
|
|
2
|
+
import type GlobalCommand from "../api/command/GlobalCommand.ts";
|
|
3
|
+
import type Command from "../api/command/Command.ts";
|
|
4
|
+
import type PrinterService from "../api/service/core/PrinterService.ts";
|
|
5
|
+
import { PRINTER_SERVICE_ID } from "../api/service/core/PrinterService.ts";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Command printing basic CLI usage.
|
|
9
|
+
*/
|
|
10
|
+
export default class UsageCommand implements GlobalCommand {
|
|
11
|
+
readonly name = "usage";
|
|
12
|
+
readonly description = "Show usage information";
|
|
13
|
+
|
|
14
|
+
readonly #helpCommand: Command;
|
|
15
|
+
|
|
16
|
+
public constructor(
|
|
17
|
+
helpCommand: Command,
|
|
18
|
+
) {
|
|
19
|
+
this.#helpCommand = helpCommand;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
public async execute(
|
|
23
|
+
context: Context,
|
|
24
|
+
): Promise<void> {
|
|
25
|
+
const printerService = context.getServiceById(
|
|
26
|
+
PRINTER_SERVICE_ID,
|
|
27
|
+
) as PrinterService;
|
|
28
|
+
|
|
29
|
+
await printerService.print(
|
|
30
|
+
`${printerService.secondary("Try running:")}\n\n ${
|
|
31
|
+
printerService.primary(context.cliConfig.name)
|
|
32
|
+
} --${printerService.primary(this.#helpCommand.name)}\n\n`,
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type Context from "../api/Context.ts";
|
|
2
|
+
import type GlobalCommand from "../api/command/GlobalCommand.ts";
|
|
3
|
+
import type PrinterService from "../api/service/core/PrinterService.ts";
|
|
4
|
+
import { PRINTER_SERVICE_ID } from "../api/service/core/PrinterService.ts";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Implementation of a {@link GlobalCommand} which outputs the version of the CLI.
|
|
8
|
+
*/
|
|
9
|
+
export default class VersionCommand implements GlobalCommand {
|
|
10
|
+
readonly name = "version";
|
|
11
|
+
readonly description = "Show version information";
|
|
12
|
+
readonly shortAlias = "v";
|
|
13
|
+
|
|
14
|
+
public async execute(
|
|
15
|
+
context: Context,
|
|
16
|
+
): Promise<void> {
|
|
17
|
+
const printerService = context.getServiceById(
|
|
18
|
+
PRINTER_SERVICE_ID,
|
|
19
|
+
) as PrinterService;
|
|
20
|
+
await printerService.print(`${context.cliConfig.version}\n`);
|
|
21
|
+
}
|
|
22
|
+
}
|