Skip to content
This repository has been archived by the owner on Jan 18, 2024. It is now read-only.

[xdl] refactor start command folder #3186

Merged
merged 2 commits into from
Feb 5, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions packages/xdl/src/Env.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { ExpoConfig } from '@expo/config';
import getenv from 'getenv';
import os from 'os';

import * as Versions from './Versions';

export function home(): string {
return os.homedir();
}
Expand All @@ -16,3 +19,12 @@ export function isLocal(): boolean {
export function maySkipManifestValidation(): boolean {
return !!getenv.string('EXPO_SKIP_MANIFEST_VALIDATION_TOKEN');
}

/**
* Returns true if we should use Metro using its JS APIs via @expo/dev-server (the modern and fast
* way), false if we should fall back to spawning it as a subprocess (supported for backwards
* compatibility with SDK39 and older).
*/
export function shouldUseDevServer(exp: ExpoConfig) {
return Versions.gteSdkVersion(exp, '40.0.0') || getenv.boolish('EXPO_USE_DEV_SERVER', false);
}
114 changes: 5 additions & 109 deletions packages/xdl/src/Project.ts
Original file line number Diff line number Diff line change
@@ -1,119 +1,15 @@
import { ExpoConfig, getConfig } from '@expo/config';

import Analytics from './Analytics';
import * as Android from './Android';
import Config from './Config';
import * as DevSession from './DevSession';
import * as ProjectSettings from './ProjectSettings';
import * as Webpack from './Webpack';
import XDLError from './XDLError';
import * as ProjectUtils from './project/ProjectUtils';
import { assertValidProjectRoot } from './project/errors';
import { startTunnelsAsync, stopTunnelsAsync } from './project/ngrok';
import {
shouldUseDevServer,
startDevServerAsync,
StartOptions,
} from './project/startDevServerAsync';
import { startExpoServerAsync, stopExpoServerAsync } from './project/startLegacyExpoServerAsync';
import { startTunnelsAsync, stopTunnelsAsync } from './start/ngrok';
import { StartOptions } from './start/startDevServerAsync';
import { startExpoServerAsync } from './start/startLegacyExpoServerAsync';
import {
startReactNativeServerAsync,
stopReactNativeServerAsync,
} from './project/startLegacyReactNativeServerAsync';

export async function startAsync(
projectRoot: string,
{ exp = getConfig(projectRoot).exp, ...options }: StartOptions & { exp?: ExpoConfig } = {},
verbose: boolean = true
): Promise<ExpoConfig> {
assertValidProjectRoot(projectRoot);
Analytics.logEvent('Start Project', {
projectRoot,
developerTool: Config.developerTool,
sdkVersion: exp.sdkVersion ?? null,
});

if (options.webOnly) {
await Webpack.restartAsync(projectRoot, options);
DevSession.startSession(projectRoot, exp, 'web');
return exp;
} else if (shouldUseDevServer(exp) || options.devClient) {
await startDevServerAsync(projectRoot, options);
DevSession.startSession(projectRoot, exp, 'native');
} else {
await startExpoServerAsync(projectRoot);
await startReactNativeServerAsync({ projectRoot, exp, options, verbose });
DevSession.startSession(projectRoot, exp, 'native');
}

const { hostType } = await ProjectSettings.readAsync(projectRoot);

if (!Config.offline && hostType === 'tunnel') {
try {
await startTunnelsAsync(projectRoot);
} catch (e) {
ProjectUtils.logDebug(projectRoot, 'expo', `Error starting tunnel ${e.message}`);
}
}
return exp;
}
} from './start/startLegacyReactNativeServerAsync';

export async function stopWebOnlyAsync(projectRoot: string): Promise<void> {
DevSession.stopSession();
await Webpack.stopAsync(projectRoot);
}

