1*edeec536SEvan Baconimport spawnAsync from '@expo/spawn-async';
2*edeec536SEvan Baconimport { type ChildProcess } from 'child_process';
3*edeec536SEvan Baconimport open from 'open';
4*edeec536SEvan Baconimport path from 'path';
5*edeec536SEvan Bacon
6*edeec536SEvan Baconimport {
7*edeec536SEvan Bacon  LaunchBrowserTypes,
8*edeec536SEvan Bacon  type LaunchBrowserImpl,
9*edeec536SEvan Bacon  type LaunchBrowserInstance,
10*edeec536SEvan Bacon} from './LaunchBrowser.types';
11*edeec536SEvan Bacon
12*edeec536SEvan Bacon/**
13*edeec536SEvan Bacon * Browser implementation for Linux
14*edeec536SEvan Bacon */
15*edeec536SEvan Baconexport default class LaunchBrowserImplLinux implements LaunchBrowserImpl, LaunchBrowserInstance {
16*edeec536SEvan Bacon  private _appId: string | undefined;
17*edeec536SEvan Bacon  private _process: ChildProcess | undefined;
18*edeec536SEvan Bacon
19*edeec536SEvan Bacon  MAP = {
20*edeec536SEvan Bacon    [LaunchBrowserTypes.CHROME]: ['google-chrome', 'google-chrome-stable', 'chromium'],
21*edeec536SEvan Bacon    [LaunchBrowserTypes.EDGE]: ['microsoft-edge', 'microsoft-edge-dev'],
22*edeec536SEvan Bacon  };
23*edeec536SEvan Bacon
24*edeec536SEvan Bacon  /**
25*edeec536SEvan Bacon   * On Linux, the supported appId is an array, this function finds the available appId and caches it
26*edeec536SEvan Bacon   */
27*edeec536SEvan Bacon  private async getAppId(browserType: LaunchBrowserTypes): Promise<string> {
28*edeec536SEvan Bacon    if (this._appId == null || !this.MAP[browserType].includes(this._appId)) {
29*edeec536SEvan Bacon      for (const appId of this.MAP[browserType]) {
30*edeec536SEvan Bacon        try {
31*edeec536SEvan Bacon          const { status } = await spawnAsync('which', [appId], { stdio: 'ignore' });
32*edeec536SEvan Bacon          if (status === 0) {
33*edeec536SEvan Bacon            this._appId = appId;
34*edeec536SEvan Bacon            break;
35*edeec536SEvan Bacon          }
36*edeec536SEvan Bacon        } catch {}
37*edeec536SEvan Bacon      }
38*edeec536SEvan Bacon    }
39*edeec536SEvan Bacon
40*edeec536SEvan Bacon    if (this._appId == null) {
41*edeec536SEvan Bacon      throw new Error(
42*edeec536SEvan Bacon        `Unable to find supported browser - tried[${this.MAP[browserType].join(', ')}]`
43*edeec536SEvan Bacon      );
44*edeec536SEvan Bacon    }
45*edeec536SEvan Bacon
46*edeec536SEvan Bacon    return this._appId;
47*edeec536SEvan Bacon  }
48*edeec536SEvan Bacon
49*edeec536SEvan Bacon  async isSupportedBrowser(browserType: LaunchBrowserTypes): Promise<boolean> {
50*edeec536SEvan Bacon    let result = false;
51*edeec536SEvan Bacon    try {
52*edeec536SEvan Bacon      await this.getAppId(browserType);
53*edeec536SEvan Bacon      result = true;
54*edeec536SEvan Bacon    } catch {
55*edeec536SEvan Bacon      result = false;
56*edeec536SEvan Bacon    }
57*edeec536SEvan Bacon    return result;
58*edeec536SEvan Bacon  }
59*edeec536SEvan Bacon
60*edeec536SEvan Bacon  async createTempBrowserDir(baseDirName: string) {
61*edeec536SEvan Bacon    return path.join(require('temp-dir'), baseDirName);
62*edeec536SEvan Bacon  }
63*edeec536SEvan Bacon
64*edeec536SEvan Bacon  async launchAsync(
65*edeec536SEvan Bacon    browserType: LaunchBrowserTypes,
66*edeec536SEvan Bacon    args: string[]
67*edeec536SEvan Bacon  ): Promise<LaunchBrowserInstance> {
68*edeec536SEvan Bacon    const appId = await this.getAppId(browserType);
69*edeec536SEvan Bacon    this._process = await open.openApp(appId, { arguments: args });
70*edeec536SEvan Bacon    return this;
71*edeec536SEvan Bacon  }
72*edeec536SEvan Bacon
73*edeec536SEvan Bacon  async close(): Promise<void> {
74*edeec536SEvan Bacon    this._process?.kill();
75*edeec536SEvan Bacon    this._process = undefined;
76*edeec536SEvan Bacon    this._appId = undefined;
77*edeec536SEvan Bacon  }
78*edeec536SEvan Bacon}
79