feat(typedoc-plugin-appium): create appium typedoc plugin

This is a typedoc plugin based on `typedoc-plugin-markdown`.

It outputs everything that plugin outputs _plus_ it outputs information about commands (API endpoints) as defined in drivers, plugins, and the builtins in `@appium/base-driver`

It's written in TS because that was the easiest way, because a) there was some sort of disagreement between Babel and TS re: exports, and b) everything it's built on is written in TS.
This commit is contained in:
Christopher Hiller
2022-11-15 14:00:32 -08:00
parent 8886b8ef23
commit be7a479b4b
31 changed files with 1329 additions and 76 deletions
+112 -74
View File
@@ -155,6 +155,10 @@
"resolved": "packages/test-support",
"link": true
},
"node_modules/@appium/typedoc-plugin-appium": {
"resolved": "packages/typedoc-plugin-appium",
"link": true
},
"node_modules/@appium/types": {
"resolved": "packages/types",
"link": true
@@ -2574,15 +2578,7 @@
"wrap-ansi": "^7.0.0"
}
},
"node_modules/@lerna/cli/node_modules/is-fullwidth-code-point": {
"version": "3.0.0",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/@lerna/cli/node_modules/string-width": {
"node_modules/@lerna/cli/node_modules/cliui/node_modules/string-width": {
"version": "4.2.3",
"dev": true,
"license": "MIT",
@@ -2595,6 +2591,14 @@
"node": ">=8"
}
},
"node_modules/@lerna/cli/node_modules/is-fullwidth-code-point": {
"version": "3.0.0",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/@lerna/cli/node_modules/yargs": {
"version": "16.2.0",
"dev": true,
@@ -2620,6 +2624,19 @@
"node": ">=10"
}
},
"node_modules/@lerna/cli/node_modules/yargs/node_modules/string-width": {
"version": "4.2.3",
"dev": true,
"license": "MIT",
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/@lerna/collect-uncommitted": {
"version": "6.1.0",
"dev": true,
@@ -4101,15 +4118,15 @@
}
},
"node_modules/@nrwl/cli": {
"version": "15.2.3",
"version": "15.2.1",
"dev": true,
"license": "MIT",
"dependencies": {
"nx": "15.2.3"
"nx": "15.2.1"
}
},
"node_modules/@nrwl/devkit": {
"version": "15.2.3",
"version": "15.2.1",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4154,11 +4171,11 @@
"license": "0BSD"
},
"node_modules/@nrwl/tao": {
"version": "15.2.3",
"version": "15.2.1",
"dev": true,
"license": "MIT",
"dependencies": {
"nx": "15.2.3"
"nx": "15.2.1"
},
"bin": {
"tao": "index.js"
@@ -4621,7 +4638,7 @@
"license": "MIT"
},
"node_modules/@types/lodash": {
"version": "4.14.190",
"version": "4.14.191",
"dev": true,
"license": "MIT"
},
@@ -5327,6 +5344,29 @@
"resolved": "packages/appium",
"link": true
},
"node_modules/appium-adb": {
"version": "9.10.17",
"license": "Apache-2.0",
"dependencies": {
"@appium/support": "^2.60.0",
"@babel/runtime": "^7.0.0",
"adbkit-apkreader": "^3.1.2",
"async-lock": "^1.0.0",
"asyncbox": "^2.6.0",
"bluebird": "^3.4.7",
"ini": "^3.0.0",
"lodash": "^4.0.0",
"lru-cache": "^7.3.0",
"semver": "^7.0.0",
"source-map-support": "^0.x",
"teen_process": "^2.0.1",
"utf7": "^1.0.2"
},
"engines": {
"node": ">=14",
"npm": ">=8"
}
},
"node_modules/aproba": {
"version": "2.0.0",
"license": "ISC"
@@ -6321,7 +6361,7 @@
}
},
"node_modules/cacache/node_modules/minimatch": {
"version": "5.1.1",
"version": "5.1.0",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -9462,7 +9502,7 @@
}
},
"node_modules/filelist/node_modules/minimatch": {
"version": "5.1.1",
"version": "5.1.0",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -12121,7 +12161,6 @@
},
"node_modules/handlebars": {
"version": "4.7.7",
"dev": true,
"license": "MIT",
"dependencies": {
"minimist": "^1.2.5",
@@ -12506,7 +12545,7 @@
}
},
"node_modules/ignore-walk/node_modules/minimatch": {
"version": "5.1.1",
"version": "5.1.0",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -13741,7 +13780,6 @@
},
"node_modules/jsonc-parser": {
"version": "3.2.0",
"dev": true,
"license": "MIT"
},
"node_modules/jsonfile": {
@@ -14847,7 +14885,6 @@
},
"node_modules/lunr": {
"version": "2.3.9",
"dev": true,
"license": "MIT"
},
"node_modules/make-dir": {
@@ -14944,7 +14981,6 @@
},
"node_modules/marked": {
"version": "4.2.3",
"dev": true,
"license": "MIT",
"bin": {
"marked": "bin/marked.js"
@@ -16021,7 +16057,6 @@
},
"node_modules/neo-async": {
"version": "2.6.2",
"dev": true,
"license": "MIT"
},
"node_modules/next-tick": {
@@ -16419,7 +16454,7 @@
}
},
"node_modules/npm-packlist/node_modules/minimatch": {
"version": "5.1.1",
"version": "5.1.0",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -16746,13 +16781,13 @@
}
},
"node_modules/nx": {
"version": "15.2.3",
"version": "15.2.1",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"@nrwl/cli": "15.2.3",
"@nrwl/tao": "15.2.3",
"@nrwl/cli": "15.2.1",
"@nrwl/tao": "15.2.1",
"@parcel/watcher": "2.0.4",
"@yarnpkg/lockfile": "^1.1.0",
"@yarnpkg/parsers": "^3.0.0-rc.18",
@@ -17664,6 +17699,8 @@
},
"node_modules/os-shim": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/os-shim/-/os-shim-0.1.3.tgz",
"integrity": "sha512-jd0cvB8qQ5uVt0lvCIexBaROw1KyKm5sbulg2fWOHjETisuCzWyt+eTZKEMs8v6HwzoGs8xik26jg7eCM6pS+A==",
"dev": true,
"engines": {
"node": ">= 0.4.0"
@@ -18300,9 +18337,10 @@
},
"node_modules/pre-commit": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/pre-commit/-/pre-commit-1.2.2.tgz",
"integrity": "sha512-qokTiqxD6GjODy5ETAIgzsRgnBWWQHQH2ghy86PU7mIn/wuWeTwF3otyNQZxWBwVn8XNr8Tdzj/QfUXpH+gRZA==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"cross-spawn": "^5.0.1",
"spawn-sync": "^1.0.15",
@@ -18311,8 +18349,9 @@
},
"node_modules/pre-commit/node_modules/cross-spawn": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
"integrity": "sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==",
"dev": true,
"license": "MIT",
"dependencies": {
"lru-cache": "^4.0.1",
"shebang-command": "^1.2.0",
@@ -18321,8 +18360,9 @@
},
"node_modules/pre-commit/node_modules/lru-cache": {
"version": "4.1.5",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
"integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
"dev": true,
"license": "ISC",
"dependencies": {
"pseudomap": "^1.0.2",
"yallist": "^2.1.2"
@@ -18330,8 +18370,9 @@
},
"node_modules/pre-commit/node_modules/shebang-command": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
"integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==",
"dev": true,
"license": "MIT",
"dependencies": {
"shebang-regex": "^1.0.0"
},
@@ -18341,16 +18382,18 @@
},
"node_modules/pre-commit/node_modules/shebang-regex": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
"integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/pre-commit/node_modules/which": {
"version": "1.2.14",
"resolved": "https://registry.npmjs.org/which/-/which-1.2.14.tgz",
"integrity": "sha512-16uPglFkRPzgiUXYMi1Jf8Z5EzN1iB4V0ZtMXcHZnwsBtQhhHeCqoWw7tsUY42hJGNDWtUsVLTjakIa5BgAxCw==",
"dev": true,
"license": "ISC",
"dependencies": {
"isexe": "^2.0.0"
},
@@ -18360,8 +18403,9 @@
},
"node_modules/pre-commit/node_modules/yallist": {
"version": "2.1.2",
"dev": true,
"license": "ISC"
"resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
"integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==",
"dev": true
},
"node_modules/prelude-ls": {
"version": "1.2.1",
@@ -18495,8 +18539,9 @@
},
"node_modules/pseudomap": {
"version": "1.0.2",
"dev": true,
"license": "ISC"
"resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
"integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==",
"dev": true
},
"node_modules/public-encrypt": {
"version": "4.0.3",
@@ -18863,7 +18908,7 @@
}
},
"node_modules/read-package-json/node_modules/minimatch": {
"version": "5.1.1",
"version": "5.1.0",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -20013,7 +20058,6 @@
},
"node_modules/shiki": {
"version": "0.11.1",
"dev": true,
"license": "MIT",
"dependencies": {
"jsonc-parser": "^3.0.0",
@@ -20384,9 +20428,10 @@
},
"node_modules/spawn-sync": {
"version": "1.0.15",
"resolved": "https://registry.npmjs.org/spawn-sync/-/spawn-sync-1.0.15.tgz",
"integrity": "sha512-9DWBgrgYZzNghseho0JOuh+5fg9u6QWhAWa51QC7+U5rCheZ/j1DrEZnyE0RBBRqZ9uEXGPgSSM0nky6burpVw==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"concat-stream": "^1.4.7",
"os-shim": "^0.1.2"
@@ -21690,7 +21735,6 @@
},
"node_modules/typedoc": {
"version": "0.23.21",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"lunr": "^2.3.9",
@@ -21710,7 +21754,6 @@
},
"node_modules/typedoc-plugin-markdown": {
"version": "3.13.6",
"dev": true,
"license": "MIT",
"dependencies": {
"handlebars": "^4.7.7"
@@ -21735,7 +21778,6 @@
},
"node_modules/typedoc/node_modules/brace-expansion": {
"version": "2.0.1",
"dev": true,
"license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0"
@@ -21743,7 +21785,6 @@
},
"node_modules/typedoc/node_modules/minimatch": {
"version": "5.1.0",
"dev": true,
"license": "ISC",
"dependencies": {
"brace-expansion": "^2.0.1"
@@ -21754,7 +21795,6 @@
},
"node_modules/typescript": {
"version": "4.7.4",
"devOptional": true,
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
@@ -21783,7 +21823,6 @@
},
"node_modules/uglify-js": {
"version": "3.17.4",
"dev": true,
"license": "BSD-2-Clause",
"optional": true,
"bin": {
@@ -22340,12 +22379,10 @@
},
"node_modules/vscode-oniguruma": {
"version": "1.6.2",
"dev": true,
"license": "MIT"
},
"node_modules/vscode-textmate": {
"version": "6.0.0",
"dev": true,
"license": "MIT"
},
"node_modules/walk-up-path": {
@@ -22577,7 +22614,6 @@
},
"node_modules/wordwrap": {
"version": "1.0.0",
"dev": true,
"license": "MIT"
},
"node_modules/workerpool": {
@@ -23184,29 +23220,6 @@
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
"packages/doctor/node_modules/appium-adb": {
"version": "9.10.17",
"license": "Apache-2.0",
"dependencies": {
"@appium/support": "^2.60.0",
"@babel/runtime": "^7.0.0",
"adbkit-apkreader": "^3.1.2",
"async-lock": "^1.0.0",
"asyncbox": "^2.6.0",
"bluebird": "^3.4.7",
"ini": "^3.0.0",
"lodash": "^4.0.0",
"lru-cache": "^7.3.0",
"semver": "^7.0.0",
"source-map-support": "^0.x",
"teen_process": "^2.0.1",
"utf7": "^1.0.2"
},
"engines": {
"node": ">=14",
"npm": ">=8"
}
},
"packages/doctor/node_modules/chalk": {
"version": "4.1.2",
"license": "MIT",
@@ -23339,7 +23352,7 @@
}
},
"packages/driver-test-support/node_modules/@types/lodash": {
"version": "4.14.191",
"version": "4.14.188",
"license": "MIT"
},
"packages/eslint-config-appium": {
@@ -23742,7 +23755,32 @@
}
},
"packages/typedoc-plugin-appium": {
"extraneous": true
"name": "@appium/typedoc-plugin-appium",
"version": "0.1.0",
"license": "Apache-2.0",
"dependencies": {
"handlebars": "4.7.7",
"type-fest": "3.2.0",
"typedoc-plugin-markdown": "3.13.6"
},
"engines": {
"node": ">=14",
"npm": ">=8"
},
"peerDependencies": {
"typedoc": ">=0.23.14"
}
},
"packages/typedoc-plugin-appium/node_modules/type-fest": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.2.0.tgz",
"integrity": "sha512-Il3wdLRzWvbAEtocgxGQA9YOoRVeVUGOMBtel5LdEpNeEAol6GJTLw8GbX6Z8EIMfvfhoOXs2bwOijtAZdK5og==",
"engines": {
"node": ">=14.16"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"packages/types": {
"name": "@appium/types",
+11
View File
@@ -0,0 +1,11 @@
# `@appium/typedoc-plugin-appium`
> TypeDoc plugin for Appium & its extensions
## Usage
```
const typedocPluginAppium = require('@appium/typedoc-plugin-appium');
// TODO: DEMONSTRATE API
```
+1
View File
@@ -0,0 +1 @@
module.exports = require('./build/plugin.js');
@@ -0,0 +1,104 @@
import {Context, Logger, ReflectionKind} from 'typedoc';
import {AppiumPluginLogger} from '../logger';
import {
CommandInfo,
CommandRef,
CommandReflection,
CommandsReflection,
ExecuteCommandRef,
NAME_EXECUTE_ROUTE,
ParentReflection,
ProjectCommands,
Route,
} from '../model';
export class CommandTreeBuilder {
#log: AppiumPluginLogger;
constructor(log: AppiumPluginLogger) {
this.#log = log.createChildLogger('builder');
}
public createReflections(ctx: Context, commands: ProjectCommands): void {
const {project} = ctx;
const modules = project.getChildrenByKind(ReflectionKind.Module);
const projectCmdInfo = commands.get(project);
if (modules.length) {
for (const module of modules) {
const commandInfo = commands.get(module);
if (commandInfo) {
this.#createCommandsReflection(module, ctx, commandInfo);
}
}
} else if (projectCmdInfo?.hasCommands) {
this.#createCommandsReflection(project, ctx, projectCmdInfo);
} else {
this.#log.warn(`No commands found in project ${project.name}`);
}
}
/**
* Creates and adds a child {@linkcode CommandReflection} to this reflection
* @param ref Command reference
* @param route Route
* @param parent Commands reflection
*/
#createCommandReflection(
ctx: Context,
ref: CommandRef | ExecuteCommandRef,
route: Route,
parent: CommandsReflection
) {
const commandReflection = new CommandReflection(ref, parent.parent, route, parent);
ctx.postReflectionCreation(commandReflection, undefined, undefined);
ctx.finalizeDeclarationReflection(commandReflection);
}
/**
* Create a new {@linkcode CommandsReflection}
* @param module - Parent module (or project)
* @param ctx - Current context
* @param commandInfo - Command information for `module`
* @returns New {@linkcode CommandsReflection}
*/
#createCommandsReflection(
module: ParentReflection,
ctx: Context,
commandInfo: CommandInfo
): CommandsReflection {
const commandsRef = new CommandsReflection(module.name, ctx.project, commandInfo, module);
ctx.postReflectionCreation(commandsRef, undefined, undefined);
const {routes, executeCommands} = commandInfo;
// sort routes in alphabetical order
const sortedRoutes = new Map([...routes.entries()].sort());
for (const [route, commandMap] of sortedRoutes) {
for (const ref of commandMap.values()) {
this.#createCommandReflection(ctx.withScope(commandsRef), ref, route, commandsRef);
}
}
// sort execute commands in alphabetical order
const sortedExecuteCommands = new Set([...executeCommands].sort());
for (const executeCommandRef of sortedExecuteCommands) {
this.#createCommandReflection(
ctx.withScope(commandsRef),
executeCommandRef,
NAME_EXECUTE_ROUTE,
commandsRef
);
}
ctx.finalizeDeclarationReflection(commandsRef);
return commandsRef;
}
}
export function createReflections(
ctx: Context,
log: AppiumPluginLogger,
commands: ProjectCommands
): void {
new CommandTreeBuilder(log).createReflections(ctx, commands);
}
@@ -0,0 +1,315 @@
import {Context, DeclarationReflection, Logger, ReflectionKind} from 'typedoc';
import {AppiumPluginLogger} from '../logger';
import {
AllowedHttpMethod,
CommandInfo,
CommandMap,
ExecuteCommandSet,
ParentReflection,
ProjectCommands,
RouteMap,
} from '../model';
import {
isDeclarationReflection,
isIntrinsicType,
isLiteralType,
isReflectionType,
isTupleType,
isTypeOperatorType,
} from '../guards';
import {BaseDriverDeclarationReflection, MethodMapDeclarationReflection} from './types';
/**
* Name of the static `newMethodMap` property in a Driver
*/
export const NAME_NEW_METHOD_MAP = 'newMethodMap';
/**
* Name of the static `executeMethodMap` property in a Driver
*/
export const NAME_EXECUTE_METHOD_MAP = 'executeMethodMap';
/**
* Name of the builtin method map in `@appium/base-driver`
*/
export const NAME_METHOD_MAP = 'METHOD_MAP';
/**
* Name of the field in a method map's parameters prop which contains required parameters
*/
export const NAME_REQUIRED = 'required';
/**
* Name of the field in a method map's parameters prop which contains optional parameters
*/
export const NAME_OPTIONAL = 'optional';
/**
* Name of the field in an _execute_ method map which contains parameters
*/
export const NAME_PARAMS = 'params';
/**
* Name of the command in a method map
*/
export const NAME_COMMAND = 'command';
/**
* Name of the field in a _regular_ method map which contains parameters
*/
export const NAME_PAYLOAD_PARAMS = 'payloadParams';
/**
* Name of the module which contains the builtin method map
*/
export const NAME_BUILTIN_COMMAND_MODULE = '@appium/base-driver';
function isBaseDriverDeclarationReflection(value: any): value is BaseDriverDeclarationReflection {
return (
value instanceof DeclarationReflection &&
value.name === NAME_BUILTIN_COMMAND_MODULE &&
value.kindOf(ReflectionKind.Module)
);
}
function isMethodMapDeclarationReflection(value: any): value is MethodMapDeclarationReflection {
return (
isDeclarationReflection(value) &&
((value.name === NAME_NEW_METHOD_MAP && value.flags.isStatic) ||
value.name === NAME_METHOD_MAP) &&
isReflectionType(value.type) &&
isDeclarationReflection(value.type.declaration)
);
}
/**
* Converts declarations to information about Appium commands
*/
export class CommandConverter {
#ctx: Context;
#log: AppiumPluginLogger;
constructor(ctx: Context, log: AppiumPluginLogger) {
this.#ctx = ctx;
this.#log = log.createChildLogger('converter');
}
/**
* Converts declarations into command information
*
* @returns Command info for entire project
*/
public convert(): ProjectCommands {
const ctx = this.#ctx;
const {project} = ctx;
const projectCommands: ProjectCommands = new Map();
// handle baseDriver if it's present
const baseDriver = project.getChildByName(NAME_BUILTIN_COMMAND_MODULE);
if (baseDriver && isBaseDriverDeclarationReflection(baseDriver)) {
this.#log.verbose('Found %s', NAME_BUILTIN_COMMAND_MODULE);
projectCommands.set(baseDriver, this.#convertBaseDriver(baseDriver));
} else {
this.#log.verbose('Did not find %s', NAME_BUILTIN_COMMAND_MODULE);
}
// convert all modules (or just project if no modules)
const modules = project.getChildrenByKind(ReflectionKind.Module);
if (modules.length) {
for (const mod of modules) {
this.#log.verbose('Converting module %s', mod.name);
const cmdInfo = this.#convertModuleClasses(mod);
if (cmdInfo.hasCommands) {
projectCommands.set(mod, this.#convertModuleClasses(mod));
}
this.#log.info('Converted module %s', mod.name);
}
} else {
projectCommands.set(project, this.#convertModuleClasses(project));
}
this.#log.info('Found commands in %d module(s)', projectCommands.size);
return projectCommands;
}
#convertBaseDriver(baseDriver: BaseDriverDeclarationReflection): CommandInfo {
const baseDriverRoutes = this.#convertMethodMap(baseDriver);
if (!baseDriverRoutes.size) {
throw new TypeError(`Could not find any commands in BaseDriver!?`);
}
// no execute commands in BaseDriver
return new CommandInfo(baseDriverRoutes);
}
#convertExecuteMethodMap(refl: DeclarationReflection): ExecuteCommandSet {
const executeMethodMap = refl.getChildByName(NAME_EXECUTE_METHOD_MAP);
const commandRefs: ExecuteCommandSet = new Set();
if (
executeMethodMap?.flags.isStatic &&
isDeclarationReflection(executeMethodMap) &&
isReflectionType(executeMethodMap.type)
) {
for (const newMethodProp of executeMethodMap.type.declaration.getChildrenByKind(
ReflectionKind.Property
)) {
const comment = newMethodProp.comment;
const script = newMethodProp.originalName;
if (isDeclarationReflection(newMethodProp) && isReflectionType(newMethodProp.type)) {
const commandProp = newMethodProp.type.declaration.getChildByName(NAME_COMMAND);
if (isDeclarationReflection(commandProp) && isLiteralType(commandProp.type)) {
const command = String(commandProp.type.value);
const paramsProp = newMethodProp.type.declaration.getChildByName(NAME_PARAMS);
let requiredParams: string[] = [];
let optionalParams: string[] = [];
if (isDeclarationReflection(paramsProp)) {
requiredParams = this.#parseRequiredParams(paramsProp);
optionalParams = this.#parseOptionalParams(paramsProp);
}
commandRefs.add({
command,
requiredParams,
optionalParams,
script,
comment,
});
}
}
}
}
return commandRefs;
}
/**
* Extracts information about `MethodMap` objects
* @param refl - Some reflection we want to inspect. Could refer to a module or a class
* @returns Lookup of routes to {@linkcode CommandMap} objects
*/
#convertMethodMap(refl: DeclarationReflection): RouteMap {
const routes: RouteMap = new Map();
let methodMap: MethodMapDeclarationReflection;
const child = isBaseDriverDeclarationReflection(refl)
? refl.getChildByName(NAME_METHOD_MAP)
: refl.getChildByName(NAME_NEW_METHOD_MAP);
if (isMethodMapDeclarationReflection(child)) {
methodMap = child;
const routeProps = methodMap.type.declaration.getChildrenByKind(ReflectionKind.Property);
if (!routeProps.length) {
this.#log.warn(`No routes found in ${refl.name}`);
}
for (const routeProp of routeProps) {
const route = routeProp.originalName;
if (isDeclarationReflection(routeProp) && isReflectionType(routeProp.type)) {
const httpMethodProps = routeProp.type.declaration.getChildrenByKind(
ReflectionKind.Property
);
if (!httpMethodProps.length) {
this.#log.warn(`No HTTP methods found in route ${refl.name}.${route}`);
}
for (const httpMethodProp of httpMethodProps) {
const comment = httpMethodProp.comment;
const httpMethod = httpMethodProp.originalName as AllowedHttpMethod;
if (isDeclarationReflection(httpMethodProp) && isReflectionType(httpMethodProp.type)) {
const commandProp = httpMethodProp.type.declaration.getChildByName(NAME_COMMAND);
// commandProp is optional.
if (commandProp) {
if (isDeclarationReflection(commandProp) && isLiteralType(commandProp.type)) {
const command = String(commandProp.type.value);
const payloadParamsProp =
httpMethodProp.type.declaration.getChildByName(NAME_PAYLOAD_PARAMS);
let requiredParams: string[] = [];
let optionalParams: string[] = [];
if (isDeclarationReflection(payloadParamsProp)) {
requiredParams = this.#parseRequiredParams(payloadParamsProp);
optionalParams = this.#parseOptionalParams(payloadParamsProp);
}
let commandMap: CommandMap = routes.get(route) ?? new Map();
commandMap.set(command, {
command,
requiredParams,
optionalParams,
httpMethod,
route,
comment,
});
routes.set(route, commandMap);
} else if (
isDeclarationReflection(commandProp) &&
isIntrinsicType(commandProp.type)
) {
this.#log.warn(
`Found intrinsic type for ${commandProp.originalName} ("${commandProp.type.name}"). ${refl.name} must be defined "as const" to associate with a method.`
);
} else {
this.#log.warn(
`Found unknown type for ${commandProp.originalName}: ${commandProp}`
);
}
}
} else {
this.#log.warn(`Invalid {MethodMap} found in ${refl.name}.${route}.${httpMethod}`);
}
}
} else {
this.#log.warn(`Empty route in ${refl.name}.${route}`);
}
}
} else {
this.#log.verbose(`No {MethodMap} found in class ${refl.name}`);
}
return routes;
}
#convertModuleClasses(mod: ParentReflection) {
let routes: RouteMap = new Map();
let executeCommands: ExecuteCommandSet = new Set();
const classReflections = mod.getChildrenByKind(ReflectionKind.Class);
for (const classRef of classReflections) {
this.#log.verbose(`Converting class ${classRef.name}`);
const newMethodMap = this.#convertMethodMap(classRef);
if (newMethodMap.size) {
routes = new Map([...routes, ...newMethodMap]);
}
const executeMethodMap = this.#convertExecuteMethodMap(classRef);
if (executeMethodMap.size) {
executeCommands = new Set([...executeCommands, ...executeMethodMap]);
}
this.#log.verbose(`Converted class ${classRef.name}`);
}
return new CommandInfo(routes, executeCommands);
}
#parseOptionalParams(prop: DeclarationReflection): string[] {
return this.#parseParams(prop, NAME_OPTIONAL);
}
#parseParams(prop: DeclarationReflection, name: string): string[] {
const params = [];
if (isReflectionType(prop.type)) {
const requiredProp = prop.type.declaration.getChildByName(name);
if (
isDeclarationReflection(requiredProp) &&
isTypeOperatorType(requiredProp.type) &&
isTupleType(requiredProp.type.target)
) {
for (const reqd of requiredProp.type.target.elements) {
if (isLiteralType(reqd)) {
params.push(String(reqd.value));
}
}
}
}
return params;
}
#parseRequiredParams(prop: DeclarationReflection): string[] {
return this.#parseParams(prop, NAME_REQUIRED);
}
}
export function convertCommands(ctx: Context, log: AppiumPluginLogger): ProjectCommands {
return new CommandConverter(ctx, log).convert();
}
@@ -0,0 +1,2 @@
export * from './converter';
export * from './builder';
@@ -0,0 +1,11 @@
import {DeclarationReflection, ReflectionType} from 'typedoc';
import {NAME_BUILTIN_COMMAND_MODULE, NAME_METHOD_MAP, NAME_NEW_METHOD_MAP} from './converter';
export type MethodMapDeclarationReflection = DeclarationReflection & {
name: typeof NAME_METHOD_MAP | typeof NAME_NEW_METHOD_MAP;
type: ReflectionType;
};
export type BaseDriverDeclarationReflection = DeclarationReflection & {
name: typeof NAME_BUILTIN_COMMAND_MODULE;
};
@@ -0,0 +1,27 @@
import {
DeclarationReflection,
ReflectionType,
TypeOperatorType,
TupleType,
LiteralType,
IntrinsicType,
} from 'typedoc';
export function isDeclarationReflection(value: any): value is DeclarationReflection {
return value instanceof DeclarationReflection;
}
export function isReflectionType(value: any): value is ReflectionType {
return value instanceof ReflectionType;
}
export function isTypeOperatorType(value: any): value is TypeOperatorType {
return value instanceof TypeOperatorType;
}
export function isLiteralType(value: any): value is LiteralType {
return value instanceof LiteralType;
}
export function isIntrinsicType(value: any): value is IntrinsicType {
return value instanceof IntrinsicType;
}
export function isTupleType(value: any): value is TupleType {
return value instanceof TupleType;
}
@@ -0,0 +1,151 @@
/**
* Adapted from `@knodes/typedoc-pluginutils`
*
* Portions Copyright (c) 2022 KnodesCommunity
* Licensed MIT
*
* @module
* @see https://github.com/KnodesCommunity/typedoc-plugins/blob/ed5e4e87f5d80abf6352e8de353ea376c4f7db6d/packages/pluginutils/src/plugin-logger.ts
*
*/
import {format} from 'node:util';
import {Logger, LogLevel} from 'typedoc';
const LogMethods = {
[LogLevel.Error]: 'error',
[LogLevel.Warn]: 'warn',
[LogLevel.Info]: 'info',
[LogLevel.Verbose]: 'verbose',
} as const;
export class AppiumPluginLogger extends Logger {
/**
* Function provided by `AppiumPluginLogger` parent loggers to log through them.
*/
readonly #logThroughParent?: ParentLogger;
/**
* Parent logger
*/
readonly #parent: Logger;
/**
* Namespace to prepend to log messages
*/
public readonly ns: string;
public constructor(logger: Logger, ns: string, logThroughParent?: ParentLogger) {
super();
this.#parent = logger;
this.ns = ns;
this.level = this.#parent.level;
this.#logThroughParent = logThroughParent;
}
/**
* Log the given error message.
*
* @param text - The error that should be logged.
*/
public error(text: string, ...args: any[]): void {
this.#log(LogLevel.Error, text, ...args);
}
/**
* Log the given info message.
*
* @param text - The message that should be logged.
*/
public info(text: string, ...args: any[]): void {
this.#log(LogLevel.Info, text, ...args);
}
/**
* Print a log message.
*
* Does _not_ support `printf`-style syntax for compatibility with {@linkcode Logger}.
*
* @param text - The message itself.
* @param level - The urgency of the log message.
*/
public log(text: string, level: LogLevel): void {
this.#log(level, text);
}
/**
* Create a new {@link AppiumPluginLogger} for the given context.
*
* @param ns - New sub-namespace; will be appended to the current namespace.
* @returns the new logger.
*/
public createChildLogger(ns: string) {
const newLogger = new AppiumPluginLogger(
this.#parent,
`${this.ns}:${ns}`,
this.#logThrough.bind(this)
);
newLogger.level = this.level;
return newLogger;
}
/**
* Log the given verbose message.
*
* @param text - The message that should be logged.
*/
public verbose(text: string, ...args: any): void {
this.#log(LogLevel.Verbose, text, ...args);
}
/**
* Log the given warning message.
*
* @param text - The warning that should be logged.
*/
public warn(text: string, ...args: any[]): void {
this.#log(LogLevel.Warn, text, ...args);
}
/**
* Format the given message.
*
* Uses the `util.format` function to format the message.
*
* @param ns - Namespace
* @param message - The message to format.
* @returns the formatted message;
*/
#formatMessage(ns: string, message: string, ...args: any[]) {
return format(`[${ns}] ${message}`, ...args);
}
/**
* Print a log message.
*
* @param text - The message itself.
* @param level - The urgency of the log message.
*/
#log(level: LogLevel, text: string, ...args: any[]): void {
if (level < this.level) {
return;
}
this.#logThrough(level, this.ns, text, ...args);
}
/**
* Pass a log message to the parent.
*
* @param level - The urgency of the log message.
* @param message - The message itself.
*/
#logThrough(level: LogLevel, ns: string, message: string, ...args: any[]) {
if (this.#logThroughParent) {
this.#logThroughParent(level, ns, message, ...args);
} else {
const parentMethod = LogMethods[level];
this.#parent[parentMethod](this.#formatMessage(ns, message, ...args));
}
}
}
type ParentLogger = (level: LogLevel, message: string, ...args: any[]) => void;
@@ -0,0 +1,18 @@
import {ExecuteCommandSet, RouteMap} from './types';
/**
* Data structure describing routes and commands for a particular module (or project)
*/
export class CommandInfo {
constructor(
public readonly routes: RouteMap,
public readonly executeCommands: ExecuteCommandSet = new Set()
) {}
/**
* `true` if this instance has some actual data
*/
public get hasCommands() {
return this.executeCommands.size > 0 || this.routes.size > 0;
}
}
@@ -0,0 +1,3 @@
export * from './types';
export * from './reflection';
export * from './command-info';
@@ -0,0 +1,66 @@
import {Comment} from 'typedoc';
import {CommandRef, ExecuteCommandRef, ParentReflection, Route} from '../types';
import {CommandsReflection} from './commands';
import {AppiumPluginReflectionKind} from './kind';
import {AppiumPluginReflection} from './plugin';
/**
* The route will be this
*/
export const NAME_EXECUTE_ROUTE = '/session/:sessionId/execute';
export const HTTP_METHOD_EXECUTE = 'POST';
export class CommandReflection extends AppiumPluginReflection {
public readonly httpMethod: string;
public readonly optionalParams: string[];
public readonly requiredParams: string[];
public readonly route: Route;
public readonly script?: string;
public readonly comment?: Comment;
constructor(
readonly commandRef: CommandRef | ExecuteCommandRef,
module: ParentReflection,
route: Route,
parent: CommandsReflection
) {
const name = CommandReflection.isExecuteCommandRef(commandRef) ? commandRef.script : route;
super(
name,
(CommandReflection.isExecuteCommandRef(commandRef)
? AppiumPluginReflectionKind.EXECUTE_COMMAND
: AppiumPluginReflectionKind.COMMAND) as any,
module,
parent
);
this.route = route;
this.httpMethod = 'httpMethod' in commandRef ? commandRef.httpMethod : HTTP_METHOD_EXECUTE;
this.requiredParams = commandRef.requiredParams ?? [];
this.optionalParams = commandRef.optionalParams ?? [];
this.script = 'script' in commandRef ? commandRef.script : undefined;
this.comment = commandRef.comment;
}
public get hasRequiredParams(): boolean {
return Boolean(this.requiredParams.length);
}
public get hasOptionalParams(): boolean {
return Boolean(this.optionalParams.length);
}
public get isExecuteCommand(): boolean {
return Boolean(this.script && this.route === NAME_EXECUTE_ROUTE);
}
/**
* Type guard for execute command refs
* @param ref Command reference
* @returns `true` if it's an execute command
*/
public static isExecuteCommandRef(ref: CommandRef | ExecuteCommandRef): ref is ExecuteCommandRef {
return 'script' in ref;
}
}
@@ -0,0 +1,39 @@
import {CommandInfo} from '../command-info';
import {ExecuteCommandSet, ParentReflection, RouteMap} from '../types';
import {AppiumPluginReflectionKind} from './kind';
import {AppiumPluginReflection} from './plugin';
export class CommandsReflection extends AppiumPluginReflection {
public readonly executeCommandRefs: ExecuteCommandSet;
public readonly routeMap: RouteMap;
constructor(
name: string,
module: ParentReflection,
commands: CommandInfo,
public override parent: ParentReflection = module
) {
super(name, AppiumPluginReflectionKind.COMMANDS as any, module, parent);
this.parent = parent;
this.routeMap = commands.routes;
this.executeCommandRefs = commands.executeCommands;
}
/**
* Returns the name of the module to which this command belongs.
*
* @see {CommandTreeBuilder.getName}
*/
public get moduleName(): string {
return this.module.name;
}
public get hasExecuteCommands(): boolean {
return Boolean(this.executeCommandRefs.size);
}
public get hasRoutes(): boolean {
return Boolean(this.routeMap.size);
}
}
@@ -0,0 +1,4 @@
export * from './command';
export * from './commands';
export * from './kind';
export * from './plugin';
@@ -0,0 +1,27 @@
import {ReflectionKind} from 'typedoc';
import {addReflectionKind} from './utils';
export const NS = 'appium';
/**
* Extends the {@link ReflectionKind} to add custom Page, Menu & Any kinds.
*/
export enum AppiumPluginReflectionKind {
ROOT = addReflectionKind(NS, 'Root'),
COMMANDS = addReflectionKind(NS, 'Commands'),
COMMAND = addReflectionKind(NS, 'Command'),
EXECUTE_COMMAND = addReflectionKind(NS, 'ExecuteCommand'),
MENU = addReflectionKind(NS, 'Menu'),
ANY = addReflectionKind(NS, 'Any', MENU | COMMAND | EXECUTE_COMMAND | COMMANDS),
}
addReflectionKind(
NS,
'Root Commands',
AppiumPluginReflectionKind.ROOT | AppiumPluginReflectionKind.COMMANDS
);
addReflectionKind(
NS,
'Root Menu',
AppiumPluginReflectionKind.ROOT | AppiumPluginReflectionKind.MENU
);
AppiumPluginReflectionKind as unknown as ReflectionKind;
@@ -0,0 +1,16 @@
import {DeclarationReflection, Reflection} from 'typedoc';
import {ParentReflection} from '../types';
/**
* Abstract base class for all reflections defined by this plugin
*/
export abstract class AppiumPluginReflection extends DeclarationReflection {
constructor(
name: string,
kind: number,
public readonly module: ParentReflection,
parent: Reflection = module
) {
super(name, kind, parent);
}
}
@@ -0,0 +1,31 @@
/**
* Adapted from `@knodes/typedoc-pluginutils`
* @see https://github.com/knodescommunity/typedoc-plugins
* Copyright (c) 2022 KnodesCommunity
* Licensed MIT
* @see https://github.com/KnodesCommunity/typedoc-plugins/blob/05717565fae14357b1c4be8122f3156e1d46d332/LICENSE
*/
import {ReflectionKind} from 'typedoc';
const getHigherBitMask = () =>
Math.max(
...Object.values({...ReflectionKind, All: -1})
.filter((value) => typeof value === 'number')
.map((v) => v.toString(2))
.filter((v) => v.match(/^0*10*$/))
.map((v) => parseInt(v, 2))
);
export const addReflectionKind = (ns: string, name: string, value?: number | null) => {
const fullName = `${ns}:${name}`;
const kindAny = ReflectionKind as any;
const existingValue = kindAny[fullName];
if (existingValue !== null && existingValue !== undefined) {
return existingValue;
}
const defaultedValue = value ?? getHigherBitMask() * 2;
kindAny[fullName] = defaultedValue;
kindAny[defaultedValue] = fullName;
return defaultedValue;
};
@@ -0,0 +1,37 @@
import {Comment, DeclarationReflection, ProjectReflection} from 'typedoc';
import {CommandInfo} from './command-info';
import {CommandsReflection} from './reflection';
/**
* I don't think we allow other HTTP methods?
*/
export type AllowedHttpMethod = 'GET' | 'POST' | 'DELETE';
export type Route = string;
export type Command = string;
export type CommandMap = Map<Command, CommandRef>;
export type ProjectCommands = Map<ParentReflection, CommandInfo>;
export interface BaseCommandRef {
command: string;
optionalParams?: string[];
requiredParams?: string[];
comment?: Comment;
}
export interface CommandRef extends BaseCommandRef {
httpMethod: AllowedHttpMethod;
route: Route;
}
export interface ExecuteCommandRef extends BaseCommandRef {
script: string;
}
export type ParentReflection = DeclarationReflection | ProjectReflection;
export type CommandReflectionMap = Map<ParentReflection, CommandsReflection>;
export type RouteMap = Map<Route, CommandMap>;
export type ExecuteCommandSet = Set<ExecuteCommandRef>;
@@ -0,0 +1 @@
export * from './theme';
@@ -0,0 +1,57 @@
import {ContainerReflection, PageEvent, Renderer} from 'typedoc';
import {MarkdownTheme} from 'typedoc-plugin-markdown';
import {AppiumPluginLogger} from '../../logger';
import {AppiumPluginReflectionKind} from '../../model';
import {compileTemplate, registerHelpers, Template} from './utils';
/**
* Name of the theme; used at definition time
*/
export const THEME_NAME = 'appium';
/**
* Factory for `AppiumTheme` class; needs custom logger otherwise inaccessible
* @param log - Custom logger
* @returns `AppiumTheme` class
*/
export function getTheme(log: AppiumPluginLogger): new (renderer: Renderer) => MarkdownTheme {
return class AppiumTheme extends MarkdownTheme {
#log = log.createChildLogger('theme');
#commandsTemplateRenderer: TemplateRenderer;
constructor(renderer: Renderer) {
super(renderer);
this.#commandsTemplateRenderer = this.#getTemplate(Template.Commands);
// this ensures we overwrite what MarkdownTheme does
registerHelpers();
}
public override get mappings() {
return [
{
kind: [AppiumPluginReflectionKind.COMMANDS as any],
isLeaf: true,
directory: 'commands',
template: this.#commandsTemplateRenderer,
},
...super.mappings,
];
}
#getTemplate(template: Template): TemplateRenderer {
const render = compileTemplate(template);
return (pageEvent: PageEvent<ContainerReflection>) => {
this.#log.verbose('Rendering template for model %s', pageEvent.model.name);
return render(pageEvent, {
allowProtoMethodsByDefault: true,
allowProtoPropertiesByDefault: true,
data: {theme: this},
});
};
}
};
}
type TemplateRenderer = (pageEvent: PageEvent<ContainerReflection>) => string;
@@ -0,0 +1,2 @@
export * from './types';
export * from './appium';
@@ -0,0 +1,14 @@
import {RenderTemplate, RendererEvent, Theme} from 'typedoc';
import {CommandReflection} from '../../model';
export type RenderCommandLinkProps = {page: CommandReflection; label?: string};
export interface IAppiumPluginThemeMethods {
renderPageLink: RenderTemplate<RenderCommandLinkProps>;
}
export interface IAppiumPluginTheme extends Theme {
appiumPlugin(event: RendererEvent): IAppiumPluginThemeMethods;
}
export function isAppiumPluginTheme(theme: Theme): theme is IAppiumPluginTheme {
return 'appiumPlugin' in theme;
}
@@ -0,0 +1,60 @@
import _ from 'lodash';
import fs from 'node:fs';
import path from 'node:path';
import Handlebars from 'handlebars';
import {ContainerReflection, PageEvent, ReflectionKind} from 'typedoc';
import {AppiumPluginReflectionKind} from '../../model';
const RESOURCES_PATH = path.join(__dirname, '..', '..', '..', 'resources');
const TEMPLATE_PATH = path.join(RESOURCES_PATH, 'templates');
const PARTIALS_PATH = path.join(RESOURCES_PATH, 'partials');
const Partials = {
command: 'command.hbs',
executeCommand: 'execute-command.hbs',
} as const;
function registerPartials() {
for (const [name, filename] of Object.entries(Partials)) {
Handlebars.registerPartial(name, fs.readFileSync(path.join(PARTIALS_PATH, filename), 'utf8'));
}
}
registerPartials();
export enum Template {
Commands = 'commands.hbs',
}
export const compileTemplate = _.memoize((template: Template) => {
const templatePath = path.join(TEMPLATE_PATH, template);
return Handlebars.compile(fs.readFileSync(templatePath, 'utf8'));
});
export function registerHelpers() {
Handlebars.registerHelper('reflectionPath', function (this: PageEvent<ContainerReflection>) {
if (this.model) {
if (this.model.kind && this.model.kind !== ReflectionKind.Module) {
if (this.model.kind === (AppiumPluginReflectionKind.COMMANDS as any)) {
return `${this.model.name} Commands`;
}
const title: string[] = [];
if (this.model.parent && this.model.parent.parent) {
if (this.model.parent.parent.parent) {
title.push(
`[${this.model.parent.parent.name}](${Handlebars.helpers.relativeURL(
this.model?.parent?.parent.url
)})`
);
}
title.push(
`[${this.model.parent.name}](${Handlebars.helpers.relativeURL(this.model.parent.url)})`
);
}
title.push(this.model.name);
return title.length > 1 ? `${title.join('.')}` : null;
}
}
return null;
});
}
@@ -0,0 +1,27 @@
import {Application, Context, Converter} from 'typedoc';
import {convertCommands, createReflections} from './converter';
import {AppiumPluginLogger} from './logger';
import {getTheme, THEME_NAME} from './output';
/**
* Loads the Appium TypeDoc plugin
* @param app - TypeDoc Application
*/
export function load(app: Application) {
const log = new AppiumPluginLogger(app.logger, 'appium');
// register our custom theme. the user still has to choose it
app.renderer.defineTheme(THEME_NAME, getTheme(log));
app.converter.on(Converter.EVENT_RESOLVE_BEGIN, (ctx: Context) => {
// we don't want to do this work if we're not using the custom theme!
if (app.renderer.themeName === THEME_NAME) {
// this queries the declarations created by TypeDoc and extracts command information
const projectCommands = convertCommands(ctx, log);
// this creates new custom reflections from the data we gathered and registers them
// with TypeDoc
createReflections(ctx, log, projectCommands);
}
});
}
@@ -0,0 +1,60 @@
{
"name": "@appium/typedoc-plugin-appium",
"version": "0.1.0",
"description": "TypeDoc plugin for Appium & its extensions",
"homepage": "https://appium.io/",
"license": "Apache-2.0",
"main": "index.js",
"publishConfig": {
"access": "public"
},
"repository": {
"type": "git",
"url": "https://github.com/appium/appium.git",
"directory": "packages/support"
},
"keywords": [
"typedoc-plugin"
],
"author": "https://github.com/appium",
"types": "./build/plugin.d.ts",
"scripts": {
"build": "exit 0",
"dev": "npm run build -- --watch",
"fix": "npm run lint -- --fix",
"lint": "eslint -c ../../.eslintrc --ignore-path ../../.eslintignore .",
"prepare": "npm run build",
"test": "exit 0",
"test:e2e": "exit 0",
"test:smoke": "node ./index.js",
"test:unit": "exit 0"
},
"bugs": {
"url": "https://github.com/appium/appium/issues"
},
"directories": {
"lib": "lib",
"test": "test"
},
"files": [
"index.js",
"lib",
"build",
"resources"
],
"engines": {
"node": ">=14",
"npm": ">=8"
},
"typedoc": {
"entryPoint": "./lib/plugin.ts"
},
"peerDependencies": {
"typedoc": ">=0.23.14"
},
"dependencies": {
"handlebars": "4.7.7",
"type-fest": "3.2.0",
"typedoc-plugin-markdown": "3.13.6"
}
}
@@ -0,0 +1,33 @@
{{#unless hasOwnDocument}}
### {{#ifNamedAnchors}} <a id="{{anchor}}" name="{{this.anchor}}"></a> {{/ifNamedAnchors}}`{{httpMethod}}` `{{name}}`
{{#if hasComment}}
{{> comment}}
{{/if}}
{{#if hasRequiredParams}}
#### Required Parameters
{{#each requiredParams}}
- `{{this}}`
{{/each}}
{{/if}}
{{#if hasOptionalParams}}
#### Optional Parameters
{{#each optionalParams}}
- `{{this}}`
{{/each}}
{{/if}}
{{/unless}}
@@ -0,0 +1,37 @@
{{#unless hasOwnDocument}}
### {{#ifNamedAnchors}} <a id="{{anchor}}" name="{{this.anchor}}"></a> {{/ifNamedAnchors}}Script: `{{name}}`
{{#if hasComment}}
{{> comment}}
{{/if}}
#### Route
`{{httpMethod}} {{route}}`
{{#if hasRequiredParams}}
#### Required Parameters
{{#each requiredParams}}
- `{{this}}`
{{/each}}
{{/if}}
{{#if hasOptionalParams}}
#### Optional Parameters
{{#each optionalParams}}
- `{{this}}`
{{/each}}
{{/if}}
{{/unless}}
@@ -0,0 +1,39 @@
{{> header}}
{{#ifShowPageTitle}}
# {{{reflectionTitle true}}}
{{/ifShowPageTitle}}
{{#with model}}
{{#if hasComment}}
{{> comment}}
{{/if}}
{{/with}}
{{#with model}}
{{#if hasRoutes}}
## Routes
{{#each children}}
{{#unless isExecuteCommand}}
{{> command}}
{{/unless}}
{{/each}}
{{/if}}
{{#if hasExecuteCommands}}
## Execute Scripts
{{#each children}}
{{#if isExecuteCommand}}
{{> executeCommand}}
{{/if}}
{{/each}}
{{/if}}
{{/with}}
@@ -0,0 +1,18 @@
{
"extends": "../../config/tsconfig.base.json",
"compilerOptions": {
"rootDir": "lib",
"outDir": "build",
"strict": true,
"emitDeclarationOnly": false,
"module": "CommonJS",
"sourceMap": true,
"jsx": "react",
"jsxFactory": "JSX.createElement",
"jsxFragmentFactory": "JSX.Fragment",
"lib": ["ES2022"],
"downlevelIteration": true
},
"include": ["lib/**/*.ts*"],
"references": []
}
+3
View File
@@ -61,6 +61,9 @@
},
{
"path": "packages/fake-driver"
},
{
"path": "packages/typedoc-plugin-appium"
}
]
}
+3 -2
View File
@@ -1,11 +1,12 @@
{
"$schema": "https://typedoc.org/schema.json",
"entryPoints": ["./packages/appium", "./packages/base-driver", "./packages/support", "./packages/types", "./packages/base-plugin"],
"entryPoints": ["./packages/appium", "./packages/base-driver", "./packages/support", "./packages/types", "./packages/base-plugin", "./packages/fake-driver", "./packages/typedoc-plugin-appium"],
"entryPointStrategy": "packages",
"name": "Appium",
"includeVersion": false,
"tsconfig": "./tsconfig.json",
"out": "typedoc-docs",
"cleanOutputDir": true,
"plugin": ["typedoc-plugin-resolve-crossmodule-references"]
"plugin": ["typedoc-plugin-resolve-crossmodule-references", "typedoc-plugin-markdown", "./packages/typedoc-plugin-appium"],
"theme": "appium"
}