@appium/support
Version:
Support libs used across Appium packages
219 lines • 7.51 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.requirePackage = requirePackage;
exports.getObjectSize = getObjectSize;
exports.getObjectId = getObjectId;
exports.deepFreeze = deepFreeze;
exports.getModuleRootSync = getModuleRootSync;
const system_1 = require("./system");
const logger_1 = __importDefault(require("./logger"));
const lodash_1 = __importDefault(require("lodash"));
const teen_process_1 = require("teen_process");
const node_path_1 = __importDefault(require("node:path"));
const node_fs_1 = __importDefault(require("node:fs"));
const uuid_1 = require("uuid");
const OBJECTS_MAPPING = new WeakMap();
/**
* Utility function to extend node functionality, allowing us to require
* modules that are installed globally. If the package cannot be required,
* this will attempt to link the package and then re-require it
*
* @param packageName - the name of the package to be required
* @returns The package object
* @throws {Error} If the package is not found locally or globally
*/
async function requirePackage(packageName) {
try {
logger_1.default.debug(`Loading local package '${packageName}'`);
return require(packageName);
}
catch (err) {
logger_1.default.debug(`Failed to load local package '${packageName}': ${err.message}`);
}
try {
const globalPackageName = node_path_1.default.resolve(process.env.npm_config_prefix ?? '', 'lib', 'node_modules', packageName);
logger_1.default.debug(`Loading global package '${globalPackageName}'`);
return require(globalPackageName);
}
catch (err) {
logger_1.default.debug(`Failed to load global package '${packageName}': ${err.message}`);
}
try {
await linkGlobalPackage(packageName);
logger_1.default.debug(`Retrying load of linked package '${packageName}'`);
return require(packageName);
}
catch (err) {
throw logger_1.default.errorWithException(`Unable to load package '${packageName}': ${err.message}`);
}
}
/**
* Calculate the in-depth size in memory of the provided object.
* The original implementation is borrowed from https://github.com/miktam/sizeof.
*
* @param obj - An object whose size should be calculated
* @returns Object size in bytes.
*/
function getObjectSize(obj) {
return getCalculator(new WeakSet())(obj);
}
/**
* Calculates a unique object identifier
*
* @param object - Any valid ECMA object
* @returns A uuidV4 string that uniquely identifies given object
*/
function getObjectId(object) {
const existing = OBJECTS_MAPPING.get(object);
if (existing !== undefined) {
return existing;
}
const id = (0, uuid_1.v4)();
OBJECTS_MAPPING.set(object, id);
return id;
}
/**
* Perform deep freeze of the given object (e. g.
* all nested objects also become immutable).
* If the passed object is of a plain type
* then no change is done and the same object
* is returned.
* ! This function changes the given object,
* so it becomes immutable.
*
* @param object - Any valid ECMA object
* @returns The same object that was passed after it was made immutable.
*/
function deepFreeze(object) {
let propNames;
try {
propNames = Object.getOwnPropertyNames(object);
}
catch {
return object;
}
for (const name of propNames) {
const value = object[name];
if (value && typeof value === 'object') {
deepFreeze(value);
}
}
return Object.freeze(object);
}
/**
* Tries to synchronously detect the absolute path to the folder
* where the given `moduleName` is located.
*
* @param moduleName - The name of the module as it is written in package.json
* @param filePath - Full path to any of files that `moduleName` contains. Use
* `__filename` to find the root of the module where this helper is called.
* @returns Full path to the module root, or null if not found
*/
function getModuleRootSync(moduleName, filePath) {
let currentDir = node_path_1.default.dirname(node_path_1.default.resolve(filePath));
let isAtFsRoot = false;
while (!isAtFsRoot) {
const manifestPath = node_path_1.default.join(currentDir, 'package.json');
try {
if (node_fs_1.default.existsSync(manifestPath) &&
JSON.parse(node_fs_1.default.readFileSync(manifestPath, 'utf8')).name === moduleName) {
return currentDir;
}
}
catch {
// ignore
}
currentDir = node_path_1.default.dirname(currentDir);
isAtFsRoot = currentDir.length <= node_path_1.default.dirname(currentDir).length;
}
return null;
}
// #region Private
const ECMA_SIZES = Object.freeze({
STRING: 2,
BOOLEAN: 4,
NUMBER: 8,
});
async function linkGlobalPackage(packageName) {
try {
logger_1.default.debug(`Linking package '${packageName}'`);
const cmd = (0, system_1.isWindows)() ? 'npm.cmd' : 'npm';
await (0, teen_process_1.exec)(cmd, ['link', packageName], { timeout: 20000 });
}
catch (err) {
const e = err;
const msg = `Unable to load package '${packageName}', linking failed: ${e.message}`;
logger_1.default.debug(msg);
if (e.stderr) {
logger_1.default.debug(e.stderr);
}
throw new Error(msg);
}
}
function extractAllProperties(obj) {
const stringProperties = [];
for (const prop in obj) {
stringProperties.push(prop);
}
if (lodash_1.default.isFunction(Object.getOwnPropertySymbols)) {
stringProperties.push(...Object.getOwnPropertySymbols(obj));
}
return stringProperties;
}
function _getSizeOfObject(seen, object) {
if (lodash_1.default.isNil(object)) {
return 0;
}
let bytes = 0;
const properties = extractAllProperties(object);
const calc = getCalculator(seen);
for (const key of properties) {
const val = object[key];
if (typeof val === 'object' && !lodash_1.default.isNil(val)) {
if (seen.has(val)) {
continue;
}
seen.add(val);
}
bytes += calc(key);
try {
bytes += calc(val);
}
catch (ex) {
if (ex instanceof RangeError) {
bytes = 0;
}
}
}
return bytes;
}
function getCalculator(seen) {
return function calculator(obj) {
if (lodash_1.default.isBuffer(obj)) {
return obj.length;
}
switch (typeof obj) {
case 'string':
return obj.length * ECMA_SIZES.STRING;
case 'boolean':
return ECMA_SIZES.BOOLEAN;
case 'number':
return ECMA_SIZES.NUMBER;
case 'symbol':
return lodash_1.default.isFunction(Symbol.keyFor) && Symbol.keyFor(obj)
? Symbol.keyFor(obj).length * ECMA_SIZES.STRING
: (obj.toString().length - 8) * ECMA_SIZES.STRING;
case 'object':
return lodash_1.default.isArray(obj)
? obj.map(getCalculator(seen)).reduce((acc, curr) => acc + curr, 0)
: _getSizeOfObject(seen, obj);
default:
return 0;
}
};
}
// #endregion
//# sourceMappingURL=node.js.map