import type { IsNever, PascalCase, Replace, RequireAtLeastOne, SetOptional, Simplify, TupleToUnion, ValueOf } from 'type-fest'; import type { ArgumentSpec, CliOptionSpec } from 'yeoman-generator'; import type BaseCoreGenerator from '../../generators/base-core/generator.ts'; import type GeneratorsByNamespace from '../../generators/types.ts'; import type { JHipsterNamedChoice } from '../core/types.ts'; import type { JHipsterOptionDefinition } from '../jdl/core/types/parsing.ts'; import type { MergeUnion } from './support/merge-union.ts'; type NormalizePropertyValue = Replace, '.', ''>; export type DerivedProperty< Property extends string, Value extends string, > = `${Property}${Uppercase extends Value ? Value : PascalCase>}`; export type DerivedPropertiesOnlyOf = Simplify<{ [K in Choices as `${Property}${Capitalize>}`]: boolean; }>; /* * @example * ```ts * DerivedPropertiesOf<'clientFramework', 'angular' | 'no'> = * { clientFrameworkAngular: boolean; clientFrameworkNo: boolean; clientFramework: 'angular' | 'no'; clientFrameworkAny: boolean; } * ``` */ export type DerivedPropertiesOf = Simplify< { [K in Choices as `${Property}${Capitalize>}`]: boolean; } & Record & Record<`${Property}Any`, boolean> >; /* * @example * ```ts * DerivedPropertiesWithInferenceOf<'clientFramework', 'angular', 'angular', 'no'> = * { clientFrameworkAngular: true; clientFrameworkNo: false; clientFramework: 'angular'; clientFrameworkAny: true; } * ``` */ type DerivedPropertiesWithInferenceOf

