Skip to content

Commit 608737f

Browse files
Debug improvements (#3094)
* Debug improvements Includes but is not limited to: * `enableDebugging` - each promise is now resolved with an object * added docs for `debuggerAttached` event * added `debuggerDetached` event and docs for it * PR Fixes
1 parent 9afb251 commit 608737f

File tree

9 files changed

+91
-40
lines changed

9 files changed

+91
-40
lines changed

PublicAPI.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -839,6 +839,24 @@ tns.liveSyncService.on("userInteractionNeeded", data => {
839839
});
840840
```
841841
842+
* debuggerAttached - raised whenever CLI attaches the backend debugging socket and a frontend debugging client may be attached. The event is raised with an object containing the device's identifier:
843+
844+
Example:
845+
```JavaScript
846+
tns.liveSyncService.on("debuggerAttached", debugInfo => {
847+
console.log(`Backend client connected, frontend client may be connected at ${debugInfo.url}`);
848+
});
849+
```
850+
851+
* debuggerDetached - raised whenever CLI detaches the backend debugging socket. The event is raised with an object of the `IDebugInformation` type:
852+
853+
Example:
854+
```JavaScript
855+
tns.liveSyncService.on("debuggerDetached", debugInfo => {
856+
console.log(`Detached debugger for device with id ${debugInfo.deviceIdentifier}`);
857+
});
858+
```
859+
842860
## How to add a new method to Public API
843861
CLI is designed as command line tool and when it is used as a library, it does not give you access to all of the methods. This is mainly implementation detail. Most of the CLI's code is created to work in command line, not as a library, so before adding method to public API, most probably it will require some modification.
844862
For example the `$options` injected module contains information about all `--` options passed on the terminal. When the CLI is used as a library, the options are not populated. Before adding method to public API, make sure its implementation does not rely on `$options`.

lib/commands/debug.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ export class DebugPlatformCommand implements ICommand {
3131
debugData.deviceIdentifier = selectedDeviceForDebug.deviceInfo.identifier;
3232

3333
if (this.$options.start) {
34-
return this.$liveSyncService.printDebugInformation(await this.$debugService.debug(debugData, debugOptions));
34+
await this.$liveSyncService.printDebugInformation(await this.$debugService.debug(debugData, debugOptions));
35+
return;
3536
}
3637

3738
await this.$devicesService.detectCurrentlyAttachedDevices({ shouldReturnImmediateResult: false, platform: this.platform });

lib/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ export const BUILD_OUTPUT_EVENT_NAME = "buildOutput";
8787
export const CONNECTION_ERROR_EVENT_NAME = "connectionError";
8888
export const USER_INTERACTION_NEEDED_EVENT_NAME = "userInteractionNeeded";
8989
export const DEBUGGER_ATTACHED_EVENT_NAME = "debuggerAttached";
90+
export const DEBUGGER_DETACHED_EVENT_NAME = "debuggerDetached";
9091
export const VERSION_STRING = "version";
9192
export const INSPECTOR_CACHE_DIRNAME = "ios-inspector";
9293
export const POST_INSTALL_COMMAND_NAME = "post-install-cli";

lib/declarations.d.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -372,7 +372,15 @@ interface ICreateProjectOptions extends INpmInstallConfigurationOptionsBase {
372372
pathToTemplate?: string;
373373
}
374374

375-
interface IOptions extends ICommonOptions, IBundle, IPlatformTemplate, IEmulator, IClean, IProvision, ITeamIdentifier, IAndroidReleaseOptions, INpmInstallConfigurationOptions {
375+
interface IDebugInformation extends IPort {
376+
url: string;
377+
}
378+
379+
interface IPort {
380+
port: Number;
381+
}
382+
383+
interface IOptions extends ICommonOptions, IBundle, IPlatformTemplate, IEmulator, IClean, IProvision, ITeamIdentifier, IAndroidReleaseOptions, INpmInstallConfigurationOptions, IPort {
376384
all: boolean;
377385
client: boolean;
378386
compileSdk: number;
@@ -386,7 +394,6 @@ interface IOptions extends ICommonOptions, IBundle, IPlatformTemplate, IEmulator
386394
tsc: boolean;
387395
ng: boolean;
388396
androidTypings: boolean;
389-
port: Number;
390397
production: boolean; //npm flag
391398
sdk: string;
392399
syncAllFiles: boolean;

lib/definitions/debug.d.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,9 +105,9 @@ interface IDebugServiceBase extends NodeJS.EventEmitter {
105105
* Starts debug operation based on the specified debug data.
106106
* @param {IDebugData} debugData Describes information for device and application that will be debugged.
107107
* @param {IDebugOptions} debugOptions Describe possible options to modify the behaivor of the debug operation, for example stop on the first line.
108-
* @returns {Promise<T>} Array of URLs that can be used for debugging or a string representing a single url that can be used for debugging.
108+
* @returns {Promise<IDebugInformation>} Full url and port where the frontend client can be connected.
109109
*/
110-
debug(debugData: IDebugData, debugOptions: IDebugOptions): Promise<string>;
110+
debug(debugData: IDebugData, debugOptions: IDebugOptions): Promise<IDebugInformation>;
111111
}
112112

113113
interface IDebugService extends IDebugServiceBase {
@@ -122,7 +122,7 @@ interface IDebugService extends IDebugServiceBase {
122122
/**
123123
* Describes actions required for debugging on specific platform (Android or iOS).
124124
*/
125-
interface IPlatformDebugService extends IDebugServiceBase, IPlatform {
125+
interface IPlatformDebugService extends IPlatform, NodeJS.EventEmitter {
126126
/**
127127
* Starts debug operation.
128128
* @param {IDebugData} debugData Describes information for device and application that will be debugged.
@@ -136,4 +136,12 @@ interface IPlatformDebugService extends IDebugServiceBase, IPlatform {
136136
* @returns {Promise<void>}
137137
*/
138138
debugStop(): Promise<void>;
139+
140+
/**
141+
* Starts debug operation based on the specified debug data.
142+
* @param {IDebugData} debugData Describes information for device and application that will be debugged.
143+
* @param {IDebugOptions} debugOptions Describe possible options to modify the behaivor of the debug operation, for example stop on the first line.
144+
* @returns {Promise<string>} Full url where the frontend client may be connected.
145+
*/
146+
debug(debugData: IDebugData, debugOptions: IDebugOptions): Promise<string>;
139147
}

lib/definitions/livesync.d.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -206,18 +206,18 @@ interface ILiveSyncService {
206206
interface IDebugLiveSyncService extends ILiveSyncService {
207207
/**
208208
* Prints debug information.
209-
* @param {string[]} information Array of information to be printed. Note that false-like values will be stripped from the array.
210-
* @returns {void}
209+
* @param {IDebugInformation} debugInformation Information to be printed.
210+
* @returns {IDebugInformation} Full url and port where the frontend client can be connected.
211211
*/
212-
printDebugInformation(information: string): void;
212+
printDebugInformation(debugInformation: IDebugInformation): IDebugInformation;
213213

214214
/**
215215
* Enables debugging for the specified devices
216216
* @param {IEnableDebuggingDeviceOptions[]} deviceOpts Settings used for enabling debugging for each device.
217217
* @param {IDebuggingAdditionalOptions} enableDebuggingOptions Settings used for enabling debugging.
218-
* @returns {Promise<void>[]} Array of promises for each device.
218+
* @returns {Promise<IDebugInformation>[]} Array of promises for each device.
219219
*/
220-
enableDebugging(deviceOpts: IEnableDebuggingDeviceOptions[], enableDebuggingOptions: IDebuggingAdditionalOptions): Promise<void>[];
220+
enableDebugging(deviceOpts: IEnableDebuggingDeviceOptions[], enableDebuggingOptions: IDebuggingAdditionalOptions): Promise<IDebugInformation>[];
221221

222222
/**
223223
* Disables debugging for the specified devices
@@ -230,9 +230,9 @@ interface IDebugLiveSyncService extends ILiveSyncService {
230230
/**
231231
* Attaches a debugger to the specified device.
232232
* @param {IAttachDebuggerOptions} settings Settings used for controling the attaching process.
233-
* @returns {Promise<void>}
233+
* @returns {Promise<IDebugInformation>} Full url and port where the frontend client can be connected.
234234
*/
235-
attachDebugger(settings: IAttachDebuggerOptions): Promise<void>;
235+
attachDebugger(settings: IAttachDebuggerOptions): Promise<IDebugInformation>;
236236
}
237237

238238
/**

lib/services/debug-service.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { platform } from "os";
2+
import { parse } from "url";
23
import { EventEmitter } from "events";
34
import { CONNECTION_ERROR_EVENT_NAME, DebugCommandErrors } from "../constants";
45
import { CONNECTED_STATUS } from "../common/constants";
@@ -14,7 +15,7 @@ export class DebugService extends EventEmitter implements IDebugService {
1415
this._platformDebugServices = {};
1516
}
1617

17-
public async debug(debugData: IDebugData, options: IDebugOptions): Promise<string> {
18+
public async debug(debugData: IDebugData, options: IDebugOptions): Promise<IDebugInformation> {
1819
const device = this.$devicesService.getDeviceByIdentifier(debugData.deviceIdentifier);
1920

2021
if (!device) {
@@ -57,7 +58,7 @@ export class DebugService extends EventEmitter implements IDebugService {
5758
result = await debugService.debug(debugData, debugOptions);
5859
}
5960

60-
return result;
61+
return this.getDebugInformation(result);
6162
}
6263

6364
public debugStop(deviceIdentifier: string): Promise<void> {
@@ -92,6 +93,16 @@ export class DebugService extends EventEmitter implements IDebugService {
9293
connectionErrorHandler = connectionErrorHandler.bind(this);
9394
platformDebugService.on(CONNECTION_ERROR_EVENT_NAME, connectionErrorHandler);
9495
}
96+
97+
private getDebugInformation(fullUrl: string): IDebugInformation {
98+
const parseQueryString = true;
99+
const wsQueryParam = parse(fullUrl, parseQueryString).query.ws;
100+
const hostPortSplit = wsQueryParam && wsQueryParam.split(":");
101+
return {
102+
url: fullUrl,
103+
port: hostPortSplit && +hostPortSplit[1]
104+
};
105+
}
95106
}
96107

97108
$injector.register("debugService", DebugService);

lib/services/livesync/livesync-service.ts

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
import * as path from "path";
22
import * as choki from "chokidar";
3-
import { parse } from "url";
43
import { EOL } from "os";
54
import { EventEmitter } from "events";
65
import { hook } from "../../common/helpers";
7-
import { APP_FOLDER_NAME, PACKAGE_JSON_FILE_NAME, LiveSyncTrackActionNames, USER_INTERACTION_NEEDED_EVENT_NAME, DEBUGGER_ATTACHED_EVENT_NAME } from "../../constants";
6+
import { APP_FOLDER_NAME, PACKAGE_JSON_FILE_NAME, LiveSyncTrackActionNames, USER_INTERACTION_NEEDED_EVENT_NAME, DEBUGGER_ATTACHED_EVENT_NAME, DEBUGGER_DETACHED_EVENT_NAME } from "../../constants";
87
import { FileExtensions, DeviceTypes } from "../../common/constants";
98
const deviceDescriptorPrimaryKey = "identifier";
109

@@ -101,7 +100,7 @@ export class LiveSyncService extends EventEmitter implements IDebugLiveSyncServi
101100
return currentDescriptors || [];
102101
}
103102

104-
private async refreshApplication(projectData: IProjectData, liveSyncResultInfo: ILiveSyncResultInfo, debugOpts?: IDebugOptions, outputPath?: string): Promise<void> {
103+
private async refreshApplication(projectData: IProjectData, liveSyncResultInfo: ILiveSyncResultInfo, debugOpts?: IDebugOptions, outputPath?: string): Promise<void | IDebugInformation> {
105104
const deviceDescriptor = this.getDeviceDescriptor(liveSyncResultInfo.deviceAppData.device.deviceInfo.identifier, projectData.projectDir);
106105

107106
return deviceDescriptor && deviceDescriptor.debugggingEnabled ?
@@ -138,13 +137,14 @@ export class LiveSyncService extends EventEmitter implements IDebugLiveSyncServi
138137
this.$logger.info(`Successfully synced application ${liveSyncResultInfo.deviceAppData.appIdentifier} on device ${liveSyncResultInfo.deviceAppData.device.deviceInfo.identifier}.`);
139138
}
140139

141-
private async refreshApplicationWithDebug(projectData: IProjectData, liveSyncResultInfo: ILiveSyncResultInfo, debugOptions: IDebugOptions, outputPath?: string): Promise<void> {
140+
private async refreshApplicationWithDebug(projectData: IProjectData, liveSyncResultInfo: ILiveSyncResultInfo, debugOptions: IDebugOptions, outputPath?: string): Promise<IDebugInformation> {
142141
await this.$platformService.trackProjectType(projectData);
143142

144143
const deviceAppData = liveSyncResultInfo.deviceAppData;
145144

146145
const deviceIdentifier = liveSyncResultInfo.deviceAppData.device.deviceInfo.identifier;
147146
await this.$debugService.debugStop(deviceIdentifier);
147+
this.emit(DEBUGGER_DETACHED_EVENT_NAME, { deviceIdentifier });
148148

149149
const applicationId = deviceAppData.appIdentifier;
150150
const attachDebuggerOptions: IAttachDebuggerOptions = {
@@ -181,7 +181,7 @@ export class LiveSyncService extends EventEmitter implements IDebugLiveSyncServi
181181
return this.enableDebuggingCoreWithoutWaitingCurrentAction(deviceOption, { projectDir: projectData.projectDir });
182182
}
183183

184-
public async attachDebugger(settings: IAttachDebuggerOptions): Promise<void> {
184+
public async attachDebugger(settings: IAttachDebuggerOptions): Promise<IDebugInformation> {
185185
// Default values
186186
if (settings.debugOptions) {
187187
settings.debugOptions.chrome = settings.debugOptions.chrome === undefined ? true : settings.debugOptions.chrome;
@@ -208,22 +208,19 @@ export class LiveSyncService extends EventEmitter implements IDebugLiveSyncServi
208208
};
209209
debugData.pathToAppPackage = this.$platformService.lastOutputPath(settings.platform, buildConfig, projectData, settings.outputPath);
210210

211-
this.printDebugInformation(await this.$debugService.debug(debugData, settings.debugOptions));
211+
return this.printDebugInformation(await this.$debugService.debug(debugData, settings.debugOptions));
212212
}
213213

214-
public printDebugInformation(information: string): void {
215-
if (!!information) {
216-
const wsQueryParam = parse(information).query.ws;
217-
const hostPortSplit = wsQueryParam && wsQueryParam.split(":");
218-
this.emit(DEBUGGER_ATTACHED_EVENT_NAME, {
219-
url: information,
220-
port: hostPortSplit && hostPortSplit[1]
221-
});
222-
this.$logger.info(`To start debugging, open the following URL in Chrome:${EOL}${information}${EOL}`.cyan);
214+
public printDebugInformation(debugInformation: IDebugInformation): IDebugInformation {
215+
if (!!debugInformation.url) {
216+
this.emit(DEBUGGER_ATTACHED_EVENT_NAME, debugInformation);
217+
this.$logger.info(`To start debugging, open the following URL in Chrome:${EOL}${debugInformation.url}${EOL}`.cyan);
223218
}
219+
220+
return debugInformation;
224221
}
225222

226-
public enableDebugging(deviceOpts: IEnableDebuggingDeviceOptions[], debuggingAdditionalOptions: IDebuggingAdditionalOptions): Promise<void>[] {
223+
public enableDebugging(deviceOpts: IEnableDebuggingDeviceOptions[], debuggingAdditionalOptions: IDebuggingAdditionalOptions): Promise<IDebugInformation>[] {
227224
return _.map(deviceOpts, d => this.enableDebuggingCore(d, debuggingAdditionalOptions));
228225
}
229226

@@ -233,7 +230,7 @@ export class LiveSyncService extends EventEmitter implements IDebugLiveSyncServi
233230
return _.find(deviceDescriptors, d => d.identifier === deviceIdentifier);
234231
}
235232

236-
private async enableDebuggingCoreWithoutWaitingCurrentAction(deviceOption: IEnableDebuggingDeviceOptions, debuggingAdditionalOptions: IDebuggingAdditionalOptions): Promise<void> {
233+
private async enableDebuggingCoreWithoutWaitingCurrentAction(deviceOption: IEnableDebuggingDeviceOptions, debuggingAdditionalOptions: IDebuggingAdditionalOptions): Promise<IDebugInformation> {
237234
const currentDeviceDescriptor = this.getDeviceDescriptor(deviceOption.deviceIdentifier, debuggingAdditionalOptions.projectDir);
238235
if (!currentDeviceDescriptor) {
239236
this.$errors.failWithoutHelp(`Couldn't enable debugging for ${deviceOption.deviceIdentifier}`);
@@ -250,16 +247,19 @@ export class LiveSyncService extends EventEmitter implements IDebugLiveSyncServi
250247
debugOptions: deviceOption.debugOptions
251248
};
252249

