microsoft/vscode-react-native

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
0.9.3

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/extension/extensionServer.ts

292lines · 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";
51a4641cArtem Egorov7 years ago18import {IRemoteExtension, OpenFileRequest} from "../common/remoteExtension";
7daed3fcArtem Egorov8 years ago19import * as rpc from "noice-json-rpc";
20import * as WebSocket from "ws";
21import WebSocketServer = WebSocket.Server;
aca27f7fYuri Skorokhodov7 years ago22import * as nls from "vscode-nls";
23const localize = nls.loadMessageBundle();
0502b7a8dlebu10 years ago24
25export class ExtensionServer implements vscode.Disposable {
7daed3fcArtem Egorov8 years ago26public api: IRemoteExtension;
3118589cArtem Egorov8 years ago27public isDisposed: boolean = false;
7daed3fcArtem Egorov8 years ago28private serverInstance: WebSocketServer | null;
a822ac85dlebu10 years ago29private reactNativePackager: Packager;
8411fd8dDaniel Lebu10 years ago30private pipePath: string;
5c8365a6Artem Egorov8 years ago31private logCatMonitor: LogCatMonitor | null = null;
0a68f8dbArtem Egorov8 years ago32private logger: OutputChannelLogger = OutputChannelLogger.getMainChannel();
a822ac85dlebu10 years ago33
2e432a9eArtem Egorov8 years ago34public constructor(projectRootPath: string, reactNativePackager: Packager) {
7daed3fcArtem Egorov8 years ago35this.pipePath = MessagingHelper.getPath(projectRootPath);
a822ac85dlebu10 years ago36this.reactNativePackager = reactNativePackager;
37}
0502b7a8dlebu10 years ago38
39/**
40* Starts the server.
41*/
42public setup(): Q.Promise<void> {
3118589cArtem Egorov8 years ago43this.isDisposed = false;
0502b7a8dlebu10 years ago44
1950c5e9Artem Egorov8 years ago45return Q.Promise((resolve, reject) => {
46this._setup(resolve, reject);
47});
0502b7a8dlebu10 years ago48}
49
50/**
51* Stops the server.
52*/
53public dispose(): void {
3118589cArtem Egorov8 years ago54this.isDisposed = true;
0502b7a8dlebu10 years ago55if (this.serverInstance) {
56this.serverInstance.close();
acf08bc2dlebu10 years ago57this.serverInstance = null;
0502b7a8dlebu10 years ago58}
710f8655digeff10 years ago59
4edcda70Artem Egorov8 years ago60this.reactNativePackager.statusIndicator.dispose();
3118589cArtem Egorov8 years ago61this.reactNativePackager.stop(true);
c2bf3c4fdigeff10 years ago62this.stopMonitoringLogCat();
0502b7a8dlebu10 years ago63}
64
1950c5e9Artem Egorov8 years ago65private _setup(resolve: (val: void | Q.IPromise<void>) => void, reject: (reason: any) => void): void {
66const errorCallback = this.recoverServer.bind(this, resolve, reject);
67let launchCallback = (done: (val: void | Q.IPromise<void>) => void) => {
68this.logger.debug(`Extension messaging server started at ${this.pipePath}.`);
69
70if (this.serverInstance) {
71this.serverInstance.removeListener("error", errorCallback);
72this.serverInstance.on("error", this.recoverServer.bind(this, null, null));
73}
74done(void 0);
75};
76
77this.serverInstance = new WebSocketServer({port: <any>this.pipePath});
78this.api = new rpc.Server(this.serverInstance).api();
79this.serverInstance.on("listening", launchCallback.bind(this, resolve));
80this.serverInstance.on("error", errorCallback);
81
82this.setupApiHandlers();
83}
84
7daed3fcArtem Egorov8 years ago85private setupApiHandlers(): void {
86let methods: any = {};
87methods.stopMonitoringLogCat = this.stopMonitoringLogCat.bind(this);
88methods.getPackagerPort = this.getPackagerPort.bind(this);
89methods.sendTelemetry = this.sendTelemetry.bind(this);
90methods.openFileAtLocation = this.openFileAtLocation.bind(this);
91methods.showInformationMessage = this.showInformationMessage.bind(this);
92methods.launch = this.launch.bind(this);
93methods.showDevMenu = this.showDevMenu.bind(this);
94methods.reloadApp = this.reloadApp.bind(this);
95
96this.api.Extension.expose(methods);
5e651f3edigeff10 years ago97}
98
7daed3fcArtem Egorov8 years ago99private showDevMenu(deviceId?: string) {
100this.api.Debugger.emitShowDevMenu(deviceId);
514df4f4Patricio Beltran10 years ago101}
102
7daed3fcArtem Egorov8 years ago103private reloadApp(deviceId?: string) {
104this.api.Debugger.emitReloadApp(deviceId);
27710197Vladimir Kotikov8 years ago105}
0502b7a8dlebu10 years ago106
107/**
6b6eb911dlebu10 years ago108* Recovers the server in case the named socket we use already exists, but no other instance of VSCode is active.
0502b7a8dlebu10 years ago109*/
1950c5e9Artem Egorov8 years ago110private recoverServer(resolve: (value: void) => {} , reject: (reason: any) => {}, error: any): void {
6b6eb911dlebu10 years ago111let errorHandler = (e: any) => {
112/* The named socket is not used. */
113if (e.code === "ECONNREFUSED") {
8411fd8dDaniel Lebu10 years ago114new FileSystem().removePathRecursivelyAsync(this.pipePath)
6b6eb911dlebu10 years ago115.then(() => {
1950c5e9Artem Egorov8 years ago116if (resolve && reject) {
117return this._setup(resolve, reject);
118} else {
119return this.setup();
120}
6b6eb911dlebu10 years ago121})
122.done();
123}
124};
125
126/* The named socket already exists. */
127if (error.code === "EADDRINUSE") {
7daed3fcArtem Egorov8 years ago128let clientSocket = new WebSocket(`ws+unix://${this.pipePath}`);
6b6eb911dlebu10 years ago129clientSocket.on("error", errorHandler);
7daed3fcArtem Egorov8 years ago130clientSocket.on("open", function() {
131clientSocket.close();
6b6eb911dlebu10 years ago132});
0502b7a8dlebu10 years ago133}
134}
280c0746Patricio Beltran9 years ago135
7daed3fcArtem Egorov8 years ago136/**
137* Message handler for GET_PACKAGER_PORT.
138*/
2e432a9eArtem Egorov8 years ago139private getPackagerPort(program: string): number {
140return SettingsHelper.getPackagerPort(program);
7daed3fcArtem Egorov8 years ago141}
142
143/**
144* Message handler for OPEN_FILE_AT_LOCATION
145*/
51a4641cArtem Egorov7 years ago146private openFileAtLocation(openFileRequest: OpenFileRequest): Promise<void> {
147const { filename, lineNumber } = openFileRequest;
7daed3fcArtem Egorov8 years ago148return new Promise((resolve) => {
149vscode.workspace.openTextDocument(vscode.Uri.file(filename))
150.then((document: vscode.TextDocument) => {
151vscode.window.showTextDocument(document)
152.then((editor: vscode.TextEditor) => {
153let range = editor.document.lineAt(lineNumber - 1).range;
154editor.selection = new vscode.Selection(range.start, range.end);
155editor.revealRange(range, vscode.TextEditorRevealType.InCenter);
156resolve();
157});
158});
159});
160}
161
162private stopMonitoringLogCat(): void {
163if (this.logCatMonitor) {
164this.logCatMonitor.dispose();
165this.logCatMonitor = null;
166}
167}
168
169/**
170* Sends telemetry
171*/
51a4641cArtem Egorov7 years ago172private sendTelemetry(telemetryRequest: Telemetry.TelemetryRequest): void {
173const { extensionId, extensionVersion, appInsightsKey, eventName, properties, measures } = telemetryRequest;
7daed3fcArtem Egorov8 years ago174Telemetry.sendExtensionTelemetry(extensionId, extensionVersion, appInsightsKey, eventName, properties, measures);
175}
176
280c0746Patricio Beltran9 years ago177/**
178* Message handler for SHOW_INFORMATION_MESSAGE
179*/
7daed3fcArtem Egorov8 years ago180private showInformationMessage(message: string): void {
181vscode.window.showInformationMessage(message);
280c0746Patricio Beltran9 years ago182}
0a68f8dbArtem Egorov8 years ago183
7daed3fcArtem Egorov8 years ago184private launch(request: any): Promise<any> {
0a68f8dbArtem Egorov8 years ago185let mobilePlatformOptions = requestSetup(request.arguments);
186
187// We add the parameter if it's defined (adapter crashes otherwise)
188if (!isNullOrUndefined(request.arguments.logCatArguments)) {
189mobilePlatformOptions.logCatArguments = [parseLogCatArguments(request.arguments.logCatArguments)];
190}
191
192if (!isNullOrUndefined(request.arguments.variant)) {
193mobilePlatformOptions.variant = request.arguments.variant;
194}
195
196if (!isNullOrUndefined(request.arguments.scheme)) {
197mobilePlatformOptions.scheme = request.arguments.scheme;
198}
199
db6fd42aRuslan Bikkinin7 years ago200if (!isNullOrUndefined(request.arguments.productName)) {
201mobilePlatformOptions.productName = request.arguments.productName;
202}
203
2e432a9eArtem Egorov8 years ago204mobilePlatformOptions.packagerPort = SettingsHelper.getPackagerPort(request.arguments.program);
0a68f8dbArtem Egorov8 years ago205const platformDeps: MobilePlatformDeps = {
206packager: this.reactNativePackager,
207};
208const mobilePlatform = new PlatformResolver()
209.resolveMobilePlatform(request.arguments.platform, mobilePlatformOptions, platformDeps);
7daed3fcArtem Egorov8 years ago210return new Promise((resolve, reject) => {
031832ffArtem Egorov8 years ago211const extProps = {
212platform: {
213value: request.arguments.platform,
214isPii: false,
215},
216};
217
218TelemetryHelper.generate("launch", extProps, (generator) => {
7daed3fcArtem Egorov8 years ago219generator.step("checkPlatformCompatibility");
220TargetPlatformHelper.checkTargetPlatformSupport(mobilePlatformOptions.platform);
4787ec09Artem Egorov8 years ago221return mobilePlatform.beforeStartPackager()
222.then(() => {
223generator.step("startPackager");
224return mobilePlatform.startPackager();
225})
7daed3fcArtem Egorov8 years ago226.then(() => {
227// 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
228// and the user needs to Reload JS manually. We prewarm it to prevent that issue
229generator.step("prewarmBundleCache");
aca27f7fYuri Skorokhodov7 years ago230this.logger.info(localize("PrewarmingBundleCache", "Prewarming bundle cache. This may take a while ..."));
7daed3fcArtem Egorov8 years ago231return mobilePlatform.prewarmBundleCache();
232})
233.then(() => {
88908964Artem Egorov8 years ago234generator.step("mobilePlatform.runApp").add("target", mobilePlatformOptions.target, false);
aca27f7fYuri Skorokhodov7 years ago235this.logger.info(localize("BuildingAndRunningApplication", "Building and running application."));
7daed3fcArtem Egorov8 years ago236return mobilePlatform.runApp();
237})
238.then(() => {
239generator.step("mobilePlatform.enableJSDebuggingMode");
aca27f7fYuri Skorokhodov7 years ago240this.logger.info(localize("EnableJSDebugging", "Enable JS Debugging"));
7daed3fcArtem Egorov8 years ago241return mobilePlatform.enableJSDebuggingMode();
242})
243.then(() => {
244resolve();
245})
246.catch(error => {
748105d9Artem Egorov8 years ago247this.logger.error(error);
7daed3fcArtem Egorov8 years ago248reject(error);
249});
250});
0a68f8dbArtem Egorov8 years ago251});
252}
253}
254
255/**
256* Parses log cat arguments to a string
257*/
258function parseLogCatArguments(userProvidedLogCatArguments: any): string {
259return Array.isArray(userProvidedLogCatArguments)
260? userProvidedLogCatArguments.join(" ") // If it's an array, we join the arguments
261: userProvidedLogCatArguments; // If not, we leave it as-is
262}
263
264function isNullOrUndefined(value: any): boolean {
265return typeof value === "undefined" || value === null;
266}
267
268function requestSetup(args: any): any {
2e432a9eArtem Egorov8 years ago269const workspaceFolder: vscode.WorkspaceFolder = <vscode.WorkspaceFolder>vscode.workspace.getWorkspaceFolder(vscode.Uri.file(args.program));
0a68f8dbArtem Egorov8 years ago270const projectRootPath = getProjectRoot(args);
271let mobilePlatformOptions: any = {
a41f5c68Artem Egorov8 years ago272workspaceRoot: workspaceFolder.uri.fsPath,
0a68f8dbArtem Egorov8 years ago273projectRoot: projectRootPath,
274platform: args.platform,
1174ee3dArtem Egorov8 years ago275env: args.env,
276envFile: args.envFile,
23d47878Artem Egorov8 years ago277target: args.target || "simulator",
0a68f8dbArtem Egorov8 years ago278};
279
280if (!args.runArguments) {
4529aa97Artem Egorov8 years ago281let runArgs = SettingsHelper.getRunArgs(args.platform, args.target || "simulator", workspaceFolder.uri);
0a68f8dbArtem Egorov8 years ago282mobilePlatformOptions.runArguments = runArgs;
a18a84baArtem Egorov8 years ago283} else {
284mobilePlatformOptions.runArguments = args.runArguments;
0a68f8dbArtem Egorov8 years ago285}
286
287return mobilePlatformOptions;
288}
289
290function getProjectRoot(args: any): string {
4edcda70Artem Egorov8 years ago291return SettingsHelper.getReactNativeProjectRoot(args.program);
0502b7a8dlebu10 years ago292}