microsoft/vscode-react-native

Public

mirrored fromhttps://github.com/microsoft/vscode-react-nativeAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
3c172a056576b34ff7bb5d777cbd9b47ff97cd7f

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/extension/commandPaletteHandler.ts

229lines · modecode

1// 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 * as Q from "q";
6import * as XDL from "../common/exponent/xdlInterface";
7import {CommandExecutor} from "../common/commandExecutor";
8import {SettingsHelper} from "./settingsHelper";
9import {Log} from "../common/log/log";
10import {Packager, PackagerRunAs} from "../common/packager";
11import {AndroidPlatform} from "../common/android/androidPlatform";
12import {PackagerStatus, PackagerStatusIndicator} from "./packagerStatusIndicator";
13import {ReactNativeProjectHelper} from "../common/reactNativeProjectHelper";
14import {TargetPlatformHelper} from "../common/targetPlatformHelper";
15import {TelemetryHelper} from "../common/telemetryHelper";
16import {IOSDebugModeManager} from "../common/ios/iOSDebugModeManager";
17import {ExponentHelper} from "../common/exponent/exponentHelper";
18
19export class CommandPaletteHandler {
20 private reactNativePackager: Packager;
21 private reactNativePackageStatusIndicator: PackagerStatusIndicator;
22 private workspaceRoot: string;
23 private exponentHelper: ExponentHelper;
24
25 constructor(workspaceRoot: string, reactNativePackager: Packager, packagerStatusIndicator: PackagerStatusIndicator, exponentHelper: ExponentHelper) {
26 this.workspaceRoot = workspaceRoot;
27 this.reactNativePackager = reactNativePackager;
28 this.reactNativePackageStatusIndicator = packagerStatusIndicator;
29 this.exponentHelper = exponentHelper;
30 }
31
32 /**
33 * Starts the React Native packager
34 */
35 public startPackager(): Q.Promise<void> {
36 return this.executeCommandInContext("startPackager", () =>
37 this.reactNativePackager.isRunning()
38 .then((running) => {
39 return running ? this.reactNativePackager.stop() : Q.resolve(void 0);
40 })
41 )
42 .then(() => this.runStartPackagerCommandAndUpdateStatus());
43 }
44
45 /**
46 * Starts the Exponent packager
47 */
48 public startExponentPackager(): Q.Promise<void> {
49 return this.executeCommandInContext("startExponentPackager", () =>
50 this.reactNativePackager.isRunning()
51 .then((running) => {
52 return running ? this.reactNativePackager.stop() : Q.resolve(void 0);
53 })
54 ).then(() =>
55 this.exponentHelper.configureExponentEnvironment()
56 ).then(() => this.runStartPackagerCommandAndUpdateStatus(PackagerRunAs.EXPONENT));
57 }
58
59 /**
60 * Kills the React Native packager invoked by the extension's packager
61 */
62 public stopPackager(): Q.Promise<void> {
63 return this.executeCommandInContext("stopPackager", () => this.reactNativePackager.stop())
64 .then(() => this.reactNativePackageStatusIndicator.updatePackagerStatus(PackagerStatus.PACKAGER_STOPPED));
65 }
66
67 /**
68 * Restarts the React Native packager
69 */
70 public restartPackager(): Q.Promise<void> {
71 return this.executeCommandInContext("restartPackager", () =>
72 this.runRestartPackagerCommandAndUpdateStatus());
73 }
74
75 /**
76 * Execute command to publish to exponent host.
77 */
78 public publishToExpHost(): Q.Promise<void> {
79 return this.executeCommandInContext("publishToExpHost", () => {
80 return this.executePublishToExpHost().then((didPublish) => {
81 if (!didPublish) {
82 Log.logMessage("Publishing was unsuccessful. Please make sure you are logged in Exponent and your project is a valid Exponentjs project");
83 }
84 });
85 });
86 }
87
88 /**
89 * Executes the 'react-native run-android' command
90 */
91 public runAndroid(targetType: string = "simulator"): Q.Promise<void> {
92 TargetPlatformHelper.checkTargetPlatformSupport("android");
93 return this.executeCommandInContext("runAndroid", () => this.executeWithPackagerRunning(() => {
94 const packagerPort = SettingsHelper.getPackagerPort();
95 const runArgs = SettingsHelper.getRunArgs("android", targetType);
96 return new AndroidPlatform({ platform: "android", projectRoot: this.workspaceRoot, packagerPort: packagerPort, runArguments: runArgs }).runApp(/*shouldLaunchInAllDevices*/true);
97 }));
98 }
99
100 /**
101 * Executes the 'react-native run-ios' command
102 */
103 public runIos(targetType: string = "simulator"): Q.Promise<void> {
104 TargetPlatformHelper.checkTargetPlatformSupport("ios");
105 return this.executeCommandInContext("runIos", () => {
106 const runArgs = SettingsHelper.getRunArgs("ios", targetType);
107 // Set the Debugging setting to disabled, because in iOS it's persisted across runs of the app
108 return new IOSDebugModeManager(this.workspaceRoot)
109 .setSimulatorRemoteDebuggingSetting(/*enable=*/ false)
110 .catch(() => { }) // If setting the debugging mode fails, we ignore the error and we run the run ios command anyways
111 .then(() => this.executeReactNativeRunCommand("run-ios", runArgs));
112 });
113 }
114
115 private runRestartPackagerCommandAndUpdateStatus(): Q.Promise<void> {
116 return this.reactNativePackager.restart(SettingsHelper.getPackagerPort())
117 .then(() => this.reactNativePackageStatusIndicator.updatePackagerStatus(PackagerStatus.PACKAGER_STARTED));
118 }
119
120 /**
121 * Helper method to run packager and update appropriate configurations
122 */
123 private runStartPackagerCommandAndUpdateStatus(startAs: PackagerRunAs = PackagerRunAs.REACT_NATIVE): Q.Promise<any> {
124 if (startAs === PackagerRunAs.EXPONENT) {
125 return this.loginToExponent()
126 .then(() =>
127 this.reactNativePackager.startAsExponent()
128 ).then(exponentUrl => {
129 this.reactNativePackageStatusIndicator.updatePackagerStatus(PackagerStatus.EXPONENT_PACKAGER_STARTED);
130 Log.logMessage("Application is running on Exponent.");
131 const exponentOutput = `Open your exponent app at ${exponentUrl}`;
132 Log.logMessage(exponentOutput);
133 vscode.commands.executeCommand("vscode.previewHtml", vscode.Uri.parse(exponentUrl), 1, "Expo QR code");
134 });
135 }
136 return this.reactNativePackager.startAsReactNative()
137 .then(() => this.reactNativePackageStatusIndicator.updatePackagerStatus(PackagerStatus.PACKAGER_STARTED));
138 }
139
140 /**
141 * Executes a react-native command passed after starting the packager
142 * {command} The command to be executed
143 * {args} The arguments to be passed to the command
144 */
145 private executeReactNativeRunCommand(command: string, args: string[] = []): Q.Promise<void> {
146 return this.executeWithPackagerRunning(() => {
147 return new CommandExecutor(this.workspaceRoot)
148 .spawnReactCommand(command, args).outcome;
149 });
150 }
151
152 /**
153 * Executes a lambda function after starting the packager
154 * {lambda} The lambda function to be executed
155 */
156 private executeWithPackagerRunning(lambda: () => Q.Promise<void>): Q.Promise<void> {
157 // Start the packager before executing the React-Native command
158 Log.logMessage("Attempting to start the React Native packager");
159 return this.runStartPackagerCommandAndUpdateStatus().then(lambda);
160 }
161
162 /**
163 * Ensures that we are in a React Native project and then executes the operation
164 * Otherwise, displays an error message banner
165 * {operation} - a function that performs the expected operation
166 */
167 private executeCommandInContext(rnCommand: string, operation: () => Q.Promise<void>): Q.Promise<void> {
168 let reactNativeProjectHelper = new ReactNativeProjectHelper(this.workspaceRoot);
169 return TelemetryHelper.generate("RNCommand", (generator) => {
170 generator.add("command", rnCommand, false);
171 return reactNativeProjectHelper.isReactNativeProject().then(isRNProject => {
172 generator.add("isRNProject", isRNProject, false);
173 if (isRNProject) {
174 // Bring the log channel to focus
175 Log.setFocusOnLogChannel();
176
177 // Execute the operation
178 return operation();
179 } else {
180 vscode.window.showErrorMessage("Current workspace is not a React Native project.");
181 return;
182 }
183 });
184 });
185 }
186
187 /**
188 * Publish project to exponent server. In order to do this we need to make sure the user is logged in exponent and the packager is running.
189 */
190 private executePublishToExpHost(): Q.Promise<boolean> {
191 Log.logMessage("Publishing app to Exponent server. This might take a moment.");
192 return this.loginToExponent()
193 .then(user => {
194 Log.logMessage(`Publishing as ${user.username}...`);
195 return this.startExponentPackager()
196 .then(() =>
197 XDL.publish(this.workspaceRoot))
198 .then(response => {
199 if (response.err || !response.url) {
200 return false;
201 }
202 const publishedOutput = `App successfully published to ${response.url}`;
203 Log.logMessage(publishedOutput);
204 vscode.window.showInformationMessage(publishedOutput);
205 return true;
206 });
207 }).catch(() => {
208 Log.logWarning("An error has occured. Please make sure you are logged in to exponent, your project is setup correctly for publishing and your packager is running as exponent.");
209 return false;
210 });
211 }
212
213 private loginToExponent(): Q.Promise<XDL.IUser> {
214 return this.exponentHelper.loginToExponent(
215 (message, password) => {
216 return Q.Promise((resolve, reject) => {
217 vscode.window.showInputBox({ placeHolder: message, password: password })
218 .then(resolve, reject);
219 });
220 },
221 (message) => {
222 return Q.Promise((resolve, reject) => {
223 vscode.window.showInformationMessage(message)
224 .then(resolve, reject);
225 });
226 }
227 );
228 }
229}
230