@flowscripter/dynamic-cli-framework 1.1.6 → 1.2.1
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/release-bun-library.yml +1 -0
- package/README/core-commands.md +40 -0
- package/README/core-service-providers.md +198 -0
- package/README/development.md +60 -0
- package/README/implementation-details.md +474 -0
- package/README/key-concepts.md +751 -0
- package/README.md +12 -1413
- package/bun.lock +22 -18
- package/index.ts +97 -1
- package/package.json +8 -7
- package/src/api/BaseCLIFeatureOptions.ts +10 -0
- package/src/api/CLIConfig.ts +5 -0
- package/src/api/RunResult.ts +16 -0
- package/src/api/argument/Argument.ts +24 -0
- package/src/api/service/ServiceProvider.ts +3 -3
- package/src/api/service/core/ArgumentPrompterService.ts +10 -0
- package/src/api/service/core/AsciiBannerGeneratorService.ts +44 -7
- package/src/api/service/core/CompletionService.ts +36 -0
- package/src/api/service/core/DataDumpGeneratorService.ts +27 -0
- package/src/api/service/core/ImagePrinterService.ts +26 -0
- package/src/api/service/core/KeyValueService.ts +22 -5
- package/src/api/service/core/PrinterService.ts +127 -1
- package/src/api/service/core/PrompterService.ts +49 -0
- package/src/api/service/core/SecretService.ts +27 -0
- package/src/api/service/core/ShutdownService.ts +17 -1
- package/src/api/service/core/Table.ts +85 -0
- package/src/api/service/core/TableGeneratorService.ts +38 -0
- package/src/api/service/core/TreePrinterService.ts +11 -0
- package/src/cli/BaseCLI.ts +116 -44
- package/src/cli/DefaultRuntimeCLI.ts +10 -18
- package/src/command/CompletionCommand.ts +179 -0
- package/src/command/MultiCommandCliHelpCommand.ts +18 -2
- package/src/command/SingleCommandCliHelpCommand.ts +10 -1
- package/src/launcher.ts +16 -58
- package/src/runtime/command/CommandValidator.ts +3 -3
- package/src/runtime/runner.ts +102 -30
- package/src/runtime/scanner.ts +18 -15
- package/src/runtime/values/argumentValueValidation.ts +49 -5
- package/src/runtime/values/globalCommandValuePopulation.ts +1 -1
- package/src/runtime/values/subCommandValuePopulation.ts +9 -8
- package/src/service/argumentPrompter/ArgumentPrompterServiceProvider.ts +34 -0
- package/src/service/argumentPrompter/DefaultArgumentPrompterService.ts +330 -0
- package/src/service/asciiBannerGenerator/AsciiBannerGeneratorServiceProvider.ts +1 -1
- package/src/service/asciiBannerGenerator/DefaultAsciiBannerGeneratorService.ts +254 -17
- package/src/service/banner/BannerServiceProvider.ts +2 -2
- package/src/service/chiselAsciiBannerGenerator/ChiselFontAsciiBannerGeneratorService.ts +85 -0
- package/src/service/chiselAsciiBannerGenerator/chisel.flf.json +3 -0
- package/src/service/completion/CompletionServiceProvider.ts +212 -0
- package/src/service/completion/DefaultCompletionService.ts +364 -0
- package/src/service/completion/shell/ShellHandler.ts +16 -0
- package/src/service/completion/shell/bash.ts +47 -0
- package/src/service/completion/shell/fish.ts +43 -0
- package/src/service/completion/shell/powershell.ts +66 -0
- package/src/service/completion/shell/zsh.ts +50 -0
- package/src/service/configuration/ConfigurationServiceProvider.ts +138 -42
- package/src/service/configuration/DefaultKeyValueService.ts +53 -7
- package/src/service/configuration/DefaultSecretService.ts +85 -0
- package/src/service/dataDumpGenerator/DataDumpGeneratorServiceProvider.ts +41 -0
- package/src/service/dataDumpGenerator/DefaultDataDumpGeneratorService.ts +80 -0
- package/src/service/imagePrinter/DefaultImagePrinterService.ts +23 -0
- package/src/service/imagePrinter/ImagePrinterServiceProvider.ts +32 -0
- package/src/service/prettyPrinter/PrettyPrinterServiceProvider.ts +1 -1
- package/src/service/printer/DefaultPrinterService.ts +147 -27
- package/src/service/printer/PrinterServiceProvider.ts +7 -7
- package/src/service/printer/command/NoHyperlinksCommand.ts +40 -0
- package/src/service/printer/terminal/Progress.ts +21 -4
- package/src/service/printer/terminal/Spinner.ts +32 -8
- package/src/service/printer/terminal/Theme.ts +1 -1
- package/src/service/prompter/DefaultPrompterService.ts +87 -0
- package/src/service/prompter/PrompterServiceProvider.ts +36 -0
- package/src/service/prompter/command/NoPromptCommand.ts +40 -0
- package/src/service/prompter/prompt/PromptContext.ts +47 -0
- package/src/service/prompter/prompt/promptAcknowledge.ts +28 -0
- package/src/service/prompter/prompt/promptMultiSelect.ts +99 -0
- package/src/service/prompter/prompt/promptOpenUrl.ts +97 -0
- package/src/service/prompter/prompt/promptSingleSelect.ts +102 -0
- package/src/service/prompter/prompt/promptText.ts +107 -0
- package/src/service/prompter/prompt/promptToggle.ts +28 -0
- package/src/service/shutdown/DefaultShutdownService.ts +13 -0
- package/src/service/shutdown/ShutdownServiceProvider.ts +28 -9
- package/src/service/shutdown/ShutdownState.ts +5 -0
- package/src/service/syntaxHighlighter/SyntaxHighlighterServiceProvider.ts +1 -1
- package/src/service/tableGenerator/DefaultTableGeneratorService.ts +415 -0
- package/src/service/tableGenerator/TableGeneratorServiceProvider.ts +43 -0
- package/src/service/treePrinter/DefaultTreePrinterService.ts +49 -0
- package/src/service/treePrinter/TreePrinterServiceProvider.ts +40 -0
- package/src/terminal/Ansi.ts +27 -0
- package/src/terminal/ImageRenderer.ts +199 -0
- package/src/terminal/KeyReader.ts +23 -0
- package/src/terminal/PngDecoder.ts +143 -0
- package/src/{service/printer/terminal → terminal}/Styler.ts +3 -0
- package/src/{service/printer/terminal → terminal}/Terminal.ts +1 -0
- package/src/terminal/TtyKeyReader.ts +83 -0
- package/src/terminal/TtyStyler.ts +78 -0
- package/src/{service/printer/terminal → terminal}/TtyTerminal.ts +9 -1
- package/src/terminal/supportsHyperlinks.ts +110 -0
- package/src/util/configHelper.ts +2 -2
- package/src/util/envVarHelper.ts +2 -2
- package/src/util/helpHelper.ts +66 -122
- package/src/util/logger.ts +16 -14
- package/src/util/runnerHelper.ts +1 -1
- package/tests/cli/BaseCLI.test.ts +50 -54
- package/tests/command/CompletionCommand.test.ts +68 -0
- package/tests/command/UsageCommand.test.ts +3 -2
- package/tests/command/VersionCommand.test.ts +3 -2
- package/tests/fixtures/Context.ts +9 -2
- package/tests/fixtures/util.ts +1 -1
- package/tests/runtime/parser.test.ts +9 -7
- package/tests/runtime/registry/DefaultServiceProviderRegistry.test.ts +3 -3
- package/tests/runtime/values/argumentValueValidation.test.ts +193 -0
- package/tests/service/argumentPrompter/ArgumentPrompterServiceProvider.test.ts +47 -0
- package/tests/service/argumentPrompter/DefaultArgumentPrompterService.test.ts +754 -0
- package/tests/service/asciiBannerGenerator/AsciiBannerGeneratorServiceProvider.test.ts +5 -4
- package/tests/service/asciiBannerGenerator/DefaultAsciiBannerGeneratorService.test.ts +163 -3
- package/tests/service/asciiBannerGenerator/__snapshots__/DefaultAsciiBannerGeneratorService.test.ts.snap +1 -1
- package/tests/service/banner/BannerServiceProvider.test.ts +8 -4
- package/tests/service/chiselAsciiBannerGenerator/ChiselFontAsciiBannerGeneratorService.test.ts +52 -0
- package/tests/service/completion/CompletionServiceProvider.test.ts +404 -0
- package/tests/service/completion/DefaultCompletionService.test.ts +278 -0
- package/tests/service/completion/shell/bash.test.ts +73 -0
- package/tests/service/completion/shell/fish.test.ts +62 -0
- package/tests/service/completion/shell/powershell.test.ts +59 -0
- package/tests/service/completion/shell/zsh.test.ts +60 -0
- package/tests/service/configuration/ConfigurationServiceProvider.test.ts +24 -12
- package/tests/service/configuration/DefaultKeyValueService.test.ts +118 -16
- package/tests/service/configuration/DefaultSecretService.test.ts +148 -0
- package/tests/service/configuration/command/ConfigCommand.test.ts +29 -0
- package/tests/service/configuration/command/DumpConfigCommand.test.ts +60 -0
- package/tests/service/dataDumpGenerator/DataDumpGeneratorServiceProvider.test.ts +57 -0
- package/tests/service/dataDumpGenerator/DefaultDataDumpGeneratorService.test.ts +217 -0
- package/tests/service/imagePrinter/DefaultImagePrinterService.test.ts +96 -0
- package/tests/service/imagePrinter/ImagePrinterServiceProvider.test.ts +17 -0
- package/tests/service/prettyPrinter/DefaultPrettyPrinterService.test.ts +2 -2
- package/tests/service/prettyPrinter/PrettyPrinterServiceProvider.test.ts +5 -4
- package/tests/service/printer/DefaultPrinterService.test.ts +233 -2
- package/tests/service/printer/PrinterServiceProvider.test.ts +6 -5
- package/tests/service/printer/command/DarkModeCommand.test.ts +83 -0
- package/tests/service/printer/command/LogLevelCommand.test.ts +7 -5
- package/tests/service/printer/command/NoColorCommand.test.ts +83 -0
- package/tests/service/printer/command/NoHyperlinksCommand.test.ts +89 -0
- package/tests/service/printer/terminal/Progress.test.ts +64 -4
- package/tests/service/printer/terminal/Spinner.test.ts +44 -3
- package/tests/service/prompter/DefaultPrompterService.test.ts +615 -0
- package/tests/service/prompter/PrompterServiceProvider.test.ts +47 -0
- package/tests/service/prompter/command/NoPromptCommand.test.ts +58 -0
- package/tests/service/shutdown/ShutdownServiceProvider.test.ts +4 -2
- package/tests/service/syntaxHighlighter/DefaultSyntaxHighlighterService.test.ts +3 -2
- package/tests/service/syntaxHighlighter/SyntaxHighlighterServiceProvider.test.ts +5 -4
- package/tests/service/tableGenerator/DefaultTableGeneratorService.test.ts +363 -0
- package/tests/service/tableGenerator/TableGeneratorServiceProvider.test.ts +47 -0
- package/tests/service/treePrinter/DefaultTreePrinterService.test.ts +144 -0
- package/tests/service/treePrinter/TreePrinterServiceProvider.test.ts +55 -0
- package/tests/terminal/ImageRenderer.test.ts +161 -0
- package/tests/terminal/PngDecoder.test.ts +223 -0
- package/tests/terminal/supportsHyperlinks.test.ts +149 -0
- package/tests/util/configHelper.test.ts +4 -4
- package/tests/util/logger.test.ts +23 -1
- package/tsconfig.json +31 -0
- package/src/service/printer/terminal/Ansi.ts +0 -10
- package/src/service/printer/terminal/TtyStyler.ts +0 -39
package/src/cli/BaseCLI.ts
CHANGED
|
@@ -23,19 +23,38 @@ import UsageCommand from "../command/UsageCommand.ts";
|
|
|
23
23
|
import {
|
|
24
24
|
isGlobalCommand,
|
|
25
25
|
isGlobalModifierCommand,
|
|
26
|
+
isGroupCommand,
|
|
26
27
|
isSubCommand,
|
|
27
28
|
} from "../runtime/command/CommandTypeGuards.ts";
|
|
28
29
|
import CommandValidator from "../runtime/command/CommandValidator.ts";
|
|
29
30
|
import ShutdownServiceProvider from "../service/shutdown/ShutdownServiceProvider.ts";
|
|
31
|
+
import { shutdownState } from "../service/shutdown/ShutdownState.ts";
|
|
30
32
|
import ConfigurationServiceProvider from "../service/configuration/ConfigurationServiceProvider.ts";
|
|
31
33
|
import PrinterServiceProvider from "../service/printer/PrinterServiceProvider.ts";
|
|
34
|
+
import TableGeneratorServiceProvider from "../service/tableGenerator/TableGeneratorServiceProvider.ts";
|
|
32
35
|
import { run } from "../runtime/runner.ts";
|
|
33
36
|
|
|
34
37
|
import { WritableStream } from "node:stream/web";
|
|
35
|
-
import Terminal from "../
|
|
36
|
-
import PrinterService from "../api/service/core/PrinterService.ts";
|
|
38
|
+
import type Terminal from "../terminal/Terminal.ts";
|
|
39
|
+
import type PrinterService from "../api/service/core/PrinterService.ts";
|
|
37
40
|
import DefaultPrinterService from "../service/printer/DefaultPrinterService.ts";
|
|
38
|
-
import Styler from "../
|
|
41
|
+
import type Styler from "../terminal/Styler.ts";
|
|
42
|
+
import type KeyReader from "../terminal/KeyReader.ts";
|
|
43
|
+
import type BaseCLIFeatureOptions from "../api/BaseCLIFeatureOptions.ts";
|
|
44
|
+
import DefaultPrompterService, {
|
|
45
|
+
DEFAULT_PROMPTER_CONFIG,
|
|
46
|
+
} from "../service/prompter/DefaultPrompterService.ts";
|
|
47
|
+
import PrompterServiceProvider from "../service/prompter/PrompterServiceProvider.ts";
|
|
48
|
+
import DefaultArgumentPrompterService from "../service/argumentPrompter/DefaultArgumentPrompterService.ts";
|
|
49
|
+
import ArgumentPrompterServiceProvider from "../service/argumentPrompter/ArgumentPrompterServiceProvider.ts";
|
|
50
|
+
import DefaultCompletionService from "../service/completion/DefaultCompletionService.ts";
|
|
51
|
+
import CompletionServiceProvider from "../service/completion/CompletionServiceProvider.ts";
|
|
52
|
+
import ImagePrinterServiceProvider from "../service/imagePrinter/ImagePrinterServiceProvider.ts";
|
|
53
|
+
import {
|
|
54
|
+
CompletionCompleteSubCommand,
|
|
55
|
+
CompletionGroupCommand,
|
|
56
|
+
CompletionIntegrationSubCommand,
|
|
57
|
+
} from "../command/CompletionCommand.ts";
|
|
39
58
|
const logger = getLogger("BaseCLI");
|
|
40
59
|
|
|
41
60
|
/**
|
|
@@ -46,10 +65,15 @@ const logger = getLogger("BaseCLI");
|
|
|
46
65
|
* * {@link ShutdownServiceProvider}
|
|
47
66
|
* * {@link PrinterServiceProvider}
|
|
48
67
|
* * {@link ConfigurationServiceProvider}
|
|
68
|
+
* * {@link TableGeneratorServiceProvider}
|
|
49
69
|
*
|
|
50
70
|
* NOTE: The `ConfigurationServiceProvider` will not be added if the required `allow-read` and `allow-write`
|
|
51
71
|
* permissions are not granted. It can also be excluded if desired via a constructor argument.
|
|
52
72
|
*
|
|
73
|
+
* Optionally enabled via {@link BaseCLIFeatureOptions}:
|
|
74
|
+
*
|
|
75
|
+
* * {@link ImagePrinterServiceProvider}
|
|
76
|
+
*
|
|
53
77
|
* By default the following commands are added:
|
|
54
78
|
*
|
|
55
79
|
* * {@link SingleCommandCliHelpSubCommand} or {@link MultiCommandCliHelpSubCommand}
|
|
@@ -59,47 +83,32 @@ const logger = getLogger("BaseCLI");
|
|
|
59
83
|
*/
|
|
60
84
|
export default class BaseCLI implements CLI {
|
|
61
85
|
readonly #cliConfig: CLIConfig;
|
|
62
|
-
readonly #
|
|
63
|
-
readonly #
|
|
64
|
-
readonly #
|
|
86
|
+
readonly #options: Required<BaseCLIFeatureOptions>;
|
|
87
|
+
readonly #keyReader: KeyReader;
|
|
88
|
+
readonly #stderrTerminal: Terminal;
|
|
65
89
|
readonly #addedNonModifierCommands: Array<Command>;
|
|
66
90
|
readonly #serviceProviderRegistry: DefaultServiceProviderRegistry;
|
|
67
91
|
readonly #commandRegistry: DefaultCommandRegistry;
|
|
68
92
|
readonly #context: DefaultContext;
|
|
69
93
|
readonly #printerService: PrinterService;
|
|
94
|
+
readonly #stdoutTerminal: Terminal;
|
|
70
95
|
|
|
71
|
-
/**
|
|
72
|
-
* Constructor configures the instance with the specified CLI application details and WritableStream instances.
|
|
73
|
-
*
|
|
74
|
-
* @param cliConfig the {@link CLIConfig} for the CLI application.
|
|
75
|
-
* @param stdoutWritableStream the WritableStream to use for stderr output.
|
|
76
|
-
* @param stderrWritableStream the WritableStream to use for stderr output.
|
|
77
|
-
* @param stdoutIsColor true if stdout supports color.
|
|
78
|
-
* @param stderrIsColor true if stderr supports color.
|
|
79
|
-
* @param terminal the Terminal implementation to use.
|
|
80
|
-
* @param styler the Styler implementation to use.
|
|
81
|
-
* @param envVarsEnabled optionally support checking env variables for default argument values.
|
|
82
|
-
* @param configEnabled optionally enable configuration file support for default argument values.
|
|
83
|
-
* @param keyValueServiceEnabled optionally provide a {@link KeyValueService} implementation: `configEnabled` must be true in this case
|
|
84
|
-
* @param validateAllCommands optionally force validation of all i.e. include validation of core commands.
|
|
85
|
-
*/
|
|
86
96
|
constructor(
|
|
87
97
|
cliConfig: CLIConfig,
|
|
88
98
|
stdoutWritableStream: WritableStream,
|
|
89
99
|
stderrWritableStream: WritableStream,
|
|
90
100
|
stdoutIsColor: boolean,
|
|
91
101
|
stderrIsColor: boolean,
|
|
102
|
+
stdoutTerminal: Terminal,
|
|
92
103
|
stderrTerminal: Terminal,
|
|
93
104
|
styler: Styler,
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
keyValueServiceEnabled = false,
|
|
97
|
-
validateAllCommands = false,
|
|
105
|
+
keyReader: KeyReader,
|
|
106
|
+
options?: BaseCLIFeatureOptions,
|
|
98
107
|
) {
|
|
99
108
|
if (cliConfig.name.length === 0) {
|
|
100
109
|
throw new Error("Invalid empty CLI name provided");
|
|
101
110
|
}
|
|
102
|
-
if (cliConfig.name[0]
|
|
111
|
+
if (cliConfig.name[0]!.match(/[0-9\-_]/)) {
|
|
103
112
|
throw new Error(
|
|
104
113
|
`Invalid CLI name starting with a digit or dash provided: '${cliConfig.name}'`,
|
|
105
114
|
);
|
|
@@ -114,14 +123,23 @@ export default class BaseCLI implements CLI {
|
|
|
114
123
|
throw new Error("Invalid empty CLI version provided");
|
|
115
124
|
}
|
|
116
125
|
this.#cliConfig = cliConfig;
|
|
117
|
-
this.#
|
|
118
|
-
|
|
119
|
-
|
|
126
|
+
this.#options = {
|
|
127
|
+
configFileSupportEnabled: false,
|
|
128
|
+
envVarsSupportEnabled: false,
|
|
129
|
+
keyValueServiceEnabled: false,
|
|
130
|
+
secretServiceEnabled: false,
|
|
131
|
+
argumentPrompterServiceEnabled: false,
|
|
132
|
+
completionServiceEnabled: false,
|
|
133
|
+
imagePrinterServiceEnabled: false,
|
|
134
|
+
validateAllCommands: false,
|
|
135
|
+
...options,
|
|
136
|
+
};
|
|
137
|
+
this.#keyReader = keyReader;
|
|
138
|
+
this.#stderrTerminal = stderrTerminal;
|
|
120
139
|
this.#addedNonModifierCommands = [];
|
|
121
140
|
|
|
122
|
-
// create a validator if required
|
|
123
141
|
let commandValidator: CommandValidator | undefined;
|
|
124
|
-
if (validateAllCommands) {
|
|
142
|
+
if (this.#options.validateAllCommands) {
|
|
125
143
|
commandValidator = new CommandValidator(this.#cliConfig);
|
|
126
144
|
}
|
|
127
145
|
|
|
@@ -134,11 +152,13 @@ export default class BaseCLI implements CLI {
|
|
|
134
152
|
// create a context
|
|
135
153
|
this.#context = new DefaultContext(this.#cliConfig);
|
|
136
154
|
|
|
155
|
+
this.#stdoutTerminal = stdoutTerminal;
|
|
137
156
|
this.#printerService = new DefaultPrinterService(
|
|
138
157
|
stdoutWritableStream,
|
|
139
158
|
stderrWritableStream,
|
|
140
159
|
stdoutIsColor,
|
|
141
160
|
stderrIsColor,
|
|
161
|
+
stdoutTerminal,
|
|
142
162
|
stderrTerminal,
|
|
143
163
|
styler,
|
|
144
164
|
);
|
|
@@ -184,11 +204,12 @@ export default class BaseCLI implements CLI {
|
|
|
184
204
|
|
|
185
205
|
if (
|
|
186
206
|
(this.#addedNonModifierCommands.length === 1) &&
|
|
187
|
-
!isSubCommand(this.#addedNonModifierCommands[0]) &&
|
|
188
|
-
!isGlobalCommand(this.#addedNonModifierCommands[0])
|
|
207
|
+
!isSubCommand(this.#addedNonModifierCommands[0]!) &&
|
|
208
|
+
!isGlobalCommand(this.#addedNonModifierCommands[0]!) &&
|
|
209
|
+
!isGroupCommand(this.#addedNonModifierCommands[0]!)
|
|
189
210
|
) {
|
|
190
211
|
throw new Error(
|
|
191
|
-
"If only one command is added,
|
|
212
|
+
"If only one command is added, it must be a global command, sub-command, or group command!",
|
|
192
213
|
);
|
|
193
214
|
}
|
|
194
215
|
|
|
@@ -200,12 +221,45 @@ export default class BaseCLI implements CLI {
|
|
|
200
221
|
this.#printerService,
|
|
201
222
|
),
|
|
202
223
|
);
|
|
224
|
+
this.addServiceProvider(new TableGeneratorServiceProvider(70));
|
|
225
|
+
|
|
226
|
+
const prompterService = new DefaultPrompterService(
|
|
227
|
+
DEFAULT_PROMPTER_CONFIG,
|
|
228
|
+
this.#stderrTerminal,
|
|
229
|
+
this.#keyReader,
|
|
230
|
+
this.#printerService,
|
|
231
|
+
);
|
|
232
|
+
this.addServiceProvider(new PrompterServiceProvider(75, prompterService));
|
|
233
|
+
|
|
234
|
+
if (this.#options.argumentPrompterServiceEnabled) {
|
|
235
|
+
const argumentPrompterService = new DefaultArgumentPrompterService(
|
|
236
|
+
prompterService,
|
|
237
|
+
);
|
|
238
|
+
this.addServiceProvider(
|
|
239
|
+
new ArgumentPrompterServiceProvider(65, argumentPrompterService),
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (this.#options.imagePrinterServiceEnabled) {
|
|
244
|
+
this.addServiceProvider(
|
|
245
|
+
new ImagePrinterServiceProvider(55, this.#stdoutTerminal),
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
let completionService: DefaultCompletionService | undefined;
|
|
250
|
+
if (this.#options.completionServiceEnabled) {
|
|
251
|
+
completionService = new DefaultCompletionService();
|
|
252
|
+
this.addServiceProvider(
|
|
253
|
+
new CompletionServiceProvider(60, completionService),
|
|
254
|
+
);
|
|
255
|
+
}
|
|
203
256
|
|
|
204
257
|
const configurationServiceProvider = new ConfigurationServiceProvider(
|
|
205
258
|
90,
|
|
206
|
-
this.#
|
|
207
|
-
this.#
|
|
208
|
-
this.#keyValueServiceEnabled,
|
|
259
|
+
this.#options.envVarsSupportEnabled,
|
|
260
|
+
this.#options.configFileSupportEnabled,
|
|
261
|
+
this.#options.keyValueServiceEnabled,
|
|
262
|
+
this.#options.secretServiceEnabled,
|
|
209
263
|
);
|
|
210
264
|
this.addServiceProvider(configurationServiceProvider);
|
|
211
265
|
|
|
@@ -213,7 +267,7 @@ export default class BaseCLI implements CLI {
|
|
|
213
267
|
const serviceProvider of this.#serviceProviderRegistry
|
|
214
268
|
.getServiceProviders()
|
|
215
269
|
) {
|
|
216
|
-
const serviceInfo = await serviceProvider.
|
|
270
|
+
const serviceInfo = await serviceProvider.getServiceInfo(this.#cliConfig);
|
|
217
271
|
|
|
218
272
|
if (serviceInfo.service) {
|
|
219
273
|
this.#context.addServiceInstance(
|
|
@@ -235,27 +289,30 @@ export default class BaseCLI implements CLI {
|
|
|
235
289
|
|
|
236
290
|
try {
|
|
237
291
|
// setup help commands and default command based on the number of commands explicitly added via {@link addCommand)
|
|
238
|
-
if (
|
|
292
|
+
if (
|
|
293
|
+
this.#addedNonModifierCommands.length === 1 &&
|
|
294
|
+
!isGroupCommand(this.#addedNonModifierCommands[0]!)
|
|
295
|
+
) {
|
|
239
296
|
// a single-command CLI
|
|
240
297
|
defaultCommand = this.#addedNonModifierCommands[0];
|
|
241
298
|
helpSubCommand = new SingleCommandCliHelpSubCommand(
|
|
242
|
-
this.#
|
|
299
|
+
this.#options.envVarsSupportEnabled,
|
|
243
300
|
defaultCommand as SubCommand,
|
|
244
301
|
this.#commandRegistry,
|
|
245
302
|
);
|
|
246
303
|
helpGlobalCommand = new SingleCommandCliHelpGlobalCommand(
|
|
247
|
-
this.#
|
|
304
|
+
this.#options.envVarsSupportEnabled,
|
|
248
305
|
defaultCommand as SubCommand,
|
|
249
306
|
this.#commandRegistry,
|
|
250
307
|
);
|
|
251
308
|
} else {
|
|
252
309
|
// a multi-command CLI
|
|
253
310
|
helpSubCommand = new MultiCommandCliHelpSubCommand(
|
|
254
|
-
this.#
|
|
311
|
+
this.#options.envVarsSupportEnabled,
|
|
255
312
|
this.#commandRegistry,
|
|
256
313
|
);
|
|
257
314
|
helpGlobalCommand = new MultiCommandCliHelpGlobalCommand(
|
|
258
|
-
this.#
|
|
315
|
+
this.#options.envVarsSupportEnabled,
|
|
259
316
|
this.#commandRegistry,
|
|
260
317
|
);
|
|
261
318
|
}
|
|
@@ -267,6 +324,16 @@ export default class BaseCLI implements CLI {
|
|
|
267
324
|
// register the version command
|
|
268
325
|
this.#commandRegistry.addCommand(new VersionCommand());
|
|
269
326
|
|
|
327
|
+
// register completion commands (need CommandRegistry reference like help commands)
|
|
328
|
+
if (completionService) {
|
|
329
|
+
completionService.setCommandRegistry(this.#commandRegistry);
|
|
330
|
+
const integrationCommand = new CompletionIntegrationSubCommand();
|
|
331
|
+
const completeCommand = new CompletionCompleteSubCommand();
|
|
332
|
+
this.#commandRegistry.addCommand(
|
|
333
|
+
new CompletionGroupCommand(integrationCommand, completeCommand),
|
|
334
|
+
);
|
|
335
|
+
}
|
|
336
|
+
|
|
270
337
|
// run...
|
|
271
338
|
const runResult = await run(
|
|
272
339
|
args,
|
|
@@ -299,7 +366,12 @@ export default class BaseCLI implements CLI {
|
|
|
299
366
|
}
|
|
300
367
|
return runResult;
|
|
301
368
|
} catch (error) {
|
|
302
|
-
|
|
369
|
+
if (
|
|
370
|
+
shutdownState.shutdownRequested ||
|
|
371
|
+
(error as Error).message === "Interrupted"
|
|
372
|
+
) {
|
|
373
|
+
return { runState: RunState.INTERRUPTED };
|
|
374
|
+
}
|
|
303
375
|
logger.error("Runtime error: %s", (error as Error).message);
|
|
304
376
|
return {
|
|
305
377
|
runState: RunState.RUNTIME_ERROR,
|
|
@@ -3,25 +3,21 @@ import BaseCLI from "./BaseCLI.ts";
|
|
|
3
3
|
import type RunResult from "../api/RunResult.ts";
|
|
4
4
|
import { RunState } from "../api/RunResult.ts";
|
|
5
5
|
import type CLIConfig from "../api/CLIConfig.ts";
|
|
6
|
+
import type BaseCLIFeatureOptions from "../api/BaseCLIFeatureOptions.ts";
|
|
6
7
|
import { Writable } from "node:stream";
|
|
7
|
-
import TtyTerminal from "../
|
|
8
|
-
import
|
|
8
|
+
import TtyTerminal from "../terminal/TtyTerminal.ts";
|
|
9
|
+
import TtyKeyReader from "../terminal/TtyKeyReader.ts";
|
|
10
|
+
import TtyStyler from "../terminal/TtyStyler.ts";
|
|
9
11
|
import supportsColor from "supports-color";
|
|
12
|
+
import supportsHyperlinks from "../terminal/supportsHyperlinks.ts";
|
|
10
13
|
|
|
11
14
|
/**
|
|
12
15
|
* Default Bun implementation of a {@link CLI} using `process.stdout`, `process.stderr` and `process.argv`.
|
|
13
16
|
*/
|
|
14
17
|
export default class DefaultRuntimeCLI extends BaseCLI {
|
|
15
|
-
/**
|
|
16
|
-
* Constructor configures the instance with the specified CLI application details
|
|
17
|
-
* and making use of `process.stdout` and `process.stderr`.
|
|
18
|
-
*/
|
|
19
18
|
constructor(
|
|
20
19
|
cliConfig: CLIConfig,
|
|
21
|
-
|
|
22
|
-
configEnabled = false,
|
|
23
|
-
keyValueServiceEnabled = false,
|
|
24
|
-
validateAllCommands = false,
|
|
20
|
+
options?: BaseCLIFeatureOptions,
|
|
25
21
|
) {
|
|
26
22
|
super(
|
|
27
23
|
cliConfig,
|
|
@@ -29,21 +25,17 @@ export default class DefaultRuntimeCLI extends BaseCLI {
|
|
|
29
25
|
Writable.toWeb(process.stderr),
|
|
30
26
|
supportsColor.stdout !== false,
|
|
31
27
|
supportsColor.stderr !== false,
|
|
28
|
+
new TtyTerminal(process.stdout),
|
|
32
29
|
new TtyTerminal(process.stderr),
|
|
33
30
|
new TtyStyler(
|
|
34
31
|
supportsColor.stderr === false ? 1 : supportsColor.stderr.level,
|
|
32
|
+
supportsHyperlinks(process.stderr),
|
|
35
33
|
),
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
keyValueServiceEnabled,
|
|
39
|
-
validateAllCommands,
|
|
34
|
+
new TtyKeyReader(process.stdin),
|
|
35
|
+
options,
|
|
40
36
|
);
|
|
41
37
|
}
|
|
42
38
|
|
|
43
|
-
/**
|
|
44
|
-
* Run the CLI using `process.argv` for the arguments and call `process.exit()` passing the
|
|
45
|
-
* {@link RunState} value resulting from the invocation.
|
|
46
|
-
*/
|
|
47
39
|
override async run(): Promise<RunResult> {
|
|
48
40
|
const runResult = await super.run(process.argv.slice(2));
|
|
49
41
|
if (runResult.runState === RunState.RUNTIME_ERROR) {
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import type GroupCommand from "../api/command/GroupCommand.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
|
+
type ArgumentValues,
|
|
8
|
+
ArgumentValueTypeName,
|
|
9
|
+
} from "../api/argument/ArgumentValueTypes.ts";
|
|
10
|
+
import {
|
|
11
|
+
COMPLETION_SERVICE_ID,
|
|
12
|
+
ShellType,
|
|
13
|
+
} from "../api/service/core/CompletionService.ts";
|
|
14
|
+
import type DefaultCompletionService from "../service/completion/DefaultCompletionService.ts";
|
|
15
|
+
import {
|
|
16
|
+
Icon,
|
|
17
|
+
PRINTER_SERVICE_ID,
|
|
18
|
+
} from "../api/service/core/PrinterService.ts";
|
|
19
|
+
import type PrinterService from "../api/service/core/PrinterService.ts";
|
|
20
|
+
|
|
21
|
+
export class CompletionIntegrationSubCommand implements SubCommand {
|
|
22
|
+
readonly name = "integration";
|
|
23
|
+
readonly description = "Install shell completion integration";
|
|
24
|
+
readonly enableConfiguration = false;
|
|
25
|
+
|
|
26
|
+
readonly options: ReadonlyArray<Option> = [
|
|
27
|
+
{
|
|
28
|
+
name: "config-path",
|
|
29
|
+
type: ArgumentValueTypeName.STRING,
|
|
30
|
+
isOptional: true,
|
|
31
|
+
description: "Path to shell configuration file",
|
|
32
|
+
},
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
readonly positionals: ReadonlyArray<Positional> = [
|
|
36
|
+
{
|
|
37
|
+
name: "shell",
|
|
38
|
+
type: ArgumentValueTypeName.STRING,
|
|
39
|
+
allowableValues: Object.values(ShellType),
|
|
40
|
+
description: "Shell type to configure",
|
|
41
|
+
},
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
async execute(
|
|
45
|
+
context: Context,
|
|
46
|
+
argumentValues: ArgumentValues,
|
|
47
|
+
): Promise<void> {
|
|
48
|
+
const completionService = context.getServiceById(
|
|
49
|
+
COMPLETION_SERVICE_ID,
|
|
50
|
+
) as DefaultCompletionService;
|
|
51
|
+
const printerService = context.getServiceById(
|
|
52
|
+
PRINTER_SERVICE_ID,
|
|
53
|
+
) as PrinterService;
|
|
54
|
+
|
|
55
|
+
const shellType = argumentValues.shell as ShellType;
|
|
56
|
+
const configPathArg = argumentValues["config-path"] as string | undefined;
|
|
57
|
+
|
|
58
|
+
const isValid = await completionService.validateShellEnvironment(shellType);
|
|
59
|
+
if (!isValid) {
|
|
60
|
+
await printerService.warn(
|
|
61
|
+
`Shell '${shellType}' was not detected in the current environment. Proceeding anyway.\n`,
|
|
62
|
+
Icon.ALERT,
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const configPath = configPathArg ||
|
|
67
|
+
completionService.getDefaultConfigPath(shellType);
|
|
68
|
+
const cliName = context.cliConfig.name;
|
|
69
|
+
const bootstrapScript = completionService.getBootstrapScript(
|
|
70
|
+
shellType,
|
|
71
|
+
cliName,
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
const beginMarker = `# BEGIN ${cliName} completion`;
|
|
75
|
+
const endMarker = `# END ${cliName} completion`;
|
|
76
|
+
const block = `${beginMarker}\n${bootstrapScript}\n${endMarker}`;
|
|
77
|
+
|
|
78
|
+
let existingContent = "";
|
|
79
|
+
try {
|
|
80
|
+
existingContent = await Bun.file(configPath).text();
|
|
81
|
+
} catch {
|
|
82
|
+
// File doesn't exist yet
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
let newContent: string;
|
|
86
|
+
const beginIdx = existingContent.indexOf(beginMarker);
|
|
87
|
+
const endIdx = existingContent.indexOf(endMarker);
|
|
88
|
+
if (beginIdx !== -1 && endIdx !== -1) {
|
|
89
|
+
newContent = existingContent.substring(0, beginIdx) +
|
|
90
|
+
block +
|
|
91
|
+
existingContent.substring(endIdx + endMarker.length);
|
|
92
|
+
} else {
|
|
93
|
+
const separator = existingContent.length > 0 &&
|
|
94
|
+
!existingContent.endsWith("\n")
|
|
95
|
+
? "\n\n"
|
|
96
|
+
: existingContent.length > 0
|
|
97
|
+
? "\n"
|
|
98
|
+
: "";
|
|
99
|
+
newContent = existingContent + separator + block + "\n";
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
await Bun.write(configPath, newContent);
|
|
103
|
+
|
|
104
|
+
await printerService.info(
|
|
105
|
+
`Shell completion installed for ${shellType}. Restart your shell or run 'source ${configPath}' to activate.\n`,
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export class CompletionCompleteSubCommand implements SubCommand {
|
|
111
|
+
readonly name = "complete";
|
|
112
|
+
readonly description = "Generate completions for shell integration";
|
|
113
|
+
readonly enableConfiguration = false;
|
|
114
|
+
|
|
115
|
+
readonly options: ReadonlyArray<Option> = [];
|
|
116
|
+
|
|
117
|
+
readonly positionals: ReadonlyArray<Positional> = [
|
|
118
|
+
{
|
|
119
|
+
name: "shell",
|
|
120
|
+
type: ArgumentValueTypeName.STRING,
|
|
121
|
+
allowableValues: Object.values(ShellType),
|
|
122
|
+
description: "Shell type",
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
name: "args",
|
|
126
|
+
type: ArgumentValueTypeName.STRING,
|
|
127
|
+
isVarargMultiple: true,
|
|
128
|
+
isVarargOptional: true,
|
|
129
|
+
description: "Shell-specific completion context arguments",
|
|
130
|
+
},
|
|
131
|
+
];
|
|
132
|
+
|
|
133
|
+
async execute(
|
|
134
|
+
context: Context,
|
|
135
|
+
argumentValues: ArgumentValues,
|
|
136
|
+
): Promise<void> {
|
|
137
|
+
const completionService = context.getServiceById(
|
|
138
|
+
COMPLETION_SERVICE_ID,
|
|
139
|
+
) as DefaultCompletionService;
|
|
140
|
+
const printerService = context.getServiceById(
|
|
141
|
+
PRINTER_SERVICE_ID,
|
|
142
|
+
) as PrinterService;
|
|
143
|
+
|
|
144
|
+
const shellType = argumentValues.shell as ShellType;
|
|
145
|
+
const args = (argumentValues.args || []) as string[];
|
|
146
|
+
|
|
147
|
+
const completionContext = completionService.parseCompletionContext(
|
|
148
|
+
shellType,
|
|
149
|
+
args,
|
|
150
|
+
);
|
|
151
|
+
const completions = await completionService.generateCompletions(
|
|
152
|
+
shellType,
|
|
153
|
+
completionContext.line,
|
|
154
|
+
completionContext.cursorPosition,
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
const output = completionService.formatCompletions(shellType, completions);
|
|
158
|
+
if (output.length > 0) {
|
|
159
|
+
await printerService.print(output + "\n");
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export class CompletionGroupCommand implements GroupCommand {
|
|
165
|
+
readonly name = "completions";
|
|
166
|
+
readonly description = "Manage shell auto-completion";
|
|
167
|
+
readonly enableConfiguration = false;
|
|
168
|
+
readonly memberSubCommands: ReadonlyArray<SubCommand>;
|
|
169
|
+
|
|
170
|
+
constructor(
|
|
171
|
+
integrationCommand: CompletionIntegrationSubCommand,
|
|
172
|
+
completeCommand: CompletionCompleteSubCommand,
|
|
173
|
+
) {
|
|
174
|
+
this.memberSubCommands = [integrationCommand, completeCommand];
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
async execute(_context: Context): Promise<void> {
|
|
178
|
+
}
|
|
179
|
+
}
|
|
@@ -26,6 +26,8 @@ import {
|
|
|
26
26
|
Icon,
|
|
27
27
|
PRINTER_SERVICE_ID,
|
|
28
28
|
} from "../api/service/core/PrinterService.ts";
|
|
29
|
+
import type TableGeneratorService from "../api/service/core/TableGeneratorService.ts";
|
|
30
|
+
import { TABLE_GENERATOR_SERVICE_ID } from "../api/service/core/TableGeneratorService.ts";
|
|
29
31
|
|
|
30
32
|
/**
|
|
31
33
|
* Provides common implementation for both {@link MultiCommandCliHelpGlobalCommand} and {@link MultiCommandCliHelpSubCommand}.
|
|
@@ -178,6 +180,9 @@ abstract class MultiCommandCliAbstractHelpCommand {
|
|
|
178
180
|
const printerService = context.getServiceById(
|
|
179
181
|
PRINTER_SERVICE_ID,
|
|
180
182
|
) as PrinterService;
|
|
183
|
+
const tableGeneratorService = context.getServiceById(
|
|
184
|
+
TABLE_GENERATOR_SERVICE_ID,
|
|
185
|
+
) as TableGeneratorService;
|
|
181
186
|
|
|
182
187
|
const globalModifierCommands = this.#commandRegistry
|
|
183
188
|
.getGlobalModifierCommands();
|
|
@@ -210,7 +215,11 @@ abstract class MultiCommandCliAbstractHelpCommand {
|
|
|
210
215
|
),
|
|
211
216
|
);
|
|
212
217
|
|
|
213
|
-
await printHelpSections(
|
|
218
|
+
await printHelpSections(
|
|
219
|
+
printerService,
|
|
220
|
+
tableGeneratorService,
|
|
221
|
+
helpSections,
|
|
222
|
+
);
|
|
214
223
|
}
|
|
215
224
|
|
|
216
225
|
protected async printUsageHelp(
|
|
@@ -220,6 +229,9 @@ abstract class MultiCommandCliAbstractHelpCommand {
|
|
|
220
229
|
const printerService = context.getServiceById(
|
|
221
230
|
PRINTER_SERVICE_ID,
|
|
222
231
|
) as PrinterService;
|
|
232
|
+
const tableGeneratorService = context.getServiceById(
|
|
233
|
+
TABLE_GENERATOR_SERVICE_ID,
|
|
234
|
+
) as TableGeneratorService;
|
|
223
235
|
|
|
224
236
|
const groupCommands = this.#commandRegistry.getGroupCommands();
|
|
225
237
|
const subCommands = this.#commandRegistry.getSubCommands();
|
|
@@ -300,7 +312,11 @@ abstract class MultiCommandCliAbstractHelpCommand {
|
|
|
300
312
|
...getCommandExamplesHelpSections(printerService, context, subCommand),
|
|
301
313
|
);
|
|
302
314
|
|
|
303
|
-
await printHelpSections(
|
|
315
|
+
await printHelpSections(
|
|
316
|
+
printerService,
|
|
317
|
+
tableGeneratorService,
|
|
318
|
+
helpSections,
|
|
319
|
+
);
|
|
304
320
|
}
|
|
305
321
|
}
|
|
306
322
|
|
|
@@ -17,6 +17,8 @@ import type GroupCommand from "../api/command/GroupCommand.ts";
|
|
|
17
17
|
import type CommandRegistry from "../runtime/registry/CommandRegistry.ts";
|
|
18
18
|
import type PrinterService from "../api/service/core/PrinterService.ts";
|
|
19
19
|
import { PRINTER_SERVICE_ID } from "../api/service/core/PrinterService.ts";
|
|
20
|
+
import type TableGeneratorService from "../api/service/core/TableGeneratorService.ts";
|
|
21
|
+
import { TABLE_GENERATOR_SERVICE_ID } from "../api/service/core/TableGeneratorService.ts";
|
|
20
22
|
|
|
21
23
|
/**
|
|
22
24
|
* Provides common implementation for both {@link SingleCommandCliHelpGlobalCommand} and {@link SingleCommandCliHelpSubCommand}.
|
|
@@ -149,6 +151,9 @@ abstract class SingleCommandCliAbstractHelpCommand {
|
|
|
149
151
|
const printerService = context.getServiceById(
|
|
150
152
|
PRINTER_SERVICE_ID,
|
|
151
153
|
) as PrinterService;
|
|
154
|
+
const tableGeneratorService = context.getServiceById(
|
|
155
|
+
TABLE_GENERATOR_SERVICE_ID,
|
|
156
|
+
) as TableGeneratorService;
|
|
152
157
|
|
|
153
158
|
helpSections.push(
|
|
154
159
|
...getCommandExamplesHelpSections(
|
|
@@ -158,7 +163,11 @@ abstract class SingleCommandCliAbstractHelpCommand {
|
|
|
158
163
|
),
|
|
159
164
|
);
|
|
160
165
|
|
|
161
|
-
await printHelpSections(
|
|
166
|
+
await printHelpSections(
|
|
167
|
+
printerService,
|
|
168
|
+
tableGeneratorService,
|
|
169
|
+
helpSections,
|
|
170
|
+
);
|
|
162
171
|
}
|
|
163
172
|
}
|
|
164
173
|
|