1*8d307f52SEvan Baconimport { getConfig, getProjectConfigDescriptionWithPaths } from '@expo/config';
2*8d307f52SEvan Bacon
3*8d307f52SEvan Baconimport { CommandError, UnimplementedError } from '../../utils/errors';
4*8d307f52SEvan Baconimport { get } from '../../utils/obj';
5*8d307f52SEvan Bacon
6*8d307f52SEvan Bacon/** Resolves a native app identifier (bundle identifier, package name) from the project files. */
7*8d307f52SEvan Baconexport class AppIdResolver {
8*8d307f52SEvan Bacon  constructor(
9*8d307f52SEvan Bacon    protected projectRoot: string,
10*8d307f52SEvan Bacon    /** Platform to use. */
11*8d307f52SEvan Bacon    protected platform: string,
12*8d307f52SEvan Bacon    /** Nested key in the Expo config like `android.package` or `ios.bundleIdentifier`. */
13*8d307f52SEvan Bacon    protected configProperty: string
14*8d307f52SEvan Bacon  ) {}
15*8d307f52SEvan Bacon
16*8d307f52SEvan Bacon  /** Resolve the application ID for the project. */
17*8d307f52SEvan Bacon  async getAppIdAsync(): Promise<string> {
18*8d307f52SEvan Bacon    if (await this.hasNativeProjectAsync()) {
19*8d307f52SEvan Bacon      return this.getAppIdFromNativeAsync();
20*8d307f52SEvan Bacon    }
21*8d307f52SEvan Bacon    return this.getAppIdFromConfigAsync();
22*8d307f52SEvan Bacon  }
23*8d307f52SEvan Bacon
24*8d307f52SEvan Bacon  /** Returns `true` if the project has native project code. */
25*8d307f52SEvan Bacon  async hasNativeProjectAsync(): Promise<boolean> {
26*8d307f52SEvan Bacon    throw new UnimplementedError();
27*8d307f52SEvan Bacon  }
28*8d307f52SEvan Bacon
29*8d307f52SEvan Bacon  /** Return the app ID from the Expo config or assert. */
30*8d307f52SEvan Bacon  async getAppIdFromConfigAsync(): Promise<string> {
31*8d307f52SEvan Bacon    const config = getConfig(this.projectRoot);
32*8d307f52SEvan Bacon
33*8d307f52SEvan Bacon    const appId = get(config.exp, this.configProperty);
34*8d307f52SEvan Bacon    if (!appId) {
35*8d307f52SEvan Bacon      throw new CommandError(
36*8d307f52SEvan Bacon        'NO_APP_ID',
37*8d307f52SEvan Bacon        `Required property '${
38*8d307f52SEvan Bacon          this.configProperty
39*8d307f52SEvan Bacon        }' is not found in the project ${getProjectConfigDescriptionWithPaths(
40*8d307f52SEvan Bacon          this.projectRoot,
41*8d307f52SEvan Bacon          config
42*8d307f52SEvan Bacon        )}. This is required to open the app.`
43*8d307f52SEvan Bacon      );
44*8d307f52SEvan Bacon    }
45*8d307f52SEvan Bacon    return appId;
46*8d307f52SEvan Bacon  }
47*8d307f52SEvan Bacon
48*8d307f52SEvan Bacon  /** Return the app ID from the native project files or null if the app ID cannot be found. */
49*8d307f52SEvan Bacon  async resolveAppIdFromNativeAsync(): Promise<string | null> {
50*8d307f52SEvan Bacon    throw new UnimplementedError();
51*8d307f52SEvan Bacon  }
52*8d307f52SEvan Bacon
53*8d307f52SEvan Bacon  /** Return the app ID from the native project files or assert. */
54*8d307f52SEvan Bacon  async getAppIdFromNativeAsync(): Promise<string> {
55*8d307f52SEvan Bacon    const appId = await this.resolveAppIdFromNativeAsync();
56*8d307f52SEvan Bacon    if (!appId) {
57*8d307f52SEvan Bacon      throw new CommandError(
58*8d307f52SEvan Bacon        'NO_APP_ID',
59*8d307f52SEvan Bacon        `Failed to locate the ${this.platform} application identifier in the "${this.platform}/" folder. This is required to open the app.`
60*8d307f52SEvan Bacon      );
61*8d307f52SEvan Bacon    }
62*8d307f52SEvan Bacon    return appId;
63*8d307f52SEvan Bacon  }
64*8d307f52SEvan Bacon}
65