microsoft/vscode-react-native

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
1f8178688e4f25f25849eea54eba01e487164636

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/debugger/nodeDebugWrapper.ts

247lines · modeblame

65bb0c85Jimmy Thomson10 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
40e4b177Patricio Beltran10 years ago4import * as Q from "q";
65bb0c85Jimmy Thomson10 years ago5import * as path from "path";
6import * as http from "http";
7
d976d077Meena Kunnathur Balakrishnan10 years ago8import {Telemetry} from "../common/telemetry";
dd442738Jimmy Thomson10 years ago9import {TelemetryHelper} from "../common/telemetryHelper";
725cf712digeff10 years ago10import {RemoteExtension} from "../common/remoteExtension";
bb77358cMark Oswald10 years ago11import {IOSPlatform} from "./ios/iOSPlatform";
40e4b177Patricio Beltran10 years ago12import {PlatformResolver} from "./platformResolver";
13import {IRunOptions} from "../common/launchArgs";
14import {TargetPlatformHelper} from "../common/targetPlatformHelper";
15import {ExtensionTelemetryReporter, ReassignableTelemetryReporter} from "../common/telemetryReporters";
16import {NodeDebugAdapterLogger} from "../common/log/loggers";
17import {Log} from "../common/log/log";
ac7fef0cPatricio Beltran9 years ago18import {GeneralMobilePlatform} from "../common/generalMobilePlatform";
40e4b177Patricio Beltran10 years ago19
20export class NodeDebugWrapper {
21private projectRootPath: string;
22private remoteExtension: RemoteExtension;
23private telemetryReporter: ReassignableTelemetryReporter;
24private appName: string;
25private version: string;
26private mobilePlatformOptions: IRunOptions;
27
28private vscodeDebugAdapterPackage: typeof VSCodeDebugAdapter;
29private nodeDebugSession: typeof NodeDebugSession;
30private originalLaunchRequest: (response: any, args: any) => void;
31
32public constructor(appName: string, version: string, telemetryReporter: ReassignableTelemetryReporter, debugAdapter: typeof VSCodeDebugAdapter, debugSession: typeof NodeDebugSession) {
33this.appName = appName;
34this.version = version;
35this.telemetryReporter = telemetryReporter;
36this.vscodeDebugAdapterPackage = debugAdapter;
37this.nodeDebugSession = debugSession;
38this.originalLaunchRequest = this.nodeDebugSession.prototype.launchRequest;
65bb0c85Jimmy Thomson10 years ago39}
40e4b177Patricio Beltran10 years ago40
41/**
42* Calls customize methods for all requests needed
43*/
44public customizeNodeAdapterRequests(): void {
45this.customizeLaunchRequest();
46this.customizeAttachRequest();
47this.customizeDisconnectRequest();
65bb0c85Jimmy Thomson10 years ago48}
18d8ad2adigeff10 years ago49
40e4b177Patricio Beltran10 years ago50/**
51* Intecept the "launchRequest" instance method of NodeDebugSession to interpret arguments.
52* Launch should:
53* - Run the packager if needed
54* - Compile and run application
55* - Prewarm bundle
56*/
57private customizeLaunchRequest(): void {
58const nodeDebugWrapper = this;
59this.nodeDebugSession.prototype.launchRequest = function (request: any, args: ILaunchRequestArgs) {
60nodeDebugWrapper.requestSetup(this, args);
61nodeDebugWrapper.mobilePlatformOptions.target = args.target || "simulator";
62nodeDebugWrapper.mobilePlatformOptions.iosRelativeProjectPath = !nodeDebugWrapper.isNullOrUndefined(args.iosRelativeProjectPath) ?
63args.iosRelativeProjectPath :
64IOSPlatform.DEFAULT_IOS_PROJECT_RELATIVE_PATH;
65
66// We add the parameter if it's defined (adapter crashes otherwise)
67if (!nodeDebugWrapper.isNullOrUndefined(args.logCatArguments)) {
68nodeDebugWrapper.mobilePlatformOptions.logCatArguments = [nodeDebugWrapper.parseLogCatArguments(args.logCatArguments)];
69}
18d8ad2adigeff10 years ago70
40e4b177Patricio Beltran10 years ago71return TelemetryHelper.generate("launch", (generator) => {
72const resolver = new PlatformResolver();
73return nodeDebugWrapper.remoteExtension.getPackagerPort()
74.then(packagerPort => {
75nodeDebugWrapper.mobilePlatformOptions.packagerPort = packagerPort;
76const mobilePlatform = resolver.resolveMobilePlatform(args.platform, nodeDebugWrapper.mobilePlatformOptions);
299b0557Patricio Beltran10 years ago77return Q({})
78.then(() => {
79generator.step("checkPlatformCompatibility");
80TargetPlatformHelper.checkTargetPlatformSupport(nodeDebugWrapper.mobilePlatformOptions.platform);
81generator.step("startPackager");
82return mobilePlatform.startPackager();
83})
84.then(() => {
85// 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
86// and the user needs to Reload JS manually. We prewarm it to prevent that issue
87generator.step("prewarmBundleCache");
88Log.logMessage("Prewarming bundle cache. This may take a while ...");
89return mobilePlatform.prewarmBundleCache();
90})
91.then(() => {
92generator.step("mobilePlatform.runApp");
93Log.logMessage("Building and running application.");
94return mobilePlatform.runApp();
95})
96.then(() =>
97nodeDebugWrapper.attachRequest(this, request, args, mobilePlatform));
40e4b177Patricio Beltran10 years ago98}).catch(error =>
99nodeDebugWrapper.bailOut(this, error.message));
18d8ad2adigeff10 years ago100});
40e4b177Patricio Beltran10 years ago101};
102}
18d8ad2adigeff10 years ago103
40e4b177Patricio Beltran10 years ago104/**
105* Intecept the "attachRequest" instance method of NodeDebugSession to interpret arguments
106*/
107private customizeAttachRequest(): void {
108const nodeDebugWrapper = this;
109this.nodeDebugSession.prototype.attachRequest = function (request: any, args: IAttachRequestArgs) {
110nodeDebugWrapper.requestSetup(this, args);
299b0557Patricio Beltran10 years ago111nodeDebugWrapper.attachRequest(this, request, args, new GeneralMobilePlatform(nodeDebugWrapper.mobilePlatformOptions));
40e4b177Patricio Beltran10 years ago112};
113}
c96fc63eMark Oswald10 years ago114
40e4b177Patricio Beltran10 years ago115/**
116* Intecept the "disconnectRequest" instance method of NodeDebugSession to interpret arguments
117*/
118private customizeDisconnectRequest(): void {
119const originalRequest = this.nodeDebugSession.prototype.disconnectRequest;
120const nodeDebugWrapper = this;
121
122this.nodeDebugSession.prototype.disconnectRequest = function (response: any, args: any): void {
123// First we tell the extension to stop monitoring the logcat, and then we disconnect the debugging session
124
125if (nodeDebugWrapper.mobilePlatformOptions.platform === "android") {
126nodeDebugWrapper.remoteExtension.stopMonitoringLogcat()
127.catch(reason =>
128Log.logError(`WARNING: Couldn't stop monitoring logcat: ${reason.message || reason}\n`))
129.finally(() =>
130originalRequest.call(this, response, args));
131} else {
132originalRequest.call(this, response, args);
4881129dMeena Kunnathur Balakrishnan10 years ago133}
40e4b177Patricio Beltran10 years ago134};
135}
710f8655digeff10 years ago136
40e4b177Patricio Beltran10 years ago137/**
138* Makes the required setup for request customization
139* - Enables telemetry
140* - Sets up mobilePlatformOptions, remote extension and projectRootPath
141* - Starts debug server
142* - Create global logger
143*/
144private requestSetup(debugSession: NodeDebugSession, args: any) {
145this.projectRootPath = path.resolve(args.program, "../..");
146this.remoteExtension = RemoteExtension.atProjectRootPath(this.projectRootPath);
147this.mobilePlatformOptions = {
148projectRoot: this.projectRootPath,
149platform: args.platform,
18d8ad2adigeff10 years ago150};
151
40e4b177Patricio Beltran10 years ago152// Start to send telemetry
153this.telemetryReporter.reassignTo(new ExtensionTelemetryReporter(
154this.appName, this.version, Telemetry.APPINSIGHTS_INSTRUMENTATIONKEY, this.projectRootPath));
155
156// Create a server waiting for messages to re-initialize the debug session;
157const debugServerListeningPort = this.createReinitializeServer(debugSession, args.internalDebuggerPort);
158args.args = [debugServerListeningPort.toString()];
159
160Log.SetGlobalLogger(new NodeDebugAdapterLogger(this.vscodeDebugAdapterPackage, debugSession));
161}
162
163/**
164* Runs logic needed to attach.
165* Attach should:
166* - Enable js debugging
167*/
168private attachRequest(debugSession: NodeDebugSession, request: any, args: any, mobilePlatform: any): Q.Promise<void> {
169return TelemetryHelper.generate("attach", (generator) => {
170return Q({})
171.then(() => {
172generator.step("mobilePlatform.enableJSDebuggingMode");
173if (mobilePlatform) {
174return mobilePlatform.enableJSDebuggingMode();
175} else {
176Log.logMessage("Debugger ready. Enable remote debugging in app.");
177}
178}).then(() =>
179this.originalLaunchRequest.call(debugSession, request, args))
180.catch(error =>
181this.bailOut(debugSession, error.message));
182});
183}
184
185/**
186* Creates internal debug server and returns the port that the server is hook up into.
187*/
188private createReinitializeServer(debugSession: NodeDebugSession, internalDebuggerPort: string): number {
189// Create the server
190const server = http.createServer((req, res) => {
191res.statusCode = 404;
192if (req.url === "/refreshBreakpoints") {
193res.statusCode = 200;
194if (debugSession) {
195const sourceMaps = debugSession._sourceMaps;
196if (sourceMaps) {
197// Flush any cached source maps
198sourceMaps._allSourceMaps = {};
199sourceMaps._generatedToSourceMaps = {};
200sourceMaps._sourceToGeneratedMaps = {};
201}
202// Send an "initialized" event to trigger breakpoints to be re-sent
203debugSession.sendEvent(new this.vscodeDebugAdapterPackage.InitializedEvent());
204}
4881129dMeena Kunnathur Balakrishnan10 years ago205}
40e4b177Patricio Beltran10 years ago206res.end();
207});
208
209// Setup listen port and on error response
210const port = parseInt(internalDebuggerPort, 10) || 9090;
c2bf3c4fdigeff10 years ago211
40e4b177Patricio Beltran10 years ago212server.listen(port);
213server.on("error", (err: Error) => {
214TelemetryHelper.sendSimpleEvent("reinitializeServerError");
215Log.logError("Error in debug adapter server: " + err.toString());
216Log.logMessage("Breakpoints may not update. Consider restarting and specifying a different 'internalDebuggerPort' in launch.json");
217});
218
219// Return listen port
220return port;
221}
222
223/**
224* Logs error to user and finishes the debugging process.
225*/
226private bailOut(debugSession: NodeDebugSession, message: string): void {
227Log.logError(`Could not debug. ${message}`);
228debugSession.sendEvent(new this.vscodeDebugAdapterPackage.TerminatedEvent());
229process.exit(1);
230}
231
232/**
233* Parses log cat arguments to a string
234*/
235private parseLogCatArguments(userProvidedLogCatArguments: any): string {
236return Array.isArray(userProvidedLogCatArguments)
237? userProvidedLogCatArguments.join(" ") // If it's an array, we join the arguments
238: userProvidedLogCatArguments; // If not, we leave it as-is
239}
240
241/**
242* Helper method to know if a value is either null or undefined
243*/
244private isNullOrUndefined(value: any): boolean {
245return typeof value === "undefined" || value === null;
246}
247}