1import { getConfig, Platform } from '@expo/config';
2
3import { getPlatformBundlers, PlatformBundlers } from '../start/server/platformBundlers';
4import { CommandError } from '../utils/errors';
5
6export type Options = {
7  outputDir: string;
8  platforms: Platform[];
9  maxWorkers?: number;
10  dev: boolean;
11  clear: boolean;
12  minify: boolean;
13  dumpAssetmap: boolean;
14  dumpSourcemap: boolean;
15};
16
17/** Returns an array of platforms based on the input platform identifier and runtime constraints. */
18export function resolvePlatformOption(
19  platformBundlers: PlatformBundlers,
20  platform: string[] = ['all']
21): Platform[] {
22  const platformsAvailable: Partial<PlatformBundlers> = Object.fromEntries(
23    Object.entries(platformBundlers).filter(([, bundler]) => bundler === 'metro')
24  );
25
26  if (!Object.keys(platformsAvailable).length) {
27    throw new CommandError(
28      `No platforms are configured to use the Metro bundler in the project Expo config.`
29    );
30  }
31
32  const assertPlatformBundler = (platform: Platform): Platform => {
33    if (!platformsAvailable[platform]) {
34      throw new CommandError(
35        'BAD_ARGS',
36        `Platform "${platform}" is not configured to use the Metro bundler in the project Expo config.`
37      );
38    }
39
40    return platform;
41  };
42
43  const knownPlatforms = ['android', 'ios', 'web'] as Platform[];
44  const assertPlatformIsKnown = (platform: string): Platform => {
45    if (!knownPlatforms.includes(platform as Platform)) {
46      throw new CommandError(
47        `Unsupported platform "${platform}". Options are: ${knownPlatforms.join(',')},all`
48      );
49    }
50
51    return platform as Platform;
52  };
53
54  return (
55    platform
56      // Expand `all` to all available platforms.
57      .map((platform) => (platform === 'all' ? Object.keys(platformsAvailable) : platform))
58      .flat()
59      // Remove duplicated platforms
60      .filter((platform, index, list) => list.indexOf(platform) === index)
61      // Assert platforms are valid
62      .map((platform) => assertPlatformIsKnown(platform))
63      .map((platform) => assertPlatformBundler(platform))
64  );
65}
66
67export async function resolveOptionsAsync(projectRoot: string, args: any): Promise<Options> {
68  const { exp } = getConfig(projectRoot, { skipPlugins: true, skipSDKVersionRequirement: true });
69  const platformBundlers = getPlatformBundlers(exp);
70
71  return {
72    platforms: resolvePlatformOption(platformBundlers, args['--platform']),
73    outputDir: args['--output-dir'] ?? 'dist',
74    minify: !args['--no-minify'],
75    clear: !!args['--clear'],
76    dev: !!args['--dev'],
77    maxWorkers: args['--max-workers'],
78    dumpAssetmap: !!args['--dump-assetmap'],
79    dumpSourcemap: !!args['--dump-sourcemap'],
80  };
81}
82