1import * as PackageManager from '@expo/package-manager'; 2 3import * as Log from '../log'; 4import { CommandError } from '../utils/errors'; 5 6export type Options = Pick<PackageManager.CreateForProjectOptions, 'npm' | 'yarn'>; 7 8function resolveOptions(options: Options): Options { 9 if (options.npm && options.yarn) { 10 throw new CommandError('BAD_ARGS', 'Specify at most one of: --npm, --yarn'); 11 } 12 return { 13 ...options, 14 }; 15} 16 17/** Given a list of CLI args, return a sorted set of args based on categories used in a complex command. */ 18export function parseVariadicArguments(argv: string[]): { 19 variadic: string[]; 20 extras: string[]; 21 flags: Record<string, boolean>; 22} { 23 const variadic: string[] = []; 24 const flags: Record<string, boolean> = {}; 25 26 for (const arg of argv) { 27 if (!arg.startsWith('-')) { 28 variadic.push(arg); 29 } else if (arg === '--') { 30 break; 31 } else { 32 flags[arg] = true; 33 } 34 } 35 36 // Everything after `--` that is not an option is passed to the underlying install command. 37 const extras: string[] = []; 38 39 const extraOperator = argv.indexOf('--'); 40 if (extraOperator > -1 && argv.length > extraOperator + 1) { 41 const extraArgs = argv.slice(extraOperator + 1); 42 if (extraArgs.includes('--')) { 43 throw new CommandError('BAD_ARGS', 'Unexpected multiple --'); 44 } 45 extras.push(...extraArgs); 46 Log.debug('Extra arguments: ' + extras.join(', ')); 47 } 48 49 Log.debug(`Parsed arguments (variadic: %O, flags: %O, extra: %O)`, variadic, flags, extras); 50 51 return { 52 variadic, 53 flags, 54 extras, 55 }; 56} 57 58export async function resolveArgsAsync( 59 argv: string[] 60): Promise<{ variadic: string[]; options: Options; extras: string[] }> { 61 const { variadic, extras, flags } = parseVariadicArguments(argv); 62 63 assertUnexpectedObjectKeys(['--npm', '--yarn'], flags); 64 65 return { 66 // Variadic arguments like `npx expo install react react-dom` -> ['react', 'react-dom'] 67 variadic, 68 options: resolveOptions({ 69 yarn: !!flags['--yarn'], 70 npm: !!flags['--npm'], 71 }), 72 extras, 73 }; 74} 75 76function assertUnexpectedObjectKeys(keys: string[], obj: Record<string, any>): void { 77 const unexpectedKeys = Object.keys(obj).filter((key) => !keys.includes(key)); 78 if (unexpectedKeys.length > 0) { 79 throw new CommandError('BAD_ARGS', `Unexpected: ${unexpectedKeys.join(', ')}`); 80 } 81} 82