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  require('@expo/env').load(projectRoot);
37
38  if (options.type) {
39    assert.match(options.type, /^(public|prebuild|introspect)$/);
40  }
41
42  let config: ProjectConfig;
43
44  if (options.type === 'prebuild') {
45    const { getPrebuildConfigAsync } = await import('@expo/prebuild-config');
46
47    config = await profile(getPrebuildConfigAsync)(projectRoot, {
48      platforms: ['ios', 'android'],
49    });
50  } else if (options.type === 'introspect') {
51    const { getPrebuildConfigAsync } = await import('@expo/prebuild-config');
52    const { compileModsAsync } = await import('@expo/config-plugins/build/plugins/mod-compiler');
53
54    config = await profile(getPrebuildConfigAsync)(projectRoot, {
55      platforms: ['ios', 'android'],
56    });
57
58    await compileModsAsync(config.exp, {
59      projectRoot,
60      introspect: true,
61      platforms: ['ios', 'android'],
62      assertMissingModProviders: false,
63    });
64    // @ts-ignore
65    delete config.modRequest;
66    // @ts-ignore
67    delete config.modResults;
68  } else if (options.type === 'public') {
69    config = profile(getConfig)(projectRoot, {
70      skipSDKVersionRequirement: true,
71      isPublicConfig: true,
72    });
73  } else if (options.type) {
74    throw new CommandError(
75      `Invalid option: --type ${options.type}. Valid options are: public, prebuild`
76    );
77  } else {
78    config = profile(getConfig)(projectRoot, {
79      skipSDKVersionRequirement: true,
80    });
81  }
82
83  const configOutput = options.full ? config : config.exp;
84
85  if (!options.json) {
86    Log.log();
87    logConfig(configOutput);
88    Log.log();
89  } else {
90    Log.log(JSON.stringify(configOutput));
91  }
92}
93