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' ;
6
4
import * as extProtocol from '../common/extensionProtocol' ;
7
- import * as utils from '../common/utilities' ;
8
5
import { AndroidProject } from '../project/androidProject' ;
9
6
import { IosProject } from '../project/iosProject' ;
10
7
import { NativeScriptCli } from '../project/nativeScriptCli' ;
11
- import { IDebugResult } from '../project/project' ;
8
+
9
+ const reconnectAfterLiveSyncTimeout = 10 * 1000 ;
12
10
13
11
export function nativeScriptDebugAdapterGenerator ( iosProject : typeof IosProject ,
14
12
androidProject : typeof AndroidProject ,
15
13
nativeScriptCli : typeof NativeScriptCli ) {
16
14
return class NativeScriptDebugAdapter extends ChromeDebugAdapter {
17
- private _tnsProcess : ChildProcess ;
18
15
private _idCounter = 0 ;
19
16
private _pendingRequests : object = { } ;
17
+ private isLiveSync : boolean = false ;
18
+ private portWaitingResolve : any ;
20
19
21
20
public attach ( args : any ) : Promise < void > {
22
21
return this . processRequestAndAttach ( args ) ;
@@ -29,100 +28,94 @@ export function nativeScriptDebugAdapterGenerator(iosProject: typeof IosProject,
29
28
public disconnect ( args : any ) : void {
30
29
super . disconnect ( args ) ;
31
30
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' ) ;
37
33
}
38
34
}
39
35
36
+ public onPortReceived ( port ) {
37
+ this . portWaitingResolve && this . portWaitingResolve ( port ) ;
38
+ }
39
+
40
40
public onExtensionResponse ( response ) {
41
41
this . _pendingRequests [ response . requestId ] ( response . result ) ;
42
42
delete this . _pendingRequests [ response . requestId ] ;
43
43
}
44
44
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 ;
61
48
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 ;
65
52
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
+ } ) ;
70
57
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
+ }
74
61
75
- if ( args . request === 'launch' ) {
76
- let tnsArgs = args . tnsArgs ;
62
+ await super . terminateSession ( reason , disconnectArgs , restartRequestArgs ) ;
63
+ }
77
64
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 ( ) ;
82
67
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
+ }
85
70
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
+ } ;
92
91
}
93
92
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
+ } ) ;
97
96
}
97
+ }
98
98
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 ) ;
103
101
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
+ }
106
108
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 ( ) ) ;
114
111
}
115
112
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 ;
117
115
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 ;
122
117
123
- res ( args ) ;
124
- } ) ;
125
- } ) ;
118
+ return super . attach ( transformedArgs ) ;
126
119
}
127
120
128
121
private translateArgs ( args ) : any {
@@ -137,65 +130,6 @@ export function nativeScriptDebugAdapterGenerator(iosProject: typeof IosProject,
137
130
return args ;
138
131
}
139
132
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
-
199
133
private callRemoteMethod < T > ( service : string , method : string , ...args : any [ ] ) : Promise < T > {
200
134
const request : extProtocol . IRequest = { id : `req${ ++ this . _idCounter } ` , service, method, args } ;
201
135
0 commit comments