= Simplify< { [K in C as `${P}${Capitalize}`]: K extends V ? true : false; } & Record & Record<`${P}Any`, V extends 'no' ? false : true> >; /** * ```ts * type ExplodedConfigChoices = ExplodeDerivedPropertiesWithInference<['angular', 'no'], 'clientFramework'>; * type ExplodedConfigChoices = * | { clientFrameworkAngular: true; clientFrameworkNo: false; clientFramework: 'angular'; clientFrameworkAny: true; } * | { clientFrameworkAngular: false; clientFrameworkNo: true; clientFramework: 'no'; clientFrameworkAny: true; } * ``` */ export type DerivedPropertiesWithInferenceUnion = ValueOf<{ [Index in Exclude]: Choices[Index] extends infer Choice ? Choice extends string ? DerivedPropertiesWithInferenceOf : never : never; }>; type CommandConfigScope = 'storage' | 'blueprint' | 'generator' | 'context' | 'none'; type ScopedConfig = { /** * Command configuration scope * - `storage`: Used for storing configuration in `jhipsterConfig`. * - `blueprint`: Used for storing blueprint-specific configurations in `blueprintConfig`. * - `generator`: Used for generator options, will be inserted as a generator property, may conflict with existing properties. * - `context`: Used for options that are specific to the template context, will be inserted in `context`. * - `none`: Used for options that will be handled manually, such as options that are stored differently than they are received. */ readonly scope: CommandConfigScope; }; export type CommandConfigType = typeof String | typeof Boolean | typeof Number | typeof Object | ((opt: string) => any); export type CommandConfigDefault = | string | boolean | number | readonly string[] | ((this: ConfigContext | void, ctx: any) => string | boolean | number | readonly string[]); type CliSpecType = CliOptionSpec['type'] | typeof Object; export type JHipsterChoices = readonly [...(string | JHipsterNamedChoice)[]]; export type PromptSpec = { readonly type: 'input' | 'select' | 'confirm' | 'checkbox'; readonly message: string | ((arg: any) => string); readonly when?: boolean | ((arg: any) => boolean); readonly default?: any; readonly filter?: any; readonly transformer?: any; readonly validate?: any; }; type JHipsterArgumentConfig = SetOptional & Partial; export type CliSpec = Omit, 'storage'> & { env?: string; /** * Imply other options. */ implies?: Record; }; export type ConfigSpec = { readonly description?: string; readonly choices?: JHipsterChoices; readonly cli?: CliSpec; readonly argument?: JHipsterArgumentConfig; readonly internal?: { type: CommandConfigType }; readonly prompt?: PromptSpec | ((gen: ConfigContext, config: ConfigSpec) => PromptSpec); readonly jdl?: Omit; /** * The callback receives the generator as input for 'generator' scope. * The callback receives jhipsterConfigWithDefaults as input for 'storage' (default) scope. * The callback receives blueprintStorage contents as input for 'blueprint' scope. * * Default value will not be applied to generator (using 'generator' scope) in initializing priority. Use cli.default instead. * Default value will be application to templates context object (application) in loading priority. */ readonly default?: CommandConfigDefault; /** * Configure the generator according to the selected configuration. */ readonly configure?: (gen: ConfigContext, value: any) => void; } & ScopedConfig; export type JHipsterArguments = Record; export type JHipsterArgumentsWithChoices = Record; export type JHipsterConfig = RequireAtLeastOne< ConfigSpec, 'argument' | 'cli' | 'prompt' | 'jdl' | 'internal' >; export type JHipsterConfigs = Record>; export type JHipsterCommandDefinition = { readonly arguments?: JHipsterArguments; readonly configs?: JHipsterConfigs; /** * Import options from a generator. * @example ['server', 'jhipster-blueprint:server'] */ readonly import?: readonly (keyof GeneratorsByNamespace | 'base')[]; /** * @experimental * Compose with generator. * @example ['server', 'jhipster-blueprint:server'] */ readonly compose?: readonly (keyof GeneratorsByNamespace | 'base')[]; /** * Override options from the generator been blueprinted. */ readonly override?: boolean; }; /** * A simplified version of the `JHipsterCommandDefinition` type for types parsing. */ type ParsableConfig = { readonly type?: CliSpecType; readonly cli?: { readonly type: CliSpecType; }; readonly choices?: JHipsterChoices; } & ScopedConfig; type ParsableConfigs = Record; export type ParsableCommand = { readonly options?: ParsableConfigs; readonly configs?: ParsableConfigs; }; /** Extract constructor return type, eg: Boolean, String */ type ConstructorReturn = T extends new () => infer R ? R : undefined; type FilteredConfigScope = CommandConfigScope | undefined; /** Filter Options/Config by scope */ type FilterScope = D extends Record<'scope', S> ? D : never; type FilterCommandScope = { [K in keyof D as IsNever> extends true ? never : K]: D[K]; }; /** * @example * ```ts * type MergedConfigsOptions = MergeConfigsOptions<{ * configs: { clientFramework: { type: 'string'; scope: 'storage'; choices: ['angular', 'no'] } }; * options: { clientTestFramework: { type: 'string'; scope: 'storage'; choices: ['cypress', 'no'] } }; * }> * ``` */ type MergeConfigsOptions = Simplify< D extends { configs: ParsableConfigs } ? { [K in keyof FilterCommandScope]: D['configs'][K] } : object >; type GetType = C extends Record<'type', CliSpecType> ? C['type'] : C extends Record<'cli', Record<'type', CliSpecType>> ? C['cli']['type'] : C extends Record<'internal', Record<'type', CliSpecType>> ? C['internal']['type'] : undefined; type WrapperToPrimitive = T extends Boolean ? boolean : T extends String ? string : T extends Number ? number : T; type GetChoiceValue = Choice extends string ? Choice : Choice extends { value: string } ? Choice['value'] : never; /** * @example * type Normalized = NormalizeChoices<['angular', { value: 'no' }]>; * type Normalized = ['angular', 'no']; */ type NormalizeChoices = { [Index in keyof Choices]: GetChoiceValue; }; /** * @example * ```ts * type ExplodedCommandChoices = ExplodeCommandChoicesNoInference<{ clientFramework: { choices: ['angular', 'no'], scope: 'storage' }, clientTestFramework: { choices: ['cypress', 'no'], scope: 'storage' } }> * ``` */ type ExplodeCommandChoicesNoInference = { [K in keyof U]: U[K] extends infer RequiredChoices ? RequiredChoices extends { choices: any } ? K extends infer StringKey ? StringKey extends string ? NormalizeChoices extends infer NormalizedChoices ? // @ts-expect-error Mapped tuple type is loose https://github.com/microsoft/TypeScript/issues/27995 Simplify> : never : never : never : never : never; }; type PrepareConfigsWithType = Simplify<{ -readonly [K in keyof U]?: U[K] extends Record<'choices', JHipsterChoices> ? TupleToUnion> : WrapperToPrimitive>> extends infer T ? T extends undefined ? unknown : T : never; }>; /** Keep Options/Config filtered by choices */ type OnlyChoices = D extends { choices: JHipsterChoices } ? (C extends true ? D : never) : C extends true ? never : D; /** * Keep Options/Config filtered by choices * * @example * ```ts * type ConfigsWithChoice = OnlyConfigsWithChoice<{ clientFramework: { choices: ['angular', 'no'], scope: 'storage' }, clientTestFramework: { choices: ['cypress', 'no'], scope: 'storage' } }> * ``` */ type OnlyConfigsWithChoice = { [K in keyof D as OnlyChoices extends never ? never : K]: D[K]; }; /** * @example * ``` * type Prop = ExportApplicationPropertiesFromCommand<{ configs: { clientFramework: { choices: ['angular', 'no'], scope: 'storage' }, bar: { scope: 'storage' } } }>; * ``` */ export type ExportApplicationPropertiesFromCommand = MergeConfigsOptions extends infer Merged ? Merged extends ParsableConfigs ? // Add value inference to properties with choices // ? PrepareConfigsWithType> & ValueOf>> Simplify< PrepareConfigsWithType> & MergeUnion>>> > : never : never; type ExportScopedPropertiesFromCommand = MergeConfigsOptions extends infer Merged ? Merged extends ParsableConfigs ? PrepareConfigsWithType : Record : Record; export type ExportStoragePropertiesFromCommand = ExportScopedPropertiesFromCommand; export type ExportGeneratorOptionsFromCommand = ExportScopedPropertiesFromCommand; export type ExportGeneratorPropertiesFromCommand = Readonly>; export type HandleCommandTypes = Record<'Config', ExportStoragePropertiesFromCommand> & Record<'Options', ExportGeneratorOptionsFromCommand> & Record<'Generator', ExportGeneratorPropertiesFromCommand> & Record<'Application', ExportApplicationPropertiesFromCommand>;