microsoft/vscode-react-native

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
92ca3e659754cd996ac92b6a0e4b0e9f0a9c1d35

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/debugger/nodeDebugWrapper.ts

248lines · 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");
82Log.logMessage("Starting React Native Packager.");
83return mobilePlatform.startPackager();
84})
85.then(() => {
86// 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
87// and the user needs to Reload JS manually. We prewarm it to prevent that issue
88generator.step("prewarmBundleCache");
89Log.logMessage("Prewarming bundle cache. This may take a while ...");
90return mobilePlatform.prewarmBundleCache();
91})
92.then(() => {
93generator.step("mobilePlatform.runApp");
94Log.logMessage("Building and running application.");
95return mobilePlatform.runApp();
96})
97.then(() =>
98nodeDebugWrapper.attachRequest(this, request, args, mobilePlatform));
40e4b177Patricio Beltran10 years ago99}).catch(error =>
100nodeDebugWrapper.bailOut(this, error.message));
18d8ad2adigeff10 years ago101});
40e4b177Patricio Beltran10 years ago102};
103}
18d8ad2adigeff10 years ago104
40e4b177Patricio Beltran10 years ago105/**
106* Intecept the "attachRequest" instance method of NodeDebugSession to interpret arguments
107*/
108private customizeAttachRequest(): void {
109const nodeDebugWrapper = this;
110this.nodeDebugSession.prototype.attachRequest = function (request: any, args: IAttachRequestArgs) {
111nodeDebugWrapper.requestSetup(this, args);
299b0557Patricio Beltran10 years ago112nodeDebugWrapper.attachRequest(this, request, args, new GeneralMobilePlatform(nodeDebugWrapper.mobilePlatformOptions));
40e4b177Patricio Beltran10 years ago113};
114}
c96fc63eMark Oswald10 years ago115
40e4b177Patricio Beltran10 years ago116/**
117* Intecept the "disconnectRequest" instance method of NodeDebugSession to interpret arguments
118*/
119private customizeDisconnectRequest(): void {
120const originalRequest = this.nodeDebugSession.prototype.disconnectRequest;
121const nodeDebugWrapper = this;
122
123this.nodeDebugSession.prototype.disconnectRequest = function (response: any, args: any): void {
124// First we tell the extension to stop monitoring the logcat, and then we disconnect the debugging session
125
126if (nodeDebugWrapper.mobilePlatformOptions.platform === "android") {
127nodeDebugWrapper.remoteExtension.stopMonitoringLogcat()
128.catch(reason =>
129Log.logError(`WARNING: Couldn't stop monitoring logcat: ${reason.message || reason}\n`))
130.finally(() =>
131originalRequest.call(this, response, args));
132} else {
133originalRequest.call(this, response, args);
4881129dMeena Kunnathur Balakrishnan10 years ago134}
40e4b177Patricio Beltran10 years ago135};
136}
710f8655digeff10 years ago137
40e4b177Patricio Beltran10 years ago138/**
139* Makes the required setup for request customization
140* - Enables telemetry
141* - Sets up mobilePlatformOptions, remote extension and projectRootPath
142* - Starts debug server
143* - Create global logger
144*/
145private requestSetup(debugSession: NodeDebugSession, args: any) {
146this.projectRootPath = path.resolve(args.program, "../..");
147this.remoteExtension = RemoteExtension.atProjectRootPath(this.projectRootPath);
148this.mobilePlatformOptions = {
149projectRoot: this.projectRootPath,
150platform: args.platform,
18d8ad2adigeff10 years ago151};
152
40e4b177Patricio Beltran10 years ago153// Start to send telemetry
154this.telemetryReporter.reassignTo(new ExtensionTelemetryReporter(
155this.appName, this.version, Telemetry.APPINSIGHTS_INSTRUMENTATIONKEY, this.projectRootPath));
156
157// Create a server waiting for messages to re-initialize the debug session;
158const debugServerListeningPort = this.createReinitializeServer(debugSession, args.internalDebuggerPort);
159args.args = [debugServerListeningPort.toString()];
160
161Log.SetGlobalLogger(new NodeDebugAdapterLogger(this.vscodeDebugAdapterPackage, debugSession));
162}
163
164/**
165* Runs logic needed to attach.
166* Attach should:
167* - Enable js debugging
168*/
169private attachRequest(debugSession: NodeDebugSession, request: any, args: any, mobilePlatform: any): Q.Promise<void> {
170return TelemetryHelper.generate("attach", (generator) => {
171return Q({})
172.then(() => {
173generator.step("mobilePlatform.enableJSDebuggingMode");
174if (mobilePlatform) {
175return mobilePlatform.enableJSDebuggingMode();
176} else {
177Log.logMessage("Debugger ready. Enable remote debugging in app.");
178}
179}).then(() =>
180this.originalLaunchRequest.call(debugSession, request, args))
181.catch(error =>
182this.bailOut(debugSession, error.message));
183});
184}
185
186/**
187* Creates internal debug server and returns the port that the server is hook up into.
188*/
189private createReinitializeServer(debugSession: NodeDebugSession, internalDebuggerPort: string): number {
190// Create the server
191const server = http.createServer((req, res) => {
192res.statusCode = 404;
193if (req.url === "/refreshBreakpoints") {
194res.statusCode = 200;
195if (debugSession) {
196const sourceMaps = debugSession._sourceMaps;
197if (sourceMaps) {
198// Flush any cached source maps
199sourceMaps._allSourceMaps = {};
200sourceMaps._generatedToSourceMaps = {};
201sourceMaps._sourceToGeneratedMaps = {};
202}
203// Send an "initialized" event to trigger breakpoints to be re-sent
204debugSession.sendEvent(new this.vscodeDebugAdapterPackage.InitializedEvent());
205}
4881129dMeena Kunnathur Balakrishnan10 years ago206}
40e4b177Patricio Beltran10 years ago207res.end();
208});
209
210// Setup listen port and on error response
211const port = parseInt(internalDebuggerPort, 10) || 9090;
c2bf3c4fdigeff10 years ago212
40e4b177Patricio Beltran10 years ago213server.listen(port);
214server.on("error", (err: Error) => {
215TelemetryHelper.sendSimpleEvent("reinitializeServerError");
216Log.logError("Error in debug adapter server: " + err.toString());
217Log.logMessage("Breakpoints may not update. Consider restarting and specifying a different 'internalDebuggerPort' in launch.json");
218});
219
220// Return listen port
221return port;
222}
223
224/**
225* Logs error to user and finishes the debugging process.
226*/
227private bailOut(debugSession: NodeDebugSession, message: string): void {
228Log.logError(`Could not debug. ${message}`);
229debugSession.sendEvent(new this.vscodeDebugAdapterPackage.TerminatedEvent());
230process.exit(1);
231}
232
233/**
234* Parses log cat arguments to a string
235*/
236private parseLogCatArguments(userProvidedLogCatArguments: any): string {
237return Array.isArray(userProvidedLogCatArguments)
238? userProvidedLogCatArguments.join(" ") // If it's an array, we join the arguments
239: userProvidedLogCatArguments; // If not, we leave it as-is
240}
241
242/**
243* Helper method to know if a value is either null or undefined
244*/
245private isNullOrUndefined(value: any): boolean {
246return typeof value === "undefined" || value === null;
247}
248}