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    bun: args['--bun'],
17  };
18
19  if (
20    [managers.npm, managers.pnpm, managers.yarn, managers.bun, !!args['--no-install']].filter(
21      Boolean
22    ).length > 1
23  ) {
24    throw new CommandError(
25      'BAD_ARGS',
26      'Specify at most one of: --no-install, --npm, --pnpm, --yarn, --bun'
27    );
28  }
29
30  return managers;
31}
32
33/** Resolves a template option as a URL or file path pointing to a tar file. */
34export function resolveTemplateOption(template: string) {
35  if (validateUrl(template)) {
36    return template;
37  }
38  const templatePath = path.resolve(template);
39  assert(fs.existsSync(templatePath), 'template file does not exist: ' + templatePath);
40  assert(
41    fs.statSync(templatePath).isFile(),
42    'template must be a tar file created by running `npm pack` in a project: ' + templatePath
43  );
44
45  return templatePath;
46}
47
48/** Resolves dependencies to skip from a string joined by `,`. Example: `react-native,expo,lodash` */
49export function resolveSkipDependencyUpdate(value: any) {
50  if (!value || typeof value !== 'string') {
51    return [];
52  }
53  return value.split(',');
54}
55
56/** Returns an array of platforms based on the input platform identifier and runtime constraints. */
57export function resolvePlatformOption(
58  platform: string = 'all',
59  { loose }: { loose?: boolean } = {}
60): ModPlatform[] {
61  switch (platform) {
62    case 'ios':
63      return ['ios'];
64    case 'android':
65      return ['android'];
66    case 'all':
67      return loose || process.platform !== 'win32' ? ['android', 'ios'] : ['android'];
68    default:
69      return [platform as ModPlatform];
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