microsoft/vscode-react-native

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
0.4.1

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/extension/commandPaletteHandler.ts

221lines · 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)
112 .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()
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.commands.executeCommand("vscode.previewHtml", vscode.Uri.parse(exponentUrl), 1, "Expo QR code");
137 });
138 }
139 return this.reactNativePackager.startAsReactNative()
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)
151 .spawnReactCommand(command, args).outcome;
152 });
153 }
154
155 /**
156 * Executes a lambda function after starting the packager
157 * {lambda} The lambda function to be executed
158 */
159 private executeWithPackagerRunning(lambda: () => Q.Promise<void>): Q.Promise<void> {
160 // Start the packager before executing the React-Native command
161 Log.logMessage("Attempting to start the React Native packager");
162 return this.runStartPackagerCommandAndUpdateStatus().then(lambda);
163 }
164
165 /**
166 * Ensures that we are in a React Native project and then executes the operation
167 * Otherwise, displays an error message banner
168 * {operation} - a function that performs the expected operation
169 */
170 private executeCommandInContext(rnCommand: string, operation: () => Q.Promise<void> | void): Q.Promise<void> {
171 let reactNativeProjectHelper = new ReactNativeProjectHelper(this.workspaceRoot);
172 return TelemetryHelper.generate("RNCommand", (generator) => {
173 generator.add("command", rnCommand, false);
174 return reactNativeProjectHelper.isReactNativeProject().then(isRNProject => {
175 generator.add("isRNProject", isRNProject, false);
176 if (isRNProject) {
177 // Bring the log channel to focus
178 Log.setFocusOnLogChannel();
179
180 // Execute the operation
181 return operation();
182 } else {
183 vscode.window.showErrorMessage("Current workspace is not a React Native project.");
184 }
185 });
186 });
187 }
188
189 /**
190 * 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.
191 */
192 private executePublishToExpHost(): Q.Promise<boolean> {
193 Log.logMessage("Publishing app to Exponent server. This might take a moment.");
194 return this.loginToExponent()
195 .then(user => {
196 Log.logMessage(`Publishing as ${user.username}...`);
197 return this.startExponentPackager()
198 .then(() =>
199 XDL.publish(this.workspaceRoot))
200 .then(response => {
201 if (response.err || !response.url) {
202 return false;
203 }
204 const publishedOutput = `App successfully published to ${response.url}`;
205 Log.logMessage(publishedOutput);
206 vscode.window.showInformationMessage(publishedOutput);
207 return true;
208 });
209 }).catch(() => {
210 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.");
211 return false;
212 });
213 }
214
215 private loginToExponent(): Q.Promise<XDL.IUser> {
216 return this.exponentHelper.loginToExponent(
217 (message, password) => { return Q(vscode.window.showInputBox({ placeHolder: message, password: password })); },
218 (message) => { return Q(vscode.window.showInformationMessage(message)); }
219 );
220 }
221}
222