diff --git a/packages/typedoc-plugin-appium/lib/converter/builtin-method-map.ts b/packages/typedoc-plugin-appium/lib/converter/builtin-method-map.ts index 932ab68cf7b..7cb51122a93 100644 --- a/packages/typedoc-plugin-appium/lib/converter/builtin-method-map.ts +++ b/packages/typedoc-plugin-appium/lib/converter/builtin-method-map.ts @@ -5,7 +5,6 @@ import { isMethodMapDeclarationReflection, } from '../guards'; import {AppiumPluginLogger} from '../logger'; -import {ModuleCommands} from '../model'; import {BaseConverter} from './base-converter'; import {BuiltinCommands} from '../model/builtin-commands'; import {convertMethodMap} from './method-map'; @@ -28,7 +27,7 @@ export const NAME_BASE_DRIVER_CLASS = 'BaseDriver'; */ export const NAME_BUILTIN_COMMAND_MODULE = '@appium/base-driver'; -export class BuiltinMethodMapConverter extends BaseConverter { +export class BuiltinMethodMapConverter extends BaseConverter { /** * Creates a child logger for this instance * @param ctx Typedoc Context diff --git a/packages/typedoc-plugin-appium/lib/converter/overrides.ts b/packages/typedoc-plugin-appium/lib/converter/overrides.ts index a49537e1a97..bc4968a1df9 100644 --- a/packages/typedoc-plugin-appium/lib/converter/overrides.ts +++ b/packages/typedoc-plugin-appium/lib/converter/overrides.ts @@ -59,6 +59,11 @@ export function convertOverrides({ const builtinRoutes = builtinCommands.routesByCommandName.get(command); if (!builtinMethods.has(command) || !builtinRoutes) { // actually unknown method + log.verbose( + '(%s) No builtin route mapping to method "%s"; skipping', + parentRefl.name, + command + ); continue; } diff --git a/packages/typedoc-plugin-appium/lib/converter/types.ts b/packages/typedoc-plugin-appium/lib/converter/types.ts index 0f8d63d6db5..c7f4f1e9c7f 100644 --- a/packages/typedoc-plugin-appium/lib/converter/types.ts +++ b/packages/typedoc-plugin-appium/lib/converter/types.ts @@ -2,6 +2,7 @@ import { Comment, DeclarationReflection, LiteralType, + ParameterReflection, ReferenceType, ReflectionFlags, ReflectionKind, @@ -110,6 +111,9 @@ export type ExecMethodDeclarationReflection = WithName< flags: ReflectionFlags & {isStatic: true}; }; +export type WithStaticFlag = {flags: ReflectionFlags & {isStatic: true}}; +export type WithoutStaticFlag = {flags: ReflectionFlags & {isStatic: false}}; + /** * Type corresponding to the `params` prop of a `MethodDef` */ @@ -127,40 +131,109 @@ export type ExecMethodDefParamsPropDeclarationReflection = WithName< DeclarationReflectionWithReflectedType >; +/** + * Type corresponding to `@appium/types` module + */ export type AppiumTypesReflection = WithNameAndKind< typeof NAME_TYPES_MODULE, ReflectionKind.Module, ParentReflection >; +/** + * Type corresponding to a TS `interface` + */ export type InterfaceDeclarationReflection = WithKind< ReflectionKind.Interface, DeclarationReflection >; +/** + * Type corresponding to the `ExternalDriver` `interface` of `@appium/types` + */ export type ExternalDriverDeclarationReflection = WithName< typeof NAME_EXTERNAL_DRIVER, InterfaceDeclarationReflection >; -export type AsyncMethodDeclarationReflection = WithSomeType & - WithKind; +/** + * A call signature for a function that returns some sort of `Promise`. + */ +export type AsyncCallSignatureReflection = CallSignatureReflection & { + type: ReferenceType; + name: 'Promise'; +}; + +/** + * An async method or reference to an async method. A command's method must be of this type. + */ +export type AsyncMethodDeclarationReflection< + T extends ReferenceType | ReflectionType = ReferenceType +> = WithSomeType & + WithKind< + T extends ReferenceType ? ReflectionKind.Method : ReflectionKind.Property, + DeclarationReflection + > & + WithoutStaticFlag & + (T extends ReferenceType + ? { + signatures: NonEmptyArray; + } + : T extends ReflectionType + ? { + type: { + declaration: { + signatures: NonEmptyArray; + }; + }; + } + : never); +/** + * A little "struct" containing a method and potentially a derived comment (which may not have been derived from + * the method itself). + */ export interface KnownMethodData { comment?: Comment; method: AsyncMethodDeclarationReflection; } +/** + * A lookup of command names to {@linkcode KnownMethodData} objects. + */ export type KnownMethods = Map; -export interface MethodDefParam { - name: string; -} - +/** + * A {@linkcode DeclarationReflection} which is a `class`. + */ export type ClassDeclarationReflection = WithKind; +/** + * One of {@linkcode ExecMethodDefParamsPropDeclarationReflection} or + * {@linkcode MethodDefParamsPropDeclarationReflection}, which are "parameters" properties of method + * definition objects (as in a `MethodMap`) or execute method definitions (in an `ExecMethodMap`) + */ export type ParamsPropDeclarationReflection = | ExecMethodDefParamsPropDeclarationReflection | MethodDefParamsPropDeclarationReflection; +/** + * A {@linkcode SignatureReflection} which is a call signature; a function signature. + * + * (Other types of signatures include things like "constructor signatures") + */ export type CallSignatureReflection = WithKind; + +/** + * An array with a nonzero number of items. + */ +export type NonEmptyArray = [T, ...T[]]; + +/** + * A {@linkcode CallSignatureReflection} with a nonzero number of parameters. + * + * This is used to rename parameters on commands to prefer the ones as defined in the method map. + */ +export type CallSignatureReflectionWithArity = CallSignatureReflection & { + parameters: NonEmptyArray; +}; diff --git a/packages/typedoc-plugin-appium/lib/guards.ts b/packages/typedoc-plugin-appium/lib/guards.ts index c9e46985045..19147b5d648 100644 --- a/packages/typedoc-plugin-appium/lib/guards.ts +++ b/packages/typedoc-plugin-appium/lib/guards.ts @@ -31,6 +31,7 @@ import { AsyncMethodDeclarationReflection, BaseDriverDeclarationReflection, CallSignatureReflection, + CallSignatureReflectionWithArity, ClassDeclarationReflection, CommandPropDeclarationReflection, DeclarationReflectionWithReflectedType, @@ -157,7 +158,6 @@ export function isBaseDriverDeclarationReflection( /** * Type guard for a property of an object (a {@linkcode Reflection} having kind {@linkcode ReflectionKind.Property}). * @param value any value - * @returns */ export function isPropertyKind(value: any) { return value instanceof Reflection && value.kindOf(ReflectionKind.Property); @@ -245,18 +245,36 @@ export function isExecMethodDefParamsPropDeclarationReflection( return isReflectionWithReflectedType(value) && value.name === NAME_PAYLOAD_PARAMS; } +/** + * Type guard for a {@linkcode InterfaceDeclarationReflection} corresponding to a TS interface. + * @param value any value + */ export function isInterfaceDeclarationReflection( value: any ): value is InterfaceDeclarationReflection { return isDeclarationReflection(value) && value.kindOf(ReflectionKind.Interface); } +/** + * Type guard for a {@linkcode ExternalDriverDeclarationReflection} which is the `ExternalDriver` + * interface defined in `@appium/types`. + * @param value any value + */ export function isExternalDriverDeclarationReflection( value: any ): value is ExternalDriverDeclarationReflection { return isInterfaceDeclarationReflection(value) && value.name === NAME_EXTERNAL_DRIVER; } +/** + * Type guard for an {@linkcode AsyncMethodDeclarationReflection}, which is _potentially_ a method + * for a command. Not all async methods in driver classes are mapped to commands, of course! + * + * A command method cannot be static, but it can be an actual (async) function or a reference to an + * (async) function; just depends how the code is written. Either way, this asserts there's a + * call signature returning a `Promise`. + * @param value + */ export function isAsyncMethodDeclarationReflection( value: any ): value is AsyncMethodDeclarationReflection { @@ -275,14 +293,33 @@ export function isAsyncMethodDeclarationReflection( ); } +/** + * Type guard for a {@linkcode ClassDeclarationReflection} which is just a {@linkcode DeclarationReflection} + * with a `kind` of {@linkcode ReflectionKind.Class}. + * @param value any value + */ export function isClassDeclarationReflection(value: any): value is ClassDeclarationReflection { return Boolean(isDeclarationReflection(value) && value.kindOf(ReflectionKind.Class)); } -export function isCallSignatureReflectionWithParams(value: any): value is CallSignatureReflection { +/** + * Type guard for a {@linkcode CallSignatureReflection} which is just a + * {@linkcode SignatureReflection} with kind {@linkcode ReflectionKind.CallSignature}. + * @param value any value + */ +export function isCallSignatureReflection(value: any): value is CallSignatureReflection { return Boolean( - value instanceof SignatureReflection && - value.kindOf(ReflectionKind.CallSignature) && - value.parameters?.length + value instanceof SignatureReflection && value.kindOf(ReflectionKind.CallSignature) ); } + +/** + * Type guard for a {@linkcode CallSignatureReflectionWithArity}, which is a + * {@linkcode CallSignatureReflection} with an arity greater than zero. + * @param value any value + */ +export function isCallSignatureReflectionWithArity( + value: any +): value is CallSignatureReflectionWithArity { + return Boolean(isCallSignatureReflection(value) && value.parameters?.length); +} diff --git a/packages/typedoc-plugin-appium/lib/model/command-data.ts b/packages/typedoc-plugin-appium/lib/model/command-data.ts index f79307edd92..d48cc151792 100644 --- a/packages/typedoc-plugin-appium/lib/model/command-data.ts +++ b/packages/typedoc-plugin-appium/lib/model/command-data.ts @@ -13,7 +13,7 @@ import { ClassDeclarationReflection, CommentSourceType, } from '../converter'; -import {isCallSignatureReflectionWithParams} from '../guards'; +import {isCallSignatureReflectionWithArity} from '../guards'; import {AppiumPluginLogger} from '../logger'; import {AllowedHttpMethod, Command, Route} from './types'; @@ -84,7 +84,7 @@ export abstract class BaseCommandData { if (this.#parameters) { return this.#parameters; } - const sig = this.methodRefl?.signatures?.find(isCallSignatureReflectionWithParams); + const sig = this.methodRefl?.signatures?.find(isCallSignatureReflectionWithArity); if (!sig) { return []; diff --git a/packages/typedoc-plugin-appium/lib/options/declarations.ts b/packages/typedoc-plugin-appium/lib/options/declarations.ts index 4ba2d8f8a0b..b56f4bc6c64 100644 --- a/packages/typedoc-plugin-appium/lib/options/declarations.ts +++ b/packages/typedoc-plugin-appium/lib/options/declarations.ts @@ -11,23 +11,29 @@ export const declarations = { help: `(${NS}) Name of "commands" directory under the TypeDoc output directory. Not a full path`, name: 'commandsDir', type: ParameterType.String, - } as DeclarationOption, + }, forceBreadcrumbs: { defaultValue: false, help: `(${NS}) Force breadcrumbs to be shown; overrides "hideBreadcrumbs"`, name: 'forceBreadcrumbs', type: ParameterType.Boolean, - } as DeclarationOption, + }, outputBuiltinCommands: { defaultValue: false, help: `(${NS}) Output builtin commands`, name: 'outputBuiltinCommands', type: ParameterType.Boolean, - } as DeclarationOption, + }, outputModules: { defaultValue: true, help: `(${NS}) Output modules APIs in addition to commands. This is needed for complete typing information`, name: 'outputModules', type: ParameterType.Boolean, - } as DeclarationOption, + }, } as const; + +// these are extra type checks to ensure these are the correct type. +declarations.outputModules as DeclarationOption; +declarations.forceBreadcrumbs as DeclarationOption; +declarations.outputBuiltinCommands as DeclarationOption; +declarations.commandsDir as DeclarationOption; diff --git a/packages/typedoc-plugin-appium/test/unit/converter/external.spec.ts b/packages/typedoc-plugin-appium/test/unit/converter/external.spec.ts index 151876824ef..66335eada45 100644 --- a/packages/typedoc-plugin-appium/test/unit/converter/external.spec.ts +++ b/packages/typedoc-plugin-appium/test/unit/converter/external.spec.ts @@ -10,7 +10,7 @@ import { NAME_TYPES_MODULE, } from '../../../lib/converter'; import {BuiltinCommands} from '../../../lib/model/builtin-commands'; -import {isCallSignatureReflectionWithParams} from '../../../lib/guards'; +import {isCallSignatureReflectionWithArity} from '../../../lib/guards'; import {AppiumPluginLogger} from '../../../lib/logger'; import {CommandSet, ModuleCommands, ProjectCommands} from '../../../lib/model'; import {initConverter, NAME_FAKE_DRIVER_MODULE} from '../helpers'; @@ -93,9 +93,8 @@ describe('ExternalConverter', function () { it('should prefer method map parameters over method parameters', function () { const postRoute = [...sessionCmdSet].find((cmdData) => cmdData.httpMethod === 'POST')!; - const pRefls = postRoute.methodRefl!.signatures!.find( - isCallSignatureReflectionWithParams - )!.parameters!; + const pRefls = postRoute.methodRefl!.signatures!.find(isCallSignatureReflectionWithArity)! + .parameters!; // the method has 4 parameters, but the method map has 3 expect(pRefls).to.have.lengthOf(4);