mirror of
https://github.com/appium/appium.git
synced 2026-10-23 09:09:16 +08:00
e9426fcfec
The resulting solution keeps a bunch of weak refs to `Consola` objects and sets the log level on all of them if it ever changes. This is--at minimum--easier to understand than the broken `Proxy` implementation.
172 lines
4.9 KiB
TypeScript
172 lines
4.9 KiB
TypeScript
/**
|
|
* Builds reference documentation via TypeDoc. The output is _markdown_, intended to be imported into MkDocs.
|
|
*
|
|
* @module
|
|
*/
|
|
|
|
import {fs} from '@appium/support';
|
|
import _ from 'lodash';
|
|
import path from 'node:path';
|
|
import {Application, ArgumentsReader, TypeDocOptions, TypeDocReader} from 'typedoc';
|
|
import {
|
|
DEFAULT_LOG_LEVEL,
|
|
DEFAULT_REL_TYPEDOC_OUT_PATH,
|
|
NAME_BIN,
|
|
NAME_TYPEDOC_JSON,
|
|
} from '../constants';
|
|
import {DocutilsError} from '../error';
|
|
import {findTypeDocJsonPath, readTypedocJson} from '../fs';
|
|
import {getLogger} from '../logger';
|
|
import {argify, relative, stopwatch} from '../util';
|
|
|
|
const log = getLogger('builder:reference');
|
|
|
|
/**
|
|
* Executes TypeDoc _in the current process_
|
|
*
|
|
* You will probably want to run `updateNav()` after this.
|
|
*
|
|
* @privateRemarks Monkeypatches TypeDoc's homebrew "glob" implementation because it is broken
|
|
* @parma typeDocJsonPath - Path to `typedoc.json`
|
|
* @param opts - TypeDoc options
|
|
*/
|
|
export async function runTypedoc(typeDocJsonPath: string, opts: Record<string, string>) {
|
|
const args = argify(opts);
|
|
log.debug('TypeDoc args:', args);
|
|
const app = new Application();
|
|
app.options.setValue('plugin', [
|
|
'typedoc-plugin-markdown',
|
|
'typedoc-plugin-resolve-crossmodule-references',
|
|
'@appium/typedoc-plugin-appium',
|
|
]);
|
|
app.options.addReader(new TypeDocReader());
|
|
app.options.addReader(new ArgumentsReader(100, args));
|
|
app.bootstrap({options: path.dirname(typeDocJsonPath)});
|
|
const out = app.options.getValue('out');
|
|
const project = app.convert();
|
|
if (project) {
|
|
return await app.generateDocs(project, out);
|
|
}
|
|
|
|
throw new DocutilsError('TypeDoc found nothing to document. Is your package empty?');
|
|
}
|
|
|
|
/**
|
|
* Options for {@linkcode buildReferenceDocs}
|
|
*/
|
|
export interface BuildReferenceOptions {
|
|
/**
|
|
* Path to `typedoc.json`
|
|
*/
|
|
typedocJson?: string;
|
|
/**
|
|
* Current working directory
|
|
*/
|
|
cwd?: string;
|
|
/**
|
|
* Path to `package.json`
|
|
*/
|
|
packageJson?: string;
|
|
/**
|
|
* Path to `tsconfig.json`
|
|
*/
|
|
tsconfigJson?: string;
|
|
/**
|
|
* "Title" for generated docs; this corresponds to {@linkcode typedoc.TypeDocOptionMap.name}
|
|
*/
|
|
title?: string;
|
|
/**
|
|
* This is here because we pass it thru to TypeDoc
|
|
*/
|
|
logLevel?: LogLevelName;
|
|
}
|
|
|
|
/**
|
|
* Log level names as supported by this package
|
|
*
|
|
* Used to convert our log level to TypeDoc's
|
|
*/
|
|
type LogLevelName = 'debug' | 'info' | 'error' | 'warn';
|
|
|
|
/**
|
|
* Mapping of whatever our log level is to whatever TypeDoc's should be.
|
|
*
|
|
* TypeDoc's "info" is too verbose for our needs, and it's our default, so
|
|
* we map it to "warn".
|
|
*/
|
|
const TypeDocLogLevelMap: Record<LogLevelName, string> = {
|
|
debug: 'Verbose',
|
|
info: 'Warn',
|
|
warn: 'Warn',
|
|
error: 'Error',
|
|
};
|
|
|
|
/**
|
|
* Build reference documentation via TypeDoc
|
|
* @param opts - Options
|
|
*/
|
|
export async function buildReferenceDocs({
|
|
typedocJson: typeDocJsonPath,
|
|
cwd = process.cwd(),
|
|
tsconfigJson: tsconfig,
|
|
logLevel = DEFAULT_LOG_LEVEL,
|
|
title,
|
|
}: BuildReferenceOptions = {}) {
|
|
const stop = stopwatch('buildReferenceDocs');
|
|
typeDocJsonPath = typeDocJsonPath
|
|
? path.resolve(cwd, typeDocJsonPath)
|
|
: await findTypeDocJsonPath(cwd);
|
|
if (!typeDocJsonPath) {
|
|
throw new DocutilsError(
|
|
`Could not find ${NAME_TYPEDOC_JSON} from ${cwd}; run "${NAME_BIN}" to create it`
|
|
);
|
|
}
|
|
const pkgRoot = fs.findRoot(cwd);
|
|
const relativePath = relative(cwd);
|
|
const relativeTypeDocJsonPath = relativePath(typeDocJsonPath);
|
|
log.debug(`Using ${relativeTypeDocJsonPath} as typedoc.json`);
|
|
|
|
let typeDocJson: Readonly<Partial<TypeDocOptions>>;
|
|
// we only need typedoc.json to make sure we have a custom "out" path.
|
|
try {
|
|
typeDocJson = readTypedocJson(typeDocJsonPath);
|
|
log.debug('Contents of %s: %O', relativeTypeDocJsonPath, typeDocJson);
|
|
} catch (err) {
|
|
log.error(err);
|
|
throw new DocutilsError(
|
|
`Could not read ${relativeTypeDocJsonPath}; run "${NAME_BIN} init" to create it`
|
|
);
|
|
}
|
|
|
|
// if for some reason "out" is not in typedoc.json, we want to use our default path.
|
|
// otherwise, typedoc's default behavior is to write to the "docs" dir, which is the same dir that
|
|
// we use (by default) as a source dir for the mkdocs site--which might contain files under vcs.
|
|
let out: string | undefined;
|
|
if (typeDocJson.out) {
|
|
log.debug(`Found "out" option in ${NAME_TYPEDOC_JSON}: ${typeDocJson.out}`);
|
|
} else {
|
|
out = path.relative(
|
|
path.dirname(typeDocJsonPath),
|
|
path.join(pkgRoot, DEFAULT_REL_TYPEDOC_OUT_PATH)
|
|
);
|
|
log.debug('Setting "out" option to %s', out);
|
|
}
|
|
|
|
const extraTypedocOpts = _.pickBy(
|
|
{tsconfig, name: title, out, logLevel: TypeDocLogLevelMap[logLevel]},
|
|
Boolean
|
|
) as Record<string, string>;
|
|
|
|
try {
|
|
await runTypedoc(typeDocJsonPath, extraTypedocOpts);
|
|
const finalOut = (typeDocJson.out ?? out) as string;
|
|
log.success(
|
|
'Reference docs built at %s (%dms)',
|
|
path.isAbsolute(finalOut) ? relativePath(finalOut) : finalOut,
|
|
stop()
|
|
);
|
|
} catch (err) {
|
|
log.error(err);
|
|
}
|
|
}
|