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