@appium/docutils
Version:
Documentation generation utilities for Appium and related projects
96 lines (81 loc) • 3.19 kB
text/typescript
import nodeFs from 'node:fs';
import path from 'node:path';
import {fs} from '@appium/support';
import normalizePackageData from 'normalize-package-data';
import type {PackageJson as TypeFestPackageJson} from 'type-fest';
export type PackageJson = TypeFestPackageJson;
export type NormalizedPackageJson = PackageJson & {
name: string;
version: string;
readme: string;
_id: string;
bugs?: {url: string};
repository?: {type: string; url: string; directory?: string};
};
export type ReadPackageOptions = {
/** Directory containing `package.json`. Defaults to `process.cwd()`. */
cwd?: string;
/** Normalize package data. Defaults to `true`. */
normalize?: boolean;
};
export type NormalizeOptions = ReadPackageOptions & {normalize?: true};
/** Finds the directory containing the nearest `package.json` by walking upward from `dir`. */
export async function findPackageRoot(dir: string): Promise<string> {
assertNonEmptyDir(dir, 'findPackageRoot');
let current = path.resolve(dir);
const fsRoot = path.parse(current).root;
while (true) {
if (await fs.exists(path.join(current, 'package.json'))) {
return current;
}
if (current === fsRoot) {
throwPackageRootNotFound(dir);
}
current = path.dirname(current);
}
}
/** Finds the project root directory from `dir`. */
export function findPackageRootSync(dir: string): string {
assertNonEmptyDir(dir, 'findPackageRootSync');
let current = path.resolve(dir);
const fsRoot = path.parse(current).root;
while (true) {
if (nodeFs.existsSync(path.join(current, 'package.json'))) {
return current;
}
if (current === fsRoot) {
throwPackageRootNotFound(dir);
}
current = path.dirname(current);
}
}
/** Reads and parses `package.json` from `cwd`. */
export async function readPackage(options?: NormalizeOptions): Promise<NormalizedPackageJson>;
export async function readPackage(options: ReadPackageOptions): Promise<PackageJson>;
export async function readPackage(options: ReadPackageOptions = {}): Promise<PackageJson | NormalizedPackageJson> {
const {cwd, normalize = true} = options;
const contents = await fs.readFile(getPackagePath(cwd), 'utf8');
return parsePackageJson(contents, normalize);
}
function assertNonEmptyDir(dir: string, fnName: string): void {
if (!dir) {
throw new TypeError(`\`${fnName}()\` must be provided a non-empty path`);
}
}
function throwPackageRootNotFound(dir: string): never {
throw new Error(`Could not find \`package.json\` from ${dir}`);
}
function getPackagePath(cwd?: string): string {
return path.resolve(cwd ?? process.cwd(), 'package.json');
}
function parsePackageJson(contents: string, normalize: true): NormalizedPackageJson;
function parsePackageJson(contents: string, normalize: false): PackageJson;
function parsePackageJson(contents: string, normalize?: boolean): PackageJson | NormalizedPackageJson;
function parsePackageJson(contents: string, normalize = true): PackageJson | NormalizedPackageJson {
const json = JSON.parse(contents) as PackageJson;
if (normalize === false) {
return json;
}
normalizePackageData(json);
return json as NormalizedPackageJson;
}