Skip to content

Commit

Permalink
fix: custom flags with union type (#813)
Browse files Browse the repository at this point in the history
* fix: custom flags with union type

* test: use salesforcecli/cli main branch

* test: use salesforcecli/cli main branch

* fix: add folder to S3 type

* fix: add windows prop to PJSON type
  • Loading branch information
mdonnalley committed Oct 9, 2023
1 parent dbee18a commit a4afa23
Show file tree
Hide file tree
Showing 6 changed files with 36 additions and 13 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ jobs:
- run: New-Item -Path D:\a -Name "e2e" -ItemType "directory"
- run: New-Item -Path D:\a\e2e -Name "sf.e2e.ts" -ItemType "directory"
- run: |
git clone https://github.com/salesforcecli/cli.git --branch mdonnalley/esm
git clone https://github.com/salesforcecli/cli.git
cd cli
$Json = Get-Content package.json | ConvertFrom-Json
Expand Down
20 changes: 10 additions & 10 deletions src/flags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,39 +145,39 @@ export const help = (opts: Partial<BooleanFlag<boolean>> = {}): BooleanFlag<void
},
})

type ElementType<T extends ReadonlyArray<unknown>> = T[number]
type ReadonlyElementOf<T extends ReadonlyArray<unknown>> = T[number]

export function option<T extends readonly string[], P extends CustomOptions>(
defaults: Partial<OptionFlag<ElementType<T>[], P>> & {
defaults: Partial<OptionFlag<ReadonlyElementOf<T>[], P>> & {
multiple: true
options: T
} & (
| {
default: OptionFlag<ElementType<T>[], P>['default'] | undefined
default: OptionFlag<ReadonlyElementOf<T>[], P>['default'] | undefined
}
| {required: true}
),
): FlagDefinition<(typeof defaults.options)[number], P, {multiple: true; requiredOrDefaulted: true}>

export function option<T extends readonly string[], P extends CustomOptions>(
defaults: Partial<OptionFlag<ElementType<T>, P>> & {
defaults: Partial<OptionFlag<ReadonlyElementOf<T>, P>> & {
multiple?: false | undefined
options: T
} & ({default: OptionFlag<ElementType<T>, P>['default']} | {required: true}),
} & ({default: OptionFlag<ReadonlyElementOf<T>, P>['default']} | {required: true}),
): FlagDefinition<(typeof defaults.options)[number], P, {multiple: false; requiredOrDefaulted: true}>

export function option<T extends readonly string[], P extends CustomOptions>(
defaults: Partial<OptionFlag<ElementType<T>, P>> & {
default?: OptionFlag<ElementType<T>, P>['default'] | undefined
defaults: Partial<OptionFlag<ReadonlyElementOf<T>, P>> & {
default?: OptionFlag<ReadonlyElementOf<T>, P>['default'] | undefined
multiple?: false | undefined
options: T
required?: false | undefined
},
): FlagDefinition<(typeof defaults.options)[number], P, {multiple: false; requiredOrDefaulted: false}>

export function option<T extends readonly string[], P extends CustomOptions>(
defaults: Partial<OptionFlag<ElementType<T>[], P>> & {
default?: OptionFlag<ElementType<T>[], P>['default'] | undefined
defaults: Partial<OptionFlag<ReadonlyElementOf<T>[], P>> & {
default?: OptionFlag<ReadonlyElementOf<T>[], P>['default'] | undefined
multiple: true
options: T
required?: false | undefined
Expand All @@ -197,7 +197,7 @@ export function option<T extends readonly string[], P extends CustomOptions>(
* }
*/
export function option<T extends readonly string[], P extends CustomOptions>(
defaults: Partial<OptionFlag<ElementType<T>, P>> & {options: T},
defaults: Partial<OptionFlag<ReadonlyElementOf<T>, P>> & {options: T},
): FlagDefinition<(typeof defaults.options)[number], P, {multiple: boolean; requiredOrDefaulted: boolean}> {
return (options: any = {}) => ({
parse: async (input, _ctx, _opts) => input,
Expand Down
4 changes: 3 additions & 1 deletion src/interfaces/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -218,11 +218,13 @@ export type OptionFlagProps = FlagProps & {

export type FlagParserContext = Command & {token: FlagToken}

type NonNullableElementOf<T> = [NonNullable<T>] extends [Array<infer U>] ? U : T

export type FlagParser<T, I extends string | boolean, P = CustomOptions> = (
input: I,
context: FlagParserContext,
opts: P & OptionFlag<T, P>,
) => T extends Array<infer U> ? Promise<U | undefined> : Promise<T | undefined>
) => Promise<NonNullableElementOf<T> | undefined>

export type ArgParserContext = Command & {token: ArgToken}

Expand Down
6 changes: 6 additions & 0 deletions src/interfaces/pjson.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ export namespace PJSON {
identifier?: string
sign?: string
}
windows?: {
homepage?: string
keypath?: string
name?: string
}
plugins?: string[]
repositoryPrefix?: string
schema?: number
Expand Down Expand Up @@ -64,6 +69,7 @@ export namespace PJSON {
export interface S3 {
acl?: string
bucket?: string
folder?: string
gz?: boolean
host?: string
templates: {
Expand Down
1 change: 0 additions & 1 deletion test/integration/sf.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ describe('Salesforce CLI (sf)', () => {
process.env.SFDX_TELEMETRY_DISABLE_ACKNOWLEDGEMENT = 'true'
executor = await setup(__filename, {
repo: 'https://github.com/salesforcecli/cli',
branch: 'mdonnalley/esm',
})
})

Expand Down
16 changes: 16 additions & 0 deletions test/interfaces/flags.test-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,19 @@ class MyCommand extends BaseCommand {
// TODO: THIS IS A BUG. It should enforce a single value instead of allowing a single value or an array
default: ['foo'],
}),

'custom-union#defs:parse': Flags.custom<'a' | 'b' | 'c'>({
async parse(input, _ctx, _opts) {
return input as 'a' | 'b' | 'c'
},
})(),

'custom-union#defs:parse,multiple': Flags.custom<'a' | 'b' | 'c'>({
async parse(input, _ctx, _opts) {
return input as 'a' | 'b' | 'c'
},
multiple: true,
})(),
}

public flags!: MyFlags
Expand Down Expand Up @@ -510,6 +523,9 @@ class MyCommand extends BaseCommand {
// TODO: Known issue with `default` not enforcing the correct type whenever multiple is defaulted to true but then overridden to false
// expectType<string>(this.flags['custom#defs:multiple=true;opts:multiple=false,default'])

expectType<'a' | 'b' | 'c' | undefined>(this.flags['custom-union#defs:parse'])
expectType<Array<'a' | 'b' | 'c'> | undefined>(this.flags['custom-union#defs:parse,multiple'])

return result.flags
}
}

0 comments on commit a4afa23

Please sign in to comment.