mirror of
https://github.com/appium/appium.git
synced 2026-10-28 20:31:51 +08:00
127 lines
4.1 KiB
TypeScript
127 lines
4.1 KiB
TypeScript
import {homedir} from 'node:os';
|
|
import path from 'node:path';
|
|
|
|
import * as semver from 'semver';
|
|
|
|
import {type NormalizedPackageJson, readPackage} from './internal';
|
|
import {memoize} from './util';
|
|
|
|
/**
|
|
* Path to the default `APPIUM_HOME` dir (`~/.appium`).
|
|
*/
|
|
export const DEFAULT_APPIUM_HOME: string = path.resolve(homedir(), '.appium');
|
|
|
|
/**
|
|
* Basename of extension manifest file.
|
|
*/
|
|
export const MANIFEST_BASENAME = 'extensions.yaml';
|
|
|
|
/**
|
|
* Relative path to extension manifest file from `APPIUM_HOME`.
|
|
*/
|
|
export const MANIFEST_RELATIVE_PATH = path.join('node_modules', '.cache', 'appium', MANIFEST_BASENAME);
|
|
|
|
/**
|
|
* Resolves `true` if an `appium` dependency can be found somewhere in the given `cwd`.
|
|
*/
|
|
export async function hasAppiumDependency(cwd: string): Promise<boolean> {
|
|
return Boolean(await findAppiumDependencyPackage(cwd));
|
|
}
|
|
|
|
/**
|
|
* Given `cwd`, use `npm` to find the closest package _or workspace root_, and return the path if the root depends upon `appium`.
|
|
*
|
|
* Looks at `dependencies` and `devDependencies` for `appium`.
|
|
*/
|
|
export const findAppiumDependencyPackage = memoize(async function findAppiumDependencyPackage(
|
|
cwd: string = process.cwd(),
|
|
acceptableVersionRange: string | semver.Range = '>=2.0.0-beta',
|
|
): Promise<string | undefined> {
|
|
const readPkg = async (root: string): Promise<string | undefined> => {
|
|
let pkg: NormalizedPackageJson | undefined;
|
|
try {
|
|
pkg = await readPackageInDir(root);
|
|
} catch {
|
|
return undefined;
|
|
}
|
|
if (!pkg) {
|
|
return undefined;
|
|
}
|
|
try {
|
|
const version = semver.minVersion(
|
|
String(pkg.dependencies?.appium ?? pkg.devDependencies?.appium ?? pkg.peerDependencies?.appium),
|
|
);
|
|
return version && semver.satisfies(version, acceptableVersionRange) ? root : undefined;
|
|
} catch {
|
|
return undefined;
|
|
}
|
|
};
|
|
|
|
let currentDir = path.resolve(cwd);
|
|
let isAtFsRoot = false;
|
|
while (!isAtFsRoot) {
|
|
const result = await readPkg(currentDir);
|
|
if (result) {
|
|
return result;
|
|
}
|
|
currentDir = path.dirname(currentDir);
|
|
isAtFsRoot = currentDir.length <= path.dirname(currentDir).length;
|
|
}
|
|
return undefined;
|
|
});
|
|
|
|
/**
|
|
* Read a `package.json` in dir `cwd`. If none is found, resolves with `undefined`.
|
|
* @deprecated Package.json helpers in `@appium/support` are deprecated and will be removed in the next major version.
|
|
* Read `package.json` locally instead (for example with `node:fs` and `JSON.parse`).
|
|
* @returns Parsed package data, or `undefined` when `package.json` is missing in `cwd`
|
|
*/
|
|
export const readPackageInDir = memoize(async function _readPackageInDir(
|
|
cwd: string,
|
|
): Promise<NormalizedPackageJson | undefined> {
|
|
try {
|
|
return await readPackage({cwd, normalize: true});
|
|
} catch (err) {
|
|
if (isMissingPackageJsonError(err)) {
|
|
return undefined;
|
|
}
|
|
throw err;
|
|
}
|
|
});
|
|
|
|
function isMissingPackageJsonError(err: unknown): boolean {
|
|
return err instanceof Error && 'code' in err && (err as NodeJS.ErrnoException).code === 'ENOENT';
|
|
}
|
|
|
|
/**
|
|
* Determines location of Appium's "home" dir
|
|
*
|
|
* - If `APPIUM_HOME` is set in the environment, use that
|
|
* - If we find a `package.json` in or above `cwd` and it has an `appium` dependency, use that.
|
|
*
|
|
* All returned paths will be absolute.
|
|
*/
|
|
export const resolveAppiumHome = memoize(async function _resolveAppiumHome(
|
|
cwd: string = process.cwd(),
|
|
): Promise<string> {
|
|
if (!path.isAbsolute(cwd)) {
|
|
throw new TypeError('`cwd` parameter must be an absolute path');
|
|
}
|
|
|
|
if (process.env.APPIUM_HOME) {
|
|
return path.resolve(cwd, process.env.APPIUM_HOME);
|
|
}
|
|
|
|
return (await findAppiumDependencyPackage(cwd)) ?? DEFAULT_APPIUM_HOME;
|
|
});
|
|
|
|
/**
|
|
* Figure out manifest path based on `appiumHome`.
|
|
*
|
|
* The assumption is that, if `appiumHome` has been provided, it was resolved via {@link resolveAppiumHome `resolveAppiumHome()`}! If unsure,
|
|
* don't pass a parameter and let `resolveAppiumHome()` handle it.
|
|
*/
|
|
export const resolveManifestPath = memoize(async function _resolveManifestPath(appiumHome?: string): Promise<string> {
|
|
return path.join(appiumHome ?? (await resolveAppiumHome()), MANIFEST_RELATIVE_PATH);
|
|
});
|