microsoft/vscode-react-native

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
8f87e13531e1fc4465733c748fd76d4ae5710eaa

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/extension/commandPaletteHandler.ts

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