@@ -3,7 +3,6 @@ import {exec, SubProcess} from 'teen_process';
33import path , { dirname } from 'node:path' ;
44import { fileURLToPath } from 'node:url' ;
55import { log } from './logger' ;
6- import _ from 'lodash' ;
76import { PLATFORM_NAME_TVOS } from './constants' ;
87import _fs from 'node:fs' ;
98import { waitForCondition } from 'asyncbox' ;
@@ -18,13 +17,18 @@ const currentFilename =
1817
1918const currentDirname = dirname ( currentFilename ) ;
2019
20+ let moduleRootCache : string | undefined ;
21+
2122/**
2223 * Calculates the path to the current module's root folder
2324 *
2425 * @returns {string } The full path to module root
2526 * @throws {Error } If the current module root folder cannot be determined
2627 */
27- const getModuleRoot = _ . memoize ( function getModuleRoot ( ) : string {
28+ const getModuleRoot = function getModuleRoot ( ) : string {
29+ if ( moduleRootCache ) {
30+ return moduleRootCache ;
31+ }
2832 let currentDir = currentDirname ;
2933 let isAtFsRoot = false ;
3034 while ( ! isAtFsRoot ) {
@@ -34,14 +38,15 @@ const getModuleRoot = _.memoize(function getModuleRoot(): string {
3438 _fs . existsSync ( manifestPath ) &&
3539 JSON . parse ( _fs . readFileSync ( manifestPath , 'utf8' ) ) . name === 'appium-webdriveragent'
3640 ) {
41+ moduleRootCache = currentDir ;
3742 return currentDir ;
3843 }
3944 } catch { }
4045 currentDir = path . dirname ( currentDir ) ;
4146 isAtFsRoot = currentDir . length <= path . dirname ( currentDir ) . length ;
4247 }
4348 throw new Error ( 'Cannot find the root folder of the appium-webdriveragent Node.js module' ) ;
44- } ) ;
49+ } ;
4550
4651export const BOOTSTRAP_PATH = getModuleRoot ( ) ;
4752
@@ -63,7 +68,7 @@ export async function killAppUsingPattern(pgrepPattern: string): Promise<void> {
6368 const signals = [ 2 , 15 , 9 ] ;
6469 for ( const signal of signals ) {
6570 const matchedPids = await getPIDsUsingPattern ( pgrepPattern ) ;
66- if ( _ . isEmpty ( matchedPids ) ) {
71+ if ( matchedPids . length === 0 ) {
6772 return ;
6873 }
6974 const args = [ `-${ signal } ` , ...matchedPids ] ;
@@ -72,7 +77,7 @@ export async function killAppUsingPattern(pgrepPattern: string): Promise<void> {
7277 } catch ( err : any ) {
7378 log . debug ( `kill ${ args . join ( ' ' ) } -> ${ err . message } ` ) ;
7479 }
75- if ( signal === _ . last ( signals ) ) {
80+ if ( signal === signals [ signals . length - 1 ] ) {
7681 // there is no need to wait after SIGKILL
7782 return ;
7883 }
@@ -109,7 +114,7 @@ export async function killAppUsingPattern(pgrepPattern: string): Promise<void> {
109114 * @returns Return true if the platformName is tvOS
110115 */
111116export function isTvOS ( platformName : string ) : boolean {
112- return _ . toLower ( platformName ) === _ . toLower ( PLATFORM_NAME_TVOS ) ;
117+ return platformName ?. toLowerCase ( ) === PLATFORM_NAME_TVOS . toLowerCase ( ) ;
113118}
114119
115120/**
@@ -148,7 +153,7 @@ export async function setXctestrunFile(args: XctestrunFileArgs): Promise<string>
148153 wdaRemotePort ,
149154 wdaBindingIP ,
150155 ) ;
151- const newXctestRunContent = _ . merge ( xctestRunContent , updateWDAPort ) ;
156+ const newXctestRunContent = mergeObjects ( xctestRunContent , updateWDAPort ) ;
152157 await plist . updatePlistFile ( xctestrunFilePath , newXctestRunContent , true ) ;
153158
154159 return xctestrunFilePath ;
@@ -293,6 +298,23 @@ export async function getWDAUpgradeTimestamp(): Promise<number | null> {
293298 return mtime . getTime ( ) ;
294299}
295300
301+ /**
302+ * Escape regular expression metacharacters in a string.
303+ */
304+ export function escapeRegExp ( value : string ) : string {
305+ return value . replace ( / [ . * + ? ^ $ { } ( ) | [ \] \\ ] / g, '\\$&' ) ;
306+ }
307+
308+ /**
309+ * Truncate a string to the given length and append ellipsis if needed.
310+ */
311+ export function truncateString ( value : string , length : number ) : string {
312+ if ( value . length <= length ) {
313+ return value ;
314+ }
315+ return `${ value . slice ( 0 , Math . max ( 0 , length - 1 ) ) } …` ;
316+ }
317+
296318/**
297319 * Kills running XCTest processes for the particular device.
298320 */
@@ -337,7 +359,7 @@ export async function getPIDsListeningOnPort(
337359 return result ;
338360 }
339361
340- if ( ! _ . isFunction ( filteringFunc ) ) {
362+ if ( typeof filteringFunc !== 'function' ) {
341363 return result ;
342364 }
343365 const filtered = await Promise . all (
@@ -370,7 +392,7 @@ async function getPIDsUsingPattern(pattern: string): Promise<string[]> {
370392 return stdout
371393 . split ( / \s + / )
372394 . map ( ( x ) => parseInt ( x , 10 ) )
373- . filter ( _ . isInteger )
395+ . filter ( Number . isInteger )
374396 . map ( ( x ) => `${ x } ` ) ;
375397 } catch ( err : any ) {
376398 log . debug (
@@ -379,3 +401,30 @@ async function getPIDsUsingPattern(pattern: string): Promise<string[]> {
379401 return [ ] ;
380402 }
381403}
404+
405+ function mergeObjects < T extends Record < string , any > , U extends Record < string , any > > (
406+ target : T ,
407+ source : U ,
408+ ) : T & U {
409+ const output : Record < string , any > = { ...target } ;
410+ for ( const [ key , sourceValue ] of Object . entries ( source ) ) {
411+ const targetValue = output [ key ] ;
412+ if (
413+ isPlainObject ( targetValue ) &&
414+ isPlainObject ( sourceValue )
415+ ) {
416+ output [ key ] = mergeObjects ( targetValue , sourceValue ) ;
417+ continue ;
418+ }
419+ output [ key ] = sourceValue ;
420+ }
421+ return output as T & U ;
422+ }
423+
424+ function isPlainObject ( value : unknown ) : value is Record < string , any > {
425+ if ( value == null || typeof value !== 'object' || Array . isArray ( value ) ) {
426+ return false ;
427+ }
428+ const prototype = Object . getPrototypeOf ( value ) ;
429+ return prototype === Object . prototype || prototype === null ;
430+ }
0 commit comments