microsoft/vscode-react-native

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
0.4.0

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/extension/commandPaletteHandler.ts

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