microsoft/vscode-react-native

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
274b561c0cbc309bc164cdaebfb6d63d30b22e60

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/extension/extensionServer.ts

276lines · 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 net from "net";
5import * as Q from "q";
6import * as vscode from "vscode";
7
8import * as em from "../common/extensionMessaging";
9import {Log} from "../common/log/log";
10import {LogLevel} from "../common/log/logHelper";
11import {Packager} from "../common/packager";
12import {PackagerStatus, PackagerStatusIndicator} from "./packagerStatusIndicator";
13import {LogCatMonitor} from "./android/logCatMonitor";
14import {FileSystem} from "../common/node/fileSystem";
15import {ConfigurationReader} from "../common/configurationReader";
16import {SettingsHelper} from "./settingsHelper";
17import {Telemetry} from "../common/telemetry";
18import {ExponentHelper} from "../common/exponent/exponentHelper";
19
20export class ExtensionServer implements vscode.Disposable {
21 private serverInstance: net.Server = null;
22 private messageHandlerDictionary: { [id: number]: ((...argArray: any[]) => Q.Promise<any>) } = {};
23 private reactNativePackager: Packager;
24 private reactNativePackageStatusIndicator: PackagerStatusIndicator;
25 private pipePath: string;
26 private logCatMonitor: LogCatMonitor = null;
27 private exponentHelper: ExponentHelper;
28
29 public constructor(projectRootPath: string, reactNativePackager: Packager, packagerStatusIndicator: PackagerStatusIndicator, exponentHelper: ExponentHelper) {
30
31 this.pipePath = new em.MessagingChannel(projectRootPath).getPath();
32 this.reactNativePackager = reactNativePackager;
33 this.reactNativePackageStatusIndicator = packagerStatusIndicator;
34 this.exponentHelper = exponentHelper;
35
36 /* register handlers for all messages */
37 this.messageHandlerDictionary[em.ExtensionMessage.START_PACKAGER] = this.startPackager;
38 this.messageHandlerDictionary[em.ExtensionMessage.STOP_PACKAGER] = this.stopPackager;
39 this.messageHandlerDictionary[em.ExtensionMessage.RESTART_PACKAGER] = this.restartPackager;
40 this.messageHandlerDictionary[em.ExtensionMessage.PREWARM_BUNDLE_CACHE] = this.prewarmBundleCache;
41 this.messageHandlerDictionary[em.ExtensionMessage.START_MONITORING_LOGCAT] = this.startMonitoringLogCat;
42 this.messageHandlerDictionary[em.ExtensionMessage.STOP_MONITORING_LOGCAT] = this.stopMonitoringLogCat;
43 this.messageHandlerDictionary[em.ExtensionMessage.GET_PACKAGER_PORT] = this.getPackagerPort;
44 this.messageHandlerDictionary[em.ExtensionMessage.SEND_TELEMETRY] = this.sendTelemetry;
45 this.messageHandlerDictionary[em.ExtensionMessage.OPEN_FILE_AT_LOCATION] = this.openFileAtLocation;
46 this.messageHandlerDictionary[em.ExtensionMessage.START_EXPONENT_PACKAGER] = this.startExponentPackager;
47 this.messageHandlerDictionary[em.ExtensionMessage.SHOW_INFORMATION_MESSAGE] = this.showInformationMessage;
48 }
49
50 /**
51 * Starts the server.
52 */
53 public setup(): Q.Promise<void> {
54
55 let deferred = Q.defer<void>();
56
57 let launchCallback = (error: any) => {
58 Log.logInternalMessage(LogLevel.Info, `Extension messaging server started at ${this.pipePath}.`);
59 if (error) {
60 deferred.reject(error);
61 } else {
62 deferred.resolve(null);
63 }
64 };
65
66 this.serverInstance = net.createServer(this.handleSocket.bind(this));
67 this.serverInstance.on("error", this.recoverServer.bind(this));
68 this.serverInstance.listen(this.pipePath, launchCallback);
69
70 return deferred.promise;
71 }
72
73 /**
74 * Stops the server.
75 */
76 public dispose(): void {
77 if (this.serverInstance) {
78 this.serverInstance.close();
79 this.serverInstance = null;
80 }
81
82 this.stopMonitoringLogCat();
83 }
84
85 /**
86 * Message handler for GET_PACKAGER_PORT.
87 */
88 private getPackagerPort(): Q.Promise<number> {
89 return Q(SettingsHelper.getPackagerPort());
90 }
91
92 /**
93 * Message handler for START_PACKAGER.
94 */
95 private startPackager(port?: any): Q.Promise<any> {
96 return this.reactNativePackager.isRunning().then((running) => {
97 if (running) {
98 return this.reactNativePackager.stop();
99 }
100 }).then(() =>
101 this.exponentHelper.configureReactNativeEnvironment()
102 ).then(() => {
103 const portToUse = ConfigurationReader.readIntWithDefaultSync(port, SettingsHelper.getPackagerPort());
104 return this.reactNativePackager.startAsReactNative(portToUse);
105 })
106 .then(() =>
107 this.reactNativePackageStatusIndicator.updatePackagerStatus(PackagerStatus.PACKAGER_STARTED));
108 }
109
110 /**
111 * Message handler for START_EXPONENT_PACKAGER.
112 */
113 private startExponentPackager(port?: any): Q.Promise<any> {
114 return this.reactNativePackager.isRunning().then((running) => {
115 if (running) {
116 return this.reactNativePackager.stop();
117 }
118 }).then(() =>
119 this.exponentHelper.configureExponentEnvironment()
120 ).then(() =>
121 this.exponentHelper.loginToExponent(
122 (message, password) => { return Q(vscode.window.showInputBox({ placeHolder: message, password: password })); },
123 (message) => { return Q(vscode.window.showInformationMessage(message)); }
124 ))
125 .then(() => {
126 const portToUse = ConfigurationReader.readIntWithDefaultSync(port, SettingsHelper.getPackagerPort());
127 return this.reactNativePackager.startAsExponent(portToUse);
128 })
129 .then(exponentUrl => {
130 this.reactNativePackageStatusIndicator.updatePackagerStatus(PackagerStatus.EXPONENT_PACKAGER_STARTED);
131 return exponentUrl;
132 });
133 }
134
135 /**
136 * Message handler for STOP_PACKAGER.
137 */
138 private stopPackager(): Q.Promise<any> {
139 return this.reactNativePackager.stop()
140 .then(() => this.reactNativePackageStatusIndicator.updatePackagerStatus(PackagerStatus.PACKAGER_STOPPED));
141 }
142
143 /**
144 * Message handler for RESTART_PACKAGER.
145 */
146 private restartPackager(port?: any): Q.Promise<any> {
147 const portToUse = ConfigurationReader.readIntWithDefaultSync(port, SettingsHelper.getPackagerPort());
148 return this.reactNativePackager.restart(portToUse)
149 .then(() =>
150 this.reactNativePackageStatusIndicator.updatePackagerStatus(PackagerStatus.PACKAGER_STARTED));
151 }
152
153 /**
154 * Message handler for PREWARM_BUNDLE_CACHE.
155 */
156 private prewarmBundleCache(platform: string): Q.Promise<any> {
157 return this.reactNativePackager.prewarmBundleCache(platform);
158 }
159
160 /**
161 * Message handler for START_MONITORING_LOGCAT.
162 */
163 private startMonitoringLogCat(deviceId: string, logCatArguments: string): Q.Promise<any> {
164 this.stopMonitoringLogCat(); // Stop previous logcat monitor if it's running
165
166 // this.logCatMonitor can be mutated, so we store it locally too
167 const logCatMonitor = this.logCatMonitor = new LogCatMonitor(deviceId, logCatArguments);
168 logCatMonitor.start() // The LogCat will continue running forever, so we don't wait for it
169 .catch(error =>
170 Log.logWarning("Error while monitoring LogCat", error))
171 .done();
172
173 return Q.resolve<void>(void 0);
174 }
175
176 /**
177 * Message handler for OPEN_FILE_AT_LOCATION
178 */
179 private openFileAtLocation(filename: string, lineNumber: number): Q.Promise<void> {
180 return Q(vscode.workspace.openTextDocument(vscode.Uri.file(filename)).then((document: vscode.TextDocument) => {
181 return vscode.window.showTextDocument(document).then((editor: vscode.TextEditor) => {
182 let range = editor.document.lineAt(lineNumber - 1).range;
183 editor.selection = new vscode.Selection(range.start, range.end);
184 editor.revealRange(range, vscode.TextEditorRevealType.InCenter);
185 });
186 }));
187 }
188
189 private stopMonitoringLogCat(): Q.Promise<void> {
190 if (this.logCatMonitor) {
191 this.logCatMonitor.dispose();
192 this.logCatMonitor = null;
193 }
194
195 return Q.resolve<void>(void 0);
196 }
197
198 /**
199 * Sends telemetry
200 */
201 private sendTelemetry(extensionId: string, extensionVersion: string, appInsightsKey: string, eventName: string, properties: {[key: string]: string}, measures: {[key: string]: number}): Q.Promise<any> {
202 Telemetry.sendExtensionTelemetry(extensionId, extensionVersion, appInsightsKey, eventName, properties, measures);
203 return Q.resolve({});
204 }
205
206 /**
207 * Extension message handler.
208 */
209 private handleExtensionMessage(messageWithArgs: em.MessageWithArguments): Q.Promise<any> {
210 let handler = this.messageHandlerDictionary[messageWithArgs.message];
211 if (handler) {
212 Log.logInternalMessage(LogLevel.Info, "Handling message: " + em.ExtensionMessage[messageWithArgs.message]);
213 return handler.apply(this, messageWithArgs.args);
214 } else {
215 return Q.reject("Invalid message: " + messageWithArgs.message);
216 }
217 }
218
219 /**
220 * Handles connections to the server.
221 */
222 private handleSocket(socket: net.Socket): void {
223 let handleError = (e: any) => {
224 Log.logError(e);
225 socket.end(em.ErrorMarker);
226 };
227
228 let dataCallback = (data: any) => {
229 try {
230 let messageWithArgs: em.MessageWithArguments = JSON.parse(data);
231 this.handleExtensionMessage(messageWithArgs)
232 .then(result => {
233 socket.end(JSON.stringify(result));
234 })
235 .catch((e) => { handleError(e); })
236 .done();
237 } catch (e) {
238 handleError(e);
239 }
240 };
241
242 socket.on("data", dataCallback);
243 };
244
245 /**
246 * Recovers the server in case the named socket we use already exists, but no other instance of VSCode is active.
247 */
248 private recoverServer(error: any): void {
249 let errorHandler = (e: any) => {
250 /* The named socket is not used. */
251 if (e.code === "ECONNREFUSED") {
252 new FileSystem().removePathRecursivelyAsync(this.pipePath)
253 .then(() => {
254 this.serverInstance.listen(this.pipePath);
255 })
256 .done();
257 }
258 };
259
260 /* The named socket already exists. */
261 if (error.code === "EADDRINUSE") {
262 let clientSocket = new net.Socket();
263 clientSocket.on("error", errorHandler);
264 clientSocket.connect(this.pipePath, function() {
265 clientSocket.end();
266 });
267 }
268 }
269
270 /**
271 * Message handler for SHOW_INFORMATION_MESSAGE
272 */
273 private showInformationMessage(message: string): Q.Promise<void> {
274 return Q(vscode.window.showInformationMessage(message)).then(() => {});
275 }
276}