async function stopInternalAsync(projectRoot: string): Promise<void> {
DevSession.stopSession();
await Webpack.stopAsync(projectRoot);
ProjectUtils.logInfo(projectRoot, 'expo', '\u203A Closing Expo server');
await stopExpoServerAsync(projectRoot);
ProjectUtils.logInfo(projectRoot, 'expo', '\u203A Stopping Metro bundler');
await stopReactNativeServerAsync(projectRoot);
if (!Config.offline) {
try {
await stopTunnelsAsync(projectRoot);
} catch (e) {
ProjectUtils.logDebug(projectRoot, 'expo', `Error stopping ngrok ${e.message}`);
}
}

await Android.maybeStopAdbDaemonAsync();
}

async function forceQuitAsync(projectRoot: string) {
// find RN packager and ngrok pids, attempt to kill them manually
const { packagerPid, ngrokPid } = await ProjectSettings.readPackagerInfoAsync(projectRoot);
if (packagerPid) {
try {
process.kill(packagerPid);
} catch (e) {}
}
if (ngrokPid) {
try {
process.kill(ngrokPid);
} catch (e) {}
}
await ProjectSettings.setPackagerInfoAsync(projectRoot, {
expoServerPort: null,
packagerPort: null,
packagerPid: null,
expoServerNgrokUrl: null,
packagerNgrokUrl: null,
ngrokPid: null,
webpackServerPort: null,
});
}

export async function stopAsync(projectRoot: string): Promise<void> {
const result = await Promise.race([
stopInternalAsync(projectRoot),
new Promise(resolve => setTimeout(resolve, 2000, 'stopFailed')),
]);
if (result === 'stopFailed') {
await forceQuitAsync(projectRoot);
}
}
export { startAsync, stopWebOnlyAsync, stopAsync } from './start/startAsync';

