Skip to content

feat: add support for debug with hmr #244

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 22, 2019
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -29,7 +29,7 @@
"semver": "5.6.0",
"universal-analytics": "0.4.15",
"uuid": "3.3.2",
"vscode-chrome-debug-core": "6.7.45",
"vscode-chrome-debug-core": "6.7.46",
"vscode-debugadapter": "1.34.0"
},
"devDependencies": {
2 changes: 2 additions & 0 deletions src/debug-adapter/nativeScriptDebug.ts
Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@ import * as path from 'path';
import { chromeConnection, ChromeDebugSession } from 'vscode-chrome-debug-core';
import { NativeScriptDebugAdapter } from './nativeScriptDebugAdapter';
import { NativeScriptPathTransformer } from './nativeScriptPathTransformer';
import { NativeScriptSourceMapTransformer } from './nativeScriptSourceMapTransformer';
import { NativeScriptTargetDiscovery } from './nativeScriptTargetDiscovery';

class NSAndroidConnection extends chromeConnection.ChromeConnection {
@@ -18,4 +19,5 @@ ChromeDebugSession.run(ChromeDebugSession.getSession(
extensionName: 'nativescript-extension',
logFilePath: path.join(os.tmpdir(), 'nativescript-extension.txt'),
pathTransformer: NativeScriptPathTransformer,
sourceMapTransformer: NativeScriptSourceMapTransformer,
}));
35 changes: 33 additions & 2 deletions src/debug-adapter/nativeScriptDebugAdapter.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
import { existsSync, readFileSync } from 'fs';
import * as _ from 'lodash';
import { join } from 'path';
import { ChromeDebugAdapter, IRestartRequestArgs } from 'vscode-chrome-debug-core';
import {
ChromeDebugAdapter,
IRestartRequestArgs,
ISetBreakpointsArgs,
ISetBreakpointsResponseBody,
ITelemetryPropertyCollector,
} from 'vscode-chrome-debug-core';
import { Event, TerminatedEvent } from 'vscode-debugadapter';
import { DebugProtocol } from 'vscode-debugprotocol';
import * as extProtocol from '../common/extensionProtocol';
import { NativeScriptSourceMapTransformer } from './nativeScriptSourceMapTransformer';

const reconnectAfterLiveSyncTimeout = 10 * 1000;

@@ -14,6 +22,7 @@ export class NativeScriptDebugAdapter extends ChromeDebugAdapter {
private portWaitingResolve: any;
private isDisconnecting: boolean = false;
private isLiveSyncRestart: boolean = false;
private breakPointsCache: { [file: string]: { args: ISetBreakpointsArgs, requestSeq: number, ids: number[] } } = {};

public attach(args: any): Promise<void> {
return this.processRequestAndAttach(args);
@@ -41,6 +50,27 @@ export class NativeScriptDebugAdapter extends ChromeDebugAdapter {
super.disconnect(args);
}

public async setCachedBreakpointsForScript(script: string): Promise<void> {
const breakPointData = this.breakPointsCache[script];

if (breakPointData) {
await this.setBreakpoints(breakPointData.args, null, breakPointData.requestSeq, breakPointData.ids);
}
}

public async setBreakpoints(
args: ISetBreakpointsArgs,
telemetryPropertyCollector: ITelemetryPropertyCollector,
requestSeq: number,
ids?: number[]): Promise<ISetBreakpointsResponseBody> {

if (args && args.source && args.source.path) {
this.breakPointsCache[args.source.path] = { args: _.cloneDeep(args), requestSeq, ids };
}

return super.setBreakpoints(args, telemetryPropertyCollector, requestSeq, ids);
}

protected async terminateSession(reason: string, disconnectArgs?: DebugProtocol.DisconnectArguments, restart?: IRestartRequestArgs): Promise<void> {
let restartRequestArgs = restart;
let timeoutId;
@@ -113,6 +143,7 @@ export class NativeScriptDebugAdapter extends ChromeDebugAdapter {
const appDirPath = this.getAppDirPath(transformedArgs.webRoot);

(this.pathTransformer as any).setTransformOptions(args.platform, appDirPath, transformedArgs.webRoot);
(this.sourceMapTransformer as NativeScriptSourceMapTransformer).setDebugAdapter(this);
(ChromeDebugAdapter as any).SET_BREAKPOINTS_TIMEOUT = 20000;

this.isLiveSync = args.watch;
@@ -145,7 +176,7 @@ export class NativeScriptDebugAdapter extends ChromeDebugAdapter {
}

if (!args.sourceMapPathOverrides) {
args.sourceMapPathOverrides = { };
args.sourceMapPathOverrides = {};
}

if (!args.sourceMapPathOverrides['webpack:///*']) {
22 changes: 15 additions & 7 deletions src/debug-adapter/nativeScriptPathTransformer.ts
Original file line number Diff line number Diff line change
@@ -25,13 +25,14 @@ export class NativeScriptPathTransformer extends UrlPathTransformer {
}

const isAndroid = this.targetPlatform === 'android';
const isIOS = this.targetPlatform === 'ios';

if (_.startsWith(scriptUrl, 'mdha:')) {
scriptUrl = _.trimStart(scriptUrl, 'mdha:');
}

if (path.isAbsolute(scriptUrl) && fs.existsSync(scriptUrl)) {
return Promise.resolve(scriptUrl);
return scriptUrl;
}

const filePattern = this.filePatterns[this.targetPlatform];
@@ -56,20 +57,23 @@ export class NativeScriptPathTransformer extends UrlPathTransformer {
let platformSpecificPath = this.getPlatformSpecificPath(absolutePath);

if (platformSpecificPath) {
return Promise.resolve(platformSpecificPath);
return platformSpecificPath;
}

if (isAndroid) {
// handle files like /data/data/internal/ts_helpers.ts
absolutePath = path.resolve(path.join(this.webRoot, 'platforms', this.targetPlatform.toLowerCase(), 'app', 'src', 'main', 'assets', relativePath));
platformSpecificPath = this.getPlatformSpecificPath(absolutePath);
} else if (isIOS) {
absolutePath = path.resolve(path.join(this.webRoot, 'platforms', this.targetPlatform.toLowerCase(), this.getAppName(this.webRoot), relativePath));
}

if (platformSpecificPath) {
return Promise.resolve(platformSpecificPath);
}
platformSpecificPath = this.getPlatformSpecificPath(absolutePath);

if (platformSpecificPath) {
return platformSpecificPath;
}

return Promise.resolve(scriptUrl);
return scriptUrl;
}

private getPlatformSpecificPath(rawPath: string): string {
@@ -89,4 +93,8 @@ export class NativeScriptPathTransformer extends UrlPathTransformer {

return null;
}

private getAppName(projectDir: string): string {
return _.filter(projectDir.split(''), (c) => /[a-zA-Z0-9]/.test(c)).join('');
}
}
26 changes: 26 additions & 0 deletions src/debug-adapter/nativeScriptSourceMapTransformer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { BaseSourceMapTransformer } from 'vscode-chrome-debug-core';
import { NativeScriptDebugAdapter } from './nativeScriptDebugAdapter';

export class NativeScriptSourceMapTransformer extends BaseSourceMapTransformer {
private debugAdapter: NativeScriptDebugAdapter;

constructor(sourceHandles: any) {
super(sourceHandles);
}

public setDebugAdapter(debugAdapter: NativeScriptDebugAdapter): void {
this.debugAdapter = debugAdapter;
}

public async scriptParsed(pathToGenerated: string, sourceMapURL: string): Promise<string[]> {
const scriptParsedResult = await super.scriptParsed(pathToGenerated, sourceMapURL);

if (scriptParsedResult && scriptParsedResult.length) {
for (const script of scriptParsedResult) {
await this.debugAdapter.setCachedBreakpointsForScript(script);
}
}

return scriptParsedResult;
}
}
4 changes: 2 additions & 2 deletions src/main.ts
Original file line number Diff line number Diff line change
@@ -110,8 +110,8 @@ export function activate(context: vscode.ExtensionContext) {
const method = service[request.method];
const response = typeof method === 'function' ? service[request.method].call(service, ...request.args) : method;

if (response.then) {
response.then((result) => event.session.customRequest('onExtensionResponse', { requestId: request.id, result }),
if (response && response.then) {
response.then((result) => event.session && event.session.customRequest('onExtensionResponse', { requestId: request.id, result }),
(err: Error) => {
vscode.window.showErrorMessage(err.message);
event.session.customRequest('onExtensionResponse', { requestId: request.id, isError: true, message: err.message });
19 changes: 14 additions & 5 deletions src/tests/nativeScriptDebugAdapter.tests.ts
Original file line number Diff line number Diff line change
@@ -22,7 +22,7 @@ const defaultArgsMock: any = {
platform: 'android',
request: 'attach',
stopOnEntry: true,
tnsArgs: [ 'mockArgs'],
tnsArgs: ['mockArgs'],
watch: true,
};

@@ -37,6 +37,7 @@ describe('NativeScriptDebugAdapter', () => {
let chromeSessionMock: any;
let chromeConnectionMock: any;
let pathTransformerMock: any;
let sourceMapTransformer: any;

beforeEach(() => {
chromeSessionMock = {
@@ -61,16 +62,24 @@ describe('NativeScriptDebugAdapter', () => {
setTransformOptions: () => ({}),
};

nativeScriptDebugAdapter = new NativeScriptDebugAdapter(
{ chromeConnection: mockConstructor(chromeConnectionMock), pathTransformer: mockConstructor(pathTransformerMock) },
sourceMapTransformer = {
clearTargetContext: () => undefined,
setDebugAdapter: () => undefined,
};

nativeScriptDebugAdapter = new NativeScriptDebugAdapter({
chromeConnection: mockConstructor(chromeConnectionMock),
pathTransformer: mockConstructor(pathTransformerMock),
sourceMapTransformer: mockConstructor(sourceMapTransformer),
},
chromeSessionMock,
);

ChromeDebugAdapter.prototype.attach = () => Promise.resolve();
});

const platforms = [ 'android', 'ios' ];
const launchMethods = [ 'launch', 'attach' ];
const platforms = ['android', 'ios'];
const launchMethods = ['launch', 'attach'];

platforms.forEach((platform) => {
launchMethods.forEach((method) => {