microsoft/vscode-react-native

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
0.15.0

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/extension/extensionServer.ts

352lines · 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";
7daed3fcArtem Egorov8 years ago6import {MessagingHelper}from "../common/extensionMessaging";
0a68f8dbArtem Egorov8 years ago7import {OutputChannelLogger} from "./log/OutputChannelLogger";
8import {Packager} from "../common/packager";
e3706a1cRedMickey6 years ago9import {ProjectVersionHelper} from "../common/projectVersionHelper";
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";
ba953e9fRedMickey6 years ago16import {ErrorHelper} from "../common/error/errorHelper";
17import {InternalErrorCode} from "../common/error/internalErrorCode";
0a68f8dbArtem Egorov8 years ago18import {TargetPlatformHelper} from "../common/targetPlatformHelper";
19import {MobilePlatformDeps} from "./generalMobilePlatform";
51a4641cArtem Egorov7 years ago20import {IRemoteExtension, OpenFileRequest} from "../common/remoteExtension";
7daed3fcArtem Egorov8 years ago21import * as rpc from "noice-json-rpc";
22import * as WebSocket from "ws";
23import WebSocketServer = WebSocket.Server;
aca27f7fYuri Skorokhodov7 years ago24import * as nls from "vscode-nls";
af1474acRedMickey6 years ago25import {CommandExecutor} from "../common/commandExecutor";
aca27f7fYuri Skorokhodov7 years ago26const localize = nls.loadMessageBundle();
0502b7a8dlebu10 years ago27
28export class ExtensionServer implements vscode.Disposable {
7daed3fcArtem Egorov8 years ago29public api: IRemoteExtension;
3118589cArtem Egorov8 years ago30public isDisposed: boolean = false;
7daed3fcArtem Egorov8 years ago31private serverInstance: WebSocketServer | null;
a822ac85dlebu10 years ago32private reactNativePackager: Packager;
8411fd8dDaniel Lebu10 years ago33private pipePath: string;
5c8365a6Artem Egorov8 years ago34private logCatMonitor: LogCatMonitor | null = null;
0a68f8dbArtem Egorov8 years ago35private logger: OutputChannelLogger = OutputChannelLogger.getMainChannel();
a822ac85dlebu10 years ago36
2e432a9eArtem Egorov8 years ago37public constructor(projectRootPath: string, reactNativePackager: Packager) {
7daed3fcArtem Egorov8 years ago38this.pipePath = MessagingHelper.getPath(projectRootPath);
a822ac85dlebu10 years ago39this.reactNativePackager = reactNativePackager;
40}
0502b7a8dlebu10 years ago41
42/**
43* Starts the server.
44*/
45public setup(): Q.Promise<void> {
3118589cArtem Egorov8 years ago46this.isDisposed = false;
0502b7a8dlebu10 years ago47
1950c5e9Artem Egorov8 years ago48return Q.Promise((resolve, reject) => {
49this._setup(resolve, reject);
50});
0502b7a8dlebu10 years ago51}
52
53/**
54* Stops the server.
55*/
56public dispose(): void {
3118589cArtem Egorov8 years ago57this.isDisposed = true;
0502b7a8dlebu10 years ago58if (this.serverInstance) {
59this.serverInstance.close();
acf08bc2dlebu10 years ago60this.serverInstance = null;
0502b7a8dlebu10 years ago61}
710f8655digeff10 years ago62
5f0a4a46JiglioNero6 years ago63this.reactNativePackager.getStatusIndicator().dispose();
3118589cArtem Egorov8 years ago64this.reactNativePackager.stop(true);
c2bf3c4fdigeff10 years ago65this.stopMonitoringLogCat();
0502b7a8dlebu10 years ago66}
67
1950c5e9Artem Egorov8 years ago68private _setup(resolve: (val: void | Q.IPromise<void>) => void, reject: (reason: any) => void): void {
69const errorCallback = this.recoverServer.bind(this, resolve, reject);
70let launchCallback = (done: (val: void | Q.IPromise<void>) => void) => {
71this.logger.debug(`Extension messaging server started at ${this.pipePath}.`);
72
73if (this.serverInstance) {
74this.serverInstance.removeListener("error", errorCallback);
75this.serverInstance.on("error", this.recoverServer.bind(this, null, null));
76}
77done(void 0);
78};
79
80this.serverInstance = new WebSocketServer({port: <any>this.pipePath});
81this.api = new rpc.Server(this.serverInstance).api();
82this.serverInstance.on("listening", launchCallback.bind(this, resolve));
83this.serverInstance.on("error", errorCallback);
84
85this.setupApiHandlers();
86}
87
7daed3fcArtem Egorov8 years ago88private setupApiHandlers(): void {
89let methods: any = {};
90methods.stopMonitoringLogCat = this.stopMonitoringLogCat.bind(this);
91methods.getPackagerPort = this.getPackagerPort.bind(this);
92methods.sendTelemetry = this.sendTelemetry.bind(this);
93methods.openFileAtLocation = this.openFileAtLocation.bind(this);
94methods.showInformationMessage = this.showInformationMessage.bind(this);
95methods.launch = this.launch.bind(this);
96methods.showDevMenu = this.showDevMenu.bind(this);
97methods.reloadApp = this.reloadApp.bind(this);
98
99this.api.Extension.expose(methods);
5e651f3edigeff10 years ago100}
101
7daed3fcArtem Egorov8 years ago102private showDevMenu(deviceId?: string) {
103this.api.Debugger.emitShowDevMenu(deviceId);
514df4f4Patricio Beltran10 years ago104}
105
7daed3fcArtem Egorov8 years ago106private reloadApp(deviceId?: string) {
107this.api.Debugger.emitReloadApp(deviceId);
27710197Vladimir Kotikov8 years ago108}
0502b7a8dlebu10 years ago109
110/**
6b6eb911dlebu10 years ago111* Recovers the server in case the named socket we use already exists, but no other instance of VSCode is active.
0502b7a8dlebu10 years ago112*/
1950c5e9Artem Egorov8 years ago113private recoverServer(resolve: (value: void) => {} , reject: (reason: any) => {}, error: any): void {
6b6eb911dlebu10 years ago114let errorHandler = (e: any) => {
115/* The named socket is not used. */
116if (e.code === "ECONNREFUSED") {
8411fd8dDaniel Lebu10 years ago117new FileSystem().removePathRecursivelyAsync(this.pipePath)
6b6eb911dlebu10 years ago118.then(() => {
1950c5e9Artem Egorov8 years ago119if (resolve && reject) {
120return this._setup(resolve, reject);
121} else {
122return this.setup();
123}
6b6eb911dlebu10 years ago124})
125.done();
126}
127};
128
129/* The named socket already exists. */
130if (error.code === "EADDRINUSE") {
7daed3fcArtem Egorov8 years ago131let clientSocket = new WebSocket(`ws+unix://${this.pipePath}`);
6b6eb911dlebu10 years ago132clientSocket.on("error", errorHandler);
7daed3fcArtem Egorov8 years ago133clientSocket.on("open", function() {
134clientSocket.close();
6b6eb911dlebu10 years ago135});
0502b7a8dlebu10 years ago136}
137}
280c0746Patricio Beltran9 years ago138
7daed3fcArtem Egorov8 years ago139/**
140* Message handler for GET_PACKAGER_PORT.
141*/
18ea7d15Yuri Skorokhodov6 years ago142private getPackagerPort(projectFolder: string): number {
143return SettingsHelper.getPackagerPort(projectFolder);
7daed3fcArtem Egorov8 years ago144}
145
146/**
147* Message handler for OPEN_FILE_AT_LOCATION
148*/
51a4641cArtem Egorov7 years ago149private openFileAtLocation(openFileRequest: OpenFileRequest): Promise<void> {
150const { filename, lineNumber } = openFileRequest;
7daed3fcArtem Egorov8 years ago151return new Promise((resolve) => {
152vscode.workspace.openTextDocument(vscode.Uri.file(filename))
153.then((document: vscode.TextDocument) => {
154vscode.window.showTextDocument(document)
155.then((editor: vscode.TextEditor) => {
156let range = editor.document.lineAt(lineNumber - 1).range;
157editor.selection = new vscode.Selection(range.start, range.end);
158editor.revealRange(range, vscode.TextEditorRevealType.InCenter);
159resolve();
160});
161});
162});
163}
164
165private stopMonitoringLogCat(): void {
166if (this.logCatMonitor) {
167this.logCatMonitor.dispose();
168this.logCatMonitor = null;
169}
170}
171
172/**
173* Sends telemetry
174*/
51a4641cArtem Egorov7 years ago175private sendTelemetry(telemetryRequest: Telemetry.TelemetryRequest): void {
176const { extensionId, extensionVersion, appInsightsKey, eventName, properties, measures } = telemetryRequest;
7daed3fcArtem Egorov8 years ago177Telemetry.sendExtensionTelemetry(extensionId, extensionVersion, appInsightsKey, eventName, properties, measures);
178}
179
280c0746Patricio Beltran9 years ago180/**
181* Message handler for SHOW_INFORMATION_MESSAGE
182*/
7daed3fcArtem Egorov8 years ago183private showInformationMessage(message: string): void {
184vscode.window.showInformationMessage(message);
280c0746Patricio Beltran9 years ago185}
0a68f8dbArtem Egorov8 years ago186
7daed3fcArtem Egorov8 years ago187private launch(request: any): Promise<any> {
0a68f8dbArtem Egorov8 years ago188let mobilePlatformOptions = requestSetup(request.arguments);
189
190// We add the parameter if it's defined (adapter crashes otherwise)
191if (!isNullOrUndefined(request.arguments.logCatArguments)) {
192mobilePlatformOptions.logCatArguments = [parseLogCatArguments(request.arguments.logCatArguments)];
193}
194
195if (!isNullOrUndefined(request.arguments.variant)) {
196mobilePlatformOptions.variant = request.arguments.variant;
197}
198
199if (!isNullOrUndefined(request.arguments.scheme)) {
200mobilePlatformOptions.scheme = request.arguments.scheme;
201}
202
db6fd42aRuslan Bikkinin7 years ago203if (!isNullOrUndefined(request.arguments.productName)) {
204mobilePlatformOptions.productName = request.arguments.productName;
205}
206
78c2b4deRedMickey6 years ago207if (!isNullOrUndefined(request.arguments.launchActivity)) {
208mobilePlatformOptions.debugLaunchActivity = request.arguments.launchActivity;
209}
210
549baae2RedMickey6 years ago211if (request.arguments.type === "reactnativedirect") {
212mobilePlatformOptions.isDirect = true;
213}
214
18ea7d15Yuri Skorokhodov6 years ago215mobilePlatformOptions.packagerPort = SettingsHelper.getPackagerPort(request.arguments.cwd || request.arguments.program);
0a68f8dbArtem Egorov8 years ago216const platformDeps: MobilePlatformDeps = {
217packager: this.reactNativePackager,
218};
219const mobilePlatform = new PlatformResolver()
220.resolveMobilePlatform(request.arguments.platform, mobilePlatformOptions, platformDeps);
7daed3fcArtem Egorov8 years ago221return new Promise((resolve, reject) => {
549baae2RedMickey6 years ago222let extProps: any = {
031832ffArtem Egorov7 years ago223platform: {
224value: request.arguments.platform,
225isPii: false,
226},
227};
228
549baae2RedMickey6 years ago229if (mobilePlatformOptions.isDirect) {
230extProps.isDirect = {
231value: true,
232isPii: false,
233};
234}
235
e3706a1cRedMickey6 years ago236ProjectVersionHelper.getReactNativePackageVersionsFromNodeModules(mobilePlatformOptions.projectRoot, true)
7fa90b3bRedMickey6 years ago237.then(versions => {
238mobilePlatformOptions.reactNativeVersions = versions;
239extProps = TelemetryHelper.addPropertyToTelemetryProperties(versions.reactNativeVersion, "reactNativeVersion", extProps);
8411c437David Serafimov6 years ago240if (request.arguments.platform === "windows") {
241if (ProjectVersionHelper.isVersionError(versions.reactNativeWindowsVersion)) {
242throw ErrorHelper.getInternalError(InternalErrorCode.ReactNativeWindowsIsNotInstalled);
243}
7fa90b3bRedMickey6 years ago244extProps = TelemetryHelper.addPropertyToTelemetryProperties(versions.reactNativeWindowsVersion, "reactNativeWindowsVersion", extProps);
245}
ba953e9fRedMickey6 years ago246TelemetryHelper.generate("launch", extProps, (generator) => {
247generator.step("checkPlatformCompatibility");
248TargetPlatformHelper.checkTargetPlatformSupport(mobilePlatformOptions.platform);
249return mobilePlatform.beforeStartPackager()
250.then(() => {
251generator.step("startPackager");
252return mobilePlatform.startPackager();
253})
254.then(() => {
255// 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
256// and the user needs to Reload JS manually. We prewarm it to prevent that issue
257generator.step("prewarmBundleCache");
258this.logger.info(localize("PrewarmingBundleCache", "Prewarming bundle cache. This may take a while ..."));
259return mobilePlatform.prewarmBundleCache();
260})
261.then(() => {
262generator.step("mobilePlatform.runApp").add("target", mobilePlatformOptions.target, false);
263this.logger.info(localize("BuildingAndRunningApplication", "Building and running application."));
264return mobilePlatform.runApp();
265})
266.then(() => {
267if (mobilePlatformOptions.isDirect) {
268generator.step("mobilePlatform.enableDirectDebuggingMode");
269if (request.arguments.platform === "android") {
270this.logger.info(localize("PrepareHermesDebugging", "Prepare Hermes debugging (experimental)"));
271}
272return mobilePlatform.disableJSDebuggingMode();
273}
274generator.step("mobilePlatform.enableJSDebuggingMode");
275this.logger.info(localize("EnableJSDebugging", "Enable JS Debugging"));
276return mobilePlatform.enableJSDebuggingMode();
277})
278.then(() => {
279resolve();
280})
281.catch(error => {
282generator.addError(error);
283this.logger.error(error);
284reject(error);
285});
7daed3fcArtem Egorov8 years ago286});
ba953e9fRedMickey6 years ago287})
288.catch(error => {
26bea166Yuri Skorokhodov6 years ago289if (error && error.errorCode) {
290if (error.errorCode === InternalErrorCode.ReactNativePackageIsNotInstalled) {
291TelemetryHelper.sendErrorEvent(
292"ReactNativePackageIsNotInstalled",
293ErrorHelper.getInternalError(InternalErrorCode.ReactNativePackageIsNotInstalled)
294);
295} else if (error.errorCode === InternalErrorCode.ReactNativeWindowsIsNotInstalled) {
296TelemetryHelper.sendErrorEvent(
297"ReactNativeWindowsPackageIsNotInstalled",
298ErrorHelper.getInternalError(InternalErrorCode.ReactNativeWindowsIsNotInstalled)
299);
300}
301}
ba953e9fRedMickey6 years ago302this.logger.error(error);
303reject(error);
304});
0a68f8dbArtem Egorov8 years ago305});
306}
307}
308
309/**
310* Parses log cat arguments to a string
311*/
312function parseLogCatArguments(userProvidedLogCatArguments: any): string {
313return Array.isArray(userProvidedLogCatArguments)
314? userProvidedLogCatArguments.join(" ") // If it's an array, we join the arguments
315: userProvidedLogCatArguments; // If not, we leave it as-is
316}
317
318function isNullOrUndefined(value: any): boolean {
319return typeof value === "undefined" || value === null;
320}
321
322function requestSetup(args: any): any {
18ea7d15Yuri Skorokhodov6 years ago323const workspaceFolder: vscode.WorkspaceFolder = <vscode.WorkspaceFolder>vscode.workspace.getWorkspaceFolder(vscode.Uri.file(args.cwd || args.program));
0a68f8dbArtem Egorov8 years ago324const projectRootPath = getProjectRoot(args);
325let mobilePlatformOptions: any = {
a41f5c68Artem Egorov8 years ago326workspaceRoot: workspaceFolder.uri.fsPath,
0a68f8dbArtem Egorov8 years ago327projectRoot: projectRootPath,
328platform: args.platform,
1174ee3dArtem Egorov8 years ago329env: args.env,
330envFile: args.envFile,
23d47878Artem Egorov8 years ago331target: args.target || "simulator",
0a68f8dbArtem Egorov8 years ago332};
333
62c4de22RedMickey6 years ago334if (args.platform === "exponent") {
335mobilePlatformOptions.expoHostType = args.expoHostType || "tunnel";
336}
337
af1474acRedMickey6 years ago338CommandExecutor.ReactNativeCommand = SettingsHelper.getReactNativeGlobalCommandName(workspaceFolder.uri);
339
0a68f8dbArtem Egorov8 years ago340if (!args.runArguments) {
4529aa97Artem Egorov8 years ago341let runArgs = SettingsHelper.getRunArgs(args.platform, args.target || "simulator", workspaceFolder.uri);
0a68f8dbArtem Egorov8 years ago342mobilePlatformOptions.runArguments = runArgs;
a18a84baArtem Egorov8 years ago343} else {
344mobilePlatformOptions.runArguments = args.runArguments;
0a68f8dbArtem Egorov8 years ago345}
346
347return mobilePlatformOptions;
348}
349
350function getProjectRoot(args: any): string {
18ea7d15Yuri Skorokhodov6 years ago351return SettingsHelper.getReactNativeProjectRoot(args.cwd || args.program);
0502b7a8dlebu10 years ago352}