Skip to content

Commit

Permalink
feat(typedoc-plugin-appium): add options and 3p support
Browse files Browse the repository at this point in the history
E2E tests are quasi-blocking on merge & release of TypeStrong/typedoc#2130; it is a huge pain to test without this.

- Adds some options to control output
- Rename `CommandsReflection` to `ExtensionReflection` due to poor granular control over the display name
- Rename the custom "kinds"; add kinds for drivers and plugins (which affects display titles)
- Remove namespace from "kinds" for same reason
- Refactors, reformatting
- Rename entry point from `plugin.ts` to `index.ts`
- Update keywords in `package.json` for auto-discovery
- Remove cruft from root `typedoc.json`
- Update `README.md`
- Update peer dependencies; the other plugins must be peer deps or typedoc will be unable to auto-discover them
  • Loading branch information
boneskull committed Jan 5, 2023
1 parent 0ea16e2 commit daee4b2
Show file tree
Hide file tree
Showing 31 changed files with 525 additions and 185 deletions.
9 changes: 5 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Expand Up @@ -45,14 +45,15 @@
"lint:fix": "run-s \"lint -- --fix\"",
"lint:commit": "commitlint",
"lint:staged": "lint-staged",
"pack-all": "npm pack --workspaces --include-workspace-root",
"prepublishOnly": "run-s rebuild",
"reinstall": "run-s clean ci",
"rebuild": "run-s clean:artifacts build:all",
"start": "appium",
"sync-pkgs": "run-p sync-pkgs:*",
"sync-pkgs:appium-readme": "sync-monorepo-packages --force --no-package-json --packages=packages/appium README.md",
"sync-pkgs:common-fields": "sync-monorepo-packages --fields=author,license,bugs,homepage,engines",
"sync-pkgs:keywords": "sync-monorepo-packages --field=keywords --packages=\"packages/*\" --packages=\"!packages/eslint-config-appium\" --packages=\"!packages/types\"",
"sync-pkgs:keywords": "sync-monorepo-packages --field=keywords --packages=\"packages/*\" --packages=\"!packages/eslint-config-appium\" --packages=\"!packages/types\" --packages\"!packages/typedoc-plugin-appium\"",
"sync-pkgs:license": "sync-monorepo-packages --force --no-package-json LICENSE",
"stage-synced-pkgs": "git add -A packages/appium/README.md \"packages/*/package.json\"",
"test": "npm-run-all -p lint build -s test:unit",
Expand Down
61 changes: 57 additions & 4 deletions packages/typedoc-plugin-appium/README.md
@@ -1,11 +1,64 @@
# `@appium/typedoc-plugin-appium`

