import type { ArgumentSpec, CliOptionSpec } from 'yeoman-generator'; import type { IsNever, RequireAtLeastOne, SetOptional, Simplify, TupleToUnion, ValueOf } from 'type-fest'; import type { JHipsterOptionDefinition } from '../jdl/core/types/parsing.js'; import type { DerivedPropertiesOf } from '../types/utils/derived-properties.js'; import type { MergeUnion } from './support/merge-union.js'; type CommandConfigScope = 'storage' | 'blueprint' | 'generator' | 'context' | 'none'; export type ConfigScope = CommandConfigScope | 'control'; type CliSpecType = CliOptionSpec['type']; export type JHipsterNamedChoice = { value: string; name: string }; export type JHipsterChoices = readonly [...(string | JHipsterNamedChoice)[]]; export type JHipsterOption = SetOptional & { readonly name?: string; readonly scope?: ConfigScope; readonly env?: string; readonly choices?: JHipsterChoices; readonly commandName?: string; }; export type PromptSpec = { readonly type: 'input' | 'list' | 'confirm' | 'checkbox'; readonly message: string | ((any) => string); readonly when?: boolean | ((any) => boolean); readonly default?: any | ((any) => any); readonly filter?: any | ((any) => any); readonly transformer?: any | ((any) => any); readonly validate?: any | ((any) => any); }; type JHipsterArgumentConfig = SetOptional & { scope?: CommandConfigScope }; 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?: true; readonly prompt?: | PromptSpec | ((gen: ConfigContext & { jhipsterConfigWithDefaults: Record }, config: ConfigSpec) => PromptSpec); readonly jdl?: Omit; readonly scope?: CommandConfigScope; /** * 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?: | string | boolean | number | readonly string[] | ((this: ConfigContext | void, ctx: any) => string | boolean | number | readonly string[]); /** * Configure the generator according to the selected configuration. */ readonly configure?: (gen: ConfigContext) => void; }; export type JHipsterArguments = Record; export type JHipsterOptions = Record; export type JHipsterConfig = RequireAtLeastOne< ConfigSpec, 'argument' | 'cli' | 'prompt' | 'jdl' | 'internal' >; export type JHipsterConfigs = Record>; export type JHipsterCommandDefinition = { readonly arguments?: JHipsterArguments; readonly options?: JHipsterOptions; readonly configs?: JHipsterConfigs; /** * Import options from a generator. * @example ['server', 'jhipster-blueprint:server'] */ readonly import?: readonly string[]; /** * @experimental * Compose with generator. * @example ['server', 'jhipster-blueprint:server'] */ readonly compose?: readonly string[]; /** * Override options from the generator been blueprinted. */ readonly override?: boolean; /** * Load old options definition (yeoman's `this.options()`) from the generator. */ readonly loadGeneratorOptions?: boolean; }; /** * A simplified version of the `JHipsterCommandDefinition` type for types parsing. */ type ParseableConfig = { readonly type?: CliSpecType; readonly cli?: { readonly type: CliSpecType; }; readonly choices?: JHipsterChoices; readonly scope: ConfigScope; }; type ParseableConfigs = Record; type ParseableCommand = { readonly options?: ParseableConfigs; readonly configs?: ParseableConfigs; }; /** Extract contructor return type, eg: Boolean, String */ type ConstructorReturn = T extends new () => infer R ? R : undefined; type FilteredConfigScope = ConfigScope | 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: ParseableConfigs } ? { [K in keyof FilterCommandScope]: D['configs'][K] } : object) & (D extends { options: ParseableConfigs } ? { [K in keyof FilterCommandScope]: D['options'][K] } : object) >; type GetType = C extends Record<'type', CliSpecType> ? C['type'] : C extends Record<'cli', Record<'type', CliSpecType>> ? C['cli']['type'] : undefined; // eslint-disable-next-line @typescript-eslint/no-wrapper-object-types 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 typle type is loosy 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 ParseableConfigs ? // Add value inference to properties with choices // ? PrepareConfigsWithType> & ValueOf>> Simplify< PrepareConfigsWithType> & MergeUnion>>> > : never : never; type ExportScopedPropertiesFromCommand = MergeConfigsOptions extends infer Merged ? (Merged extends ParseableConfigs ? PrepareConfigsWithType : never) : never; export type ExportStoragePropertiesFromCommand = ExportScopedPropertiesFromCommand; export type ExportGeneratorOptionsFromCommand = ExportScopedPropertiesFromCommand; export type ExportControlPropertiesFromCommand = ExportScopedPropertiesFromCommand;