mirror of
https://github.com/appium/appium.git
synced 2026-02-22 03:08:47 -06:00
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).
This commit is contained in:
@@ -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,73 +75,55 @@ 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.
|
||||
*
|
||||
* @todo Determine if this should be required
|
||||
*/
|
||||
public readonly methodRefl?: CommandMethodDeclarationReflection;
|
||||
/**
|
||||
* List of optional parameter names derived from a method map
|
||||
*/
|
||||
public readonly optionalParams?: string[];
|
||||
/**
|
||||
* List of required parameter names derived from a method map
|
||||
*/
|
||||
public readonly requiredParams?: string[];
|
||||
/**
|
||||
* The thing which the method is a member of for documentation purposes
|
||||
*/
|
||||
parentRefl?: DeclarationReflection;
|
||||
|
||||
protected readonly log: AppiumPluginLogger;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
{{else}}
|
||||
{{#if hasRequiredParams}}
|
||||
|
||||
#### Required Parameters
|
||||
##### Required Parameters
|
||||
|
||||
{{#each requiredParams}}
|
||||
- `{{this}}`
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
{{#if hasOptionalParams}}
|
||||
|
||||
#### Optional Parameters
|
||||
##### Optional Parameters
|
||||
|
||||
{{#each optionalParams}}
|
||||
- `{{this}}`
|
||||
@@ -39,4 +39,38 @@
|
||||
{{/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}}
|
||||
|
||||
Reference in New Issue
Block a user