@appium/docutils
Version:
Documentation generation utilities for Appium and related projects
146 lines (127 loc) • 4.14 kB
text/typescript
/**
* Runs `mkdocs`, pulling in documentation from the `docs_dir` directory
* (as configured in `mkdocs.yml`).
*
* @module
*/
import path from 'node:path';
import type {TeenProcessExecOptions} from 'teen_process';
import {DEFAULT_SITE_DIR, NAME_BIN, NAME_MKDOCS, NAME_MKDOCS_YML} from '../constants';
import {DocutilsError} from '../error';
import {findMkDocsYml, isMkDocsInstalled, readMkDocsYml, requirePython} from '../fs';
import {getLogger} from '../logger';
import type {SpawnBackgroundProcessOpts} from '../utils';
import {execWithErrorHandling, relative, spawnBackgroundProcess, stopwatch} from '../utils';
const log = getLogger('mkdocs');
/**
* Options for {@linkcode buildSite}.
*/
export interface BuildMkDocsOpts {
/**
* Path to `mkdocs.yml`
*/
mkdocsYml?: string;
/**
* Path to output directory
*/
siteDir?: string;
/**
* MkDocs theme to use
* @defaultValue 'mkdocs'
*/
theme?: string;
/**
* Current working directory
* @defaultValue `process.cwd()`
*/
cwd?: string;
/**
* Path to `package.json`
*
* Used to find `mkdocs.yml` if unspecified.
*/
packageJson?: string;
/**
* If `true`, run `mkdocs serve` instead of `mkdocs build`
*/
serve?: boolean;
/**
* Extra options for {@linkcode teen_process.exec}
*/
execOpts?: TeenProcessExecOptions;
/**
* Extra options for {@linkcode spawnBackgroundProcess}
*/
serveOpts?: SpawnBackgroundProcessOpts;
}
/**
* Runs `mkdocs build` or `mkdocs serve`
* @param opts
*/
export async function buildSite({
mkdocsYml: mkDocsYmlPath,
siteDir,
cwd = process.cwd(),
serve = false,
serveOpts,
execOpts,
}: BuildMkDocsOpts = {}) {
const stop = stopwatch('build-mkdocs');
const pythonPath = await requirePython();
const mkdocsInstalled = await isMkDocsInstalled();
if (!mkdocsInstalled) {
throw new DocutilsError(`Could not find MkDocs executable; please run "${NAME_BIN} init"`);
}
mkDocsYmlPath = mkDocsYmlPath ? path.resolve(process.cwd(), mkDocsYmlPath) : await findMkDocsYml(cwd);
if (!mkDocsYmlPath) {
throw new DocutilsError(`Could not find ${NAME_MKDOCS_YML} from ${cwd}; please run "${NAME_BIN} init"`);
}
const mkdocsArgs = ['-f', mkDocsYmlPath];
if (siteDir) {
mkdocsArgs.push('-d', siteDir);
}
if (serve) {
// unsure about how SIGHUP is handled here
await doServe(pythonPath, mkdocsArgs, serveOpts);
} else {
log.info('Building site...');
await doBuild(pythonPath, mkdocsArgs, execOpts);
let relSiteDir;
if (siteDir) {
relSiteDir = relative(cwd, siteDir);
} else {
({site_dir: siteDir} = await readMkDocsYml(mkDocsYmlPath));
if (siteDir) {
log.debug('Found site_dir %s', siteDir);
relSiteDir = relative(path.dirname(mkDocsYmlPath), siteDir);
} else {
log.warn('No site_dir specified in args or %s; using default site_dir: %s', NAME_MKDOCS_YML, DEFAULT_SITE_DIR);
relSiteDir = relative(cwd, DEFAULT_SITE_DIR);
}
}
log.success('Finished building site into %s (%dms)', relSiteDir, stop());
}
}
/**
* Runs `mkdocs serve`
* @param pythonPath Path to Python 3 executable
* @param args Extra args to `mkdocs build`
* @param opts Extra options for `teen_process.Subprocess.start`
* @param mkDocsPath Path to `mkdocs` executable
*/
async function doServe(pythonPath: string, args: string[] = [], opts: SpawnBackgroundProcessOpts = {}) {
const finalArgs = ['-m', NAME_MKDOCS, 'serve', ...args];
log.debug('Executing %s via: %s, %O', NAME_MKDOCS, pythonPath, finalArgs);
return spawnBackgroundProcess(pythonPath, finalArgs, opts);
}
/**
* Runs `mkdocs build`
* @param pythonPath Path to Python 3 executable
* @param args Extra args to `mkdocs build`
* @param opts Extra options to `teen_process.exec`
*/
async function doBuild(pythonPath: string, args: string[] = [], opts: TeenProcessExecOptions = {}) {
const finalArgs = ['-m', NAME_MKDOCS, 'build', ...args];
log.debug('Executing %s via: %s, %O', NAME_MKDOCS, pythonPath, finalArgs);
return await execWithErrorHandling(pythonPath, finalArgs, opts);
}