feat(typedoc-plugin-appium): display response type
This now displays the type of the response in the command output.  Since all return types from commands are `Promise<T>`, it unwraps `T` and displays only that (since `Promise` is very specific to JS).
MODIFY
packages/typedoc-plugin-appium/lib/model/command-data.ts
packages/typedoc-plugin-appium/lib/model/command-data.ts
@@ -6,16 +6,60 @@ import {
ReflectionFlag,
ReflectionFlags,
ReflectionKind,
+ SignatureReflection,
} from 'typedoc';
import {CommandMethodDeclarationReflection, CommentSourceType} from '../converter';
-import {isCallSignatureReflection} from '../guards';
+import {isCallSignatureReflection, isReferenceType} from '../guards';
import {AppiumPluginLogger} from '../logger';
import {AllowedHttpMethod, Command, Route} from './types';
+/**
+ * List of fields to shallow copy from a `SignatureReflection` to a clone
+ * @internal
+ */
+const SIGNATURE_REFLECTION_CLONE_FIELDS = [
+ 'anchor',
+ 'comment',
+ 'flags',
+ 'hasOwnDocument',
+ 'implementationOf',
+ 'inheritedFrom',
+ 'kindString',
+ 'label',
+ 'originalName',
+ 'overwrites',
+ 'parameters',
+ 'sources',
+ 'typeParameters',
+ 'url',
+];
+
+/**
+ * List of fields to shallow copy from a `ParameterReflection` to a clone
+ * @internal
+ */
+const PARAMETER_REFLECTION_CLONE_FIELDS = [
+ 'anchor',
+ 'comment',
+ 'cssClasses',
+ 'defaultValue',
+ 'hasOwnDocument',
+ 'label',
+ 'originalName',
+ 'sources',
+ 'type',
+ 'url',
+];
+
/**
* Abstract representation of metadata for some sort of Appium command
*/
export abstract class BaseCommandData {
+ /**
+ * Loggher
+ */
+ protected readonly log: AppiumPluginLogger;
+
/**
* The method name of the command handler.
*/
@@ -31,9 +75,9 @@ export abstract class BaseCommandData {
*/
public readonly commentSource?: CommentSourceType;
/**
- * List of optional parameter names derived from a method map
+ * If `true`, this command is from a Plugin (not a Driver)
*/
- public readonly optionalParams?: string[];
+ public readonly isPluginCommand: boolean;
/**
* Actual method reflection.
*
@@ -41,63 +85,45 @@ export abstract class BaseCommandData {
*/
public readonly methodRefl?: CommandMethodDeclarationReflection;
/**
- * List of required parameter names derived from a method map
+ * List of optional parameter names derived from a method map
*/
- public readonly requiredParams?: string[];
+ public readonly optionalParams?: string[];
/**
- * The thing which the method is a member of for documentation purposes
+ * List of required parameter names derived from a method map
*/
- parentRefl?: DeclarationReflection;
-
- protected readonly log: AppiumPluginLogger;
+ public readonly requiredParams?: string[];
/**
- * Cached computed parameters
+ * Loops through signatures of the command's method declaration and returns the first that is a
+ * `CallSignatureReflection` (if any). This is what we think of when we think "function signature"
*/
- #parameters: ParameterReflection[] | undefined;
-
- public readonly isPluginCommand: boolean;
-
- constructor(log: AppiumPluginLogger, command: Command, opts: CommandDataOpts = {}) {
- this.command = command;
- this.optionalParams = opts.optionalParams;
- this.requiredParams = opts.requiredParams;
- this.comment = opts.comment;
- this.commentSource = opts.commentSource;
- this.methodRefl = opts.refl;
- this.parentRefl = opts.parentRefl;
- this.log = log;
- this.isPluginCommand = Boolean(opts.isPluginCommand);
- }
+ public static findCallSignature = _.memoize((cmd: BaseCommandData) =>
+ cmd.methodRefl?.getAllSignatures()?.find(isCallSignatureReflection)
+ );
/**
- * Returns a list of its `ParameterReflection` objects in this instance's method's call signature.
- *
- * Used to display actual types of parameters by templates. The result is cached.
- */
- public get parameters(): ParameterReflection[] {
- if (!this.hasCommandParams) {
+ * Returns a list of `ParameterReflection` objects in the command's method declaration;
+ * rewrites them to prefer the method map parameter list (and the param names)
+ **/
+ public static rewriteParameters = _.memoize((cmd: BaseCommandData) => {
+ if (!cmd.hasCommandParams) {
return [];
}
- if (this.#parameters) {
- return this.#parameters;
- }
- const sig = this.methodRefl?.signatures?.find(isCallSignatureReflection);
+ const sig = BaseCommandData.findCallSignature(cmd);
if (!sig) {
return [];
}
- const pRefls =
- (this.isPluginCommand ? sig.parameters?.slice(2) : sig.parameters?.slice()) ?? [];
+ const pRefls = (cmd.isPluginCommand ? sig.parameters?.slice(2) : sig.parameters?.slice()) ?? [];
- if (pRefls.length < this.requiredParams!.length + this.optionalParams!.length) {
- this.log.warn(
+ if (pRefls.length < cmd.requiredParams!.length + cmd.optionalParams!.length) {
+ cmd.log.warn(
'(%s) Method %s has fewer parameters (%d) than specified in the method map (%d)',
- this.parentRefl!.name,
- this.methodRefl!.name,
+ cmd.parentRefl!.name,
+ cmd.methodRefl!.name,
pRefls.length,
- this.requiredParams!.length + this.optionalParams!.length
+ cmd.requiredParams!.length + cmd.optionalParams!.length
);
}
@@ -114,7 +140,7 @@ export abstract class BaseCommandData {
* @returns List of refls with names matching `commandParams`, throwing out any extra refls
*/
const createNewRefls = (kind: 'required' | 'optional'): ParameterReflection[] => {
- const commandParams = this[`${kind}Params`];
+ const commandParams = cmd[`${kind}Params`];
if (!commandParams?.length) {
return [];
}
@@ -130,23 +156,9 @@ export abstract class BaseCommandData {
ReflectionKind.CallSignature,
sig
);
- _.assign(
- newPRefl,
- _.pick(pRefl, [
- 'defaultValue',
- 'comment',
- 'type',
- 'originalName',
- 'label',
- 'sources',
- 'url',
- 'anchor',
- 'hasOwnDocument',
- 'cssClasses',
- ])
- );
+ _.assign(newPRefl, _.pick(pRefl, PARAMETER_REFLECTION_CLONE_FIELDS));
- // there doesn't seem to be a straightforward way to clone this object.
+ // there doesn't seem to be a straightforward way to clone flags.
newPRefl.flags = new ReflectionFlags(...pRefl.flags);
newPRefl.flags.setFlag(ReflectionFlag.Optional, kind === 'optional');
newParamRefls.push(newPRefl);
@@ -160,17 +172,86 @@ export abstract class BaseCommandData {
if (!newParamRefls.length) {
return [];
}
- this.#parameters = newParamRefls;
return newParamRefls;
+ });
+
+ /**
+ * Rewrites a method's return value for documentation.
+ *
+ * Given a command having a method declaration, creates a clone of its call signature wherein the
+ * return type is unwrapped from `Promise`. In other words, if a method returns `Promise<T>`,
+ * this changes the return type in the signature to `T`.
+ *
+ * Note that the return type of a command's method declaration should always be a `ReferenceType` having
+ * name `Promise`.
+ */
+ public static unwrapSignatureType = _.memoize((cmd: BaseCommandData) => {
+ const callSig = BaseCommandData.findCallSignature(cmd);
+ if (!callSig) {
+ return;
+ }
+ if (isReferenceType(callSig.type) && callSig.type.name === 'Promise') {
+ const newCallSig = new SignatureReflection(
+ callSig.name,
+ ReflectionKind.CallSignature,
+ cmd.methodRefl!
+ );
+ _.assign(newCallSig, _.pick(callSig, SIGNATURE_REFLECTION_CLONE_FIELDS));
+
+ // this is the actual unwrapping. `Promise` only has a single type argument `T`,
+ // so we can safely use the first one.
+ newCallSig.type = callSig.type.typeArguments?.[0];
+
+ if (!newCallSig.type) {
+ cmd.log.warn(
+ '(%s) No type arg T found for return type Promise<T> in %s; this is a bug',
+ cmd.parentRefl!.name,
+ cmd.methodRefl!.name
+ );
+ return;
+ }
+ return newCallSig;
+ }
+ });
+
+ /**
+ * The thing which the method is a member of for documentation purposes
+ */
+ parentRefl?: DeclarationReflection;
+
+ constructor(log: AppiumPluginLogger, command: Command, opts: CommandDataOpts = {}) {
+ this.command = command;
+ this.optionalParams = opts.optionalParams;
+ this.requiredParams = opts.requiredParams;
+ this.comment = opts.comment;
+ this.commentSource = opts.commentSource;
+ this.methodRefl = opts.refl;
+ this.parentRefl = opts.parentRefl;
+ this.log = log;
+ this.isPluginCommand = Boolean(opts.isPluginCommand);
}
/**
* Returns `true` if the method or execute map defined parameters for this command
*/
- get hasCommandParams(): boolean {
+ public get hasCommandParams(): boolean {
return Boolean(this.optionalParams?.length || this.requiredParams?.length);
}
+ /**
+ * Gets a list of function parameters (for use in rendering)
+ */
+ public get parameters() {
+ return BaseCommandData.rewriteParameters(this);
+ }
+
+ /**
+ * Gets the call signature (for use in rendering)
+ */
+ public get signature() {
+ return BaseCommandData.unwrapSignatureType(this);
+ }
+
/**
* Should create a shallow clone of the implementing instance
* @param opts New options to pass to the new instance
@@ -192,29 +273,34 @@ export interface CommandDataOpts {
* For debugging purposes mainly
*/
commentSource?: CommentSourceType;
+
+ /**
+ * If `true`, `refl` represents a `PluginCommand`, wherein we will ignore
+ * the first two parameters altogether.
+ */
+ isPluginCommand?: boolean;
+
/**
* List of optional parameter names derived from a method map
*/
optionalParams?: string[];
+
+ /**
+ * The thing which the method is a member of for documentation purposes
+ */
+ parentRefl?: DeclarationReflection;
+
/**
* Actual method reflection.
*
* @todo Determine if this should be required
*/
refl?: CommandMethodDeclarationReflection;
+
/**
* List of required parameter names derived from a method map
*/
requiredParams?: string[];
- /**
- * The thing which the method is a member of for documentation purposes
- */
- parentRefl?: DeclarationReflection;
- /**
- * If `true`, `refl` represents a `PluginCommand`, wherein we will ignore
- * the first two parameters altogether.
- */
- isPluginCommand?: boolean;
}
/**
@@ -225,6 +311,7 @@ export class CommandData extends BaseCommandData {
* The HTTP method of the route
*/
public readonly httpMethod: AllowedHttpMethod;
+
/**
* The route of the command
*/

MODIFY
packages/typedoc-plugin-appium/lib/model/reflection/command.ts
packages/typedoc-plugin-appium/lib/model/reflection/command.ts
-import {Comment, DeclarationReflection, ParameterReflection} from 'typedoc';
+import {
+ Comment,
+ DeclarationReflection,
+ ParameterReflection,
+ SignatureReflection,
+ SomeType,
+} from 'typedoc';
import {CommandMethodDeclarationReflection, CommentSourceType} from '../../converter';
import {isExecMethodData} from '../../guards';
import {CommandData, ExecMethodData} from '../command-data';
@@ -52,11 +58,25 @@ export class CommandReflection extends DeclarationReflection {
*/
public readonly comment?: Comment;
+ /**
+ * Metadata about where `comment` came from
+ */
public readonly commentSource?: CommentSourceType;
+ /**
+ * Original method declaration
+ */
public readonly refl?: CommandMethodDeclarationReflection;
+ /**
+ * Parameters for template display
+ */
public readonly parameters?: ParameterReflection[];
+ /**
+ * Call signature for template display
+ */
+ public readonly signature?: SignatureReflection;
+
/**
* Sets props depending on type of `data`
* @param data Command or execute method data
@@ -81,6 +101,7 @@ export class CommandReflection extends DeclarationReflection {
methodRefl: refl,
commentSource,
parameters,
+ signature,
} = data;
// kind-specific data
@@ -109,6 +130,7 @@ export class CommandReflection extends DeclarationReflection {
this.refl = refl;
this.commentSource = commentSource;
this.parameters = parameters;
+ this.signature = signature;
}
/**

MODIFY
packages/typedoc-plugin-appium/lib/theme/resources/partials/command.hbs
packages/typedoc-plugin-appium/lib/theme/resources/partials/command.hbs
{{else}}
{{#if hasRequiredParams}}
-#### Required Parameters
+##### Required Parameters
{{#each requiredParams}}
- `{{this}}`
{{#if hasOptionalParams}}
-#### Optional Parameters
+##### Optional Parameters
{{#each optionalParams}}
- `{{this}}`
{{/if}}
{{/if}}
+{{#if signature}}
+
+#### Response
+
+{{#with signature}}
+
+{{#with type}}
+
+{{{type 'all'}}}
+
+{{/with}}
+
+{{#with comment}}
+
+{{{returns this}}}
+
+{{/with}}
+
+{{#with type}}
+
+{{#if declaration.signatures}}
+
+{{#each declaration.signatures}}
+
+{{> member.signature showSources=false }}
+
+{{/each}}
+
+{{/if}}
+
+{{/with}}
+
+{{/with}}
+{{/if}}
{{/unless}}

feat(typedoc-plugin-appium): display response type

This now displays the type of the response in the command output.  Since all return types from commands are `Promise<T>`, it unwraps `T` and displays only that (since `Promise` is very specific to JS).