1import G, { Glob } from 'glob'; 2 3/** Finds all matching files. */ 4export function everyMatchAsync(pattern: string, options: G.IOptions) { 5 return new Promise<string[]>((resolve, reject) => { 6 const g = new Glob(pattern, options); 7 let called = false; 8 const callback = (er: Error | null, matched: string[]) => { 9 if (called) return; 10 called = true; 11 if (er) reject(er); 12 else resolve(matched); 13 }; 14 g.on('error', callback); 15 g.on('end', (matches) => callback(null, matches)); 16 }); 17} 18 19/** Bails out early after finding the first matching file. */ 20export function anyMatchAsync(pattern: string, options: G.IOptions) { 21 return new Promise<string[]>((resolve, reject) => { 22 const g = new Glob(pattern, options); 23 let called = false; 24 const callback = (er: Error | null, matched: string[]) => { 25 if (called) return; 26 called = true; 27 if (er) reject(er); 28 else resolve(matched); 29 }; 30 g.on('error', callback); 31 g.on('match', (matched) => { 32 // We've disabled using abort as it breaks the entire glob package across all instances. 33 // https://github.com/isaacs/node-glob/issues/279 & https://github.com/isaacs/node-glob/issues/342 34 // For now, just collect every match. 35 // g.abort(); 36 callback(null, [matched]); 37 }); 38 g.on('end', (matches) => callback(null, matches)); 39 }); 40} 41 42/** 43 * Wait some time, then escape... 44 * Adding this because glob can sometimes freeze and fail to resolve if any other glob uses `.abort()`. 45 */ 46export function wrapGlobWithTimeout( 47 query: () => Promise<string[]>, 48 duration: number 49): Promise<string[] | false> { 50 return new Promise(async (resolve, reject) => { 51 const timeout = setTimeout(() => { 52 resolve(false); 53 }, duration); 54 55 process.on('SIGINT', () => clearTimeout(timeout)); 56 57 try { 58 resolve(await query()); 59 } catch (error) { 60 reject(error); 61 } finally { 62 clearTimeout(timeout); 63 } 64 }); 65} 66