From d7ef4593de17826456bf2b63a0b043feaa070ef3 Mon Sep 17 00:00:00 2001 From: Brent Vatne Date: Wed, 14 Oct 2020 14:23:56 -0700 Subject: [PATCH] [cli] Skip ejecting iOS on Windows --- packages/expo-cli/src/commands/eject.ts | 11 +--- packages/expo-cli/src/commands/eject/Eject.ts | 62 +++++++++++++++---- .../commands/eject/__tests__/Eject-test.ts | 11 +++- 3 files changed, 60 insertions(+), 24 deletions(-) diff --git a/packages/expo-cli/src/commands/eject.ts b/packages/expo-cli/src/commands/eject.ts index 5ed6d0fbe6..b77bdaf6d1 100644 --- a/packages/expo-cli/src/commands/eject.ts +++ b/packages/expo-cli/src/commands/eject.ts @@ -55,21 +55,12 @@ export default function (program: Command) { .command('eject [path]') .description( // TODO: Use Learn more link when it lands - `Create native iOS and Android project files. Read more: https://expo.fyi/eject` + `Create native iOS and Android project files. Read more: https://docs.expo.io/bare/customizing/` ) .longDescription( 'Create Xcode and Android Studio projects for your app. Use this if you need to add custom native functionality.' ) .helpGroup('eject') - .option( - '--eject-method [type]', - `Eject method to use. [Depreacted]: Ejecting to ExpoKit is not available on SDK >= 37 and not recommended for older SDK versions. We recommend updating to SDK >= 37 and ejecting to bare.`, - (value: string) => value.toLowerCase() - ) - .option( - '-f --force', - 'Will attempt to generate an iOS project even when the system is not running macOS. Unsafe and may fail.' - ) .option('--no-install', 'Skip installing npm packages and CocoaPods.') .option('--npm', 'Use npm to install dependencies. (default when Yarn is not installed)') .asyncActionProjectDir(action); diff --git a/packages/expo-cli/src/commands/eject/Eject.ts b/packages/expo-cli/src/commands/eject/Eject.ts index 4f2b8ad023..befcd8d77f 100644 --- a/packages/expo-cli/src/commands/eject/Eject.ts +++ b/packages/expo-cli/src/commands/eject/Eject.ts @@ -29,6 +29,7 @@ import maybeBailOnGitStatusAsync from '../utils/maybeBailOnGitStatusAsync'; import { getOrPromptForBundleIdentifier, getOrPromptForPackage } from './ConfigValidation'; type DependenciesMap = { [key: string]: string | number }; +type PlatformsArray = ('ios' | 'android')[]; export type EjectAsyncOptions = { verbose?: boolean; @@ -50,14 +51,31 @@ export type EjectAsyncOptions = { export async function ejectAsync(projectRoot: string, options?: EjectAsyncOptions): Promise { if (await maybeBailOnGitStatusAsync()) return; + const platforms: PlatformsArray = ['android']; + + // Skip ejecting for iOS on Windows + if (process.platform !== 'win32') { + platforms.push('ios'); + } + const { exp, pkg } = await ensureConfigAsync(projectRoot); const tempDir = temporary.directory(); + if (!platforms.includes('ios')) { + log.warn( + `⚠️ Skipping generating the iOS native project files. Run ${chalk.bold( + 'expo eject' + )} again from macOS or Linux to generate the iOS project.` + ); + log.newLine(); + } + const { hasNewProjectFiles, needsPodInstall } = await createNativeProjectsFromTemplateAsync( projectRoot, exp, pkg, - tempDir + tempDir, + platforms ); // Set this to true when we can detect that the user is running eject to sync new changes rather than ejecting to bare. // This will be used to prevent the node modules from being nuked every time. @@ -77,13 +95,18 @@ export async function ejectAsync(projectRoot: string, options?: EjectAsyncOption } // Apply Expo config to native projects - await configureIOSStepAsync(projectRoot); - await configureAndroidStepAsync(projectRoot); + if (platforms.includes('ios')) { + await configureIOSStepAsync(projectRoot); + } + + if (platforms.includes('android')) { + await configureAndroidStepAsync(projectRoot); + } // Install CocoaPods let podsInstalled: boolean = false; // err towards running pod install less because it's slow and users can easily run npx pod-install afterwards. - if (shouldInstall && needsPodInstall) { + if (platforms.includes('ios') && shouldInstall && needsPodInstall) { podsInstalled = await CreateApp.installCocoaPodsAsync(projectRoot); } @@ -154,8 +177,14 @@ export async function ejectAsync(projectRoot: string, options?: EjectAsyncOption 'To compile and run your project in development, execute one of the following commands:' ); - log.nested(`- ${chalk.bold(packageManager === 'npm' ? 'npm run ios' : 'yarn ios')}`); - log.nested(`- ${chalk.bold(packageManager === 'npm' ? 'npm run android' : 'yarn android')}`); + if (platforms.includes('ios')) { + log.nested(`- ${chalk.bold(packageManager === 'npm' ? 'npm run ios' : 'yarn ios')}`); + } + + if (platforms.includes('android')) { + log.nested(`- ${chalk.bold(packageManager === 'npm' ? 'npm run android' : 'yarn android')}`); + } + log.nested(`- ${chalk.bold(packageManager === 'npm' ? 'npm run web' : 'yarn web')}`); } } @@ -503,8 +532,8 @@ export function hashForDependencyMap(deps: DependenciesMap): string { return createFileHash(depsString); } -export function getTargetPaths(pkg: PackageJSONConfig) { - const targetPaths = ['ios', 'android']; +export function getTargetPaths(pkg: PackageJSONConfig, platforms: PlatformsArray) { + const targetPaths: string[] = [...platforms]; // Only create index.js if we are going to replace the app "main" entry point if (shouldDeleteMainField(pkg.main)) { @@ -524,11 +553,13 @@ async function cloneNativeDirectoriesAsync({ tempDir, exp, pkg, + platforms, }: { projectRoot: string; tempDir: string; exp: Pick; pkg: PackageJSONConfig; + platforms: PlatformsArray; }): Promise { const templateSpec = await validateBareTemplateExistsAsync(exp.sdkVersion!); @@ -538,7 +569,7 @@ async function cloneNativeDirectoriesAsync({ 'Creating native project directories (./ios and ./android) and updating .gitignore' ); - const targetPaths = getTargetPaths(pkg); + const targetPaths = getTargetPaths(pkg, platforms); let copiedPaths: string[] = []; let skippedPaths: string[] = []; @@ -550,7 +581,7 @@ async function cloneNativeDirectoriesAsync({ path.join(tempDir, '.gitignore') ); - let message = `Created native projects`; + let message = `Created native project${platforms.length > 1 ? 's' : ''}`; if (skippedPaths.length) { message += log.chalk.dim( @@ -590,11 +621,18 @@ async function createNativeProjectsFromTemplateAsync( projectRoot: string, exp: ExpoConfig, pkg: PackageJSONConfig, - tempDir: string + tempDir: string, + platforms: PlatformsArray ): Promise< { hasNewProjectFiles: boolean; needsPodInstall: boolean } & DependenciesModificationResults > { - const copiedPaths = await cloneNativeDirectoriesAsync({ projectRoot, tempDir, exp, pkg }); + const copiedPaths = await cloneNativeDirectoriesAsync({ + projectRoot, + tempDir, + exp, + pkg, + platforms, + }); writeMetroConfig({ projectRoot, pkg, tempDir }); diff --git a/packages/expo-cli/src/commands/eject/__tests__/Eject-test.ts b/packages/expo-cli/src/commands/eject/__tests__/Eject-test.ts index 21de54d3a9..278752d650 100644 --- a/packages/expo-cli/src/commands/eject/__tests__/Eject-test.ts +++ b/packages/expo-cli/src/commands/eject/__tests__/Eject-test.ts @@ -26,7 +26,7 @@ describe('hashForDependencyMap', () => { describe('getTargetPaths', () => { it(`should include index.js when pkg.main should be deleted`, () => { - expect(getTargetPaths({ main: 'node_modules/expo/AppEntry.js' })).toEqual([ + expect(getTargetPaths({ main: 'node_modules/expo/AppEntry.js' }, ['ios', 'android'])).toEqual([ 'ios', 'android', 'index.js', @@ -34,7 +34,14 @@ describe('getTargetPaths', () => { }); it(`should not include index.js when pkg.main is left alone`, () => { - expect(getTargetPaths({ main: './src/index.js' })).toEqual(['ios', 'android']); + expect(getTargetPaths({ main: './src/index.js' }, ['ios', 'android'])).toEqual([ + 'ios', + 'android', + ]); + }); + + it(`should only include paths for given platforms`, () => { + expect(getTargetPaths({ main: './src/index.js' }, ['ios'])).toEqual(['ios']); }); });