xref: /expo/packages/@expo/cli/src/utils/glob.ts (revision 8d307f52)
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