bob-core 1.0.0 → 1.2.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/dist/{Cli.d.ts → cjs/Cli.d.ts} +4 -4
- package/dist/{Cli.js → cjs/Cli.js} +6 -6
- package/dist/{Command.d.ts → cjs/Command.d.ts} +2 -2
- package/dist/{Command.js → cjs/Command.js} +4 -4
- package/dist/{Command.test.js → cjs/Command.test.js} +4 -4
- package/dist/{CommandParser.d.ts → cjs/CommandParser.d.ts} +1 -1
- package/dist/{CommandParser.js → cjs/CommandParser.js} +13 -13
- package/dist/{CommandParser.test.js → cjs/CommandParser.test.js} +5 -5
- package/dist/{CommandRegistry.d.ts → cjs/CommandRegistry.d.ts} +1 -1
- package/dist/{CommandRegistry.js → cjs/CommandRegistry.js} +21 -11
- package/dist/{ExceptionHandler.d.ts → cjs/ExceptionHandler.d.ts} +1 -1
- package/dist/{ExceptionHandler.js → cjs/ExceptionHandler.js} +2 -2
- package/dist/{commands → cjs/commands}/HelpCommand.d.ts +2 -2
- package/dist/{commands → cjs/commands}/HelpCommand.js +4 -4
- package/dist/cjs/contracts/index.d.ts +1 -0
- package/dist/{index.js → cjs/contracts/index.js} +1 -3
- package/dist/{errors → cjs/errors}/BadCommandOption.d.ts +1 -1
- package/dist/{errors → cjs/errors}/BadCommandOption.js +2 -2
- package/dist/{errors → cjs/errors}/BadCommandParameter.d.ts +1 -1
- package/dist/{errors → cjs/errors}/BadCommandParameter.js +2 -2
- package/dist/{errors → cjs/errors}/CommandNotFoundError.d.ts +1 -1
- package/dist/{errors → cjs/errors}/CommandNotFoundError.js +2 -2
- package/dist/{errors → cjs/errors}/InvalidOption.d.ts +2 -2
- package/dist/{errors → cjs/errors}/InvalidOption.js +2 -2
- package/dist/{errors → cjs/errors}/MissingRequiredArgumentValue.d.ts +2 -2
- package/dist/{errors → cjs/errors}/MissingRequiredArgumentValue.js +2 -2
- package/dist/{errors → cjs/errors}/MissingSignatureArgument.d.ts +2 -2
- package/dist/{errors → cjs/errors}/MissingSignatureArgument.js +2 -2
- package/dist/{errors → cjs/errors}/MissingSignatureOption.d.ts +2 -2
- package/dist/{errors → cjs/errors}/MissingSignatureOption.js +2 -2
- package/dist/cjs/errors/index.d.ts +3 -0
- package/dist/cjs/errors/index.js +19 -0
- package/dist/cjs/index.d.ts +5 -0
- package/dist/cjs/index.js +21 -0
- package/dist/{lib → cjs/lib}/string.js +1 -2
- package/dist/{options → cjs/options}/HelpOption.d.ts +2 -2
- package/dist/{options → cjs/options}/HelpOption.js +3 -3
- package/dist/cjs/options/index.d.ts +1 -0
- package/dist/{errors → cjs/options}/index.js +1 -3
- package/dist/cjs/package.json +1 -0
- package/dist/esm/Cli.d.ts +26 -0
- package/dist/esm/Cli.js +59 -0
- package/dist/esm/Command.d.ts +34 -0
- package/dist/esm/Command.js +93 -0
- package/dist/esm/Command.test.d.ts +1 -0
- package/dist/esm/Command.test.js +50 -0
- package/dist/esm/CommandParser.d.ts +46 -0
- package/dist/esm/CommandParser.js +232 -0
- package/dist/esm/CommandParser.test.d.ts +1 -0
- package/dist/esm/CommandParser.test.js +175 -0
- package/dist/esm/CommandRegistry.d.ts +17 -0
- package/dist/esm/CommandRegistry.js +109 -0
- package/dist/esm/ExceptionHandler.d.ts +4 -0
- package/dist/esm/ExceptionHandler.js +10 -0
- package/dist/esm/commands/HelpCommand.d.ts +14 -0
- package/dist/esm/commands/HelpCommand.js +50 -0
- package/dist/esm/contracts/CommandOption.d.ts +7 -0
- package/dist/esm/contracts/CommandOption.js +1 -0
- package/dist/esm/contracts/index.d.ts +1 -0
- package/dist/esm/contracts/index.js +1 -0
- package/dist/esm/errors/BadCommandOption.d.ts +11 -0
- package/dist/esm/errors/BadCommandOption.js +29 -0
- package/dist/esm/errors/BadCommandParameter.d.ts +11 -0
- package/dist/esm/errors/BadCommandParameter.js +29 -0
- package/dist/esm/errors/BobError.d.ts +3 -0
- package/dist/esm/errors/BobError.js +2 -0
- package/dist/esm/errors/CommandNotFoundError.d.ts +7 -0
- package/dist/esm/errors/CommandNotFoundError.js +23 -0
- package/dist/esm/errors/InvalidOption.d.ts +8 -0
- package/dist/esm/errors/InvalidOption.js +25 -0
- package/dist/esm/errors/MissingRequiredArgumentValue.d.ts +7 -0
- package/dist/esm/errors/MissingRequiredArgumentValue.js +16 -0
- package/dist/esm/errors/MissingSignatureArgument.d.ts +8 -0
- package/dist/esm/errors/MissingSignatureArgument.js +24 -0
- package/dist/esm/errors/MissingSignatureOption.d.ts +8 -0
- package/dist/esm/errors/MissingSignatureOption.js +24 -0
- package/dist/esm/errors/index.d.ts +3 -0
- package/dist/esm/errors/index.js +3 -0
- package/dist/esm/index.d.ts +5 -0
- package/dist/esm/index.js +5 -0
- package/dist/esm/lib/string.d.ts +1 -0
- package/dist/esm/lib/string.js +3 -0
- package/dist/esm/options/HelpOption.d.ts +9 -0
- package/dist/esm/options/HelpOption.js +71 -0
- package/dist/esm/options/index.d.ts +1 -0
- package/dist/esm/options/index.js +1 -0
- package/package.json +20 -5
- package/dist/CommandHelper.d.ts +0 -4
- package/dist/CommandHelper.js +0 -74
- package/dist/errors/index.d.ts +0 -3
- package/dist/index.d.ts +0 -3
- /package/dist/{Command.test.d.ts → cjs/Command.test.d.ts} +0 -0
- /package/dist/{CommandParser.test.d.ts → cjs/CommandParser.test.d.ts} +0 -0
- /package/dist/{contracts → cjs/contracts}/CommandOption.d.ts +0 -0
- /package/dist/{contracts → cjs/contracts}/CommandOption.js +0 -0
- /package/dist/{errors → cjs/errors}/BobError.d.ts +0 -0
- /package/dist/{errors → cjs/errors}/BobError.js +0 -0
- /package/dist/{lib → cjs/lib}/string.d.ts +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type":"commonjs"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { CommandRegistry } from "@/src/CommandRegistry.js";
|
|
2
|
+
import { Command } from "@/src/Command.js";
|
|
3
|
+
import HelpCommand from "@/src/commands/HelpCommand.js";
|
|
4
|
+
import { ExceptionHandler } from "@/src/ExceptionHandler.js";
|
|
5
|
+
export type CliOptions<C> = {
|
|
6
|
+
ctx?: C;
|
|
7
|
+
name?: string;
|
|
8
|
+
version?: string;
|
|
9
|
+
};
|
|
10
|
+
export declare class Cli<C> {
|
|
11
|
+
readonly commandRegistry: CommandRegistry;
|
|
12
|
+
private readonly exceptionHandler;
|
|
13
|
+
private readonly ctx?;
|
|
14
|
+
private readonly helpCommand;
|
|
15
|
+
get CommandRegistryClass(): typeof CommandRegistry;
|
|
16
|
+
get HelpCommandClass(): typeof HelpCommand;
|
|
17
|
+
get ExceptionHandlerClass(): typeof ExceptionHandler;
|
|
18
|
+
constructor(opts?: CliOptions<C>);
|
|
19
|
+
setCommandResolver(resolver: (path: string) => Promise<Command<C>>): void;
|
|
20
|
+
withCommands(...commands: Array<Command<C> | {
|
|
21
|
+
new (): Command<C>;
|
|
22
|
+
} | string>): Promise<void>;
|
|
23
|
+
runCommand(command: string | Command, ...args: any[]): Promise<number>;
|
|
24
|
+
runHelpCommand(): Promise<number>;
|
|
25
|
+
protected registerCommand(command: Command<C>): void;
|
|
26
|
+
}
|
package/dist/esm/Cli.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { CommandRegistry } from "@/src/CommandRegistry.js";
|
|
2
|
+
import HelpCommand from "@/src/commands/HelpCommand.js";
|
|
3
|
+
import { ExceptionHandler } from "@/src/ExceptionHandler.js";
|
|
4
|
+
export class Cli {
|
|
5
|
+
commandRegistry;
|
|
6
|
+
exceptionHandler;
|
|
7
|
+
ctx;
|
|
8
|
+
helpCommand;
|
|
9
|
+
get CommandRegistryClass() {
|
|
10
|
+
return CommandRegistry;
|
|
11
|
+
}
|
|
12
|
+
get HelpCommandClass() {
|
|
13
|
+
return HelpCommand;
|
|
14
|
+
}
|
|
15
|
+
get ExceptionHandlerClass() {
|
|
16
|
+
return ExceptionHandler;
|
|
17
|
+
}
|
|
18
|
+
constructor(opts = {}) {
|
|
19
|
+
this.ctx = opts.ctx;
|
|
20
|
+
this.commandRegistry = new this.CommandRegistryClass();
|
|
21
|
+
this.exceptionHandler = new this.ExceptionHandlerClass();
|
|
22
|
+
this.helpCommand = new this.HelpCommandClass({
|
|
23
|
+
cliName: opts.name,
|
|
24
|
+
cliVersion: opts.version,
|
|
25
|
+
commandRegistry: this.commandRegistry
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
setCommandResolver(resolver) {
|
|
29
|
+
this.commandRegistry.setCommandResolver(resolver);
|
|
30
|
+
}
|
|
31
|
+
async withCommands(...commands) {
|
|
32
|
+
for (const command of commands) {
|
|
33
|
+
if (typeof command === 'string') {
|
|
34
|
+
await this.commandRegistry.loadCommandsPath(command);
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
if (typeof command === 'function') {
|
|
38
|
+
this.registerCommand(new command());
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
this.registerCommand(command);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
async runCommand(command, ...args) {
|
|
47
|
+
if (!command) {
|
|
48
|
+
return await this.runHelpCommand();
|
|
49
|
+
}
|
|
50
|
+
return await this.commandRegistry.runCommand(this.ctx, command, ...args)
|
|
51
|
+
.catch(this.exceptionHandler.handle);
|
|
52
|
+
}
|
|
53
|
+
async runHelpCommand() {
|
|
54
|
+
return await this.runCommand(this.helpCommand);
|
|
55
|
+
}
|
|
56
|
+
registerCommand(command) {
|
|
57
|
+
this.commandRegistry.registerCommand(command);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { CommandParser } from "@/src/CommandParser.js";
|
|
2
|
+
import { CommandOption } from "@/src/contracts/index.js";
|
|
3
|
+
export type CommandExample = {
|
|
4
|
+
description: string;
|
|
5
|
+
command: string;
|
|
6
|
+
};
|
|
7
|
+
export declare abstract class Command<C = any> {
|
|
8
|
+
abstract signature: string;
|
|
9
|
+
abstract description: string;
|
|
10
|
+
protected ctx: C;
|
|
11
|
+
protected helperDefinitions: {
|
|
12
|
+
[key: string]: string;
|
|
13
|
+
};
|
|
14
|
+
protected commandsExamples: CommandExample[];
|
|
15
|
+
protected parser: CommandParser;
|
|
16
|
+
protected abstract handle(): Promise<void | number>;
|
|
17
|
+
private get CommandParserClass();
|
|
18
|
+
protected defaultOptions(): CommandOption<Command<C>>[];
|
|
19
|
+
get command(): string;
|
|
20
|
+
run(ctx: C, ...args: any[]): Promise<number>;
|
|
21
|
+
protected setOption(name: string, value: any): void;
|
|
22
|
+
protected setArgument(name: string, value: any): void;
|
|
23
|
+
protected option<T = string>(key: string): T | null;
|
|
24
|
+
protected option<T = string>(key: string, defaultValue: T): NoInfer<T>;
|
|
25
|
+
protected optionBoolean(key: string, defaultValue?: boolean): boolean;
|
|
26
|
+
protected optionArray<T = string>(key: string, defaultValue?: Array<T>): Array<NoInfer<T>>;
|
|
27
|
+
protected optionNumber(key: string): number | null;
|
|
28
|
+
protected optionNumber(key: string, defaultValue: number): number;
|
|
29
|
+
protected argument<T = string>(key: string): T | null;
|
|
30
|
+
protected argument<T = string>(key: string, defaultValue: T): NoInfer<T>;
|
|
31
|
+
protected argumentArray<T = string>(key: string, defaultValue?: Array<any>): Array<T>;
|
|
32
|
+
protected argumentBoolean(key: string, defaultValue?: boolean): boolean;
|
|
33
|
+
protected argumentNumber(key: string, defaultValue?: number | null): number | null;
|
|
34
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { CommandParser } from "@/src/CommandParser.js";
|
|
2
|
+
import { HelpOption } from "@/src/options/index.js";
|
|
3
|
+
export class Command {
|
|
4
|
+
ctx;
|
|
5
|
+
helperDefinitions = {};
|
|
6
|
+
commandsExamples = [];
|
|
7
|
+
parser;
|
|
8
|
+
get CommandParserClass() {
|
|
9
|
+
return CommandParser;
|
|
10
|
+
}
|
|
11
|
+
defaultOptions() {
|
|
12
|
+
return [new HelpOption];
|
|
13
|
+
}
|
|
14
|
+
get command() {
|
|
15
|
+
if (this.parser) {
|
|
16
|
+
return this.parser.command;
|
|
17
|
+
}
|
|
18
|
+
return this.signature.split(' ')[0];
|
|
19
|
+
}
|
|
20
|
+
async run(ctx, ...args) {
|
|
21
|
+
this.ctx = ctx;
|
|
22
|
+
const defaultOptions = this.defaultOptions();
|
|
23
|
+
this.parser = new this.CommandParserClass(this.signature, this.helperDefinitions, defaultOptions, ...args);
|
|
24
|
+
for (const option of defaultOptions) {
|
|
25
|
+
if (this.parser.option(option.option)) {
|
|
26
|
+
const code = await option.handler.call(this);
|
|
27
|
+
if (code && code !== 0) {
|
|
28
|
+
return code;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
this.parser.validate();
|
|
33
|
+
return (await this.handle()) ?? 0;
|
|
34
|
+
}
|
|
35
|
+
setOption(name, value) {
|
|
36
|
+
this.parser.setOption(name, value);
|
|
37
|
+
}
|
|
38
|
+
setArgument(name, value) {
|
|
39
|
+
this.parser.setArgument(name, value);
|
|
40
|
+
}
|
|
41
|
+
option(key, defaultValue = null) {
|
|
42
|
+
return this.parser.option(key) ?? defaultValue;
|
|
43
|
+
}
|
|
44
|
+
optionBoolean(key, defaultValue = false) {
|
|
45
|
+
return this.parser.option(key) ?? defaultValue;
|
|
46
|
+
}
|
|
47
|
+
optionArray(key, defaultValue = []) {
|
|
48
|
+
const values = this.parser.option(key);
|
|
49
|
+
if (!Array.isArray(values)) {
|
|
50
|
+
throw new Error(`Option ${key} is not an array`);
|
|
51
|
+
}
|
|
52
|
+
if (values.length) {
|
|
53
|
+
return values;
|
|
54
|
+
}
|
|
55
|
+
return defaultValue;
|
|
56
|
+
}
|
|
57
|
+
optionNumber(key, defaultValue = null) {
|
|
58
|
+
const value = this.parser.option(key);
|
|
59
|
+
if (!value) {
|
|
60
|
+
return defaultValue;
|
|
61
|
+
}
|
|
62
|
+
if (typeof value === 'number') {
|
|
63
|
+
return value;
|
|
64
|
+
}
|
|
65
|
+
return parseInt(value);
|
|
66
|
+
}
|
|
67
|
+
argument(key, defaultValue = null) {
|
|
68
|
+
return this.parser.argument(key) ?? defaultValue;
|
|
69
|
+
}
|
|
70
|
+
argumentArray(key, defaultValue = []) {
|
|
71
|
+
const values = this.parser.argument(key);
|
|
72
|
+
if (!Array.isArray(values)) {
|
|
73
|
+
throw new Error(`Argument ${key} is not an array`);
|
|
74
|
+
}
|
|
75
|
+
if (values?.length) {
|
|
76
|
+
return values;
|
|
77
|
+
}
|
|
78
|
+
return defaultValue;
|
|
79
|
+
}
|
|
80
|
+
argumentBoolean(key, defaultValue = false) {
|
|
81
|
+
return this.parser.argument(key) ?? defaultValue;
|
|
82
|
+
}
|
|
83
|
+
argumentNumber(key, defaultValue = null) {
|
|
84
|
+
const value = this.parser.argument(key);
|
|
85
|
+
if (!value) {
|
|
86
|
+
return defaultValue;
|
|
87
|
+
}
|
|
88
|
+
if (typeof value === 'number') {
|
|
89
|
+
return value;
|
|
90
|
+
}
|
|
91
|
+
return parseInt(value);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { Command } from '@/src/Command.js';
|
|
2
|
+
import { MissingRequiredArgumentValue } from "@/src/errors/MissingRequiredArgumentValue.js";
|
|
3
|
+
class MockCommand extends Command {
|
|
4
|
+
signature = 'mockCommand {argument} {--option}';
|
|
5
|
+
description = 'This is a mock command for testing';
|
|
6
|
+
handle() {
|
|
7
|
+
const opts = this.option('option');
|
|
8
|
+
const arg = this.argument('argument');
|
|
9
|
+
if (opts) {
|
|
10
|
+
return Promise.resolve(11);
|
|
11
|
+
}
|
|
12
|
+
if (arg === 'value') {
|
|
13
|
+
return Promise.resolve(1);
|
|
14
|
+
}
|
|
15
|
+
else if (arg) {
|
|
16
|
+
return Promise.resolve(-1);
|
|
17
|
+
}
|
|
18
|
+
return Promise.resolve(0);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
describe('Command', () => {
|
|
22
|
+
let command;
|
|
23
|
+
beforeEach(() => {
|
|
24
|
+
command = new MockCommand();
|
|
25
|
+
});
|
|
26
|
+
it('should have a command', () => {
|
|
27
|
+
expect(command.command).toBe('mockCommand');
|
|
28
|
+
});
|
|
29
|
+
it('should have a signature', () => {
|
|
30
|
+
expect(command.signature).toBe('mockCommand {argument} {--option}');
|
|
31
|
+
});
|
|
32
|
+
it('should have a description', () => {
|
|
33
|
+
expect(command.description).toBe('This is a mock command for testing');
|
|
34
|
+
});
|
|
35
|
+
it('should handle command with argument', async () => {
|
|
36
|
+
const result = await command.run(undefined, 'value');
|
|
37
|
+
expect(result).toBe(1);
|
|
38
|
+
});
|
|
39
|
+
it('should handle command with argument', async () => {
|
|
40
|
+
const result = await command.run(undefined, 'badValue');
|
|
41
|
+
expect(result).toBe(-1);
|
|
42
|
+
});
|
|
43
|
+
it('should throw error if argument is missing', async () => {
|
|
44
|
+
await expect(command.run(undefined)).rejects.toThrowError(MissingRequiredArgumentValue);
|
|
45
|
+
});
|
|
46
|
+
it('should handle command with option', async () => {
|
|
47
|
+
const result = await command.run(undefined, 'value', '--option');
|
|
48
|
+
expect(result).toBe(11);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { CommandOption } from "@/src/contracts/index.js";
|
|
2
|
+
export type ArgSignature = {
|
|
3
|
+
name: string;
|
|
4
|
+
type: string;
|
|
5
|
+
optional?: boolean;
|
|
6
|
+
variadic?: boolean;
|
|
7
|
+
alias?: string[];
|
|
8
|
+
help?: string;
|
|
9
|
+
defaultValue: string | boolean | Array<string> | null;
|
|
10
|
+
isOption?: boolean;
|
|
11
|
+
};
|
|
12
|
+
export declare class CommandParser {
|
|
13
|
+
protected readonly signature: string;
|
|
14
|
+
protected readonly helperDefinitions: {
|
|
15
|
+
[key: string]: string;
|
|
16
|
+
};
|
|
17
|
+
protected readonly defaultCommandOptions: CommandOption<any>[];
|
|
18
|
+
command: string;
|
|
19
|
+
private arguments;
|
|
20
|
+
private options;
|
|
21
|
+
private argumentsSignature;
|
|
22
|
+
private optionSignatures;
|
|
23
|
+
private optionAliases;
|
|
24
|
+
constructor(signature: string, helperDefinitions: {
|
|
25
|
+
[key: string]: string;
|
|
26
|
+
}, defaultCommandOptions: CommandOption<any>[], ...args: any[]);
|
|
27
|
+
option(name: string): any;
|
|
28
|
+
setOption(name: string, value: any): void;
|
|
29
|
+
optionHelp(name: string): string | undefined;
|
|
30
|
+
argument(name: string): any;
|
|
31
|
+
setArgument(name: string, value: any): void;
|
|
32
|
+
argumentHelp(name: string): string | undefined;
|
|
33
|
+
getArgumentSignatures(): {
|
|
34
|
+
[argument: string]: ArgSignature;
|
|
35
|
+
};
|
|
36
|
+
getOptionSignatures(): {
|
|
37
|
+
[option: string]: ArgSignature;
|
|
38
|
+
};
|
|
39
|
+
private getParamValue;
|
|
40
|
+
private handleArguments;
|
|
41
|
+
private handleOptions;
|
|
42
|
+
private parseSignature;
|
|
43
|
+
private parseDefaultOptions;
|
|
44
|
+
private parseParamSignature;
|
|
45
|
+
validate(): void;
|
|
46
|
+
}
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import minimist from 'minimist';
|
|
2
|
+
import { MissingRequiredArgumentValue } from "@/src/errors/MissingRequiredArgumentValue.js";
|
|
3
|
+
import { MissingSignatureOption } from "@/src/errors/MissingSignatureOption.js";
|
|
4
|
+
import { MissingSignatureArgument } from "@/src/errors/MissingSignatureArgument.js";
|
|
5
|
+
import { InvalidOption } from "@/src/errors/InvalidOption.js";
|
|
6
|
+
export class CommandParser {
|
|
7
|
+
signature;
|
|
8
|
+
helperDefinitions;
|
|
9
|
+
defaultCommandOptions;
|
|
10
|
+
command;
|
|
11
|
+
arguments = {};
|
|
12
|
+
options = {};
|
|
13
|
+
argumentsSignature = {};
|
|
14
|
+
optionSignatures = {};
|
|
15
|
+
optionAliases = {};
|
|
16
|
+
constructor(signature, helperDefinitions, defaultCommandOptions, ...args) {
|
|
17
|
+
this.signature = signature;
|
|
18
|
+
this.helperDefinitions = helperDefinitions;
|
|
19
|
+
this.defaultCommandOptions = defaultCommandOptions;
|
|
20
|
+
const [command, ...signatureParams] = signature.split(/\{(.*?)\}/g).map(param => param.trim()).filter(Boolean);
|
|
21
|
+
const { _: paramValues, ...optionValues } = minimist(args);
|
|
22
|
+
this.command = command;
|
|
23
|
+
this.parseSignature(signatureParams);
|
|
24
|
+
this.parseDefaultOptions();
|
|
25
|
+
this.handleArguments(paramValues);
|
|
26
|
+
this.handleOptions(optionValues);
|
|
27
|
+
}
|
|
28
|
+
option(name) {
|
|
29
|
+
if (!this.optionSignatures[name]) {
|
|
30
|
+
throw new MissingSignatureOption(name, Object.values(this.optionSignatures));
|
|
31
|
+
}
|
|
32
|
+
return this.options[name];
|
|
33
|
+
}
|
|
34
|
+
setOption(name, value) {
|
|
35
|
+
if (!this.optionSignatures[name]) {
|
|
36
|
+
throw new MissingSignatureOption(name, Object.values(this.optionSignatures));
|
|
37
|
+
}
|
|
38
|
+
this.options[name] = value;
|
|
39
|
+
}
|
|
40
|
+
optionHelp(name) {
|
|
41
|
+
const optionSignature = this.optionSignatures[name];
|
|
42
|
+
if (!optionSignature) {
|
|
43
|
+
throw new MissingSignatureOption(name, Object.values(this.optionSignatures));
|
|
44
|
+
}
|
|
45
|
+
return this.optionSignatures[name].help;
|
|
46
|
+
}
|
|
47
|
+
argument(name) {
|
|
48
|
+
if (!this.argumentsSignature[name]) {
|
|
49
|
+
throw new MissingSignatureArgument(name, Object.values(this.argumentsSignature));
|
|
50
|
+
}
|
|
51
|
+
return this.arguments[name];
|
|
52
|
+
}
|
|
53
|
+
setArgument(name, value) {
|
|
54
|
+
if (!this.argumentsSignature[name]) {
|
|
55
|
+
throw new MissingSignatureArgument(name, Object.values(this.argumentsSignature));
|
|
56
|
+
}
|
|
57
|
+
this.arguments[name] = value;
|
|
58
|
+
}
|
|
59
|
+
argumentHelp(name) {
|
|
60
|
+
const argumentSignature = this.argumentsSignature[name];
|
|
61
|
+
if (!argumentSignature) {
|
|
62
|
+
throw new MissingSignatureArgument(name, Object.values(this.argumentsSignature));
|
|
63
|
+
}
|
|
64
|
+
return this.argumentsSignature[name].help;
|
|
65
|
+
}
|
|
66
|
+
getArgumentSignatures() {
|
|
67
|
+
return this.argumentsSignature;
|
|
68
|
+
}
|
|
69
|
+
getOptionSignatures() {
|
|
70
|
+
return this.optionSignatures;
|
|
71
|
+
}
|
|
72
|
+
getParamValue(value, signature) {
|
|
73
|
+
if (signature.type === 'boolean') {
|
|
74
|
+
if (value === 'true' || value === '1') {
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
else if (value === 'false' || value === '0') {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
return Boolean(value);
|
|
81
|
+
}
|
|
82
|
+
if (signature.type === 'array') {
|
|
83
|
+
if (!value) {
|
|
84
|
+
return [];
|
|
85
|
+
}
|
|
86
|
+
return Array.isArray(value) ? value : [value];
|
|
87
|
+
}
|
|
88
|
+
return value ?? signature.defaultValue;
|
|
89
|
+
}
|
|
90
|
+
handleArguments(paramValues) {
|
|
91
|
+
for (const [argument, value] of Object.entries(this.arguments)) {
|
|
92
|
+
const argSignature = this.argumentsSignature[argument];
|
|
93
|
+
if (argSignature.variadic) {
|
|
94
|
+
this.arguments[argument] = paramValues;
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
const paramValue = paramValues.shift();
|
|
98
|
+
this.arguments[argument] = this.getParamValue(paramValue, argSignature);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
handleOptions(optionValues) {
|
|
103
|
+
for (const [option, value] of Object.entries(optionValues)) {
|
|
104
|
+
const optionAlias = this.optionAliases[option];
|
|
105
|
+
const optionSignature = this.optionSignatures[option] ?? this.optionSignatures[optionAlias];
|
|
106
|
+
if (!optionSignature) {
|
|
107
|
+
throw new InvalidOption(option, Object.values(this.optionSignatures));
|
|
108
|
+
}
|
|
109
|
+
this.options[option] = this.getParamValue(value, optionSignature);
|
|
110
|
+
for (const alias of optionSignature.alias ?? []) {
|
|
111
|
+
if (optionValues[alias]) {
|
|
112
|
+
this.options[optionSignature.name] = optionValues[alias];
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
parseSignature(params) {
|
|
118
|
+
for (const paramSignature of params) {
|
|
119
|
+
const param = this.parseParamSignature(paramSignature);
|
|
120
|
+
if (param.isOption) {
|
|
121
|
+
this.options[param.name] = param.defaultValue ?? null;
|
|
122
|
+
this.optionSignatures[param.name] = param;
|
|
123
|
+
for (const alias of param.alias ?? []) {
|
|
124
|
+
this.optionAliases[alias] = param.name;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
this.arguments[param.name] = param.defaultValue ?? null;
|
|
129
|
+
this.argumentsSignature[param.name] = param;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
parseDefaultOptions() {
|
|
134
|
+
if (this.defaultCommandOptions.length) {
|
|
135
|
+
for (const option of this.defaultCommandOptions) {
|
|
136
|
+
this.optionSignatures[option.option] = {
|
|
137
|
+
name: option.option,
|
|
138
|
+
type: option.defaultValue == null ? 'string' : typeof option.defaultValue,
|
|
139
|
+
optional: true,
|
|
140
|
+
alias: option.alias,
|
|
141
|
+
variadic: false,
|
|
142
|
+
help: option.description,
|
|
143
|
+
defaultValue: option.defaultValue ?? null,
|
|
144
|
+
isOption: true
|
|
145
|
+
};
|
|
146
|
+
this.options[option.option] = option.defaultValue;
|
|
147
|
+
if (option.alias) {
|
|
148
|
+
for (const alias of option.alias) {
|
|
149
|
+
this.optionAliases[alias] = option.option;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
parseParamSignature(argument) {
|
|
156
|
+
const arg = {
|
|
157
|
+
name: argument,
|
|
158
|
+
optional: false,
|
|
159
|
+
type: 'string',
|
|
160
|
+
help: undefined,
|
|
161
|
+
defaultValue: null,
|
|
162
|
+
variadic: false,
|
|
163
|
+
isOption: false
|
|
164
|
+
};
|
|
165
|
+
if (arg.name.includes(':')) {
|
|
166
|
+
const [name, help] = arg.name.split(':');
|
|
167
|
+
arg.name = name.trim();
|
|
168
|
+
arg.help = help.trim();
|
|
169
|
+
}
|
|
170
|
+
if (arg.name.includes('=')) {
|
|
171
|
+
const [name, defaultValue] = arg.name.split('=');
|
|
172
|
+
arg.name = name.trim();
|
|
173
|
+
arg.defaultValue = defaultValue.trim();
|
|
174
|
+
arg.optional = true;
|
|
175
|
+
if (!arg.defaultValue.length) {
|
|
176
|
+
arg.defaultValue = null;
|
|
177
|
+
}
|
|
178
|
+
else if (arg.defaultValue === 'true') {
|
|
179
|
+
arg.defaultValue = true;
|
|
180
|
+
arg.type = 'boolean';
|
|
181
|
+
}
|
|
182
|
+
else if (arg.defaultValue === 'false') {
|
|
183
|
+
arg.defaultValue = false;
|
|
184
|
+
arg.type = 'boolean';
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
if (arg.name.startsWith('--')) {
|
|
189
|
+
arg.optional = true;
|
|
190
|
+
arg.defaultValue = false;
|
|
191
|
+
arg.type = 'boolean';
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
if (arg.name.includes('|')) {
|
|
195
|
+
const [name, ...alias] = arg.name.split('|');
|
|
196
|
+
arg.name = name.trim();
|
|
197
|
+
arg.alias = alias.map(a => a.trim());
|
|
198
|
+
}
|
|
199
|
+
if (arg.name.startsWith('--')) {
|
|
200
|
+
arg.isOption = true;
|
|
201
|
+
arg.name = arg.name.slice(2);
|
|
202
|
+
}
|
|
203
|
+
if (arg.defaultValue === '*') {
|
|
204
|
+
arg.defaultValue = [];
|
|
205
|
+
arg.type = 'array';
|
|
206
|
+
}
|
|
207
|
+
if (arg.name.endsWith('?')) {
|
|
208
|
+
arg.optional = true;
|
|
209
|
+
arg.name = arg.name.slice(0, -1);
|
|
210
|
+
}
|
|
211
|
+
if (arg.name.endsWith('*')) {
|
|
212
|
+
arg.type = 'array';
|
|
213
|
+
arg.variadic = true;
|
|
214
|
+
arg.defaultValue = [];
|
|
215
|
+
arg.name = arg.name.slice(0, -1);
|
|
216
|
+
}
|
|
217
|
+
arg.help = arg.help ?? this.helperDefinitions[arg.name] ?? this.helperDefinitions[`--${arg.name}`];
|
|
218
|
+
return arg;
|
|
219
|
+
}
|
|
220
|
+
validate() {
|
|
221
|
+
// validate arguments
|
|
222
|
+
for (const [argument, value] of Object.entries(this.arguments)) {
|
|
223
|
+
const argSignature = this.argumentsSignature[argument];
|
|
224
|
+
if (!value && !argSignature.optional) {
|
|
225
|
+
throw new MissingRequiredArgumentValue(argSignature);
|
|
226
|
+
}
|
|
227
|
+
if (!value?.length && argSignature.variadic && !argSignature.optional) {
|
|
228
|
+
throw new MissingRequiredArgumentValue(argSignature);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|