@drizzle-team/brocli
Advanced tools
Comparing version 0.2.4 to 0.3.0
@@ -36,3 +36,3 @@ /** | ||
private config; | ||
constructor(); | ||
constructor(config?: TBuilderConfig); | ||
string<TName extends string>(name: TName): Omit<OptionBuilderBase<BuilderConfig, string | undefined, TOmit | OptionType | 'min' | 'max' | 'int'>, TOmit | OptionType | 'min' | 'max' | 'int'>; | ||
@@ -44,2 +44,3 @@ string(): Omit<OptionBuilderBase<BuilderConfig, string | undefined, TOmit | OptionType | 'min' | 'max' | 'int'>, TOmit | OptionType | 'min' | 'max' | 'int'>; | ||
boolean(): Omit<OptionBuilderBase<BuilderConfig, boolean | undefined, TOmit | OptionType | 'min' | 'max' | 'enum' | 'int'>, TOmit | OptionType | 'min' | 'max' | 'enum' | 'int'>; | ||
positional<TName extends string>(displayName: TName): Omit<OptionBuilderBase<BuilderConfig, string | undefined, TOmit | OptionType | 'min' | 'max' | 'int' | 'alias'>, TOmit | OptionType | 'min' | 'max' | 'int' | 'alias'>; | ||
positional(): Omit<OptionBuilderBase<BuilderConfig, string | undefined, TOmit | OptionType | 'min' | 'max' | 'int' | 'alias'>, TOmit | OptionType | 'min' | 'max' | 'int' | 'alias'>; | ||
@@ -95,13 +96,16 @@ alias(...aliases: string[]): Omit<OptionBuilderBase<BuilderConfig, TOutput, TOmit | 'alias'>, TOmit | 'alias'>; | ||
declare function boolean(): Omit<OptionBuilderBase<BuilderConfig, boolean | undefined, OptionType | 'min' | 'max' | 'int' | 'enum'>, OptionType | 'min' | 'max' | 'int' | 'enum'>; | ||
declare function positional<TName extends string>(displayName: TName): Omit<OptionBuilderBase<BuilderConfig, string | undefined, OptionType | 'min' | 'max' | 'int' | 'alias'>, OptionType | 'min' | 'max' | 'int' | 'alias'>; | ||
declare function positional(): Omit<OptionBuilderBase<BuilderConfig, string | undefined, OptionType | 'min' | 'max' | 'int' | 'alias'>, OptionType | 'min' | 'max' | 'int' | 'alias'>; | ||
type HelpHandler = (calledFor: Command | Command[]) => any; | ||
type CommandHandler<TOpts extends Record<string, GenericBuilderInternals> | undefined = Record<string, GenericBuilderInternals> | undefined> = (options: TOpts extends Record<string, GenericBuilderInternals> ? TypeOf<TOpts> : undefined) => any; | ||
type BroCliConfig = { | ||
argSource?: string[]; | ||
help?: string | Function; | ||
help?: HelpHandler; | ||
version?: string | Function; | ||
omitKeysOfUndefinedOptions?: boolean; | ||
}; | ||
type GenericCommandHandler = (options?: Record<string, OutputType> | undefined) => any; | ||
type RawCommand<TOpts extends Record<string, GenericBuilderInternals> | undefined = Record<string, GenericBuilderInternals> | undefined> = { | ||
name: string; | ||
name?: string; | ||
aliases?: [string, ...string[]]; | ||
@@ -112,3 +116,3 @@ description?: string; | ||
help?: string | Function; | ||
handler: CommandHandler<TOpts>; | ||
handler?: CommandHandler<TOpts>; | ||
}; | ||
@@ -135,2 +139,2 @@ type Command = { | ||
export { type AssignConfigName, type BroCliConfig, BroCliError as BrocliError, type BuilderConfig, type Command, type CommandHandler, type GenericBuilderInternals, type GenericBuilderInternalsFields, type GenericCommandHandler, type GenericProcessedOptions, OptionBuilderBase, type OptionType, type OutputType, type ProcessedOptions, type RawCommand, type Simplify, type TypeOf, boolean, command, handler, number, positional, runCli, string }; | ||
export { type AssignConfigName, type BroCliConfig, BroCliError, type BuilderConfig, type Command, type CommandHandler, type GenericBuilderInternals, type GenericBuilderInternalsFields, type GenericCommandHandler, type GenericProcessedOptions, type HelpHandler, OptionBuilderBase, type OptionType, type OutputType, type ProcessedOptions, type RawCommand, type Simplify, type TypeOf, boolean, command, handler, number, positional, runCli, string }; |
168
index.js
@@ -12,9 +12,42 @@ // src/brocli-error.ts | ||
// src/help-themes.ts | ||
var defaultTheme = (calledFor) => { | ||
if (Array.isArray(calledFor)) { | ||
const cmds = calledFor.filter((cmd) => !cmd.hidden); | ||
const tableCmds = cmds.map((cmd) => ({ | ||
name: cmd.name, | ||
aliases: cmd.aliases ? cmd.aliases.join(", ") : "-", | ||
description: cmd.description ?? "-" | ||
})); | ||
console.log(`Here's the list of all available commands:`); | ||
console.table(tableCmds); | ||
console.log( | ||
"To read the details about any particular command type: help [commandName] | help --command=<commandName> | help -c <comandName>" | ||
); | ||
} else { | ||
const options = calledFor.options ? Object.values(calledFor.options).filter((opt) => !opt.config?.isHidden).map( | ||
({ config: opt }) => ({ | ||
name: opt.name, | ||
aliases: opt.aliases.length ? `${opt.aliases.join(", ")}` : "-", | ||
description: opt.description ?? "-", | ||
type: opt.type, | ||
required: opt.isRequired ? "\u2713" : "\u2717" | ||
}) | ||
) : void 0; | ||
console.log( | ||
`Command: ${calledFor.name}${calledFor.aliases ? ` [${calledFor.aliases.join(", ")}]` : ""}${calledFor.description ? ` - ${calledFor.description}` : ""}` | ||
); | ||
if (!options?.length) return; | ||
console.log("\nOptions:"); | ||
console.table(options); | ||
} | ||
}; | ||
// src/option-builder.ts | ||
var OptionBuilderBase = class { | ||
var OptionBuilderBase = class _OptionBuilderBase { | ||
_; | ||
config = () => this._.config; | ||
constructor() { | ||
constructor(config) { | ||
this._ = { | ||
config: { | ||
config: config ?? { | ||
aliases: [], | ||
@@ -27,38 +60,36 @@ type: "string" | ||
string(name) { | ||
this.config().type = "string"; | ||
this.config().name = name; | ||
return this; | ||
const config = this.config(); | ||
return new _OptionBuilderBase({ ...config, type: "string", name }); | ||
} | ||
number(name) { | ||
this.config().type = "number"; | ||
this.config().name = name; | ||
return this; | ||
const config = this.config(); | ||
return new _OptionBuilderBase({ ...config, type: "number", name }); | ||
} | ||
boolean(name) { | ||
this.config().type = "boolean"; | ||
this.config().name = name; | ||
return this; | ||
const config = this.config(); | ||
return new _OptionBuilderBase({ ...config, type: "boolean", name }); | ||
} | ||
positional() { | ||
this.config().type = "positional"; | ||
return this; | ||
positional(displayName) { | ||
const config = this.config(); | ||
return new _OptionBuilderBase({ ...config, type: "positional", name: displayName }); | ||
} | ||
alias(...aliases) { | ||
this.config().aliases = aliases; | ||
return this; | ||
const config = this.config(); | ||
return new _OptionBuilderBase({ ...config, aliases }); | ||
} | ||
desc(description) { | ||
this.config().description = description; | ||
return this; | ||
const config = this.config(); | ||
return new _OptionBuilderBase({ ...config, description }); | ||
} | ||
hidden() { | ||
this.config().isHidden = true; | ||
return this; | ||
const config = this.config(); | ||
return new _OptionBuilderBase({ ...config, isHidden: true }); | ||
} | ||
required() { | ||
this.config().isRequired = true; | ||
return this; | ||
const config = this.config(); | ||
return new _OptionBuilderBase({ ...config, isRequired: true }); | ||
} | ||
default(value) { | ||
const enums = this.config().enumVals; | ||
const config = this.config(); | ||
const enums = config.enumVals; | ||
if (enums && !enums.find((v) => value === v)) { | ||
@@ -69,7 +100,7 @@ throw new Error( | ||
} | ||
this.config().default = value; | ||
return this; | ||
return new _OptionBuilderBase({ ...config, default: value }); | ||
} | ||
enum(...values) { | ||
const defaultVal = this.config().default; | ||
const config = this.config(); | ||
const defaultVal = config.default; | ||
if (defaultVal !== void 0 && !values.find((v) => defaultVal === v)) { | ||
@@ -80,24 +111,23 @@ throw new Error( | ||
} | ||
this.config().enumVals = values; | ||
return this; | ||
return new _OptionBuilderBase({ ...config, enumVals: values }); | ||
} | ||
min(value) { | ||
const maxVal = this.config().maxVal; | ||
const config = this.config(); | ||
const maxVal = config.maxVal; | ||
if (maxVal !== void 0 && maxVal < value) { | ||
throw new BroCliError("Unable to define option's min value to be higher than max value!"); | ||
} | ||
this.config().minVal = value; | ||
return this; | ||
return new _OptionBuilderBase({ ...config, minVal: value }); | ||
} | ||
max(value) { | ||
const minVal = this.config().minVal; | ||
const config = this.config(); | ||
const minVal = config.minVal; | ||
if (minVal !== void 0 && minVal < value) { | ||
throw new BroCliError("Unable to define option's max value to be lower than min value!"); | ||
} | ||
this.config().maxVal = value; | ||
return this; | ||
return new _OptionBuilderBase({ ...config, maxVal: value }); | ||
} | ||
int() { | ||
this.config().isInt = true; | ||
return this; | ||
const config = this.config(); | ||
return new _OptionBuilderBase({ ...config, isInt: true }); | ||
} | ||
@@ -114,4 +144,4 @@ }; | ||
} | ||
function positional() { | ||
return new OptionBuilderBase().positional(); | ||
function positional(displayName) { | ||
return typeof displayName === "number" ? new OptionBuilderBase().positional(displayName) : new OptionBuilderBase().positional(); | ||
} | ||
@@ -125,13 +155,2 @@ | ||
// src/command-core.ts | ||
var help = (commands) => () => { | ||
const cmds = commands.filter((cmd) => !cmd.hidden); | ||
const tableCmds = cmds.map((cmd) => ({ | ||
name: cmd.name, | ||
aliases: cmd.aliases ? cmd.aliases.join(", ") : "-", | ||
description: cmd.description ?? "-" | ||
})); | ||
console.log(`Here's the list of all available commands:`); | ||
console.table(tableCmds); | ||
console.log("To read the details about any particular command type: help --command=<command-name>"); | ||
}; | ||
var unknownCommand = () => { | ||
@@ -210,2 +229,3 @@ const msg = `Unable to recognize any of the commands. | ||
if (cfg.name === void 0) cfg.name = key; | ||
if (cfg.type === "positional") continue; | ||
if (cfg.name.includes("=")) { | ||
@@ -223,3 +243,3 @@ throw new BroCliError( | ||
} | ||
cfg.name = cfg.type === "positional" ? cfg.name : generatePrefix(cfg.name); | ||
cfg.name = generatePrefix(cfg.name); | ||
cfg.aliases = cfg.aliases.map((a) => generatePrefix(a)); | ||
@@ -229,2 +249,3 @@ } | ||
const cfg = value._.config; | ||
if (cfg.type === "positional") continue; | ||
const reservedNames = ["--help", "-h", "--version", "-v"]; | ||
@@ -269,2 +290,6 @@ const allNames = [cfg.name, ...cfg.aliases]; | ||
cmd.options = processedOptions; | ||
cmd.name = cmd.name ?? cmd.aliases?.shift(); | ||
if (!cmd.name) throw new BroCliError(`Can't define command without name!`); | ||
cmd.aliases = cmd.aliases?.length ? cmd.aliases : void 0; | ||
if (!cmd.handler) throw new BroCliError(`Can't define command '${cmd.name}' - command must have a handler!`); | ||
if (cmd.name.startsWith("-")) { | ||
@@ -375,3 +400,3 @@ throw new BroCliError(`Can't define command '${cmd.name}' - command name can't start with '-'!`); | ||
}; | ||
var parseOptions = (command2, args) => { | ||
var parseOptions = (command2, args, omitKeysOfUndefinedOptions) => { | ||
const options = command2.options; | ||
@@ -398,3 +423,8 @@ const optEntries = Object.entries(options ?? {}); | ||
for (const [optKey, { config: option }] of optEntries) { | ||
result[optKey] = result[optKey] ?? option.default; | ||
const data = result[optKey] ?? option.default; | ||
if (!omitKeysOfUndefinedOptions) { | ||
result[optKey] = data; | ||
} else { | ||
if (data !== void 0) result[optKey] = data; | ||
} | ||
if (option.isRequired && result[optKey] === void 0) missingRequiredArr.push([option.name, ...option.aliases]); | ||
@@ -414,15 +444,16 @@ } | ||
options: { | ||
command: string().alias("c", "cmd").desc("List command details") | ||
command: string().alias("c", "cmd").desc("Target command"), | ||
pos: positional().desc("Target command") | ||
}, | ||
hidden: true, | ||
handler: async (options) => { | ||
const { command: command2 } = options; | ||
if (command2 === void 0) { | ||
return typeof helpHandler === "string" ? console.log(helpHandler) : helpHandler(commands); | ||
const { command: command2, pos } = options; | ||
if (command2 === void 0 && pos === void 0) { | ||
return await helpHandler(commands); | ||
} | ||
const cmd = commands.find((e) => e.name === command2); | ||
const cmd = commands.find((e) => e.name === pos || e.aliases?.find((a) => a === pos)) ?? commands.find((e) => e.name === command2 || e.aliases?.find((a) => a === command2)); | ||
if (cmd) { | ||
return executeOrLog(cmd.help); | ||
return cmd.help ? await executeOrLog(cmd.help) : await helpHandler(cmd); | ||
} | ||
return typeof helpHandler === "string" ? console.log(helpHandler) : helpHandler(commands); | ||
return await helpHandler(commands); | ||
} | ||
@@ -460,9 +491,10 @@ }); | ||
let cmd; | ||
const rawCmds = validateCommands(commands); | ||
const processedCmds = validateCommands(commands); | ||
const argSource = config?.argSource ?? process.argv; | ||
const version = config?.version; | ||
const helpHandler = config?.help ?? help(rawCmds); | ||
const cmds = [...rawCmds, helpCommand(rawCmds, helpHandler)]; | ||
const helpHandler = config?.help ?? defaultTheme; | ||
const omitKeysOfUndefinedOptions = config?.omitKeysOfUndefinedOptions ?? false; | ||
const cmds = [...processedCmds, helpCommand(processedCmds, helpHandler)]; | ||
let args = argSource.slice(2, argSource.length); | ||
if (!args.length) return await executeOrLog(helpHandler); | ||
if (!args.length) return await helpHandler(processedCmds); | ||
const helpIndex = args.findIndex((arg) => arg === "--help" || arg === "-h"); | ||
@@ -481,7 +513,7 @@ if (helpIndex !== -1 && (helpIndex > 0 ? args[helpIndex - 1]?.startsWith("-") ? false : true : true)) { | ||
} | ||
return command3 ? await executeOrLog(command3.help) : await executeOrLog(helpHandler); | ||
return command3 ? command3.help ? await executeOrLog(command3.help) : await helpHandler(command3) : await helpHandler(processedCmds); | ||
} | ||
const versionIndex = args.findIndex((arg) => arg === "--version" || arg === "-v"); | ||
if (versionIndex !== -1 && (versionIndex > 0 ? args[versionIndex - 1]?.startsWith("-") ? false : true : true)) { | ||
return executeOrLog(version); | ||
return await executeOrLog(version); | ||
} | ||
@@ -491,3 +523,3 @@ const { command: command2, index } = getCommand(cmds, args); | ||
args = [...args.slice(0, index), ...args.slice(index + 1, args.length)]; | ||
options = parseOptions(command2, args); | ||
options = parseOptions(command2, args, omitKeysOfUndefinedOptions); | ||
cmd = command2; | ||
@@ -508,3 +540,3 @@ await cmd.handler(options); | ||
export { | ||
BroCliError as BrocliError, | ||
BroCliError, | ||
boolean, | ||
@@ -511,0 +543,0 @@ command, |
@@ -5,3 +5,3 @@ { | ||
"author": "Drizzle Team", | ||
"version": "0.2.4", | ||
"version": "0.3.0", | ||
"description": "Typed CLI command runner", | ||
@@ -8,0 +8,0 @@ "license": "Apache-2.0", |
@@ -41,3 +41,3 @@ # BroCLI | ||
:speech_balloon: - will be automatically prefixed with `-` if one character long, `--` if longer | ||
If you wish to have only single hyphen as a prefix on multi character name - simply specify name with it: `string('-longname')` | ||
If you wish to have only single hyphen as a prefix on multi character name - simply specify name with it: `number('-longname')` | ||
@@ -51,4 +51,6 @@ - `boolean(name?: string)` - defines option as a boolean-type option which requires data to be passed as `--option` | ||
- `positional()` - defines option as a positional-type option which requires data to be passed after a command as `command value` | ||
:warning: - does not consume options and data that starts with `-` | ||
- `positional(displayName?: string)` - defines option as a positional-type option which requires data to be passed after a command as `command value` | ||
- `displayName` - name by which option is passed in cli args | ||
If not specified, defaults to key of this option | ||
:warning: - does not consume options and data that starts with | ||
@@ -55,0 +57,0 @@ Extensions: |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
170553
1229
215