microsoft/vscode-react-native

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
0.5.1

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/extension/extensionServer.ts

258lines · modeblame

0502b7a8dlebu10 years ago1// Copyright (c) Microsoft Corporation. All rights reserved.
2// Licensed under the MIT license. See LICENSE file in the project root for details.
3
710f8655digeff10 years ago4import * as Q from "q";
acf08bc2dlebu10 years ago5import * as vscode from "vscode";
6
7daed3fcArtem Egorov8 years ago7import {MessagingHelper}from "../common/extensionMessaging";
0a68f8dbArtem Egorov8 years ago8import {OutputChannelLogger} from "./log/OutputChannelLogger";
9import {Packager} from "../common/packager";
710f8655digeff10 years ago10import {LogCatMonitor} from "./android/logCatMonitor";
11import {FileSystem} from "../common/node/fileSystem";
df4bce40digeff10 years ago12import {SettingsHelper} from "./settingsHelper";
6e4d7a62Joshua Skelton10 years ago13import {Telemetry} from "../common/telemetry";
0a68f8dbArtem Egorov8 years ago14import {PlatformResolver} from "./platformResolver";
15import {TelemetryHelper} from "../common/telemetryHelper";
16import {TargetPlatformHelper} from "../common/targetPlatformHelper";
17import {MobilePlatformDeps} from "./generalMobilePlatform";
7daed3fcArtem Egorov8 years ago18import {IRemoteExtension} from "../common/remoteExtension";
19import * as rpc from "noice-json-rpc";
20import * as WebSocket from "ws";
21import WebSocketServer = WebSocket.Server;
0502b7a8dlebu10 years ago22
23export class ExtensionServer implements vscode.Disposable {
7daed3fcArtem Egorov8 years ago24public api: IRemoteExtension;
3118589cArtem Egorov8 years ago25public isDisposed: boolean = false;
7daed3fcArtem Egorov8 years ago26private serverInstance: WebSocketServer | null;
a822ac85dlebu10 years ago27private reactNativePackager: Packager;
8411fd8dDaniel Lebu10 years ago28private pipePath: string;
5c8365a6Artem Egorov8 years ago29private logCatMonitor: LogCatMonitor | null = null;
0a68f8dbArtem Egorov8 years ago30private logger: OutputChannelLogger = OutputChannelLogger.getMainChannel();
a822ac85dlebu10 years ago31
2e432a9eArtem Egorov8 years ago32public constructor(projectRootPath: string, reactNativePackager: Packager) {
7daed3fcArtem Egorov8 years ago33this.pipePath = MessagingHelper.getPath(projectRootPath);
a822ac85dlebu10 years ago34this.reactNativePackager = reactNativePackager;
35}
0502b7a8dlebu10 years ago36
37/**
38* Starts the server.
39*/
40public setup(): Q.Promise<void> {
3118589cArtem Egorov8 years ago41this.isDisposed = false;
0502b7a8dlebu10 years ago42let deferred = Q.defer<void>();
43
44let launchCallback = (error: any) => {
0a68f8dbArtem Egorov8 years ago45this.logger.debug(`Extension messaging server started at ${this.pipePath}.`);
7daed3fcArtem Egorov8 years ago46deferred.resolve(void 0);
0502b7a8dlebu10 years ago47};
48
7daed3fcArtem Egorov8 years ago49this.serverInstance = new WebSocketServer({port: <any>this.pipePath});
50this.api = new rpc.Server(this.serverInstance).api();
3118589cArtem Egorov8 years ago51this.serverInstance.on("listening", launchCallback.bind(this));
6b6eb911dlebu10 years ago52this.serverInstance.on("error", this.recoverServer.bind(this));
7daed3fcArtem Egorov8 years ago53
54this.setupApiHandlers();
0502b7a8dlebu10 years ago55
56return deferred.promise;
57}
58
59/**
60* Stops the server.
61*/
62public dispose(): void {
3118589cArtem Egorov8 years ago63this.isDisposed = true;
0502b7a8dlebu10 years ago64if (this.serverInstance) {
65this.serverInstance.close();
acf08bc2dlebu10 years ago66this.serverInstance = null;
0502b7a8dlebu10 years ago67}
710f8655digeff10 years ago68
4edcda70Artem Egorov8 years ago69this.reactNativePackager.statusIndicator.dispose();
3118589cArtem Egorov8 years ago70this.reactNativePackager.stop(true);
c2bf3c4fdigeff10 years ago71this.stopMonitoringLogCat();
0502b7a8dlebu10 years ago72}
73
7daed3fcArtem Egorov8 years ago74private setupApiHandlers(): void {
75let methods: any = {};
76methods.stopMonitoringLogCat = this.stopMonitoringLogCat.bind(this);
77methods.getPackagerPort = this.getPackagerPort.bind(this);
78methods.sendTelemetry = this.sendTelemetry.bind(this);
79methods.openFileAtLocation = this.openFileAtLocation.bind(this);
80methods.showInformationMessage = this.showInformationMessage.bind(this);
81methods.launch = this.launch.bind(this);
82methods.showDevMenu = this.showDevMenu.bind(this);
83methods.reloadApp = this.reloadApp.bind(this);
84
85this.api.Extension.expose(methods);
5e651f3edigeff10 years ago86}
87
7daed3fcArtem Egorov8 years ago88private showDevMenu(deviceId?: string) {
89this.api.Debugger.emitShowDevMenu(deviceId);
514df4f4Patricio Beltran10 years ago90}
91
7daed3fcArtem Egorov8 years ago92private reloadApp(deviceId?: string) {
93this.api.Debugger.emitReloadApp(deviceId);
27710197Vladimir Kotikov8 years ago94}
0502b7a8dlebu10 years ago95
96/**
6b6eb911dlebu10 years ago97* Recovers the server in case the named socket we use already exists, but no other instance of VSCode is active.
0502b7a8dlebu10 years ago98*/
6b6eb911dlebu10 years ago99private recoverServer(error: any): void {
100let errorHandler = (e: any) => {
101/* The named socket is not used. */
102if (e.code === "ECONNREFUSED") {
8411fd8dDaniel Lebu10 years ago103new FileSystem().removePathRecursivelyAsync(this.pipePath)
6b6eb911dlebu10 years ago104.then(() => {
7daed3fcArtem Egorov8 years ago105return this.setup();
6b6eb911dlebu10 years ago106})
107.done();
108}
109};
110
111/* The named socket already exists. */
112if (error.code === "EADDRINUSE") {
7daed3fcArtem Egorov8 years ago113let clientSocket = new WebSocket(`ws+unix://${this.pipePath}`);
6b6eb911dlebu10 years ago114clientSocket.on("error", errorHandler);
7daed3fcArtem Egorov8 years ago115clientSocket.on("open", function() {
116clientSocket.close();
6b6eb911dlebu10 years ago117});
0502b7a8dlebu10 years ago118}
119}
280c0746Patricio Beltran9 years ago120
7daed3fcArtem Egorov8 years ago121/**
122* Message handler for GET_PACKAGER_PORT.
123*/
2e432a9eArtem Egorov8 years ago124private getPackagerPort(program: string): number {
125return SettingsHelper.getPackagerPort(program);
7daed3fcArtem Egorov8 years ago126}
127
128/**
129* Message handler for OPEN_FILE_AT_LOCATION
130*/
131private openFileAtLocation(filename: string, lineNumber: number): Promise<void> {
132return new Promise((resolve) => {
133vscode.workspace.openTextDocument(vscode.Uri.file(filename))
134.then((document: vscode.TextDocument) => {
135vscode.window.showTextDocument(document)
136.then((editor: vscode.TextEditor) => {
137let range = editor.document.lineAt(lineNumber - 1).range;
138editor.selection = new vscode.Selection(range.start, range.end);
139editor.revealRange(range, vscode.TextEditorRevealType.InCenter);
140resolve();
141});
142});
143});
144}
145
146private stopMonitoringLogCat(): void {
147if (this.logCatMonitor) {
148this.logCatMonitor.dispose();
149this.logCatMonitor = null;
150}
151}
152
153/**
154* Sends telemetry
155*/
156private sendTelemetry(extensionId: string, extensionVersion: string, appInsightsKey: string, eventName: string, properties: {[key: string]: string}, measures: {[key: string]: number}): void {
157Telemetry.sendExtensionTelemetry(extensionId, extensionVersion, appInsightsKey, eventName, properties, measures);
158}
159
280c0746Patricio Beltran9 years ago160/**
161* Message handler for SHOW_INFORMATION_MESSAGE
162*/
7daed3fcArtem Egorov8 years ago163private showInformationMessage(message: string): void {
164vscode.window.showInformationMessage(message);
280c0746Patricio Beltran9 years ago165}
0a68f8dbArtem Egorov8 years ago166
7daed3fcArtem Egorov8 years ago167private launch(request: any): Promise<any> {
0a68f8dbArtem Egorov8 years ago168let mobilePlatformOptions = requestSetup(request.arguments);
169
170// We add the parameter if it's defined (adapter crashes otherwise)
171if (!isNullOrUndefined(request.arguments.logCatArguments)) {
172mobilePlatformOptions.logCatArguments = [parseLogCatArguments(request.arguments.logCatArguments)];
173}
174
175if (!isNullOrUndefined(request.arguments.variant)) {
176mobilePlatformOptions.variant = request.arguments.variant;
177}
178
179if (!isNullOrUndefined(request.arguments.scheme)) {
180mobilePlatformOptions.scheme = request.arguments.scheme;
181}
182
2e432a9eArtem Egorov8 years ago183mobilePlatformOptions.packagerPort = SettingsHelper.getPackagerPort(request.arguments.program);
0a68f8dbArtem Egorov8 years ago184const platformDeps: MobilePlatformDeps = {
185packager: this.reactNativePackager,
186};
187const mobilePlatform = new PlatformResolver()
188.resolveMobilePlatform(request.arguments.platform, mobilePlatformOptions, platformDeps);
7daed3fcArtem Egorov8 years ago189return new Promise((resolve, reject) => {
190TelemetryHelper.generate("launch", (generator) => {
191generator.step("checkPlatformCompatibility");
192TargetPlatformHelper.checkTargetPlatformSupport(mobilePlatformOptions.platform);
193generator.step("startPackager");
194return mobilePlatform.startPackager()
195.then(() => {
196// We've seen that if we don't prewarm the bundle cache, the app fails on the first attempt to connect to the debugger logic
197// and the user needs to Reload JS manually. We prewarm it to prevent that issue
198generator.step("prewarmBundleCache");
199this.logger.info("Prewarming bundle cache. This may take a while ...");
200return mobilePlatform.prewarmBundleCache();
201})
202.then(() => {
203generator.step("mobilePlatform.runApp");
204this.logger.info("Building and running application.");
205return mobilePlatform.runApp();
206})
207.then(() => {
208generator.step("mobilePlatform.enableJSDebuggingMode");
209return mobilePlatform.enableJSDebuggingMode();
210})
211.then(() => {
212resolve();
213})
214.catch(error => {
748105d9Artem Egorov8 years ago215this.logger.error(error);
7daed3fcArtem Egorov8 years ago216reject(error);
217});
218});
0a68f8dbArtem Egorov8 years ago219});
220}
221}
222
223/**
224* Parses log cat arguments to a string
225*/
226function parseLogCatArguments(userProvidedLogCatArguments: any): string {
227return Array.isArray(userProvidedLogCatArguments)
228? userProvidedLogCatArguments.join(" ") // If it's an array, we join the arguments
229: userProvidedLogCatArguments; // If not, we leave it as-is
230}
231
232function isNullOrUndefined(value: any): boolean {
233return typeof value === "undefined" || value === null;
234}
235
236function requestSetup(args: any): any {
2e432a9eArtem Egorov8 years ago237const workspaceFolder: vscode.WorkspaceFolder = <vscode.WorkspaceFolder>vscode.workspace.getWorkspaceFolder(vscode.Uri.file(args.program));
0a68f8dbArtem Egorov8 years ago238const projectRootPath = getProjectRoot(args);
239let mobilePlatformOptions: any = {
a41f5c68Artem Egorov8 years ago240workspaceRoot: workspaceFolder.uri.fsPath,
0a68f8dbArtem Egorov8 years ago241projectRoot: projectRootPath,
242platform: args.platform,
23d47878Artem Egorov8 years ago243target: args.target || "simulator",
0a68f8dbArtem Egorov8 years ago244};
245
246if (!args.runArguments) {
4529aa97Artem Egorov8 years ago247let runArgs = SettingsHelper.getRunArgs(args.platform, args.target || "simulator", workspaceFolder.uri);
0a68f8dbArtem Egorov8 years ago248mobilePlatformOptions.runArguments = runArgs;
a18a84baArtem Egorov8 years ago249} else {
250mobilePlatformOptions.runArguments = args.runArguments;
0a68f8dbArtem Egorov8 years ago251}
252
253return mobilePlatformOptions;
254}
255
256function getProjectRoot(args: any): string {
4edcda70Artem Egorov8 years ago257return SettingsHelper.getReactNativeProjectRoot(args.program);
0502b7a8dlebu10 years ago258}