1import { ExpoConfig, getConfig, ProjectConfig } from '@expo/config';
2import assert from 'assert';
3import util from 'util';
4
5import * as Log from '../log';
6import { CommandError } from '../utils/errors';
7import { profile } from '../utils/profile';
8
9type Options = {
10  type?: string;
11  full?: boolean;
12  json?: boolean;
13};
14
15export function logConfig(config: ExpoConfig | ProjectConfig) {
16  const isObjStr = (str: string): boolean => /^\w+: {/g.test(str);
17  Log.log(
18    util.inspect(config, {
19      colors: true,
20      compact: false,
21      // Sort objects to the end so that smaller values aren't hidden between large objects.
22      sorted(a: string, b: string) {
23        if (isObjStr(a)) return 1;
24        if (isObjStr(b)) return -1;
25        return 0;
26      },
27      showHidden: false,
28      depth: null,
29    })
30  );
31}
32
33export async function configAsync(projectRoot: string, options: Options) {
34  if (options.type) {
35    assert.match(options.type, /^(public|prebuild|introspect)$/);
36  }
37
38  let config: ProjectConfig;
39
40  if (options.type === 'prebuild') {
41    const { getPrebuildConfigAsync } = await import('@expo/prebuild-config');
42
43    config = await profile(getPrebuildConfigAsync)(projectRoot, {
44      platforms: ['ios', 'android'],
45    });
46  } else if (options.type === 'introspect') {
47    const { getPrebuildConfigAsync } = await import('@expo/prebuild-config');
48    const { compileModsAsync } = await import('@expo/config-plugins/build/plugins/mod-compiler');
49
50    config = await profile(getPrebuildConfigAsync)(projectRoot, {
51      platforms: ['ios', 'android'],
52    });
53
54    await compileModsAsync(config.exp, {
55      projectRoot,
56      introspect: true,
57      platforms: ['ios', 'android'],
58      assertMissingModProviders: false,
59    });
60    // @ts-ignore
61    delete config.modRequest;
62    // @ts-ignore
63    delete config.modResults;
64  } else if (options.type === 'public') {
65    config = profile(getConfig)(projectRoot, {
66      skipSDKVersionRequirement: true,
67      isPublicConfig: true,
68    });
69  } else if (options.type) {
70    throw new CommandError(
71      `Invalid option: --type ${options.type}. Valid options are: public, prebuild`
72    );
73  } else {
74    config = profile(getConfig)(projectRoot, {
75      skipSDKVersionRequirement: true,
76    });
77  }
78
79  const configOutput = options.full ? config : config.exp;
80
81  if (!options.json) {
82    Log.log();
83    logConfig(configOutput);
84    Log.log();
85  } else {
86    Log.log(JSON.stringify(configOutput));
87  }
88}
89