microsoft/vscode-react-native

Public

mirrored from https://github.com/microsoft/vscode-react-nativeAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
1.0.1

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

src/extension/appLauncher.ts

376lines · modeblame

6e1bcd36RedMickey6 years ago1// Copyright (c) Microsoft Corporation. All rights reserved.
2// Licensed under the MIT license. See LICENSE file in the project root for details.
3
4import * as vscode from "vscode";
5import {Packager} from "../common/packager";
6import {RNPackageVersions} from "../common/projectVersionHelper";
7import {ExponentHelper} from "./exponent/exponentHelper";
8import {ReactDirManager} from "./reactDirManager";
9import {SettingsHelper} from "./settingsHelper";
10import {PackagerStatusIndicator} from "./packagerStatusIndicator";
11import {CommandExecutor} from "../common/commandExecutor";
12import {isNullOrUndefined} from "../common/utils";
13import {OutputChannelLogger} from "./log/OutputChannelLogger";
68a5b8d5JiglioNero5 years ago14import {MobilePlatformDeps, GeneralMobilePlatform} from "./generalMobilePlatform";
6e1bcd36RedMickey6 years ago15import {PlatformResolver} from "./platformResolver";
16import {ProjectVersionHelper} from "../common/projectVersionHelper";
17import {TelemetryHelper} from "../common/telemetryHelper";
18import {ErrorHelper} from "../common/error/errorHelper";
19import {InternalErrorCode} from "../common/error/internalErrorCode";
20import {TargetPlatformHelper} from "../common/targetPlatformHelper";
21import {LogCatMonitor} from "./android/logCatMonitor";
f872f4d5RedMickey6 years ago22import {ProjectsStorage} from "./projectsStorage";
a6562589RedMickey6 years ago23import {ReactNativeCDPProxy} from "../cdp-proxy/reactNativeCDPProxy";
24import {generateRandomPortNumber} from "../common/extensionHelper";
5514e287RedMickey6 years ago25import {DEBUG_TYPES} from "./debugConfigurationProvider";
6e1bcd36RedMickey6 years ago26import * as nls from "vscode-nls";
7e74daf7Yuri Skorokhodov6 years ago27import { MultipleLifetimesAppWorker } from "../debugger/appWorker";
259c018fYuri Skorokhodov5 years ago28import { PlatformType } from "./launchArgs";
68a5b8d5JiglioNero5 years ago29import { LaunchScenariosManager } from "./launchScenariosManager";
30import { IVirtualDevice } from "./VirtualDeviceManager";
2d8af448Yuri Skorokhodov6 years ago31nls.config({ messageFormat: nls.MessageFormat.bundle, bundleFormat: nls.BundleFormat.standalone })();
6e1bcd36RedMickey6 years ago32const localize = nls.loadMessageBundle();
33
34export class AppLauncher {
a6562589RedMickey6 years ago35private readonly cdpProxyPort: number;
36private readonly cdpProxyHostAddress: string;
37
7e74daf7Yuri Skorokhodov6 years ago38private appWorker: MultipleLifetimesAppWorker | null;
6e1bcd36RedMickey6 years ago39private packager: Packager;
40private exponentHelper: ExponentHelper;
41private reactDirManager: ReactDirManager;
42private workspaceFolder: vscode.WorkspaceFolder;
43private reactNativeVersions?: RNPackageVersions;
a6562589RedMickey6 years ago44private rnCdpProxy: ReactNativeCDPProxy;
6e1bcd36RedMickey6 years ago45private logger: OutputChannelLogger = OutputChannelLogger.getMainChannel();
46private logCatMonitor: LogCatMonitor | null = null;
68a5b8d5JiglioNero5 years ago47private launchScenariosManager: LaunchScenariosManager;
6e1bcd36RedMickey6 years ago48
f872f4d5RedMickey6 years ago49public static getAppLauncherByProjectRootPath(projectRootPath: string): AppLauncher {
af1b9666RedMickey6 years ago50const appLauncher = ProjectsStorage.projectsCache[projectRootPath.toLowerCase()];
51if (!appLauncher) {
f872f4d5RedMickey6 years ago52throw new Error(`Could not find AppLauncher by the project root path ${projectRootPath}`);
53}
af1b9666RedMickey6 years ago54
55return appLauncher;
f872f4d5RedMickey6 years ago56}
57
6e1bcd36RedMickey6 years ago58constructor(reactDirManager: ReactDirManager, workspaceFolder: vscode.WorkspaceFolder) {
a6562589RedMickey6 years ago59// constants definition
60this.cdpProxyPort = generateRandomPortNumber();
61this.cdpProxyHostAddress = "127.0.0.1"; // localhost
62
6e1bcd36RedMickey6 years ago63const rootPath = workspaceFolder.uri.fsPath;
68a5b8d5JiglioNero5 years ago64this.launchScenariosManager = new LaunchScenariosManager(rootPath);
6e1bcd36RedMickey6 years ago65const projectRootPath = SettingsHelper.getReactNativeProjectRoot(rootPath);
66this.exponentHelper = new ExponentHelper(rootPath, projectRootPath);
869b2b5cJiglioNero5 years ago67const packagerStatusIndicator: PackagerStatusIndicator = new PackagerStatusIndicator(rootPath);
6e1bcd36RedMickey6 years ago68this.packager = new Packager(rootPath, projectRootPath, SettingsHelper.getPackagerPort(workspaceFolder.uri.fsPath), packagerStatusIndicator);
c036b0d3JiglioNero6 years ago69this.packager.setExponentHelper(this.exponentHelper);
6e1bcd36RedMickey6 years ago70this.reactDirManager = reactDirManager;
71this.workspaceFolder = workspaceFolder;
a6562589RedMickey6 years ago72this.rnCdpProxy = new ReactNativeCDPProxy(
73this.cdpProxyHostAddress,
74this.cdpProxyPort
75);
76}
77
78public getCdpProxyPort(): number {
79return this.cdpProxyPort;
80}
81
82public getRnCdpProxy(): ReactNativeCDPProxy {
83return this.rnCdpProxy;
6e1bcd36RedMickey6 years ago84}
85
86public getPackager(): Packager {
87return this.packager;
88}
89
90public getWorkspaceFolderUri(): vscode.Uri {
91return this.workspaceFolder.uri;
92}
93
94public getWorkspaceFolder(): vscode.WorkspaceFolder {
95return this.workspaceFolder;
96}
97
98public getReactNativeVersions(): RNPackageVersions | undefined {
99return this.reactNativeVersions;
100}
101
102public getExponentHelper(): ExponentHelper {
103return this.exponentHelper;
104}
105
106public getReactDirManager(): ReactDirManager {
107return this.reactDirManager;
108}
109
110public setReactNativeVersions(reactNativeVersions: RNPackageVersions): void {
111this.reactNativeVersions = reactNativeVersions;
112}
113
7e74daf7Yuri Skorokhodov6 years ago114public setAppWorker(appWorker: MultipleLifetimesAppWorker): void {
115this.appWorker = appWorker;
116}
117
118public getAppWorker(): MultipleLifetimesAppWorker | null {
119return this.appWorker;
120}
121
6e1bcd36RedMickey6 years ago122public dispose(): void {
de838bbfJiglioNero6 years ago123this.packager.getStatusIndicator().dispose();
6e1bcd36RedMickey6 years ago124this.packager.stop(true);
125this.stopMonitoringLogCat();
126}
127
128public stopMonitoringLogCat(): void {
129if (this.logCatMonitor) {
130this.logCatMonitor.dispose();
131this.logCatMonitor = null;
132}
133}
134
ce5e88eeYuri Skorokhodov5 years ago135public openFileAtLocation(filename: string, lineNumber: number): Promise<void> {
136return new Promise((resolve) => {
6e1bcd36RedMickey6 years ago137vscode.workspace.openTextDocument(vscode.Uri.file(filename))
138.then((document: vscode.TextDocument) => {
139vscode.window.showTextDocument(document)
140.then((editor: vscode.TextEditor) => {
141let range = editor.document.lineAt(lineNumber - 1).range;
142editor.selection = new vscode.Selection(range.start, range.end);
143editor.revealRange(range, vscode.TextEditorRevealType.InCenter);
ce5e88eeYuri Skorokhodov5 years ago144resolve();
6e1bcd36RedMickey6 years ago145});
146});
147});
148}
149
150public getPackagerPort(projectFolder: string): number {
151return SettingsHelper.getPackagerPort(projectFolder);
152}
153
154public launch(launchArgs: any): Promise<any> {
155let mobilePlatformOptions = this.requestSetup(launchArgs);
156
157// We add the parameter if it's defined (adapter crashes otherwise)
158if (!isNullOrUndefined(launchArgs.logCatArguments)) {
159mobilePlatformOptions.logCatArguments = [this.parseLogCatArguments(launchArgs.logCatArguments)];
160}
161
162if (!isNullOrUndefined(launchArgs.variant)) {
163mobilePlatformOptions.variant = launchArgs.variant;
164}
165
166if (!isNullOrUndefined(launchArgs.scheme)) {
167mobilePlatformOptions.scheme = launchArgs.scheme;
168}
169
170if (!isNullOrUndefined(launchArgs.productName)) {
171mobilePlatformOptions.productName = launchArgs.productName;
172}
173
174if (!isNullOrUndefined(launchArgs.launchActivity)) {
175mobilePlatformOptions.debugLaunchActivity = launchArgs.launchActivity;
176}
177
5514e287RedMickey6 years ago178if (launchArgs.type === DEBUG_TYPES.REACT_NATIVE_DIRECT) {
6e1bcd36RedMickey6 years ago179mobilePlatformOptions.isDirect = true;
180}
181
182mobilePlatformOptions.packagerPort = SettingsHelper.getPackagerPort(launchArgs.cwd || launchArgs.program);
183const platformDeps: MobilePlatformDeps = {
184packager: this.packager,
185};
186const mobilePlatform = new PlatformResolver()
187.resolveMobilePlatform(launchArgs.platform, mobilePlatformOptions, platformDeps);
188return new Promise((resolve, reject) => {
189let extProps: any = {
190platform: {
191value: launchArgs.platform,
192isPii: false,
193},
194};
195
196if (mobilePlatformOptions.isDirect) {
197extProps.isDirect = {
198value: true,
199isPii: false,
200};
201}
202
ce5e88eeYuri Skorokhodov5 years ago203return ProjectVersionHelper.getReactNativePackageVersionsFromNodeModules(mobilePlatformOptions.projectRoot, true)
6e1bcd36RedMickey6 years ago204.then(versions => {
205mobilePlatformOptions.reactNativeVersions = versions;
206extProps = TelemetryHelper.addPropertyToTelemetryProperties(versions.reactNativeVersion, "reactNativeVersion", extProps);
259c018fYuri Skorokhodov5 years ago207if (launchArgs.platform === PlatformType.Windows) {
6e1bcd36RedMickey6 years ago208if (ProjectVersionHelper.isVersionError(versions.reactNativeWindowsVersion)) {
209throw ErrorHelper.getInternalError(InternalErrorCode.ReactNativeWindowsIsNotInstalled);
210}
211extProps = TelemetryHelper.addPropertyToTelemetryProperties(versions.reactNativeWindowsVersion, "reactNativeWindowsVersion", extProps);
212}
213TelemetryHelper.generate("launch", extProps, (generator) => {
68a5b8d5JiglioNero5 years ago214generator.step("resolveEmulator");
215return this.resolveAndSaveVirtualDevice(mobilePlatform, launchArgs, mobilePlatformOptions)
259c018fYuri Skorokhodov5 years ago216.then(() => mobilePlatform.beforeStartPackager())
217.then(() => {
218generator.step("checkPlatformCompatibility");
219TargetPlatformHelper.checkTargetPlatformSupport(mobilePlatformOptions.platform);
220})
221.then(() => {
222generator.step("startPackager");
223return mobilePlatform.startPackager();
224})
225.then(() => {
226// We've seen that if we don't prewarm the bundle cache, the app fails on the first attempt to connect to the debugger logic
227// and the user needs to Reload JS manually. We prewarm it to prevent that issue
228generator.step("prewarmBundleCache");
229this.logger.info(localize("PrewarmingBundleCache", "Prewarming bundle cache. This may take a while ..."));
230return mobilePlatform.prewarmBundleCache();
231})
232.then(() => {
233generator.step("mobilePlatform.runApp").add("target", mobilePlatformOptions.target, false);
234this.logger.info(localize("BuildingAndRunningApplication", "Building and running application."));
235return mobilePlatform.runApp();
236})
237.then(() => {
238if (mobilePlatformOptions.isDirect || !mobilePlatformOptions.enableDebug) {
239if (mobilePlatformOptions.isDirect && launchArgs.platform === PlatformType.Android) {
240generator.step("mobilePlatform.enableDirectDebuggingMode");
241if (mobilePlatformOptions.enableDebug) {
242this.logger.info(localize("PrepareHermesDebugging", "Prepare Hermes debugging (experimental)"));
243} else {
244this.logger.info(localize("PrepareHermesLaunch", "Prepare Hermes launch (experimental)"));
245}
5514e287RedMickey6 years ago246} else {
259c018fYuri Skorokhodov5 years ago247generator.step("mobilePlatform.disableJSDebuggingMode");
248this.logger.info(localize("DisableJSDebugging", "Disable JS Debugging"));
6e1bcd36RedMickey6 years ago249}
259c018fYuri Skorokhodov5 years ago250return mobilePlatform.disableJSDebuggingMode();
6e1bcd36RedMickey6 years ago251}
259c018fYuri Skorokhodov5 years ago252generator.step("mobilePlatform.enableJSDebuggingMode");
253this.logger.info(localize("EnableJSDebugging", "Enable JS Debugging"));
254return mobilePlatform.enableJSDebuggingMode();
255})
256.then(resolve)
257.catch(error => {
258if (!mobilePlatformOptions.enableDebug && launchArgs.platform === PlatformType.iOS && launchArgs.type === DEBUG_TYPES.REACT_NATIVE) {
259// If we disable debugging mode for iOS scenarios, we'll we ignore the error and run the 'run-ios' command anyway,
260// since the error doesn't affects an application launch process
261return resolve();
262}
263generator.addError(error);
264this.logger.error(error);
265reject(error);
266});
6e1bcd36RedMickey6 years ago267});
268})
269.catch(error => {
270if (error && error.errorCode) {
271if (error.errorCode === InternalErrorCode.ReactNativePackageIsNotInstalled) {
272TelemetryHelper.sendErrorEvent(
273"ReactNativePackageIsNotInstalled",
274ErrorHelper.getInternalError(InternalErrorCode.ReactNativePackageIsNotInstalled)
275);
276} else if (error.errorCode === InternalErrorCode.ReactNativeWindowsIsNotInstalled) {
277TelemetryHelper.sendErrorEvent(
278"ReactNativeWindowsPackageIsNotInstalled",
279ErrorHelper.getInternalError(InternalErrorCode.ReactNativeWindowsIsNotInstalled)
280);
281}
282}
283this.logger.error(error);
284reject(error);
285});
286});
287}
288
68a5b8d5JiglioNero5 years ago289private resolveAndSaveVirtualDevice(mobilePlatform: GeneralMobilePlatform, launchArgs: any, mobilePlatformOptions: any): Promise<void> {
259c018fYuri Skorokhodov5 years ago290if (launchArgs.target && (mobilePlatformOptions.platform === PlatformType.Android || mobilePlatformOptions.platform === PlatformType.iOS)) {
68a5b8d5JiglioNero5 years ago291return mobilePlatform.resolveVirtualDevice(launchArgs.target)
292.then((emulator: IVirtualDevice | null) => {
30e70966JiglioNero5 years ago293if (emulator) {
259c018fYuri Skorokhodov5 years ago294if (emulator.name && launchArgs.platform === PlatformType.Android) {
119d7878JiglioNero5 years ago295mobilePlatformOptions.target = emulator.id;
30e70966JiglioNero5 years ago296this.launchScenariosManager.updateLaunchScenario(launchArgs, {target: emulator.name});
68a5b8d5JiglioNero5 years ago297}
259c018fYuri Skorokhodov5 years ago298if (launchArgs.platform === PlatformType.iOS) {
119d7878JiglioNero5 years ago299this.launchScenariosManager.updateLaunchScenario(launchArgs, {target: emulator.id});
300}
30e70966JiglioNero5 years ago301launchArgs.target = emulator.id;
68a5b8d5JiglioNero5 years ago302}
507fd5fdJiglioNero5 years ago303else if (mobilePlatformOptions.target.indexOf("device") < 0) {
304this.cleanupTargetModifications(mobilePlatform, mobilePlatformOptions);
305}
306})
307.catch(error => {
308if (error && error.errorCode && error.errorCode === InternalErrorCode.VirtualDeviceSelectionError) {
309TelemetryHelper.sendErrorEvent(
310"VirtualDeviceSelectionError",
311ErrorHelper.getInternalError(InternalErrorCode.VirtualDeviceSelectionError)
312);
313
314this.logger.warning(error);
315this.logger.warning(localize("ContinueWithRnCliWorkflow", "Continue using standard RN CLI workflow."));
316
317if (mobilePlatformOptions.target.indexOf("device") < 0) {
318this.cleanupTargetModifications(mobilePlatform, mobilePlatformOptions);
319}
320return Promise.resolve();
321}
322else {
323return Promise.reject(error);
68a5b8d5JiglioNero5 years ago324}
325});
326}
327return Promise.resolve();
328}
329
507fd5fdJiglioNero5 years ago330private cleanupTargetModifications(mobilePlatform: GeneralMobilePlatform, mobilePlatformOptions: any) {
331mobilePlatformOptions.target = "simulator";
332mobilePlatform.runArguments = mobilePlatform.getRunArguments();
333}
334
6e1bcd36RedMickey6 years ago335private requestSetup(args: any): any {
336const workspaceFolder: vscode.WorkspaceFolder = <vscode.WorkspaceFolder>vscode.workspace.getWorkspaceFolder(vscode.Uri.file(args.cwd || args.program));
337const projectRootPath = this.getProjectRoot(args);
338let mobilePlatformOptions: any = {
339workspaceRoot: workspaceFolder.uri.fsPath,
340projectRoot: projectRootPath,
341platform: args.platform,
342env: args.env,
343envFile: args.envFile,
344target: args.target || "simulator",
5514e287RedMickey6 years ago345enableDebug: args.enableDebug,
6e1bcd36RedMickey6 years ago346};
347
259c018fYuri Skorokhodov5 years ago348if (args.platform === PlatformType.Exponent) {
6e1bcd36RedMickey6 years ago349mobilePlatformOptions.expoHostType = args.expoHostType || "tunnel";
350}
351
352CommandExecutor.ReactNativeCommand = SettingsHelper.getReactNativeGlobalCommandName(workspaceFolder.uri);
353
354if (!args.runArguments) {
355let runArgs = SettingsHelper.getRunArgs(args.platform, args.target || "simulator", workspaceFolder.uri);
356mobilePlatformOptions.runArguments = runArgs;
357} else {
358mobilePlatformOptions.runArguments = args.runArguments;
359}
360
361return mobilePlatformOptions;
362}
363
364private getProjectRoot(args: any): string {
365return SettingsHelper.getReactNativeProjectRoot(args.cwd || args.program);
366}
367
368/**
369* Parses log cat arguments to a string
370*/
371private parseLogCatArguments(userProvidedLogCatArguments: any): string {
372return Array.isArray(userProvidedLogCatArguments)
373? userProvidedLogCatArguments.join(" ") // If it's an array, we join the arguments
374: userProvidedLogCatArguments; // If not, we leave it as-is
375}
376}