1- import { ChildProcess } from 'child_process' ;
2- import * as fs from 'fs' ;
3- import * as path from 'path' ;
4- import { ChromeDebugAdapter , logger } from 'vscode-chrome-debug-core' ;
5- import { Event , OutputEvent , TerminatedEvent } from 'vscode-debugadapter' ;
1+ import { ChromeDebugAdapter , IRestartRequestArgs , logger } from 'vscode-chrome-debug-core' ;
2+ import { Event , TerminatedEvent } from 'vscode-debugadapter' ;
3+ import { DebugProtocol } from 'vscode-debugprotocol' ;
64import * as extProtocol from '../common/extensionProtocol' ;
7- import * as utils from '../common/utilities' ;
85import { AndroidProject } from '../project/androidProject' ;
96import { IosProject } from '../project/iosProject' ;
107import { NativeScriptCli } from '../project/nativeScriptCli' ;
11- import { IDebugResult } from '../project/project' ;
8+
9+ const reconnectAfterLiveSyncTimeout = 10 * 1000 ;
1210
1311export function nativeScriptDebugAdapterGenerator ( iosProject : typeof IosProject ,
1412 androidProject : typeof AndroidProject ,
1513 nativeScriptCli : typeof NativeScriptCli ) {
1614 return class NativeScriptDebugAdapter extends ChromeDebugAdapter {
17- private _tnsProcess : ChildProcess ;
1815 private _idCounter = 0 ;
1916 private _pendingRequests : object = { } ;
17+ private isLiveSync : boolean = false ;
18+ private portWaitingResolve : any ;
2019
2120 public attach ( args : any ) : Promise < void > {
2221 return this . processRequestAndAttach ( args ) ;
@@ -29,100 +28,94 @@ export function nativeScriptDebugAdapterGenerator(iosProject: typeof IosProject,
2928 public disconnect ( args : any ) : void {
3029 super . disconnect ( args ) ;
3130
32- if ( this . _tnsProcess ) {
33- this . _tnsProcess . stdout . removeAllListeners ( ) ;
34- this . _tnsProcess . stderr . removeAllListeners ( ) ;
35- this . _tnsProcess . removeAllListeners ( ) ;
36- utils . killProcess ( this . _tnsProcess ) ;
31+ if ( ! args . restart ) {
32+ this . callRemoteMethod ( 'buildService' , 'disconnect' ) ;
3733 }
3834 }
3935
36+ public onPortReceived ( port ) {
37+ this . portWaitingResolve && this . portWaitingResolve ( port ) ;
38+ }
39+
4040 public onExtensionResponse ( response ) {
4141 this . _pendingRequests [ response . requestId ] ( response . result ) ;
4242 delete this . _pendingRequests [ response . requestId ] ;
4343 }
4444
45- private async processRequestAndAttach ( args : any ) {
46- const transformedArgs = await this . processRequest ( args ) ;
47-
48- ( this . pathTransformer as any ) . setTargetPlatform ( args . platform ) ;
49- ( ChromeDebugAdapter as any ) . SET_BREAKPOINTS_TIMEOUT = 20000 ;
50-
51- return super . attach ( transformedArgs ) ;
52- }
53-
54- private async processRequest ( args : any ) : Promise < any > {
55- args = this . translateArgs ( args ) ;
56-
57- this . _session . sendEvent ( new Event ( extProtocol . BEFORE_DEBUG_START ) ) ;
58-
59- const tnsPath = await this . callRemoteMethod < string > ( 'workspaceConfigService' , 'tnsPath' ) ;
60- const cli = new nativeScriptCli ( tnsPath , logger ) ;
45+ protected async terminateSession ( reason : string , disconnectArgs ?: DebugProtocol . DisconnectArguments , restart ?: IRestartRequestArgs ) : Promise < void > {
46+ let restartRequestArgs ;
47+ let timeoutId ;
6148
62- const project = args . platform === 'ios' ?
63- new iosProject ( args . appRoot , cli ) :
64- new androidProject ( args . appRoot , cli ) ;
49+ if ( this . isLiveSync ) {
50+ const portProm = new Promise < any > ( ( res , rej ) => {
51+ this . portWaitingResolve = res ;
6552
66- this . callRemoteMethod ( 'analyticsService' , 'launchDebugger' , args . request , args . platform ) ;
67-
68- // Run CLI Command
69- const version = project . cli . executeGetVersion ( ) ;
53+ timeoutId = setTimeout ( ( ) => {
54+ res ( ) ;
55+ } , reconnectAfterLiveSyncTimeout ) ;
56+ } ) ;
7057
71- this . log ( `[NSDebugAdapter] Using tns CLI v ${ version } on path ' ${ project . cli . path } '\n` ) ;
72- this . log ( '[NSDebugAdapter] Running tns command...\n' ) ;
73- let cliCommand : IDebugResult ;
58+ restartRequestArgs = await portProm ;
59+ clearTimeout ( timeoutId ) ;
60+ }
7461
75- if ( args . request === 'launch' ) {
76- let tnsArgs = args . tnsArgs ;
62+ await super . terminateSession ( reason , disconnectArgs , restartRequestArgs ) ;
63+ }
7764
78- // For iOS the TeamID is required if there's more than one.
79- // Therefore if not set, show selection to the user.
80- if ( args . platform && args . platform . toLowerCase ( ) === 'ios' ) {
81- const teamId = this . getTeamId ( path . join ( args . appRoot , 'app' ) , tnsArgs ) ;
65+ protected hookConnectionEvents ( ) : void {
66+ super . hookConnectionEvents ( ) ;
8267
83- if ( ! teamId ) {
84- const selectedTeam = await this . callRemoteMethod < { id : string , name : string } > ( 'iOSTeamService' , 'selectTeam' ) ;
68+ this . chrome . Log . onEntryAdded ( ( params ) => this . onEntryAdded ( params ) ) ;
69+ }
8570
86- if ( selectedTeam ) {
87- // add the selected by the user Team Id
88- tnsArgs = ( tnsArgs || [ ] ) . concat ( [ '--teamId' , selectedTeam . id ] ) ;
89- this . log ( `[NSDebugAdapter] Using iOS Team ID '${ selectedTeam . id } ', you can change this in the workspace settings.\n` ) ;
90- }
91- }
71+ protected onEntryAdded ( params : any ) : void {
72+ if ( params && params . entry && params . entry . level ) {
73+ const message = params . entry ;
74+
75+ message . type = message . level ;
76+
77+ const that = this as any ;
78+ const script = that . getScriptByUrl && that . getScriptByUrl ( params . entry . url ) ;
79+
80+ if ( script ) {
81+ message . stack = {
82+ callFrames : [
83+ {
84+ columnNumber : 0 ,
85+ lineNumber : params . entry . lineNumber ,
86+ scriptId : script . scriptId ,
87+ url : params . entry . url ,
88+ } ,
89+ ] ,
90+ } ;
9291 }
9392
94- cliCommand = project . debug ( { stopOnEntry : args . stopOnEntry , watch : args . watch } , tnsArgs ) ;
95- } else if ( args . request === 'attach' ) {
96- cliCommand = project . attach ( args . tnsArgs ) ;
93+ this . onMessageAdded ( {
94+ message ,
95+ } ) ;
9796 }
97+ }
9898
99- if ( cliCommand . tnsProcess ) {
100- this . _tnsProcess = cliCommand . tnsProcess ;
101- cliCommand . tnsProcess . stdout . on ( 'data' , ( data ) => { this . log ( data . toString ( ) ) ; } ) ;
102- cliCommand . tnsProcess . stderr . on ( 'data' , ( data ) => { this . log ( data . toString ( ) ) ; } ) ;
99+ private async processRequestAndAttach ( args : any ) {
100+ const transformedArgs = this . translateArgs ( args ) ;
103101
104- cliCommand . tnsProcess . on ( 'close' , ( code , signal ) => {
105- this . log ( `[NSDebugAdapter] The tns command finished its execution with code ${ code } .\n` ) ;
102+ if ( args . __restart ) {
103+ transformedArgs . port = args . __restart . port ;
104+ } else {
105+ this . _session . sendEvent ( new Event ( extProtocol . BEFORE_DEBUG_START ) ) ;
106+ transformedArgs . port = await this . callRemoteMethod ( 'buildService' , 'processRequest' , transformedArgs ) ;
107+ }
106108
107- // Sometimes we execute "tns debug android --start" and the process finishes
108- // which is totally fine. If there's an error we need to Terminate the session.
109- if ( code !== 0 ) {
110- this . log ( `The tns command finished its execution with code ${ code } ` ) ;
111- this . _session . sendEvent ( new TerminatedEvent ( ) ) ;
112- }
113- } ) ;
109+ if ( ! transformedArgs . port ) {
110+ this . _session . sendEvent ( new TerminatedEvent ( ) ) ;
114111 }
115112
116- this . log ( '[NSDebugAdapter] Watching the tns CLI output to receive a connection token\n' ) ;
113+ ( this . pathTransformer as any ) . setTargetPlatform ( args . platform ) ;
114+ ( ChromeDebugAdapter as any ) . SET_BREAKPOINTS_TIMEOUT = 20000 ;
117115
118- return new Promise < string | number > ( ( res , rej ) => {
119- cliCommand . tnsOutputEventEmitter . on ( 'readyForConnection' , ( connectionToken : string | number ) => {
120- this . log ( `[NSDebugAdapter] Ready to attach to application on ${ connectionToken } \n` ) ;
121- args . port = connectionToken ;
116+ this . isLiveSync = args . watch ;
122117
123- res ( args ) ;
124- } ) ;
125- } ) ;
118+ return super . attach ( transformedArgs ) ;
126119 }
127120
128121 private translateArgs ( args ) : any {
@@ -137,65 +130,6 @@ export function nativeScriptDebugAdapterGenerator(iosProject: typeof IosProject,
137130 return args ;
138131 }
139132
140- private log ( text : string ) : void {
141- this . _session . sendEvent ( new OutputEvent ( text ) ) ;
142- }
143-
144- private getTeamId ( appRoot : string , tnsArgs ?: string [ ] ) : string {
145- // try to get the TeamId from the TnsArgs
146- if ( tnsArgs ) {
147- const teamIdArgIndex = tnsArgs . indexOf ( '--teamId' ) ;
148-
149- if ( teamIdArgIndex > 0 && teamIdArgIndex + 1 < tnsArgs . length ) {
150- return tnsArgs [ teamIdArgIndex + 1 ] ;
151- }
152- }
153-
154- // try to get the TeamId from the buildxcconfig or teamid file
155- const teamIdFromConfig = this . readTeamId ( appRoot ) ;
156-
157- if ( teamIdFromConfig ) {
158- return teamIdFromConfig ;
159- }
160-
161- // we should get the Teams from the machine and ask the user if they are more than 1
162- return null ;
163- }
164-
165- private readXCConfig ( appRoot : string , flag : string ) : string {
166- const xcconfigFile = path . join ( appRoot , 'App_Resources/iOS/build.xcconfig' ) ;
167-
168- if ( fs . existsSync ( xcconfigFile ) ) {
169- const text = fs . readFileSync ( xcconfigFile , { encoding : 'utf8' } ) ;
170- let teamId : string ;
171-
172- text . split ( / \r ? \n / ) . forEach ( ( line ) => {
173- line = line . replace ( / \/ ( \/ ) [ ^ \n ] * $ / , '' ) ;
174- if ( line . indexOf ( flag ) >= 0 ) {
175- teamId = line . split ( '=' ) [ 1 ] . trim ( ) ;
176- if ( teamId [ teamId . length - 1 ] === ';' ) {
177- teamId = teamId . slice ( 0 , - 1 ) ;
178- }
179- }
180- } ) ;
181- if ( teamId ) {
182- return teamId ;
183- }
184- }
185-
186- const fileName = path . join ( appRoot , 'teamid' ) ;
187-
188- if ( fs . existsSync ( fileName ) ) {
189- return fs . readFileSync ( fileName , { encoding : 'utf8' } ) ;
190- }
191-
192- return null ;
193- }
194-
195- private readTeamId ( appRoot ) : string {
196- return this . readXCConfig ( appRoot , 'DEVELOPMENT_TEAM' ) ;
197- }
198-
199133 private callRemoteMethod < T > ( service : string , method : string , ...args : any [ ] ) : Promise < T > {
200134 const request : extProtocol . IRequest = { id : `req${ ++ this . _idCounter } ` , service, method, args } ;
201135
0 commit comments