/**
* @deprecated Use `ProjectSettings.setPackagerInfoAsync`
Expand Down
2 changes: 1 addition & 1 deletion packages/xdl/src/project/exportAppAsync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import urljoin from 'url-join';
import uuid from 'uuid';

import * as EmbeddedAssets from '../EmbeddedAssets';
import { shouldUseDevServer } from '../Env';
import logger from '../Logger';
import { Asset, exportAssetsAsync } from '../ProjectAssets';
import UserManager, { ANONYMOUS_USERNAME } from '../User';
Expand All @@ -18,7 +19,6 @@ import { writeArtifactSafelyAsync } from '../tools/ArtifactUtils';
import { getPublishExpConfigAsync, PublishOptions } from './getPublishExpConfigAsync';
import { buildPublishBundlesAsync } from './publishAsync';
import { prepareHooks, runHook } from './runHook';
import { shouldUseDevServer } from './startDevServerAsync';

const bundlePlatforms: BundlePlatform[] = ['android', 'ios'];

Expand Down
10 changes: 5 additions & 5 deletions packages/xdl/src/project/publishAsync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import Analytics from '../Analytics';
import ApiV2 from '../ApiV2';
import Config from '../Config';
import * as EmbeddedAssets from '../EmbeddedAssets';
import { shouldUseDevServer } from '../Env';
import { ErrorCode } from '../ErrorCode';
import logger from '../Logger';
import { publishAssetsAsync } from '../ProjectAssets';
Expand All @@ -26,16 +27,15 @@ import XDLError from '../XDLError';
import * as ExponentTools from '../detach/ExponentTools';
import * as TableText from '../logs/TableText';
import { learnMore } from '../logs/TerminalLink';
import {
startReactNativeServerAsync,
stopReactNativeServerAsync,
} from '../start/startLegacyReactNativeServerAsync';
import { resolveEntryPoint } from '../tools/resolveEntryPoint';
import * as Doctor from './Doctor';
import * as ProjectUtils from './ProjectUtils';
import { getPublishExpConfigAsync, PublishOptions } from './getPublishExpConfigAsync';
import { LoadedHook, prepareHooks, runHook } from './runHook';
import { shouldUseDevServer } from './startDevServerAsync';
import {
startReactNativeServerAsync,
stopReactNativeServerAsync,
} from './startLegacyReactNativeServerAsync';

const MINIMUM_BUNDLE_SIZE = 500;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ import UserManager, { ANONYMOUS_USERNAME } from '../User';
import UserSettings from '../UserSettings';
import * as Versions from '../Versions';
import { learnMore } from '../logs/TerminalLink';
import * as Doctor from '../project/Doctor';
import * as ProjectUtils from '../project/ProjectUtils';
import { resolveEntryPoint } from '../tools/resolveEntryPoint';
import * as Doctor from './Doctor';
import * as ProjectUtils from './ProjectUtils';

interface HostInfo {
host: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jest.mock('../../User', () => {
}),
};
});
jest.mock('../ExpSchema', () => {
jest.mock('../../project/ExpSchema', () => {
// const user = jest.requireActual('../../User');
return {
getAssetSchemasAsync() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import * as UrlUtils from '../UrlUtils';
import UserManager, { ANONYMOUS_USERNAME } from '../User';
import UserSettings from '../UserSettings';
import XDLError from '../XDLError';
import * as Logger from '../project/ProjectUtils';
import { assertValidProjectRoot } from '../project/errors';
import { delayAsync } from '../utils/delayAsync';
import * as Logger from './ProjectUtils';
import { assertValidProjectRoot } from './errors';
import { NgrokOptions, resolveNgrokAsync } from './resolveNgrok';

function getNgrokConfigPath() {
Expand Down
112 changes: 112 additions & 0 deletions packages/xdl/src/start/startAsync.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import { ExpoConfig, getConfig } from '@expo/config';

import Analytics from '../Analytics';
import * as Android from '../Android';
import Config from '../Config';
import * as DevSession from '../DevSession';
import { shouldUseDevServer } from '../Env';
import * as ProjectSettings from '../ProjectSettings';
import * as Webpack from '../Webpack';
import * as ProjectUtils from '../project/ProjectUtils';
import { assertValidProjectRoot } from '../project/errors';
import { startTunnelsAsync, stopTunnelsAsync } from './ngrok';
import { startDevServerAsync, StartOptions } from './startDevServerAsync';
import { startExpoServerAsync, stopExpoServerAsync } from './startLegacyExpoServerAsync';
import {
startReactNativeServerAsync,
stopReactNativeServerAsync,
} from './startLegacyReactNativeServerAsync';

export async function startAsync(
projectRoot: string,
{ exp = getConfig(projectRoot).exp, ...options }: StartOptions & { exp?: ExpoConfig } = {},
verbose: boolean = true
): Promise<ExpoConfig> {
assertValidProjectRoot(projectRoot);
Analytics.logEvent('Start Project', {
projectRoot,
developerTool: Config.developerTool,
sdkVersion: exp.sdkVersion ?? null,
});

if (options.webOnly) {
await Webpack.restartAsync(projectRoot, options);
DevSession.startSession(projectRoot, exp, 'web');
return exp;
} else if (shouldUseDevServer(exp) || options.devClient) {
await startDevServerAsync(projectRoot, options);
DevSession.startSession(projectRoot, exp, 'native');
} else {
await startExpoServerAsync(projectRoot);
await startReactNativeServerAsync({ projectRoot, exp, options, verbose });
DevSession.startSession(projectRoot, exp, 'native');
}

const { hostType } = await ProjectSettings.readAsync(projectRoot);

if (!Config.offline && hostType === 'tunnel') {
try {
await startTunnelsAsync(projectRoot);
} catch (e) {
ProjectUtils.logDebug(projectRoot, 'expo', `Error starting tunnel ${e.message}`);
}
}
return exp;
}

export async function stopWebOnlyAsync(projectRoot: string): Promise<void> {
DevSession.stopSession();
await Webpack.stopAsync(projectRoot);
}

async function stopInternalAsync(projectRoot: string): Promise<void> {
DevSession.stopSession();
await Webpack.stopAsync(projectRoot);
ProjectUtils.logInfo(projectRoot, 'expo', '\u203A Closing Expo server');
await stopExpoServerAsync(projectRoot);
ProjectUtils.logInfo(projectRoot, 'expo', '\u203A Stopping Metro bundler');
await stopReactNativeServerAsync(projectRoot);
if (!Config.offline) {
try {
await stopTunnelsAsync(projectRoot);
} catch (e) {
ProjectUtils.logDebug(projectRoot, 'expo', `Error stopping ngrok ${e.message}`);
}
}

await Android.maybeStopAdbDaemonAsync();
}

async function forceQuitAsync(projectRoot: string) {
// find RN packager and ngrok pids, attempt to kill them manually
const { packagerPid, ngrokPid } = await ProjectSettings.readPackagerInfoAsync(projectRoot);
if (packagerPid) {
try {
process.kill(packagerPid);
} catch (e) {}
}
if (ngrokPid) {
try {
process.kill(ngrokPid);
} catch (e) {}
}
await ProjectSettings.setPackagerInfoAsync(projectRoot, {
expoServerPort: null,
packagerPort: null,
packagerPid: null,
expoServerNgrokUrl: null,
packagerNgrokUrl: null,
ngrokPid: null,
webpackServerPort: null,
});
}

export async function stopAsync(projectRoot: string): Promise<void> {
const result = await Promise.race([
stopInternalAsync(projectRoot),
new Promise(resolve => setTimeout(resolve, 2000, 'stopFailed')),
]);
if (result === 'stopFailed') {
await forceQuitAsync(projectRoot);
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import { ExpoConfig, ProjectTarget } from '@expo/config';
import { ProjectTarget } from '@expo/config';
import { MetroDevServerOptions, runMetroDevServerAsync } from '@expo/dev-server';
import { boolish } from 'getenv';

import * as ProjectSettings from '../ProjectSettings';
import * as Versions from '../Versions';
import * as ProjectUtils from '../project/ProjectUtils';
import { assertValidProjectRoot } from '../project/errors';
import { getManifestHandler } from './ManifestHandler';
import * as ProjectUtils from './ProjectUtils';
import { assertValidProjectRoot } from './errors';
import { getFreePortAsync } from './getFreePortAsync';

export type StartOptions = {
Expand All @@ -19,15 +17,6 @@ export type StartOptions = {
target?: ProjectTarget;
};

/**
* Returns true if we should use Metro using its JS APIs via @expo/dev-server (the modern and fast
* way), false if we should fall back to spawning it as a subprocess (supported for backwards
* compatibility with SDK39 and older).
*/
export function shouldUseDevServer(exp: ExpoConfig) {
return Versions.gteSdkVersion(exp, '40.0.0') || boolish('EXPO_USE_DEV_SERVER', false);
}

