mirror of
https://github.com/chenasraf/massarg.git
synced 2026-05-18 01:39:05 +00:00
fix: exported types fixes
This commit is contained in:
2
.github/FUNDING.yml
vendored
2
.github/FUNDING.yml
vendored
@@ -1,6 +1,6 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
github: chenasraf
|
||||
patreon: # Replace with a single Patreon username
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: casraf
|
||||
|
||||
@@ -30,6 +30,7 @@ module.exports = {
|
||||
'@semantic-release/changelog',
|
||||
{
|
||||
changelogFile: 'CHANGELOG.md',
|
||||
changelogTitle: '# Change Log',
|
||||
},
|
||||
],
|
||||
[
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
import { DeepRequired, setOrPush, deepMerge } from './utils'
|
||||
import { MassargExample, ExampleConfig } from './example'
|
||||
|
||||
export const CommandConfig = <RunArgs extends z.ZodType>(args: RunArgs) =>
|
||||
export const CommandConfig = <RunArgs extends ArgsObject = ArgsObject>(args: z.ZodType<RunArgs>) =>
|
||||
z.object({
|
||||
/** Command name */
|
||||
name: z.string(),
|
||||
@@ -26,16 +26,18 @@ export const CommandConfig = <RunArgs extends z.ZodType>(args: RunArgs) =>
|
||||
run: z
|
||||
.function()
|
||||
.args(args, z.any())
|
||||
.returns(z.union([z.promise(z.void()), z.void()])) as z.ZodType<Runner<z.infer<RunArgs>>>,
|
||||
.returns(z.union([z.promise(z.void()), z.void()])) as z.ZodType<Runner<RunArgs>>,
|
||||
})
|
||||
|
||||
export type CommandConfig<T = unknown> = z.infer<ReturnType<typeof CommandConfig<z.ZodType<T>>>>
|
||||
export type CommandConfig<RunArgs extends ArgsObject = ArgsObject> = z.infer<
|
||||
ReturnType<typeof CommandConfig<RunArgs>>
|
||||
>
|
||||
|
||||
export type ArgsObject = Record<string, unknown>
|
||||
export type ArgsObject = object
|
||||
|
||||
export type Runner<Args extends ArgsObject> = <A extends ArgsObject = Args>(
|
||||
options: A,
|
||||
instance: MassargCommand<A>,
|
||||
export type Runner<Args extends ArgsObject> = (
|
||||
options: Args,
|
||||
instance: MassargCommand<Args>,
|
||||
) => Promise<void> | void
|
||||
|
||||
/**
|
||||
@@ -178,12 +180,18 @@ export class MassargCommand<Args extends ArgsObject = ArgsObject> {
|
||||
* value passed to the command. This is useful if you want to parse a string
|
||||
* into a more complex type, or if you want to validate the value.
|
||||
*/
|
||||
option<T = string>(config: MassargOption<T>): MassargCommand<Args>
|
||||
option<T = string>(config: TypedOptionConfig<T>): MassargCommand<Args>
|
||||
option<T = string>(config: TypedOptionConfig<T> | MassargOption<T>): MassargCommand<Args> {
|
||||
option<T = string, A extends ArgsObject = Args>(config: MassargOption<T, A>): MassargCommand<Args>
|
||||
option<T = string, A extends ArgsObject = Args>(
|
||||
config: TypedOptionConfig<T, A>,
|
||||
): MassargCommand<Args>
|
||||
option<T = string, A extends ArgsObject = Args>(
|
||||
config: TypedOptionConfig<T, A> | MassargOption<T, A>,
|
||||
): MassargCommand<Args> {
|
||||
try {
|
||||
const option =
|
||||
config instanceof MassargOption ? config : MassargOption.fromTypedConfig(config)
|
||||
config instanceof MassargOption
|
||||
? config
|
||||
: MassargOption.fromTypedConfig(config as TypedOptionConfig<T, A>)
|
||||
const existing = this.options.find((c) => c.name === option.name)
|
||||
if (existing) {
|
||||
throw new ValidationError({
|
||||
@@ -256,7 +264,7 @@ export class MassargCommand<Args extends ArgsObject = ArgsObject> {
|
||||
*
|
||||
* If none is provided, help will be printed.
|
||||
*/
|
||||
main<A extends ArgsObject = Args>(run: Runner<A>): MassargCommand<Args> {
|
||||
main(run: Runner<Args>): MassargCommand<Args> {
|
||||
this._run = run
|
||||
return this
|
||||
}
|
||||
@@ -281,7 +289,7 @@ export class MassargCommand<Args extends ArgsObject = ArgsObject> {
|
||||
message: 'Unknown option',
|
||||
})
|
||||
}
|
||||
const res = option._parseDetails([arg, ...argv])
|
||||
const res = option._parseDetails([arg, ...argv], { ...this.args })
|
||||
this.args[res.key as keyof Args] = setOrPush<Args[keyof Args]>(
|
||||
res.value,
|
||||
this.args[res.key as keyof Args],
|
||||
@@ -361,7 +369,7 @@ export class MassargCommand<Args extends ArgsObject = ArgsObject> {
|
||||
|
||||
// no sub command found, run main command
|
||||
if (this._run) {
|
||||
this._run(this.args, parent ?? this)
|
||||
this._run(this.args as Args, parent ?? this)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -380,13 +388,15 @@ export class MassargCommand<Args extends ArgsObject = ArgsObject> {
|
||||
}
|
||||
}
|
||||
|
||||
export class MassargHelpCommand<T extends ArgsObject = ArgsObject> extends MassargCommand<T> {
|
||||
export class MassargHelpCommand<
|
||||
T extends { command?: string } = { command?: string },
|
||||
> extends MassargCommand<T> {
|
||||
constructor(config: Partial<Omit<CommandConfig<T>, 'run'>> = {}) {
|
||||
super({
|
||||
name: 'help',
|
||||
aliases: ['h'],
|
||||
description: 'Print help for this command, or a subcommand if specified',
|
||||
run: (args, parent) => {
|
||||
run: (args: { command?: string }, parent) => {
|
||||
if (args.command) {
|
||||
const command = parent.commands.find((c) => c.name === args.command)
|
||||
if (command) {
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import { z } from 'zod'
|
||||
import { isZodError, ParseError } from './error'
|
||||
import { toCamelCase } from './utils'
|
||||
import { ArgsObject } from './command'
|
||||
|
||||
export const OptionConfig = <T extends z.ZodType>(type: T) =>
|
||||
export const OptionConfig = <OptionType, Args extends ArgsObject = ArgsObject>(
|
||||
type: z.ZodType<OptionType>,
|
||||
) =>
|
||||
z.object({
|
||||
/** Name of the option */
|
||||
name: z.string(),
|
||||
@@ -16,7 +19,9 @@ export const OptionConfig = <T extends z.ZodType>(type: T) =>
|
||||
* Parse the value of the option. You can return any type here, or throw an error if the value
|
||||
* is invalid.
|
||||
*/
|
||||
parse: z.function().args(z.string()).returns(type).optional(),
|
||||
parse: z.function().args(z.string(), z.any()).returns(type).optional() as z.ZodOptional<
|
||||
z.ZodType<Parser<Args, OptionType>>
|
||||
>,
|
||||
/**
|
||||
* Whether the option is an array.
|
||||
*
|
||||
@@ -41,24 +46,33 @@ export const OptionConfig = <T extends z.ZodType>(type: T) =>
|
||||
/** Specify a custom name for the output, which will be used when parsing the args. */
|
||||
outputName: z.string().optional(),
|
||||
})
|
||||
export type OptionConfig<T = unknown> = z.infer<ReturnType<typeof OptionConfig<z.ZodType<T>>>>
|
||||
export type OptionConfig<T = unknown, Args extends ArgsObject = ArgsObject> = z.infer<
|
||||
ReturnType<typeof OptionConfig<T, Args>>
|
||||
>
|
||||
|
||||
export const TypedOptionConfig = <T extends z.ZodType>(type: T) =>
|
||||
OptionConfig(type).merge(
|
||||
export type Parser<Args extends ArgsObject = ArgsObject, OptionType extends any = any> = (
|
||||
x: string,
|
||||
y: Args,
|
||||
) => OptionType
|
||||
|
||||
export const TypedOptionConfig = <OptionType, Args extends ArgsObject = ArgsObject>(
|
||||
type: z.ZodType<OptionType>,
|
||||
) =>
|
||||
OptionConfig<OptionType, Args>(type).merge(
|
||||
z.object({
|
||||
type: z.enum(['number']).optional(),
|
||||
}),
|
||||
)
|
||||
export type TypedOptionConfig<T = unknown> = z.infer<
|
||||
ReturnType<typeof TypedOptionConfig<z.ZodType<T>>>
|
||||
export type TypedOptionConfig<T, A extends ArgsObject = ArgsObject> = z.infer<
|
||||
ReturnType<typeof TypedOptionConfig<T, A>>
|
||||
>
|
||||
|
||||
/**
|
||||
* @see OptionConfig
|
||||
* @see ArrayOptionConfig
|
||||
*/
|
||||
export const ArrayOptionConfig = <T extends z.ZodType>(type: T) =>
|
||||
TypedOptionConfig(z.array(type)).merge(
|
||||
export const ArrayOptionConfig = <T, A extends ArgsObject = ArgsObject>(type: z.ZodType<T>) =>
|
||||
TypedOptionConfig<T[], A>(z.array(type)).merge(
|
||||
// OptionConfig(z.array(type)).merge(
|
||||
z.object({
|
||||
defaultValue: z.array(type).optional(),
|
||||
@@ -105,29 +119,31 @@ export type ArgvValue<T> = { argv: string[]; value: T; key: string }
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
export class MassargOption<T = unknown> {
|
||||
export class MassargOption<OptionType extends any = unknown, Args extends ArgsObject = ArgsObject> {
|
||||
name: string
|
||||
description: string
|
||||
defaultValue?: T
|
||||
defaultValue?: OptionType
|
||||
aliases: string[]
|
||||
parse: (value: string) => T
|
||||
parse: Parser<Args, OptionType>
|
||||
isArray: boolean
|
||||
isDefault: boolean
|
||||
outputName?: string
|
||||
|
||||
constructor(options: OptionConfig<T>) {
|
||||
constructor(options: OptionConfig<OptionType, Args>) {
|
||||
OptionConfig(z.any()).parse(options)
|
||||
this.name = options.name
|
||||
this.description = options.description
|
||||
this.defaultValue = options.defaultValue
|
||||
this.aliases = options.aliases
|
||||
this.parse = options.parse ?? ((x) => x as unknown as T)
|
||||
this.parse = options.parse ?? ((x: string) => x as OptionType)
|
||||
this.isArray = options.array ?? false
|
||||
this.isDefault = options.isDefault ?? false
|
||||
this.outputName = options.outputName
|
||||
}
|
||||
|
||||
static fromTypedConfig<T = unknown>(config: TypedOptionConfig<T>): MassargOption<T> {
|
||||
static fromTypedConfig<T = unknown, A extends ArgsObject = ArgsObject>(
|
||||
config: TypedOptionConfig<T, A>,
|
||||
): MassargOption<T> {
|
||||
switch (config.type) {
|
||||
case 'number':
|
||||
return new MassargNumber(config as OptionConfig<number>) as MassargOption<T>
|
||||
@@ -139,7 +155,7 @@ export class MassargOption<T = unknown> {
|
||||
return this.outputName || toCamelCase(this.name)
|
||||
}
|
||||
|
||||
_parseDetails(argv: string[]): ArgvValue<T> {
|
||||
_parseDetails(argv: string[], options: ArgsObject): ArgvValue<OptionType> {
|
||||
// TODO: support --option=value
|
||||
let input = ''
|
||||
try {
|
||||
@@ -153,7 +169,7 @@ export class MassargOption<T = unknown> {
|
||||
}
|
||||
argv.shift()
|
||||
input = argv.shift()!
|
||||
const value = this.parse(input)
|
||||
const value = this.parse(input, options as Args)
|
||||
return { key: this.getOutputName(), value, argv }
|
||||
} catch (e) {
|
||||
if (isZodError(e)) {
|
||||
@@ -243,13 +259,13 @@ export class MassargNumber extends MassargOption<number> {
|
||||
constructor(options: Omit<OptionConfig<number>, 'parse'>) {
|
||||
super({
|
||||
...options,
|
||||
parse: (value) => Number(value),
|
||||
parse: (value) => Number(value) as any,
|
||||
})
|
||||
}
|
||||
|
||||
_parseDetails(argv: string[]): ArgvValue<number> {
|
||||
_parseDetails(argv: string[], options: ArgsObject): ArgvValue<number> {
|
||||
try {
|
||||
const { argv: _argv, value } = super._parseDetails(argv)
|
||||
const { argv: _argv, value } = super._parseDetails(argv, options)
|
||||
if (isNaN(value)) {
|
||||
throw new ParseError({
|
||||
path: [this.name],
|
||||
@@ -298,7 +314,7 @@ export class MassargFlag extends MassargOption<boolean> {
|
||||
constructor(options: Omit<OptionConfig<boolean>, 'parse'>) {
|
||||
super({
|
||||
...options,
|
||||
parse: () => true,
|
||||
parse: () => true as any,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user