Skip to content

Commit a0aa8ec

Browse files
committed
fix: exported types fixes
1 parent 47dbe32 commit a0aa8ec

4 files changed

Lines changed: 65 additions & 38 deletions

File tree

.github/FUNDING.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# These are supported funding model platforms
22

3-
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
3+
github: chenasraf
44
patreon: # Replace with a single Patreon username
55
open_collective: # Replace with a single Open Collective username
66
ko_fi: casraf

release.config.cjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ module.exports = {
3030
'@semantic-release/changelog',
3131
{
3232
changelogFile: 'CHANGELOG.md',
33+
changelogTitle: '# Change Log',
3334
},
3435
],
3536
[

src/command.ts

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {
1111
import { DeepRequired, setOrPush, deepMerge } from './utils'
1212
import { MassargExample, ExampleConfig } from './example'
1313

14-
export const CommandConfig = <RunArgs extends z.ZodType>(args: RunArgs) =>
14+
export const CommandConfig = <RunArgs extends ArgsObject = ArgsObject>(args: z.ZodType<RunArgs>) =>
1515
z.object({
1616
/** Command name */
1717
name: z.string(),
@@ -26,16 +26,18 @@ export const CommandConfig = <RunArgs extends z.ZodType>(args: RunArgs) =>
2626
run: z
2727
.function()
2828
.args(args, z.any())
29-
.returns(z.union([z.promise(z.void()), z.void()])) as z.ZodType<Runner<z.infer<RunArgs>>>,
29+
.returns(z.union([z.promise(z.void()), z.void()])) as z.ZodType<Runner<RunArgs>>,
3030
})
3131

32-
export type CommandConfig<T = unknown> = z.infer<ReturnType<typeof CommandConfig<z.ZodType<T>>>>
32+
export type CommandConfig<RunArgs extends ArgsObject = ArgsObject> = z.infer<
33+
ReturnType<typeof CommandConfig<RunArgs>>
34+
>
3335

34-
export type ArgsObject = Record<string, unknown>
36+
export type ArgsObject = object
3537

36-
export type Runner<Args extends ArgsObject> = <A extends ArgsObject = Args>(
37-
options: A,
38-
instance: MassargCommand<A>,
38+
export type Runner<Args extends ArgsObject> = (
39+
options: Args,
40+
instance: MassargCommand<Args>,
3941
) => Promise<void> | void
4042

4143
/**
@@ -178,12 +180,18 @@ export class MassargCommand<Args extends ArgsObject = ArgsObject> {
178180
* value passed to the command. This is useful if you want to parse a string
179181
* into a more complex type, or if you want to validate the value.
180182
*/
181-
option<T = string>(config: MassargOption<T>): MassargCommand<Args>
182-
option<T = string>(config: TypedOptionConfig<T>): MassargCommand<Args>
183-
option<T = string>(config: TypedOptionConfig<T> | MassargOption<T>): MassargCommand<Args> {
183+
option<T = string, A extends ArgsObject = Args>(config: MassargOption<T, A>): MassargCommand<Args>
184+
option<T = string, A extends ArgsObject = Args>(
185+
config: TypedOptionConfig<T, A>,
186+
): MassargCommand<Args>
187+
option<T = string, A extends ArgsObject = Args>(
188+
config: TypedOptionConfig<T, A> | MassargOption<T, A>,
189+
): MassargCommand<Args> {
184190
try {
185191
const option =
186-
config instanceof MassargOption ? config : MassargOption.fromTypedConfig(config)
192+
config instanceof MassargOption
193+
? config
194+
: MassargOption.fromTypedConfig(config as TypedOptionConfig<T, A>)
187195
const existing = this.options.find((c) => c.name === option.name)
188196
if (existing) {
189197
throw new ValidationError({
@@ -256,7 +264,7 @@ export class MassargCommand<Args extends ArgsObject = ArgsObject> {
256264
*
257265
* If none is provided, help will be printed.
258266
*/
259-
main<A extends ArgsObject = Args>(run: Runner<A>): MassargCommand<Args> {
267+
main(run: Runner<Args>): MassargCommand<Args> {
260268
this._run = run
261269
return this
262270
}
@@ -281,7 +289,7 @@ export class MassargCommand<Args extends ArgsObject = ArgsObject> {
281289
message: 'Unknown option',
282290
})
283291
}
284-
const res = option._parseDetails([arg, ...argv])
292+
const res = option._parseDetails([arg, ...argv], { ...this.args })
285293
this.args[res.key as keyof Args] = setOrPush<Args[keyof Args]>(
286294
res.value,
287295
this.args[res.key as keyof Args],
@@ -361,7 +369,7 @@ export class MassargCommand<Args extends ArgsObject = ArgsObject> {
361369

362370
// no sub command found, run main command
363371
if (this._run) {
364-
this._run(this.args, parent ?? this)
372+
this._run(this.args as Args, parent ?? this)
365373
}
366374
}
367375

@@ -380,13 +388,15 @@ export class MassargCommand<Args extends ArgsObject = ArgsObject> {
380388
}
381389
}
382390

383-
export class MassargHelpCommand<T extends ArgsObject = ArgsObject> extends MassargCommand<T> {
391+
export class MassargHelpCommand<
392+
T extends { command?: string } = { command?: string },
393+
> extends MassargCommand<T> {
384394
constructor(config: Partial<Omit<CommandConfig<T>, 'run'>> = {}) {
385395
super({
386396
name: 'help',
387397
aliases: ['h'],
388398
description: 'Print help for this command, or a subcommand if specified',
389-
run: (args, parent) => {
399+
run: (args: { command?: string }, parent) => {
390400
if (args.command) {
391401
const command = parent.commands.find((c) => c.name === args.command)
392402
if (command) {

src/option.ts

Lines changed: 37 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
import { z } from 'zod'
22
import { isZodError, ParseError } from './error'
33
import { toCamelCase } from './utils'
4+
import { ArgsObject } from './command'
45

5-
export const OptionConfig = <T extends z.ZodType>(type: T) =>
6+
export const OptionConfig = <OptionType, Args extends ArgsObject = ArgsObject>(
7+
type: z.ZodType<OptionType>,
8+
) =>
69
z.object({
710
/** Name of the option */
811
name: z.string(),
@@ -16,7 +19,9 @@ export const OptionConfig = <T extends z.ZodType>(type: T) =>
1619
* Parse the value of the option. You can return any type here, or throw an error if the value
1720
* is invalid.
1821
*/
19-
parse: z.function().args(z.string()).returns(type).optional(),
22+
parse: z.function().args(z.string(), z.any()).returns(type).optional() as z.ZodOptional<
23+
z.ZodType<Parser<Args, OptionType>>
24+
>,
2025
/**
2126
* Whether the option is an array.
2227
*
@@ -41,24 +46,33 @@ export const OptionConfig = <T extends z.ZodType>(type: T) =>
4146
/** Specify a custom name for the output, which will be used when parsing the args. */
4247
outputName: z.string().optional(),
4348
})
44-
export type OptionConfig<T = unknown> = z.infer<ReturnType<typeof OptionConfig<z.ZodType<T>>>>
49+
export type OptionConfig<T = unknown, Args extends ArgsObject = ArgsObject> = z.infer<
50+
ReturnType<typeof OptionConfig<T, Args>>
51+
>
52+
53+
export type Parser<Args extends ArgsObject = ArgsObject, OptionType extends any = any> = (
54+
x: string,
55+
y: Args,
56+
) => OptionType
4557

46-
export const TypedOptionConfig = <T extends z.ZodType>(type: T) =>
47-
OptionConfig(type).merge(
58+
export const TypedOptionConfig = <OptionType, Args extends ArgsObject = ArgsObject>(
59+
type: z.ZodType<OptionType>,
60+
) =>
61+
OptionConfig<OptionType, Args>(type).merge(
4862
z.object({
4963
type: z.enum(['number']).optional(),
5064
}),
5165
)
52-
export type TypedOptionConfig<T = unknown> = z.infer<
53-
ReturnType<typeof TypedOptionConfig<z.ZodType<T>>>
66+
export type TypedOptionConfig<T, A extends ArgsObject = ArgsObject> = z.infer<
67+
ReturnType<typeof TypedOptionConfig<T, A>>
5468
>
5569

5670
/**
5771
* @see OptionConfig
5872
* @see ArrayOptionConfig
5973
*/
60-
export const ArrayOptionConfig = <T extends z.ZodType>(type: T) =>
61-
TypedOptionConfig(z.array(type)).merge(
74+
export const ArrayOptionConfig = <T, A extends ArgsObject = ArgsObject>(type: z.ZodType<T>) =>
75+
TypedOptionConfig<T[], A>(z.array(type)).merge(
6276
// OptionConfig(z.array(type)).merge(
6377
z.object({
6478
defaultValue: z.array(type).optional(),
@@ -105,29 +119,31 @@ export type ArgvValue<T> = { argv: string[]; value: T; key: string }
105119
* })
106120
* ```
107121
*/
108-
export class MassargOption<T = unknown> {
122+
export class MassargOption<OptionType extends any = unknown, Args extends ArgsObject = ArgsObject> {
109123
name: string
110124
description: string
111-
defaultValue?: T
125+
defaultValue?: OptionType
112126
aliases: string[]
113-
parse: (value: string) => T
127+
parse: Parser<Args, OptionType>
114128
isArray: boolean
115129
isDefault: boolean
116130
outputName?: string
117131

118-
constructor(options: OptionConfig<T>) {
132+
constructor(options: OptionConfig<OptionType, Args>) {
119133
OptionConfig(z.any()).parse(options)
120134
this.name = options.name
121135
this.description = options.description
122136
this.defaultValue = options.defaultValue
123137
this.aliases = options.aliases
124-
this.parse = options.parse ?? ((x) => x as unknown as T)
138+
this.parse = options.parse ?? ((x: string) => x as OptionType)
125139
this.isArray = options.array ?? false
126140
this.isDefault = options.isDefault ?? false
127141
this.outputName = options.outputName
128142
}
129143

130-
static fromTypedConfig<T = unknown>(config: TypedOptionConfig<T>): MassargOption<T> {
144+
static fromTypedConfig<T = unknown, A extends ArgsObject = ArgsObject>(
145+
config: TypedOptionConfig<T, A>,
146+
): MassargOption<T> {
131147
switch (config.type) {
132148
case 'number':
133149
return new MassargNumber(config as OptionConfig<number>) as MassargOption<T>
@@ -139,7 +155,7 @@ export class MassargOption<T = unknown> {
139155
return this.outputName || toCamelCase(this.name)
140156
}
141157

142-
_parseDetails(argv: string[]): ArgvValue<T> {
158+
_parseDetails(argv: string[], options: ArgsObject): ArgvValue<OptionType> {
143159
// TODO: support --option=value
144160
let input = ''
145161
try {
@@ -153,7 +169,7 @@ export class MassargOption<T = unknown> {
153169
}
154170
argv.shift()
155171
input = argv.shift()!
156-
const value = this.parse(input)
172+
const value = this.parse(input, options as Args)
157173
return { key: this.getOutputName(), value, argv }
158174
} catch (e) {
159175
if (isZodError(e)) {
@@ -243,13 +259,13 @@ export class MassargNumber extends MassargOption<number> {
243259
constructor(options: Omit<OptionConfig<number>, 'parse'>) {
244260
super({
245261
...options,
246-
parse: (value) => Number(value),
262+
parse: (value) => Number(value) as any,
247263
})
248264
}
249265

250-
_parseDetails(argv: string[]): ArgvValue<number> {
266+
_parseDetails(argv: string[], options: ArgsObject): ArgvValue<number> {
251267
try {
252-
const { argv: _argv, value } = super._parseDetails(argv)
268+
const { argv: _argv, value } = super._parseDetails(argv, options)
253269
if (isNaN(value)) {
254270
throw new ParseError({
255271
path: [this.name],
@@ -298,7 +314,7 @@ export class MassargFlag extends MassargOption<boolean> {
298314
constructor(options: Omit<OptionConfig<boolean>, 'parse'>) {
299315
super({
300316
...options,
301-
parse: () => true,
317+
parse: () => true as any,
302318
})
303319
}
304320

0 commit comments

Comments
 (0)