export async function startDevServerAsync(projectRoot: string, startOptions: StartOptions) {
assertValidProjectRoot(projectRoot);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import { AddressInfo } from 'net';

import * as ConnectionStatus from '../ConnectionStatus';
import * as ProjectSettings from '../ProjectSettings';
import * as Doctor from './Doctor';
import * as Doctor from '../project/Doctor';
import * as ProjectUtils from '../project/ProjectUtils';
import { assertValidProjectRoot } from '../project/errors';
import { getManifestHandler } from './ManifestHandler';
import * as ProjectUtils from './ProjectUtils';
import { assertValidProjectRoot } from './errors';
import { getFreePortAsync } from './getFreePortAsync';

type ConsoleLogLevel = 'info' | 'warn' | 'error' | 'debug';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ import * as ProjectSettings from '../ProjectSettings';
import * as UrlUtils from '../UrlUtils';
import * as Versions from '../Versions';
import * as Watchman from '../Watchman';
import * as ProjectUtils from '../project/ProjectUtils';
import { assertValidProjectRoot } from '../project/errors';
import { delayAsync } from '../utils/delayAsync';
import * as ProjectUtils from './ProjectUtils';
import { assertValidProjectRoot } from './errors';
import { getFreePortAsync } from './getFreePortAsync';
import { StartOptions } from './startDevServerAsync';

Expand Down