250+
let debugInformation: IDebugInformation;
253251
try {
254-
await this.attachDebugger(attachDebuggerOptions);
252+
debugInformation = await this.attachDebugger(attachDebuggerOptions);
255253
} catch (err) {
256254
this.$logger.trace("Couldn't attach debugger, will modify options and try again.", err);
257255
attachDebuggerOptions.debugOptions.start = false;
258-
await this.attachDebugger(attachDebuggerOptions);
256+
debugInformation = await this.attachDebugger(attachDebuggerOptions);
259257
}
258+
259+
return debugInformation;
260260
}
261261

262-
private async enableDebuggingCore(deviceOption: IEnableDebuggingDeviceOptions, debuggingAdditionalOptions: IDebuggingAdditionalOptions): Promise<void> {
262+
private async enableDebuggingCore(deviceOption: IEnableDebuggingDeviceOptions, debuggingAdditionalOptions: IDebuggingAdditionalOptions): Promise<IDebugInformation> {
263263
const liveSyncProcessInfo: ILiveSyncProcessInfo = this.liveSyncProcessesInfo[debuggingAdditionalOptions.projectDir];
264264
if (liveSyncProcessInfo && liveSyncProcessInfo.currentSyncAction) {
265265
await liveSyncProcessInfo.currentSyncAction;
@@ -290,7 +290,8 @@ export class LiveSyncService extends EventEmitter implements IDebugLiveSyncServi
290290
this.$errors.failWithoutHelp(`Couldn't disable debugging for ${deviceOption.deviceIdentifier}. Could not find device.`);
291291
}
292292

293-
return this.$debugService.debugStop(currentDevice.deviceInfo.identifier);
293+
await this.$debugService.debugStop(currentDevice.deviceInfo.identifier);
294+
this.emit(DEBUGGER_DETACHED_EVENT_NAME, { deviceIdentifier: currentDeviceDescriptor.identifier });
294295
}
295296

296297
@hook("liveSync")

test/services/debug-service.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ import { EventEmitter } from "events";
66
import * as constants from "../../lib/common/constants";
77
import { CONNECTION_ERROR_EVENT_NAME, DebugCommandErrors } from "../../lib/constants";
88

9-
const fakeChromeDebugUrl = "fakeChromeDebugUrl";
9+
const fakeChromeDebugPort = 123;
10+
const fakeChromeDebugUrl = `fakeChromeDebugUrl?experiments=true&ws=localhost:${fakeChromeDebugPort}`;
1011
class PlatformDebugService extends EventEmitter /* implements IPlatformDebugService */ {
1112
public async debug(debugData: IDebugData, debugOptions: IDebugOptions): Promise<string> {
1213
return fakeChromeDebugUrl;
@@ -202,7 +203,7 @@ describe("debugService", () => {
202203
});
203204
});
204205

205-
describe("returns chrome url returned by platform specific debug service", () => {
206+
describe("returns chrome url along with port returned by platform specific debug service", () => {
206207
_.each(["android", "iOS"], platform => {
207208
it(`for ${platform} device`, async () => {
208209
const testData = getDefaultTestData();
@@ -212,9 +213,12 @@ describe("debugService", () => {
212213
const debugService = testInjector.resolve<IDebugServiceBase>(DebugService);
213214

214215
const debugData = getDebugData();
215-
const url = await debugService.debug(debugData, null);
216+
const debugInfo = await debugService.debug(debugData, null);
216217

217-
assert.deepEqual(url, fakeChromeDebugUrl);
218+
assert.deepEqual(debugInfo, {
219+
url: fakeChromeDebugUrl,
220+
port: fakeChromeDebugPort
221+
});
218222
});
219223
});
220224
});

0 commit comments

Comments
 (0)