@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
package/src/launcher.ts
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import process from "node:process";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import type CLIConfig from "./api/CLIConfig.ts";
|
|
4
|
+
import type SubCommand from "./api/command/SubCommand.ts";
|
|
5
|
+
import DefaultRuntimeCLI from "./cli/DefaultRuntimeCLI.ts";
|
|
6
|
+
import type Command from "./api/command/Command.ts";
|
|
7
|
+
import type RunResult from "./api/RunResult.ts";
|
|
8
|
+
import type { ServiceProvider } from "./api/service/ServiceProvider.ts";
|
|
9
|
+
|
|
10
|
+
function parseVersion(moduleUrl?: string): string | undefined {
|
|
11
|
+
if (moduleUrl === undefined) {
|
|
12
|
+
return undefined;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const v = moduleUrl.match(/@([^\/]+)[\/$]?/);
|
|
16
|
+
if (v === null) {
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
return v[1];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Launch a {@link DefaultRuntimeCLI} with a single specified {@link SubCommand} instance.
|
|
24
|
+
*
|
|
25
|
+
* @param command the {@link SubCommand} to add to the CLI.
|
|
26
|
+
* @param description optional description of the CLI.
|
|
27
|
+
* @param name optional name of the CLI. If not provided, `process.execPath` will be used in an attempt to derive the name.
|
|
28
|
+
* @param version optional version of the CLI. If not provided the optionally provided {@link callingModuleUrl} will be used in an attempt to derive the version. If not possible the default is `N/A`.
|
|
29
|
+
* @param envVarsEnabled optionally support checking env variables for default argument values.
|
|
30
|
+
* @param configEnabled optionally enable configuration file support for default argument values.
|
|
31
|
+
* @param keyValueServiceEnabled optionally provide a {@link KeyValueService} implementation: `configEnabled` must be true in this case
|
|
32
|
+
* @param callingModuleUrl optional URL of the calling module, can be used in an attempt to derive the version of the CLI if {@link version} is not provided. If not possible the default is `N/A`.
|
|
33
|
+
* @param serviceProviders optional array of {@link ServiceProvider} instances to add to the CLI.
|
|
34
|
+
*/
|
|
35
|
+
export async function launchSingleCommandCLI(
|
|
36
|
+
command: SubCommand,
|
|
37
|
+
description?: string,
|
|
38
|
+
name?: string,
|
|
39
|
+
version?: string,
|
|
40
|
+
envVarsEnabled = false,
|
|
41
|
+
configEnabled = false,
|
|
42
|
+
keyValueServiceEnabled = false,
|
|
43
|
+
callingModuleUrl?: string,
|
|
44
|
+
serviceProviders?: ReadonlyArray<ServiceProvider>,
|
|
45
|
+
): Promise<RunResult> {
|
|
46
|
+
if (!name) {
|
|
47
|
+
name = path.basename(process.execPath);
|
|
48
|
+
}
|
|
49
|
+
const cliConfig: CLIConfig = {
|
|
50
|
+
description,
|
|
51
|
+
name,
|
|
52
|
+
version: version || parseVersion(callingModuleUrl) || "N/A",
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
if (!configEnabled && keyValueServiceEnabled) {
|
|
56
|
+
throw new Error(
|
|
57
|
+
"configEnabled must be true if keyValueServiceEnabled is true",
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
const validateAllCommands = process.env.CLI_VALIDATE_ALL !== undefined;
|
|
61
|
+
const cli = new DefaultRuntimeCLI(
|
|
62
|
+
cliConfig,
|
|
63
|
+
envVarsEnabled,
|
|
64
|
+
configEnabled,
|
|
65
|
+
keyValueServiceEnabled,
|
|
66
|
+
validateAllCommands,
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
cli.addCommand(command);
|
|
70
|
+
|
|
71
|
+
serviceProviders?.forEach((service) => cli.addServiceProvider(service));
|
|
72
|
+
|
|
73
|
+
return await cli.run();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Launch a {@link DefaultRuntimeCLI} with multiple specified {@link Command} instances.
|
|
78
|
+
*
|
|
79
|
+
* @param commands the {@link Command} instances to add to the CLI.
|
|
80
|
+
* @param description optional description of the CLI.
|
|
81
|
+
* @param name optional name of the CLI. If not provided, `process.execPath` will be used in an attempt to derive the name.
|
|
82
|
+
* @param version optional version of the CLI. If not provided the optionally provided {@link callingModuleUrl} will be used in an attempt to derive the version. If not possible the default is `N/A`.
|
|
83
|
+
* @param envVarsEnabled optionally support checking env variables for default argument values.
|
|
84
|
+
* @param configEnabled optionally enable configuration file support for default argument values.
|
|
85
|
+
* @param keyValueServiceEnabled optionally provide a {@link KeyValueService} implementation: `configEnabled` must be true in this case
|
|
86
|
+
* @param callingModuleUrl optional URL of the calling module, can be used in an attempt to derive the version of the CLI if {@link version} is not provided. If not possible the default is `N/A`.
|
|
87
|
+
* @param serviceProviders optional array of {@link ServiceProvider} instances to add to the CLI.
|
|
88
|
+
*/
|
|
89
|
+
export async function launchMultiCommandCLI(
|
|
90
|
+
commands: ReadonlyArray<Command>,
|
|
91
|
+
description?: string,
|
|
92
|
+
name?: string,
|
|
93
|
+
version?: string,
|
|
94
|
+
envVarsEnabled = false,
|
|
95
|
+
configEnabled = false,
|
|
96
|
+
keyValueServiceEnabled = false,
|
|
97
|
+
callingModuleUrl?: string,
|
|
98
|
+
serviceProviders?: ReadonlyArray<ServiceProvider>,
|
|
99
|
+
): Promise<RunResult> {
|
|
100
|
+
if (!name) {
|
|
101
|
+
name = path.basename(process.execPath);
|
|
102
|
+
}
|
|
103
|
+
const cliConfig: CLIConfig = {
|
|
104
|
+
description,
|
|
105
|
+
name,
|
|
106
|
+
version: version || parseVersion(callingModuleUrl) || "N/A",
|
|
107
|
+
};
|
|
108
|
+
if (!configEnabled && keyValueServiceEnabled) {
|
|
109
|
+
throw new Error(
|
|
110
|
+
"configEnabled must be true if keyValueServiceEnabled is true",
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
const validateAllCommands = process.env.CLI_VALIDATE_ALL !== undefined;
|
|
114
|
+
const cli = new DefaultRuntimeCLI(
|
|
115
|
+
cliConfig,
|
|
116
|
+
envVarsEnabled,
|
|
117
|
+
configEnabled,
|
|
118
|
+
keyValueServiceEnabled,
|
|
119
|
+
validateAllCommands,
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
commands.forEach((command) => cli.addCommand(command));
|
|
123
|
+
|
|
124
|
+
serviceProviders?.forEach((service) => cli.addServiceProvider(service));
|
|
125
|
+
|
|
126
|
+
return await cli.run();
|
|
127
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type Context from "../api/Context.ts";
|
|
2
|
+
import type CLIConfig from "../api/CLIConfig.ts";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Default implementation of a {@link Context}.
|
|
6
|
+
*/
|
|
7
|
+
export default class DefaultContext implements Context {
|
|
8
|
+
readonly #serviceInstancesById: Map<string, unknown> = new Map();
|
|
9
|
+
|
|
10
|
+
public constructor(readonly cliConfig: CLIConfig) {
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
public addServiceInstance(id: string, serviceInstance: unknown) {
|
|
14
|
+
if (this.#serviceInstancesById.has(id)) {
|
|
15
|
+
throw new Error(
|
|
16
|
+
`Service ID: ${id} duplicates the ID of an existing service`,
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
this.#serviceInstancesById.set(id, serviceInstance);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
public getServiceById(id: string): unknown {
|
|
23
|
+
if (!this.#serviceInstancesById.has(id)) {
|
|
24
|
+
throw new Error(`Service with ID: ${id} does not exist`);
|
|
25
|
+
}
|
|
26
|
+
return this.#serviceInstancesById.get(id);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
public doesServiceExist(id: string): boolean {
|
|
30
|
+
return this.#serviceInstancesById.has(id);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type Argument from "../../api/argument/Argument.ts";
|
|
2
|
+
import type ComplexOption from "../../api/argument/ComplexOption.ts";
|
|
3
|
+
|
|
4
|
+
export function isComplexOption(
|
|
5
|
+
argument: Argument | ComplexOption,
|
|
6
|
+
): argument is ComplexOption {
|
|
7
|
+
return (argument as ComplexOption).properties !== undefined;
|
|
8
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type SubCommand from "../../api/command/SubCommand.ts";
|
|
2
|
+
import type GroupCommand from "../../api/command/GroupCommand.ts";
|
|
3
|
+
import type GlobalCommand from "../../api/command/GlobalCommand.ts";
|
|
4
|
+
import type GlobalModifierCommand from "../../api/command/GlobalModifierCommand.ts";
|
|
5
|
+
import type Command from "../../api/command/Command.ts";
|
|
6
|
+
|
|
7
|
+
export function isGroupCommand(
|
|
8
|
+
command: Command,
|
|
9
|
+
): command is GroupCommand {
|
|
10
|
+
return (command as GroupCommand).memberSubCommands !== undefined;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function isSubCommand(
|
|
14
|
+
command: Command,
|
|
15
|
+
): command is SubCommand {
|
|
16
|
+
return (command as SubCommand).options !== undefined;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function isGlobalModifierCommand(
|
|
20
|
+
command: Command,
|
|
21
|
+
): command is GlobalModifierCommand {
|
|
22
|
+
return (command as GlobalModifierCommand).executePriority !== undefined;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function isGlobalCommand(
|
|
26
|
+
command: Command,
|
|
27
|
+
): command is GlobalCommand {
|
|
28
|
+
return !isGroupCommand(command) && !isSubCommand(command) &&
|
|
29
|
+
!isGlobalModifierCommand(command);
|
|
30
|
+
}
|