1import { ModPlatform } from '@expo/config-plugins';
2import assert from 'assert';
3import chalk from 'chalk';
4import fs from 'fs';
5import path from 'path';
6
7import * as Log from '../log';
8import { CommandError } from '../utils/errors';
9import { validateUrl } from '../utils/url';
10
11export function resolvePackageManagerOptions(args: any) {
12  const managers: Record<string, boolean> = {
13    npm: args['--npm'],
14    yarn: args['--yarn'],
15    pnpm: args['--pnpm'],
16  };
17
18  if (
19    [managers.npm, managers.pnpm, managers.yarn, !!args['--no-install']].filter(Boolean).length > 1
20  ) {
21    throw new CommandError(
22      'BAD_ARGS',
23      'Specify at most one of: --no-install, --npm, --pnpm, --yarn'
24    );
25  }
26
27  return managers;
28}
29
30/** Resolves a template option as a URL or file path pointing to a tar file. */
31export function resolveTemplateOption(template: string) {
32  if (validateUrl(template)) {
33    return template;
34  }
35  const templatePath = path.resolve(template);
36  assert(fs.existsSync(templatePath), 'template file does not exist: ' + templatePath);
37  assert(
38    fs.statSync(templatePath).isFile(),
39    'template must be a tar file created by running `npm pack` in a project: ' + templatePath
40  );
41
42  return templatePath;
43}
44
45/** Resolves dependencies to skip from a string joined by `,`. Example: `react-native,expo,lodash` */
46export function resolveSkipDependencyUpdate(value: any) {
47  if (!value || typeof value !== 'string') {
48    return [];
49  }
50  return value.split(',');
51}
52
53/** Returns an array of platforms based on the input platform identifier and runtime constraints. */
54export function resolvePlatformOption(
55  platform: string = 'all',
56  { loose }: { loose?: boolean } = {}
57): ModPlatform[] {
58  switch (platform) {
59    case 'ios':
60      return ['ios'];
61    case 'android':
62      return ['android'];
63    case 'all':
64      if (loose || process.platform !== 'win32') {
65        return ['android', 'ios'];
66      }
67      return ['android'];
68    default:
69      throw new CommandError(`Unsupported platform "${platform}". Options are: ios, android, all`);
70  }
71}
72
73/** Warns and filters out unsupported platforms based on the runtime constraints. Essentially this means no iOS on Windows devices. */
74export function ensureValidPlatforms(platforms: ModPlatform[]): ModPlatform[] {
75  // Skip ejecting for iOS on Windows
76  if (process.platform === 'win32' && platforms.includes('ios')) {
77    Log.warn(
78      chalk`⚠️  Skipping generating the iOS native project files. Run {bold expo eject} again from macOS or Linux to generate the iOS project.\n`
79    );
80    return platforms.filter((platform) => platform !== 'ios');
81  }
82  return platforms;
83}
84
85/** Asserts platform length must be greater than zero. */
86export function assertPlatforms(platforms: ModPlatform[]) {
87  if (!platforms?.length) {
88    throw new CommandError('At least one platform must be enabled when syncing');
89  }
90}
91