microsoft/vscode-react-native

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
b648d5cc9c9052afe2bcb20bb398734e167df2b7

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/debugger/nodeDebugWrapper.ts

253lines · 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;
774cffd7Vladimir Kotikov9 years ago30private sourceMapsConstructor: typeof SourceMaps;
40e4b177Patricio Beltran10 years ago31private originalLaunchRequest: (response: any, args: any) => void;
32
774cffd7Vladimir Kotikov9 years ago33public constructor(appName: string, version: string, telemetryReporter: ReassignableTelemetryReporter,
34debugAdapter: typeof VSCodeDebugAdapter, debugSession: typeof NodeDebugSession, sourceMaps: typeof SourceMaps) {
35
40e4b177Patricio Beltran10 years ago36this.appName = appName;
37this.version = version;
38this.telemetryReporter = telemetryReporter;
39this.vscodeDebugAdapterPackage = debugAdapter;
40this.nodeDebugSession = debugSession;
774cffd7Vladimir Kotikov9 years ago41this.sourceMapsConstructor = sourceMaps;
40e4b177Patricio Beltran10 years ago42this.originalLaunchRequest = this.nodeDebugSession.prototype.launchRequest;
65bb0c85Jimmy Thomson10 years ago43}
40e4b177Patricio Beltran10 years ago44
45/**
46* Calls customize methods for all requests needed
47*/
48public customizeNodeAdapterRequests(): void {
49this.customizeLaunchRequest();
50this.customizeAttachRequest();
51this.customizeDisconnectRequest();
65bb0c85Jimmy Thomson10 years ago52}
18d8ad2adigeff10 years ago53
40e4b177Patricio Beltran10 years ago54/**
55* Intecept the "launchRequest" instance method of NodeDebugSession to interpret arguments.
56* Launch should:
57* - Run the packager if needed
58* - Compile and run application
59* - Prewarm bundle
60*/
61private customizeLaunchRequest(): void {
62const nodeDebugWrapper = this;
63this.nodeDebugSession.prototype.launchRequest = function (request: any, args: ILaunchRequestArgs) {
64nodeDebugWrapper.requestSetup(this, args);
65nodeDebugWrapper.mobilePlatformOptions.target = args.target || "simulator";
66nodeDebugWrapper.mobilePlatformOptions.iosRelativeProjectPath = !nodeDebugWrapper.isNullOrUndefined(args.iosRelativeProjectPath) ?
67args.iosRelativeProjectPath :
68IOSPlatform.DEFAULT_IOS_PROJECT_RELATIVE_PATH;
69
70// We add the parameter if it's defined (adapter crashes otherwise)
71if (!nodeDebugWrapper.isNullOrUndefined(args.logCatArguments)) {
72nodeDebugWrapper.mobilePlatformOptions.logCatArguments = [nodeDebugWrapper.parseLogCatArguments(args.logCatArguments)];
73}
18d8ad2adigeff10 years ago74
40e4b177Patricio Beltran10 years ago75return TelemetryHelper.generate("launch", (generator) => {
76const resolver = new PlatformResolver();
77return nodeDebugWrapper.remoteExtension.getPackagerPort()
78.then(packagerPort => {
79nodeDebugWrapper.mobilePlatformOptions.packagerPort = packagerPort;
80const mobilePlatform = resolver.resolveMobilePlatform(args.platform, nodeDebugWrapper.mobilePlatformOptions);
299b0557Patricio Beltran10 years ago81return Q({})
82.then(() => {
83generator.step("checkPlatformCompatibility");
84TargetPlatformHelper.checkTargetPlatformSupport(nodeDebugWrapper.mobilePlatformOptions.platform);
85generator.step("startPackager");
86return mobilePlatform.startPackager();
87})
88.then(() => {
89// 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
90// and the user needs to Reload JS manually. We prewarm it to prevent that issue
91generator.step("prewarmBundleCache");
92Log.logMessage("Prewarming bundle cache. This may take a while ...");
93return mobilePlatform.prewarmBundleCache();
94})
95.then(() => {
96generator.step("mobilePlatform.runApp");
97Log.logMessage("Building and running application.");
98return mobilePlatform.runApp();
99})
100.then(() =>
101nodeDebugWrapper.attachRequest(this, request, args, mobilePlatform));
40e4b177Patricio Beltran10 years ago102}).catch(error =>
103nodeDebugWrapper.bailOut(this, error.message));
18d8ad2adigeff10 years ago104});
40e4b177Patricio Beltran10 years ago105};
106}
18d8ad2adigeff10 years ago107
40e4b177Patricio Beltran10 years ago108/**
109* Intecept the "attachRequest" instance method of NodeDebugSession to interpret arguments
110*/
111private customizeAttachRequest(): void {
112const nodeDebugWrapper = this;
113this.nodeDebugSession.prototype.attachRequest = function (request: any, args: IAttachRequestArgs) {
114nodeDebugWrapper.requestSetup(this, args);
299b0557Patricio Beltran10 years ago115nodeDebugWrapper.attachRequest(this, request, args, new GeneralMobilePlatform(nodeDebugWrapper.mobilePlatformOptions));
40e4b177Patricio Beltran10 years ago116};
117}
c96fc63eMark Oswald10 years ago118
40e4b177Patricio Beltran10 years ago119/**
120* Intecept the "disconnectRequest" instance method of NodeDebugSession to interpret arguments
121*/
122private customizeDisconnectRequest(): void {
123const originalRequest = this.nodeDebugSession.prototype.disconnectRequest;
124const nodeDebugWrapper = this;
125
126this.nodeDebugSession.prototype.disconnectRequest = function (response: any, args: any): void {
127// First we tell the extension to stop monitoring the logcat, and then we disconnect the debugging session
128
129if (nodeDebugWrapper.mobilePlatformOptions.platform === "android") {
130nodeDebugWrapper.remoteExtension.stopMonitoringLogcat()
131.catch(reason =>
132Log.logError(`WARNING: Couldn't stop monitoring logcat: ${reason.message || reason}\n`))
133.finally(() =>
134originalRequest.call(this, response, args));
135} else {
136originalRequest.call(this, response, args);
4881129dMeena Kunnathur Balakrishnan10 years ago137}
40e4b177Patricio Beltran10 years ago138};
139}
710f8655digeff10 years ago140
40e4b177Patricio Beltran10 years ago141/**
142* Makes the required setup for request customization
143* - Enables telemetry
144* - Sets up mobilePlatformOptions, remote extension and projectRootPath
145* - Starts debug server
146* - Create global logger
147*/
148private requestSetup(debugSession: NodeDebugSession, args: any) {
149this.projectRootPath = path.resolve(args.program, "../..");
150this.remoteExtension = RemoteExtension.atProjectRootPath(this.projectRootPath);
151this.mobilePlatformOptions = {
152projectRoot: this.projectRootPath,
153platform: args.platform,
18d8ad2adigeff10 years ago154};
155
40e4b177Patricio Beltran10 years ago156// Start to send telemetry
157this.telemetryReporter.reassignTo(new ExtensionTelemetryReporter(
158this.appName, this.version, Telemetry.APPINSIGHTS_INSTRUMENTATIONKEY, this.projectRootPath));
159
160// Create a server waiting for messages to re-initialize the debug session;
774cffd7Vladimir Kotikov9 years ago161const debugServerListeningPort = this.createReinitializeServer(debugSession, args.internalDebuggerPort, args.outDir);
40e4b177Patricio Beltran10 years ago162args.args = [debugServerListeningPort.toString()];
163
164Log.SetGlobalLogger(new NodeDebugAdapterLogger(this.vscodeDebugAdapterPackage, debugSession));
165}
166
167/**
168* Runs logic needed to attach.
169* Attach should:
170* - Enable js debugging
171*/
172private attachRequest(debugSession: NodeDebugSession, request: any, args: any, mobilePlatform: any): Q.Promise<void> {
173return TelemetryHelper.generate("attach", (generator) => {
174return Q({})
175.then(() => {
176generator.step("mobilePlatform.enableJSDebuggingMode");
177if (mobilePlatform) {
178return mobilePlatform.enableJSDebuggingMode();
179} else {
180Log.logMessage("Debugger ready. Enable remote debugging in app.");
181}
182}).then(() =>
183this.originalLaunchRequest.call(debugSession, request, args))
184.catch(error =>
185this.bailOut(debugSession, error.message));
186});
187}
188
189/**
190* Creates internal debug server and returns the port that the server is hook up into.
191*/
774cffd7Vladimir Kotikov9 years ago192private createReinitializeServer(debugSession: NodeDebugSession, internalDebuggerPort: string, sourcesDir: string): number {
40e4b177Patricio Beltran10 years ago193// Create the server
194const server = http.createServer((req, res) => {
195res.statusCode = 404;
196if (req.url === "/refreshBreakpoints") {
197res.statusCode = 200;
198if (debugSession) {
199const sourceMaps = debugSession._sourceMaps;
200if (sourceMaps) {
201// Flush any cached source maps
774cffd7Vladimir Kotikov9 years ago202// Rather than cleaning internal caches we recreate
203// SourceMaps to add downloaded bundle map to cache
8f87e135Vladimir Kotikov9 years ago204const bundlePattern = path.join(sourcesDir, "*.bundle");
774cffd7Vladimir Kotikov9 years ago205const sourceMaps = new this.sourceMapsConstructor(debugSession, sourcesDir, [bundlePattern]);
206debugSession._sourceMaps = sourceMaps;
40e4b177Patricio Beltran10 years ago207}
208// Send an "initialized" event to trigger breakpoints to be re-sent
209debugSession.sendEvent(new this.vscodeDebugAdapterPackage.InitializedEvent());
210}
4881129dMeena Kunnathur Balakrishnan10 years ago211}
40e4b177Patricio Beltran10 years ago212res.end();
213});
214
215// Setup listen port and on error response
216const port = parseInt(internalDebuggerPort, 10) || 9090;
c2bf3c4fdigeff10 years ago217
40e4b177Patricio Beltran10 years ago218server.listen(port);
219server.on("error", (err: Error) => {
220TelemetryHelper.sendSimpleEvent("reinitializeServerError");
221Log.logError("Error in debug adapter server: " + err.toString());
222Log.logMessage("Breakpoints may not update. Consider restarting and specifying a different 'internalDebuggerPort' in launch.json");
223});
224
225// Return listen port
226return port;
227}
228
229/**
230* Logs error to user and finishes the debugging process.
231*/
232private bailOut(debugSession: NodeDebugSession, message: string): void {
233Log.logError(`Could not debug. ${message}`);
234debugSession.sendEvent(new this.vscodeDebugAdapterPackage.TerminatedEvent());
235process.exit(1);
236}
237
238/**
239* Parses log cat arguments to a string
240*/
241private parseLogCatArguments(userProvidedLogCatArguments: any): string {
242return Array.isArray(userProvidedLogCatArguments)
243? userProvidedLogCatArguments.join(" ") // If it's an array, we join the arguments
244: userProvidedLogCatArguments; // If not, we leave it as-is
245}
246
247/**
248* Helper method to know if a value is either null or undefined
249*/
250private isNullOrUndefined(value: any): boolean {
251return typeof value === "undefined" || value === null;
252}
774cffd7Vladimir Kotikov9 years ago253}