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