feat(docutils): better mkdocs validation
When validating `mkdocs.yml`, `appium-docs` is now aware of the `INHERITS` property, and expands this property (by loading more `mkdocs.yml` files).
Also:
Updated the types for `mkdocs.yml`
Squelch warnings from `YAML.parse()`
Change YAML indentation back to 2, since that seems to be correct and I was confused
MODIFY
packages/docutils/lib/fs.ts
packages/docutils/lib/fs.ts
@@ -22,6 +22,7 @@ import {
NAME_PYTHON,
} from './constants';
import {DocutilsError} from './error';
+import {MkDocsYml} from './model';
const log = logger.withTag('fs');
@@ -34,14 +35,12 @@ export const findPkgDir = _.memoize(_pkgDir);
/**
* Stringifies a thing into a YAML
- *
- * The indent is 4 because Prettier
* @param value Something to yamlify
* @returns Some nice YAML 4 u
*/
export const stringifyYaml: (value: JsonValue) => string = _.partialRight(
YAML.stringify,
- {indent: 4},
+ {indent: 2},
undefined
);
@@ -70,7 +69,10 @@ export const stringifyJson: (value: JsonValue) => string = _.partialRight(
* Reads a YAML file, parses it and caches the result
*/
export const readYaml = _.memoize(async (filepath: string) =>
- YAML.parse(await fs.readFile(filepath, 'utf8'))
+ YAML.parse(await fs.readFile(filepath, 'utf8'), {
+ prettyErrors: false,
+ logLevel: 'silent',
+ })
);
/**
@@ -214,3 +216,39 @@ export const whichNpm = _.partial(cachedWhich, NAME_NPM);
* Finds `python` executable
*/
export const whichPython = _.partial(cachedWhich, NAME_PYTHON);
+
+/**
+ * Reads an `mkdocs.yml` file, merges inherited configs, and returns the result. The result is cached.
+ *
+ * **IMPORTANT**: The paths of `site_dir` and `docs_dir` are resolved to absolute paths, since they
+ * are expressed as relative paths, and each inherited config file can live in different paths.
+ * @param filepath Patgh to an `mkdocs.yml` file
+ * @returns Parsed `mkdocs.yml` file
+ */
+export const readMkDocsYml = _.memoize(
+ async (filepath: string, cwd = process.cwd()): Promise<MkDocsYml> => {
+ let mkDocsYml = <MkDocsYml>await readYaml(filepath);
+ if (mkDocsYml.site_dir) {
+ mkDocsYml.site_dir = path.resolve(cwd, path.dirname(filepath), mkDocsYml.site_dir);
+ }
+ if (mkDocsYml.INHERIT) {
+ let inheritPath: string | undefined = path.resolve(path.dirname(filepath), mkDocsYml.INHERIT);
+ while (inheritPath) {
+ const inheritYml = <MkDocsYml>await readYaml(inheritPath);
+ if (inheritYml.site_dir) {
+ inheritYml.site_dir = path.resolve(path.dirname(inheritPath), inheritYml.site_dir);
+ log.debug('Resolved site_dir to %s', inheritYml.site_dir);
+ }
+ if (inheritYml.docs_dir) {
+ inheritYml.docs_dir = path.resolve(path.dirname(inheritPath), inheritYml.docs_dir);
+ log.debug('Resolved docs_dir to %s', inheritYml.docs_dir);
+ }
+ mkDocsYml = _.defaultsDeep(mkDocsYml, inheritYml);
+ inheritPath = inheritYml.INHERIT
+ ? path.resolve(path.dirname(inheritPath), inheritYml.INHERIT)
+ : undefined;
+ }
+ }
+ return mkDocsYml;
+ }
+);

MODIFY
packages/docutils/lib/model.ts
packages/docutils/lib/model.ts
@@ -33,6 +33,12 @@ export type TypeDocJson = Jsonify<
}
>;
+/**
+ * The `nav` prop of an `mkdocs.yml` file
+ * @see {@linkcode MkDocsYml}
+ */
+export type MkDocsYmlNav = Array<string | Record<string, string> | Record<string, MkDocsYmlNav>>;
+
/**
* This was built by hand from the MkDocs documentation
* @see https://www.mkdocs.org/user-guide/configuration/
@@ -48,7 +54,7 @@ export type MkDocsYml = Jsonify<{
hooks?: string[];
INHERIT?: string;
markdown_extensions?: Array<string | Record<string, JsonValue>>;
- nav?: Array<string | Record<string, string[]>>;
+ nav?: MkDocsYmlNav;
plugins?: Array<string | Record<string, JsonValue>>;
repo_name?: string;
repo_url?: string;

MODIFY
packages/docutils/lib/validate.ts
packages/docutils/lib/validate.ts
@@ -40,6 +40,7 @@ import {
whichMkDocs,
whichNpm,
whichPython,
+ readMkDocsYml,
} from './fs';
import logger from './logger';
import {MkDocsYml, PipPackage, TypeDocJson} from './model';
@@ -294,7 +295,6 @@ export class DocutilsValidator extends EventEmitter {
log.debug('Raw %s: %s', NAME_REQUIREMENTS_TXT, requirementsTxt);
for (const line of requirementsTxt.split(/\r?\n/)) {
const [name, version] = line.trim().split('==');
- log.debug('Need Python package %s @ %s', name, version);
requiredPackages.push({name, version});
}
log.debug('Parsed %s: %O', NAME_REQUIREMENTS_TXT, requiredPackages);
@@ -302,7 +302,7 @@ export class DocutilsValidator extends EventEmitter {
throw new DocutilsError(`Could not find ${REQUIREMENTS_TXT_PATH}. This is a bug`);
}
- return requiredPackages;
+ return (this.requirementsTxt = requiredPackages);
}
/**
@@ -359,16 +359,16 @@ export class DocutilsValidator extends EventEmitter {
*
* It checks if the file exists, if it can be parsed as YAML, and if it has a `site_name` property.
*/
- protected async validateMkDocsConfig() {
- let mkDocsYmlPath = this.mkDocsYmlPath ?? (await findMkDocsYml(this.cwd));
+ protected async validateMkDocsConfig(mkDocsYmlPath?: string) {
+ mkDocsYmlPath = mkDocsYmlPath ?? this.mkDocsYmlPath ?? (await findMkDocsYml(this.cwd));
if (!mkDocsYmlPath) {
return this.fail(
`Could not find ${NAME_MKDOCS_YML} from ${this.cwd}. Run "${NAME_BIN} init" to create it`
);
}
- let mkDocsYml: MkDocsYml | undefined;
+ let mkDocsYml: MkDocsYml;
try {
- mkDocsYml = await readYaml(mkDocsYmlPath);
+ mkDocsYml = await readMkDocsYml(mkDocsYmlPath);
} catch (e) {
const err = e as NodeJS.ErrnoException;
if (err.code === NAME_ERR_ENOENT) {
@@ -379,11 +379,11 @@ export class DocutilsValidator extends EventEmitter {
return this.fail(`Could not parse ${mkDocsYmlPath}: ${err}`);
}
- if (!mkDocsYml?.site_name) {
- return this.fail(`No site_name in ${mkDocsYmlPath}; this is required by MkDocs`);
+ if (!mkDocsYml.site_name) {
+ return this.fail(`Could not find required property "site_name" in ${mkDocsYmlPath}`);
}
- this.ok('MkDocs config OK');
+ this.ok(`MkDocs config at ${mkDocsYmlPath} OK`);
}
/**

feat(docutils): better mkdocs validation

When validating `mkdocs.yml`, `appium-docs` is now aware of the `INHERITS` property, and expands this property (by loading more `mkdocs.yml` files).

Also:

- Updated the types for `mkdocs.yml`
- Squelch warnings from `YAML.parse()`
- Change YAML indentation back to 2, since that seems to be correct and I was confused