> TypeDoc plugin for Appium & its extensions
> TypeDoc plugin for [Appium](https://appium.io) & its extensions
## Usage
## Overview

This package leverages [TypeDoc](https://typedoc.org) to generate command documentation (HTTP endpoints, payload information, etc.) for Appium v2+ drivers and plugins.

## Installation

```bash
npm install appium@next typedoc @appium/typedoc-plugin-appium --save-dev
```
const typedocPluginAppium = require('@appium/typedoc-plugin-appium');

// TODO: DEMONSTRATE API
`typedoc` and `appium` are peer dependencies of this package. Newer versions of `npm` will install these automatically (if possible).

## Usage

TypeDoc is configured via a `typedoc.json` or `typedoc.js` file ([read the docs](https://typedoc.org/guides/options/) for more information).

An Appium extension author wishing to generate documentation for their extension will need to create a `typedoc.json`. At minimum, it should contain:

> TODO: The plugin should be able to detect the entry points automatically, so this should not be necessary
```json
{
"entryPointStrategy": "packages",
"entryPoints": [
"./node_modules/appium",
"./node_modules/@appium/base-driver",
"./node_modules/@appium/types",
"."
],
"name": "<name of extension>",
"out": "<path to output directory>"
}
```

Once this file is created, you can run `typedoc` to generate the documentation, and it will be output into the `out` directory as configured above.

## Options

This plugin supports all of the options from [typedoc-plugin-markdown](https://npm.im/typedoc-plugin-markdown), as well as the following:

### `commandsDir`

> TODO
### `forceBreadcrumbs`

> TODO
### `outputBuiltinCommands`

> TODO
### `outputModules`

> TODO
## License

Copyright © 2022 OpenJS Foundation. Licensed Apache-2.0

2 changes: 1 addition & 1 deletion packages/typedoc-plugin-appium/index.js
@@ -1 +1 @@
module.exports = require('./build/lib/plugin.js');
module.exports = require('./build/lib/index.js');
32 changes: 16 additions & 16 deletions packages/typedoc-plugin-appium/lib/converter/builder.ts
Expand Up @@ -13,7 +13,7 @@ import {
CommandData,
ModuleCommands,
CommandReflection,
CommandsReflection,
ExtensionReflection,
ExecMethodData,
ParentReflection,
ProjectCommands,
Expand Down Expand Up @@ -42,7 +42,7 @@ export function createCommandReflection(
log: AppiumPluginLogger,
ctx: Context,
data: CommandData | ExecMethodData,
parent: CommandsReflection,
parent: ExtensionReflection,
route?: Route
): void {
const commandRefl = new CommandReflection(data, parent, route);
Expand All @@ -52,7 +52,7 @@ export function createCommandReflection(
}

/**
* Create a new {@linkcode CommandsReflection} and all {@linkcode CommandReflection} children within
* Create a new {@linkcode ExtensionReflection} and all {@linkcode CommandReflection} children within
* it.
*
* Note that the return value is mainly for informational purposes, since this method mutates
Expand All @@ -63,27 +63,27 @@ export function createCommandReflection(
* @param moduleCmds - Command information for `module`
* @internal
*/
export function createCommandsReflection(
export function createExtensionReflection(
log: AppiumPluginLogger,
ctx: Context,
parent: ParentReflection,
moduleCmds: ModuleCommands
): CommandsReflection {
): ExtensionReflection {
// TODO: parent.name may not be right here
const commandsRefl = new CommandsReflection(parent.name, ctx.project, moduleCmds);
const extRefl = new ExtensionReflection(parent.name, parent, moduleCmds);
/**
* See note in {@link createCommandReflection} above about this call
*/
ctx.postReflectionCreation(commandsRefl, undefined, undefined);
ctx.postReflectionCreation(extRefl, undefined, undefined);

const parentCtx = ctx.withScope(commandsRefl);
const parentCtx = ctx.withScope(extRefl);
const {routeMap: routeMap, execMethodDataSet: execCommandsData} = moduleCmds;

const sortedRouteMap = new Map([...routeMap].sort(([a], [b]) => a.localeCompare(b)));

for (const [route, commandSet] of sortedRouteMap) {
for (const data of commandSet) {
createCommandReflection(log, parentCtx, data, commandsRefl, route);
createCommandReflection(log, parentCtx, data, extRefl, route);
}
}

Expand All @@ -92,11 +92,11 @@ export function createCommandsReflection(
a.script.localeCompare(b.script)
);
for (const data of sortedExecCommandsData) {
createCommandReflection(log, parentCtx, data, commandsRefl);
createCommandReflection(log, parentCtx, data, extRefl);
}

ctx.finalizeDeclarationReflection(commandsRefl);
return commandsRefl;
ctx.finalizeDeclarationReflection(extRefl);
return extRefl;
}

/**
Expand All @@ -106,18 +106,18 @@ export function createCommandsReflection(
* @param ctx TypeDoc Context
* @param parentLog Plugin logger
* @param projectCmds Command info from converter; a map of parent reflections to parsed data
* @returns List of {@linkcode CommandsReflection} instances
* @returns List of {@linkcode ExtensionReflection} instances
*/
export function createReflections(
ctx: Context,
parentLog: AppiumPluginLogger,
projectCmds: ProjectCommands
): CommandsReflection[] {
): ExtensionReflection[] {
const log = parentLog.createChildLogger('builder');
const {project} = ctx;

if (_.isEmpty(projectCmds)) {
log.error('No Appium commands found in the entire project!');
log.warn('Nothing to do.');
return [];
}

Expand All @@ -127,7 +127,7 @@ export function createReflections(
? project
: findChildByNameAndGuard(project, parentName, isParentReflection)!;

const cmdsRefl = createCommandsReflection(log, ctx, parentRefl, parentCmds);
const cmdsRefl = createExtensionReflection(log, ctx, parentRefl, parentCmds);

log.info(
'(%s) Created %d new command %s',
Expand Down
@@ -1,19 +1,24 @@
import {Context, ReflectionKind} from 'typedoc';
import {
isAppiumTypesReflection,
isAsyncMethodDeclarationReflection,
isExternalDriverDeclarationReflection,
} from '../guards';
import pluralize from 'pluralize';
import {Context} from 'typedoc';
import {isAppiumTypesReflection, isExternalDriverDeclarationReflection} from '../guards';
import {AppiumPluginLogger} from '../logger';
import {deriveComment} from './comment';
import {BaseConverter} from './base-converter';
import {AppiumTypesReflection, KnownMethodData, KnownMethods} from './types';
import {findParentReflectionByName} from './utils';
import {AppiumTypesReflection, KnownMethods} from './types';
import {findAsyncMethodsInReflection, findParentReflectionByName} from './utils';

/**
* Name of the module containing `ExternalDriver`
*/
export const NAME_TYPES_MODULE = '@appium/types';

/**
* Name of `ExternalDriver` interface
*/
export const NAME_EXTERNAL_DRIVER = 'ExternalDriver';

/**
* Converts `@appium/types` into a `KnownMethods`, if it can.
*/
export class BuiltinExternalDriverConverter extends BaseConverter<KnownMethods> {
/**
* Creates a child logger for this instance
Expand All @@ -24,54 +29,37 @@ export class BuiltinExternalDriverConverter extends BaseConverter<KnownMethods>
super(ctx, log.createChildLogger('interfaces'));
}

public convertMethodDeclarations(refl: AppiumTypesReflection): KnownMethods {
#convertMethodDeclarations(refl: AppiumTypesReflection): KnownMethods {
const externalDriver = refl.getChildByName(NAME_EXTERNAL_DRIVER);
let methods: KnownMethods = new Map();

if (!isExternalDriverDeclarationReflection(externalDriver)) {
this.log.warn('Could not find %s in %s', NAME_EXTERNAL_DRIVER, NAME_TYPES_MODULE);
return methods;
}
const methodRefs = externalDriver.getChildrenByKind(ReflectionKind.Method);

if (!methodRefs.length) {
methods = findAsyncMethodsInReflection(externalDriver);

if (!methods.size) {
this.log.warn('No methods found in %s', NAME_EXTERNAL_DRIVER);
return methods;
}

methods = new Map(
methodRefs.reduce((methods, method) => {
if (isAsyncMethodDeclarationReflection(method)) {
const commentData = deriveComment(method.name, undefined, method);
return [
...methods,
[
method.name,
{
method,
comment: commentData?.comment,
commentSource: commentData?.commentSource,
},
],
];
}
this.log.verbose('(%s) Ignoring method %s', externalDriver.name, method.name);
return methods;
}, [] as [string, KnownMethodData][])
this.log.verbose(
'Found %s in %s',
pluralize('method declaration', methods.size, true),
NAME_EXTERNAL_DRIVER
);

this.log.verbose('Found %d method declarations in %s', methods.size, NAME_EXTERNAL_DRIVER);

return methods;
}

public convert(): KnownMethods {
public override convert(): KnownMethods {
const {project} = this.ctx;
let methods: KnownMethods = new Map();

const typesModule = findParentReflectionByName(project, NAME_TYPES_MODULE);
if (!isAppiumTypesReflection(typesModule)) {
this.log.warn('Invalid or missing %s', NAME_TYPES_MODULE);
this.log.error('Could not find %s', NAME_TYPES_MODULE);
return methods;
}

Expand All @@ -81,6 +69,6 @@ export class BuiltinExternalDriverConverter extends BaseConverter<KnownMethods>
NAME_EXTERNAL_DRIVER
);

return this.convertMethodDeclarations(typesModule);
return this.#convertMethodDeclarations(typesModule);
}
}
Expand Up @@ -12,7 +12,7 @@ import {convertMethodMap} from './method-map';
import {KnownMethods} from './types';
import {
findChildByNameAndGuard,
findMethodsInClassReflection,
findAsyncMethodsInReflection,
findParentReflectionByName,
} from './utils';

Expand Down Expand Up @@ -85,7 +85,7 @@ export class BuiltinMethodMapConverter extends BaseConverter<BuiltinCommands | u
log: this.log,
methodMapRef: methodMap,
parentRefl: baseDriverModuleRefl,
methods: findMethodsInClassReflection(baseDriverClassRefl, this.knownMethods),
methods: findAsyncMethodsInReflection(baseDriverClassRefl, this.knownMethods),
});

if (!baseDriverRoutes.size) {
Expand Down
4 changes: 2 additions & 2 deletions packages/typedoc-plugin-appium/lib/converter/external.ts
Expand Up @@ -23,7 +23,7 @@ import {
filterChildrenByGuard,
findChildByGuard,
findChildByNameAndGuard,
findMethodsInClassReflection,
findAsyncMethodsInReflection,
} from './utils';

/**
Expand Down Expand Up @@ -139,7 +139,7 @@ export class ExternalConverter extends BaseConverter<ProjectCommands> {
const classReflections = filterChildrenByGuard(parentRefl, isClassDeclarationReflection);

for (const classRefl of classReflections) {
const methods = findMethodsInClassReflection(classRefl, this.knownMethods);
const methods = findAsyncMethodsInReflection(classRefl, this.knownMethods);

if (!methods.size) {
this.log.warn('(%s) No async methods found', classRefl.name);
Expand Down

0 comments on commit daee4b2

Please sign in to comment.