import assert from 'assert'; import { identity, memoize, omit } from 'lodash'; import { Simplify } from 'type-fest'; import { DeclarationOption, DeclarationOptionToOptionType, ParameterTypeToOptionTypeMap } from 'typedoc'; import type { OptionGroup } from './option-group'; import { DeclarationOptionConfig, ParameterValueType } from './utils'; import type { ABasePlugin } from '../base-plugin'; type MapFn = ( value: TIn ) => TOut; export type MapperPart> = ParameterValueType extends TOpt ? [mapper?: MapFn, TOpt>] : [mapper: MapFn, TOpt>] type InferParameterType = keyof {[key in keyof ParameterTypeToOptionTypeMap as ParameterTypeToOptionTypeMap[key] extends T ? key : never]: true}; export type InferDeclarationType = Simplify}>; export class Option< T, TDeclaration extends DeclarationOption = DeclarationOption, > { public readonly name: string; private readonly _mapper: MapFn, T>; private readonly _declaration: TDeclaration; public get fullName(){ return this.name === '__' ? this.plugin.optionsPrefix : `${this.plugin.optionsPrefix}:${this.name}`; } /** * Generate a type-helper factory to constraint the option to be of the given {@link T2 type}. * * TODO: change signature once https://github.com/microsoft/TypeScript/pull/26349 is merged. * * @param plugin - The plugin declaring the option. * @returns a function to call with the option declaration (& optional mapper). */ public static factory( plugin: ABasePlugin ) { return ( declaration: TDec, ...mapper: MapperPart ) => new Option( plugin, null, declaration, ...mapper ); } public constructor( public readonly plugin: ABasePlugin, public readonly group: OptionGroup | null, declaration: TDeclaration, ...[ mapper ]: MapperPart ){ this.name = declaration.name; if( !group ){ if( declaration.name === '__' ){ assert( this.plugin.application.options.getDeclarations().filter( d => d.name.startsWith( `${plugin.optionsPrefix}:` ) ).length === 0 ); } else { assert( this.plugin.application.options.getDeclarations().filter( d => d.name === plugin.optionsPrefix ).length === 0 ); } } this._declaration = { ...( omit( declaration, 'mapper' ) as any as TDeclaration ), name: this.fullName, help: `[${this.plugin.package.name}]: ${declaration.help}`, }; this.plugin.application.options.addDeclaration( this._declaration ); mapper ??= identity; this._mapper = memoize( mapper as any ); } /** * Get the mapped value. * * @returns the value. */ public getValue(): T { const rawValue = this.plugin.application.options.getValue( this.fullName ) as ParameterValueType; return this._mapper( rawValue ); } /** * Set the raw value. * * @param value - The value to set. */ public setValue( value: DeclarationOptionToOptionType ) { this.plugin.application.options.setValue( this.fullName, value ); } }