feat(docutils): add config file support
e.g., `appium-docs.config.js` and the its ilk. Not strictly necessary, but can be useful if there are a bunch of custom paths or multiple builds.
MODIFY
package-lock.json
package-lock.json
"version": "0.7.2",
"license": "(Unlicense OR Apache-2.0)"
},
+ "node_modules/@sliphua/lilconfig-ts-loader": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/@sliphua/lilconfig-ts-loader/-/lilconfig-ts-loader-3.2.2.tgz",
+ "integrity": "sha512-nX2aBwAykiG50fSUzK9eyA5UvWcrEKzA0ZzCq9mLwHMwpKxM+U05YH8PHba1LJrbeZ7R1HSjJagWKMqFyq8cxw==",
+ "dependencies": {
+ "lodash.get": "^4",
+ "make-error": "^1",
+ "ts-node": "^9",
+ "tslib": "^2"
+ },
+ "engines": {
+ "node": ">=10.0.0"
+ },
+ "peerDependencies": {
+ "lilconfig": ">=2"
+ }
+ },
+ "node_modules/@sliphua/lilconfig-ts-loader/node_modules/diff": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
+ "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
+ "engines": {
+ "node": ">=0.3.1"
+ }
+ },
+ "node_modules/@sliphua/lilconfig-ts-loader/node_modules/ts-node": {
+ "version": "9.1.1",
+ "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz",
+ "integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==",
+ "dependencies": {
+ "arg": "^4.1.0",
+ "create-require": "^1.1.0",
+ "diff": "^4.0.1",
+ "make-error": "^1.1.1",
+ "source-map-support": "^0.5.17",
+ "yn": "3.1.1"
+ },
+ "bin": {
+ "ts-node": "dist/bin.js",
+ "ts-node-script": "dist/bin-script.js",
+ "ts-node-transpile-only": "dist/bin-transpile.js",
+ "ts-script": "dist/bin-script-deprecated.js"
+ },
+ "engines": {
+ "node": ">=10.0.0"
+ },
+ "peerDependencies": {
+ "typescript": ">=2.7"
+ }
+ },
"node_modules/@szmarczak/http-timer": {
"version": "4.0.6",
"license": "MIT",
},
"node_modules/arg": {
"version": "4.1.3",
- "dev": true,
"license": "MIT"
},
"node_modules/argparse": {
},
"node_modules/create-require": {
"version": "1.1.1",
- "dev": true,
"license": "MIT"
},
"node_modules/cross-env": {
},
"node_modules/make-error": {
"version": "1.3.6",
- "dev": true,
"license": "ISC"
},
"node_modules/make-fetch-happen": {
},
"node_modules/yn": {
"version": "3.1.1",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
"@appium/support": "^3.1.4",
"@appium/tsconfig": "^0.2.3",
"@appium/typedoc-plugin-appium": "^0.3.4",
+ "@sliphua/lilconfig-ts-loader": "3.2.2",
"consola": "2.15.3",
"diff": "5.1.0",
"glob": "8.1.0",
"json5": "2.2.3",
+ "lilconfig": "2.0.6",
"lodash": "4.17.21",
"pkg-dir": "5.0.0",
"pluralize": "8.0.0",
"typedoc-plugin-resolve-crossmodule-references": "0.3.3",
"typescript": "4.7.4",
"yaml": "2.2.1",
- "yargs": "17.6.2"
+ "yargs": "17.6.2",
+ "yargs-parser": "20.2.9"
},
"bin": {
"appium-docs": "build/lib/cli/index.js"
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "packages/docutils/node_modules/yargs-parser": {
+ "version": "20.2.9",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
+ "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
+ "engines": {
+ "node": ">=10"
+ }
+ },
"packages/driver-test-support": {
"name": "@appium/driver-test-support",
"version": "0.3.6",

ADD
packages/docutils/lib/cli/config.ts
+/**
+ * Handles reading of a config file for docutils
+ * @module
+ */
+
+import loadTs from '@sliphua/lilconfig-ts-loader';
+import {LogLevel} from 'consola';
+import {lilconfig, Loader} from 'lilconfig';
+import _ from 'lodash';
+import path from 'node:path';
+import YAML from 'yaml';
+import parser from 'yargs-parser';
+import {hideBin} from 'yargs/helpers';
+import {NAME_BIN} from '../constants';
+import logger from '../logger';
+import {relative} from '../util';
+const log = logger.withTag('config');
+
+/**
+ * `lilconfig` loader for YAML
+ */
+const loadYaml: Loader = _.rearg(YAML.parse, [2, 0, 1]);
+/**
+ * `lilconfig` loader for ESM/CJS
+ */
+const loadEsm: Loader = (filepath: string) => import(filepath);
+
+/**
+ * Controls how we load/find a config file.
+ *
+ * Takes _raw_ args from the CLI, and uses `yargs-parser` to parse them as to not interfere with the
+ * main usage of args.
+ *
+ * We're looking for various things in the CLI args:
+ * - `--no-config` - if this is present, we don't load a config file
+ * - `--log-level` - if this is present, we set the log level
+ * - `--verbose` - same as above
+ * - `--config` - if this is present, we load the config file at the given path
+ * - `--help`, `--version` - do nothing
+ * @param argv Raw CLI args
+ * @returns
+ */
+export async function findConfig(argv: string[] = hideBin(process.argv)) {
+ const preArgs = parser(argv);
+
+ if (preArgs.verbose || preArgs.logLevel === 'debug') {
+ log.level = LogLevel.Debug;
+ }
+
+ return preArgs.noConfig || preArgs.help || preArgs.version
+ ? {}
+ : await loadConfig(preArgs.config as string | undefined);
+}
+
+/**
+ * Loads a config file or finds and loads one if none provided
+ * @param filepath Config file path, if provided
+ * @param cwd Current working directory
+ * @returns A config object or an empty object. Could be anything; `yargs` will validate it.
+ */
+export async function loadConfig(filepath?: string, cwd = process.cwd()): Promise<any> {
+ const relativePath = relative(cwd);
+ const searcher = lilconfig(NAME_BIN, {
+ loaders: {
+ '.yaml': loadYaml,
+ '.yml': loadYaml,
+ '.ts': loadTs,
+ '.js': loadEsm,
+ '.cjs': loadEsm,
+ '.mjs': loadEsm,
+ },
+ });
+
+ const result = filepath
+ ? await searcher.load(path.normalize(filepath))
+ : await searcher.search(cwd);
+ if (result === null) {
+ log.debug('No config found');
+ return {};
+ }
+ if (result.isEmpty) {
+ log.debug('Config loaded at %s but it was empty', result.filepath);
+ return {};
+ }
+ const relFilepath = relativePath(result.filepath);
+ log.success('Loaded config from %s', relFilepath);
+ log.debug('Config contents: %O', result.config);
+ return result.config;
+}

MODIFY
packages/docutils/lib/cli/index.ts
packages/docutils/lib/cli/index.ts
@@ -16,8 +16,11 @@ const LogLevelName = {
info: LogLevel.Info,
debug: LogLevel.Debug,
} as const;
+import {findConfig} from './config';
export async function main(argv = hideBin(process.argv)) {
+ const config = await findConfig(argv);
+
const y = yargs(argv);
return await y
.scriptName(NAME_BIN)
@@ -37,6 +40,19 @@ export async function main(argv = hideBin(process.argv)) {
describe: 'Sets the log level',
coerce: _.identity as (x: string) => keyof typeof LogLevelName,
},
+ config: {
+ alias: 'c',
+ type: 'string',
+ describe: 'Path to config file',
+ normalize: true,
+ nargs: 1,
+ requiresArg: true,
+ defaultDescription: '(discovered automatically)',
+ },
+ 'no-config': {
+ type: 'boolean',
+ describe: 'Disable config file discovery',
+ },
})
.middleware(
/**
@@ -66,6 +82,7 @@ export async function main(argv = hideBin(process.argv)) {
y.exit(1, error);
}
)
+ .config(config)
// at least one command is required (but not for --version or --help)
.demandCommand(1)
// fail if unknown option or command is provided

MODIFY
packages/docutils/package.json
packages/docutils/package.json
"@appium/support": "^3.1.4",
"@appium/tsconfig": "^0.2.3",
"@appium/typedoc-plugin-appium": "^0.3.4",
+ "@sliphua/lilconfig-ts-loader": "3.2.2",
"consola": "2.15.3",
"diff": "5.1.0",
"glob": "8.1.0",
"json5": "2.2.3",
+ "lilconfig": "2.0.6",
"lodash": "4.17.21",
"pkg-dir": "5.0.0",
"pluralize": "8.0.0",
"typedoc-plugin-resolve-crossmodule-references": "0.3.3",
"typescript": "4.7.4",
"yaml": "2.2.1",
- "yargs": "17.6.2"
+ "yargs": "17.6.2",
+ "yargs-parser": "20.2.9"
},
"engines": {
"node": "^14.17.0 || ^16.13.0 || >=18.0.0",

feat(docutils): add config file support

e.g., `appium-docs.config.js` and the its ilk. Not strictly necessary, but can be useful if there are a bunch of custom paths or multiple builds.