1
- import { IInitializeRequestArgs , ChromeDebugAdapter , IAttachRequestArgs , utils , ISetBreakpointsArgs , ISetBreakpointsResponseBody , ILaunchRequestArgs , ITelemetryPropertyCollector } from 'vscode-chrome-debug-core' ;
2
- import { NativeScriptDebugLauncher } from './nativeScriptDebugLauncher'
1
+ import { IInitializeRequestArgs , ChromeDebugAdapter , IAttachRequestArgs , ISetBreakpointsArgs , ISetBreakpointsResponseBody , ILaunchRequestArgs , ITelemetryPropertyCollector } from 'vscode-chrome-debug-core' ;
3
2
import { OutputEvent , TerminatedEvent } from 'vscode-debugadapter' ;
3
+ import * as utils from '../common/utilities' ;
4
+ import { IosProject } from '../project/iosProject' ;
5
+ import { AndroidProject } from '../project/androidProject' ;
6
+ import { ChildProcess } from 'child_process' ;
7
+ import * as fs from 'fs' ;
8
+ import * as path from 'path' ;
9
+ import { DebugResult } from '../project/project' ;
10
+ import { Services } from '../services/debugAdapterServices'
4
11
5
12
export class NativeScriptDebugAdapter extends ChromeDebugAdapter {
6
- private nativeScriptDebugLauncher : NativeScriptDebugLauncher ;
7
-
8
- public initialize ( args : any ) {
9
- this . nativeScriptDebugLauncher = new NativeScriptDebugLauncher ( ) ;
10
- this . nativeScriptDebugLauncher . on ( NativeScriptDebugLauncher . TNS_PROCESS_CRASHED_EVENT , ( reason ) => this . _session . sendEvent ( new TerminatedEvent ( ) ) ) ;
11
- this . nativeScriptDebugLauncher . on ( NativeScriptDebugLauncher . TNS_PROCESS_LOG , ( log ) => this . _session . sendEvent ( new OutputEvent ( log ) ) ) ;
12
-
13
- return super . initialize ( args ) ;
14
- }
13
+ private _tnsProcess : ChildProcess ;
15
14
16
15
public async attach ( args : any ) : Promise < void > {
17
- const attachArgs = await this . nativeScriptDebugLauncher . processRequest ( args ) ;
16
+ const attachArgs = await this . processRequest ( args ) ;
18
17
( this . pathTransformer as any ) . setTargetPlatform ( args . platform ) ;
19
18
20
19
return super . attach ( attachArgs ) ;
21
20
}
22
21
23
22
public async launch ( args : any , telemetryPropertyCollector ?: ITelemetryPropertyCollector ) : Promise < void > {
24
- const launchArgs = await this . nativeScriptDebugLauncher . processRequest ( args ) as any ;
23
+ const launchArgs = await this . processRequest ( args ) as any ;
25
24
( this . pathTransformer as any ) . setTargetPlatform ( args . platform ) ;
26
25
27
- super . launch ( args , telemetryPropertyCollector ) ;
28
-
29
26
return super . attach ( launchArgs ) ;
30
27
}
31
28
32
29
public async disconnect ( args : any ) {
33
- if ( this . nativeScriptDebugLauncher != null ) {
34
- this . nativeScriptDebugLauncher . disconnect ( ) ;
35
- this . nativeScriptDebugLauncher . removeAllListeners ( NativeScriptDebugLauncher . TNS_PROCESS_CRASHED_EVENT ) ;
36
- this . nativeScriptDebugLauncher . removeAllListeners ( NativeScriptDebugLauncher . TNS_PROCESS_LOG ) ;
37
- this . nativeScriptDebugLauncher = null ;
30
+ if ( this . _tnsProcess ) {
31
+ utils . killProcess ( this . _tnsProcess ) ;
32
+ this . _tnsProcess = null ;
38
33
}
39
34
40
35
super . disconnect ( args ) ;
41
36
}
37
+
38
+ private async processRequest ( args : any ) : Promise < any > {
39
+ args = this . translateArgs ( args ) ;
40
+ Services . appRoot = args . appRoot ;
41
+ Services . extensionClient ( ) . cleanBeforeDebug ( ) ;
42
+ const settings = await Services . extensionClient ( ) . getInitSettings ( ) ;
43
+
44
+ Services . cliPath = settings . tnsPath || Services . cliPath ;
45
+
46
+ const project = args . platform == "ios" ?
47
+ new IosProject ( args . appRoot , Services . cli ( ) ) :
48
+ new AndroidProject ( args . appRoot , Services . cli ( ) ) ;
49
+
50
+ Services . extensionClient ( ) . analyticsLaunchDebugger ( { request : args . request , platform : args . platform } ) ;
51
+
52
+ // Run CLI Command
53
+ this . log ( `[NSDebugAdapter] Using tns CLI v${ project . cli . version . version } on path '${ project . cli . path } '\n` ) ;
54
+ this . log ( '[NSDebugAdapter] Running tns command...\n' ) ;
55
+ let cliCommand : DebugResult ;
56
+
57
+ if ( args . request === "launch" ) {
58
+ let tnsArgs = args . tnsArgs ;
59
+
60
+ // For iOS the TeamID is required if there's more than one.
61
+ // Therefore if not set, show selection to the user.
62
+ if ( args . platform && args . platform . toLowerCase ( ) === 'ios' ) {
63
+ let teamId = this . getTeamId ( path . join ( Services . appRoot , 'app' ) , tnsArgs ) ;
64
+ if ( ! teamId ) {
65
+ let selectedTeam = ( await Services . extensionClient ( ) . selectTeam ( ) ) ;
66
+ if ( selectedTeam ) {
67
+ // add the selected by the user Team Id
68
+ tnsArgs = ( tnsArgs || [ ] ) . concat ( [ '--teamId' , selectedTeam . id ] ) ;
69
+ this . log ( `[NSDebugAdapter] Using iOS Team ID '${ selectedTeam . id } ', you can change this in the workspace settings.\n` ) ;
70
+ }
71
+ }
72
+ }
73
+
74
+ cliCommand = project . debug ( { stopOnEntry : args . stopOnEntry , watch : args . watch } , tnsArgs ) ;
75
+ }
76
+ else if ( args . request === "attach" ) {
77
+ cliCommand = project . attach ( args . tnsArgs ) ;
78
+ }
79
+
80
+ if ( cliCommand . tnsProcess ) {
81
+ this . _tnsProcess = cliCommand . tnsProcess ;
82
+ cliCommand . tnsProcess . stdout . on ( 'data' , data => { this . log ( data . toString ( ) ) ; } ) ;
83
+ cliCommand . tnsProcess . stderr . on ( 'data' , data => { this . log ( data . toString ( ) ) ; } ) ;
84
+
85
+ cliCommand . tnsProcess . on ( 'close' , ( code , signal ) => {
86
+ this . log ( `[NSDebugAdapter] The tns command finished its execution with code ${ code } .\n` ) ;
87
+
88
+ // Sometimes we execute "tns debug android --start" and the process finishes
89
+ // which is totally fine. If there's an error we need to Terminate the session.
90
+ if ( code !== 0 ) {
91
+ this . log ( `The tns command finished its execution with code ${ code } ` ) ;
92
+ this . _session . sendEvent ( new TerminatedEvent ( ) ) ;
93
+ }
94
+ } ) ;
95
+ }
96
+
97
+ this . log ( '[NSDebugAdapter] Watching the tns CLI output to receive a connection token\n' ) ;
98
+
99
+ return new Promise < string | number > ( ( res , rej ) => {
100
+ cliCommand . tnsOutputEventEmitter . on ( 'readyForConnection' , ( connectionToken : string | number ) => {
101
+ this . log ( `[NSDebugAdapter] Ready to attach to application on ${ connectionToken } \n` ) ;
102
+ args . port = connectionToken ;
103
+
104
+ res ( args ) ;
105
+ } ) ;
106
+ } ) ;
107
+ }
108
+
109
+ private translateArgs ( args ) : any {
110
+ if ( args . diagnosticLogging ) {
111
+ args . trace = args . diagnosticLogging ;
112
+ }
113
+
114
+ if ( args . appRoot ) {
115
+ args . webRoot = args . appRoot ;
116
+ }
117
+
118
+ return args ;
119
+ }
120
+
121
+ private log ( text : string ) : void {
122
+ this . _session . sendEvent ( new OutputEvent ( text ) ) ;
123
+ }
124
+
125
+ private getTeamId ( appRoot : string , tnsArgs ?: string [ ] ) : string {
126
+ // try to get the TeamId from the TnsArgs
127
+ if ( tnsArgs ) {
128
+ const teamIdArgIndex = tnsArgs . indexOf ( '--teamId' ) ;
129
+ if ( teamIdArgIndex > 0 && teamIdArgIndex + 1 < tnsArgs . length ) {
130
+ return tnsArgs [ teamIdArgIndex + 1 ] ;
131
+ }
132
+ }
133
+
134
+ // try to get the TeamId from the buildxcconfig or teamid file
135
+ const teamIdFromConfig = this . readTeamId ( appRoot ) ;
136
+ if ( teamIdFromConfig ) {
137
+ return teamIdFromConfig ;
138
+ }
139
+
140
+ // we should get the Teams from the machine and ask the user if they are more than 1
141
+ return null ;
142
+ }
143
+
144
+ private readXCConfig ( appRoot : string , flag : string ) : string {
145
+ let xcconfigFile = path . join ( appRoot , "App_Resources/iOS/build.xcconfig" ) ;
146
+ if ( fs . existsSync ( xcconfigFile ) ) {
147
+ let text = fs . readFileSync ( xcconfigFile , { encoding : 'utf8' } ) ;
148
+ let teamId : string ;
149
+ text . split ( / \r ? \n / ) . forEach ( ( line ) => {
150
+ line = line . replace ( / \/ ( \/ ) [ ^ \n ] * $ / , "" ) ;
151
+ if ( line . indexOf ( flag ) >= 0 ) {
152
+ teamId = line . split ( "=" ) [ 1 ] . trim ( ) ;
153
+ if ( teamId [ teamId . length - 1 ] === ';' ) {
154
+ teamId = teamId . slice ( 0 , - 1 ) ;
155
+ }
156
+ }
157
+ } ) ;
158
+ if ( teamId ) {
159
+ return teamId ;
160
+ }
161
+ }
162
+
163
+ let fileName = path . join ( appRoot , "teamid" ) ;
164
+ if ( fs . existsSync ( fileName ) ) {
165
+ return fs . readFileSync ( fileName , { encoding : 'utf8' } ) ;
166
+ }
167
+
168
+ return null ;
169
+ }
170
+
171
+ private readTeamId ( appRoot ) : string {
172
+ return this . readXCConfig ( appRoot , "DEVELOPMENT_TEAM" ) ;
173
+ }
42
174
}
0 commit comments