microsoft/vscode-react-native

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
81f88231b0c98e7265fe93cd7fb01b25ee66f67f

Branches

Tags

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

Clone

HTTPS

Download ZIP

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