microsoft/vscode-react-native

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
600e4a2b3141c1fe2b7665a88aa23a812d348c2d

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/extension/commandPaletteHandler.ts

244lines · 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 "./exponent/xdlInterface";
7import {SettingsHelper} from "./settingsHelper";
8import {OutputChannelLogger} from "./log/OutputChannelLogger";
9import {Packager, PackagerRunAs} from "../common/packager";
10import {AndroidPlatform} from "./android/androidPlatform";
11import {IOSPlatform} from "./ios/iOSPlatform";
12import {PackagerStatus, PackagerStatusIndicator} from "./packagerStatusIndicator";
13import {ReactNativeProjectHelper} from "../common/reactNativeProjectHelper";
14import {TargetPlatformHelper} from "../common/targetPlatformHelper";
15import {TelemetryHelper} from "../common/telemetryHelper";
16import {ExponentHelper} from "./exponent/exponentHelper";
17
18export class CommandPaletteHandler {
19 private reactNativePackager: Packager;
20 private reactNativePackageStatusIndicator: PackagerStatusIndicator;
21 private workspaceRoot: string;
22 private exponentHelper: ExponentHelper;
23 private logger: OutputChannelLogger = OutputChannelLogger.getMainChannel();
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 this.logger.warning("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(target: "device" | "simulator" = "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", target);
96 const platform = new AndroidPlatform({ platform: "android", projectRoot: this.workspaceRoot, packagerPort: packagerPort, runArguments: runArgs }, {
97 packager: this.reactNativePackager,
98 packageStatusIndicator: this.reactNativePackageStatusIndicator,
99 });
100 return platform.runApp(/*shouldLaunchInAllDevices*/true)
101 .then(() => {
102 return platform.disableJSDebuggingMode();
103 });
104 }));
105 }
106
107 /**
108 * Executes the 'react-native run-ios' command
109 */
110 public runIos(target: "device" | "simulator" = "simulator"): Q.Promise<void> {
111 TargetPlatformHelper.checkTargetPlatformSupport("ios");
112 return this.executeCommandInContext("runIos", () => this.executeWithPackagerRunning(() => {
113 const packagerPort = SettingsHelper.getPackagerPort();
114 const runArgs = SettingsHelper.getRunArgs("ios", target);
115 const platform = new IOSPlatform({ platform: "ios", projectRoot: this.workspaceRoot, packagerPort, runArguments: runArgs }, { packager: this.reactNativePackager, packageStatusIndicator: this.reactNativePackageStatusIndicator });
116
117 // Set the Debugging setting to disabled, because in iOS it's persisted across runs of the app
118 return platform.disableJSDebuggingMode()
119 .catch(() => {}) // If setting the debugging mode fails, we ignore the error and we run the run ios command anyways
120 .then(() => {
121 return platform.runApp();
122 });
123 }));
124 }
125
126 public showDevMenu(): Q.Promise<void> {
127 AndroidPlatform.showDevMenu()
128 .catch(() => {}); // Ignore any errors
129 IOSPlatform.showDevMenu()
130 .catch(() => {}); // Ignore any errors
131 return Q.resolve(void 0);
132 }
133
134 public reloadApp(): Q.Promise<void> {
135 AndroidPlatform.reloadApp()
136 .catch(() => {}); // Ignore any errors
137 IOSPlatform.reloadApp()
138 .catch(() => {}); // Ignore any errors
139 return Q.resolve(void 0);
140 }
141
142 private runRestartPackagerCommandAndUpdateStatus(): Q.Promise<void> {
143 return this.reactNativePackager.restart(SettingsHelper.getPackagerPort())
144 .then(() => this.reactNativePackageStatusIndicator.updatePackagerStatus(PackagerStatus.PACKAGER_STARTED));
145 }
146
147 /**
148 * Helper method to run packager and update appropriate configurations
149 */
150 private runStartPackagerCommandAndUpdateStatus(startAs: PackagerRunAs = PackagerRunAs.REACT_NATIVE): Q.Promise<any> {
151 if (startAs === PackagerRunAs.EXPONENT) {
152 return this.loginToExponent()
153 .then(() =>
154 this.reactNativePackager.startAsExponent()
155 ).then(exponentUrl => {
156 this.reactNativePackageStatusIndicator.updatePackagerStatus(PackagerStatus.EXPONENT_PACKAGER_STARTED);
157 this.logger.info("Application is running on Exponent.");
158 const exponentOutput = `Open your exponent app at ${exponentUrl}`;
159 this.logger.info(exponentOutput);
160 vscode.commands.executeCommand("vscode.previewHtml", vscode.Uri.parse(exponentUrl), 1, "Expo QR code");
161 });
162 }
163 return this.reactNativePackager.startAsReactNative()
164 .then(() => this.reactNativePackageStatusIndicator.updatePackagerStatus(PackagerStatus.PACKAGER_STARTED));
165 }
166
167 /**
168 * Executes a lambda function after starting the packager
169 * {lambda} The lambda function to be executed
170 */
171 private executeWithPackagerRunning(lambda: () => Q.Promise<void>): Q.Promise<void> {
172 // Start the packager before executing the React-Native command
173 this.logger.info("Attempting to start the React Native packager");
174 return this.runStartPackagerCommandAndUpdateStatus().then(lambda);
175 }
176
177 /**
178 * Ensures that we are in a React Native project and then executes the operation
179 * Otherwise, displays an error message banner
180 * {operation} - a function that performs the expected operation
181 */
182 private executeCommandInContext(rnCommand: string, operation: () => Q.Promise<void>): Q.Promise<void> {
183 let reactNativeProjectHelper = new ReactNativeProjectHelper(this.workspaceRoot);
184 return TelemetryHelper.generate("RNCommand", (generator) => {
185 generator.add("command", rnCommand, false);
186 return reactNativeProjectHelper.isReactNativeProject().then(isRNProject => {
187 generator.add("isRNProject", isRNProject, false);
188 if (isRNProject) {
189 // Bring the log channel to focus
190 this.logger.setFocusOnLogChannel();
191
192 // Execute the operation
193 return operation();
194 } else {
195 vscode.window.showErrorMessage("Current workspace is not a React Native project.");
196 return;
197 }
198 });
199 });
200 }
201
202 /**
203 * 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.
204 */
205 private executePublishToExpHost(): Q.Promise<boolean> {
206 this.logger.info("Publishing app to Exponent server. This might take a moment.");
207 return this.loginToExponent()
208 .then(user => {
209 this.logger.debug(`Publishing as ${user.username}...`);
210 return this.startExponentPackager()
211 .then(() =>
212 XDL.publish(this.workspaceRoot))
213 .then(response => {
214 if (response.err || !response.url) {
215 return false;
216 }
217 const publishedOutput = `App successfully published to ${response.url}`;
218 this.logger.info(publishedOutput);
219 vscode.window.showInformationMessage(publishedOutput);
220 return true;
221 });
222 }).catch(() => {
223 this.logger.warning("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.");
224 return false;
225 });
226 }
227
228 private loginToExponent(): Q.Promise<XDL.IUser> {
229 return this.exponentHelper.loginToExponent(
230 (message, password) => {
231 return Q.Promise((resolve, reject) => {
232 vscode.window.showInputBox({ placeHolder: message, password: password })
233 .then(resolve, reject);
234 });
235 },
236 (message) => {
237 return Q.Promise((resolve, reject) => {
238 vscode.window.showInformationMessage(message)
239 .then(resolve, reject);
240 });
241 }
242 );
243 }
244}