1
- import { DeviceLiveSyncServiceBase } from "./device-livesync-service-base" ;
2
1
import * as helpers from "../../common/helpers" ;
3
2
import * as net from "net" ;
3
+ import Future = require( "fibers/future" ) ;
4
4
5
5
let currentPageReloadId = 0 ;
6
6
7
- class IOSLiveSyncService extends DeviceLiveSyncServiceBase < Mobile . IiOSDevice > implements IDeviceLiveSyncService {
7
+ class IOSLiveSyncService implements IDeviceLiveSyncService {
8
8
private static BACKEND_PORT = 18181 ;
9
9
private socket : net . Socket ;
10
+ private device : Mobile . IiOSDevice ;
10
11
11
12
constructor ( _device : Mobile . IDevice ,
12
13
private $iOSSocketRequestExecutor : IiOSSocketRequestExecutor ,
@@ -17,8 +18,9 @@ class IOSLiveSyncService extends DeviceLiveSyncServiceBase<Mobile.IiOSDevice> im
17
18
private $options : IOptions ,
18
19
private $iOSDebugService : IDebugService ,
19
20
private $childProcess : IChildProcess ,
20
- $liveSyncProvider : ILiveSyncProvider ) {
21
- super ( _device , $liveSyncProvider ) ;
21
+ private $fs : IFileSystem ,
22
+ private $liveSyncProvider : ILiveSyncProvider ) {
23
+ this . device = < Mobile . IiOSDevice > ( _device ) ;
22
24
}
23
25
24
26
public get debugService ( ) : IDebugService {
@@ -31,76 +33,155 @@ class IOSLiveSyncService extends DeviceLiveSyncServiceBase<Mobile.IiOSDevice> im
31
33
} ) . future < boolean > ( ) ( ) ;
32
34
}
33
35
36
+ private setupSocketIfNeeded ( ) : IFuture < boolean > {
37
+ return ( ( ) => {
38
+ if ( this . socket ) {
39
+ return true ;
40
+ }
41
+
42
+ let enableDebuggerMessage = `{ "method":"Debugger.enable","id":${ ++ currentPageReloadId } }` ;
43
+ if ( this . device . isEmulator ) {
44
+ this . $iOSEmulatorServices . postDarwinNotification ( this . $iOSNotification . attachRequest ) . wait ( ) ;
45
+ try {
46
+ this . socket = helpers . connectEventuallyUntilTimeout ( ( ) => net . connect ( IOSLiveSyncService . BACKEND_PORT ) , 5000 ) . wait ( ) ;
47
+ } catch ( e ) {
48
+ this . $logger . warn ( e ) ;
49
+
50
+ return false ;
51
+ }
52
+ } else {
53
+ let timeout = 9000 ;
54
+ this . $iOSSocketRequestExecutor . executeAttachRequest ( this . device , timeout ) . wait ( ) ;
55
+ this . socket = this . device . connectToPort ( IOSLiveSyncService . BACKEND_PORT ) ;
56
+ }
57
+
58
+ this . attachEventHandlers ( ) ;
59
+ this . sendMessage ( enableDebuggerMessage ) . wait ( ) ;
60
+
61
+ return true ;
62
+ } ) . future < boolean > ( ) ( ) ;
63
+ }
64
+
34
65
public removeFiles ( appIdentifier : string , localToDevicePaths : Mobile . ILocalToDevicePathData [ ] ) : IFuture < void > {
35
66
return ( ( ) => {
36
67
_ . each ( localToDevicePaths , localToDevicePathData => this . device . fileSystem . deleteFile ( localToDevicePathData . getDevicePath ( ) , appIdentifier ) ) ;
37
68
} ) . future < void > ( ) ( ) ;
38
69
}
39
70
40
- protected restartApplication ( deviceAppData : Mobile . IDeviceAppData ) : IFuture < void > {
71
+ public refreshApplication ( deviceAppData : Mobile . IDeviceAppData , localToDevicePaths : Mobile . ILocalToDevicePathData [ ] , forceExecuteFullSync : boolean ) : IFuture < void > {
72
+ return ( ( ) => {
73
+ if ( forceExecuteFullSync ) {
74
+ this . restartApplication ( deviceAppData ) . wait ( ) ;
75
+ return ;
76
+ }
77
+ let scriptFiles = _ . filter ( localToDevicePaths , localToDevicePath => _ . endsWith ( localToDevicePath . getDevicePath ( ) , ".js" ) ) ;
78
+ let otherFiles = _ . difference ( localToDevicePaths , scriptFiles ) ;
79
+ let shouldRestart = _ . some ( otherFiles , ( localToDevicePath : Mobile . ILocalToDevicePathData ) => ! this . $liveSyncProvider . canExecuteFastSync ( localToDevicePath . getLocalPath ( ) , deviceAppData . platform ) ) ;
80
+
81
+ if ( shouldRestart ) {
82
+ this . restartApplication ( deviceAppData ) . wait ( ) ;
83
+
84
+ return ;
85
+ }
86
+
87
+ if ( ! this . $options . liveEdit && scriptFiles . length ) {
88
+ this . restartApplication ( deviceAppData ) . wait ( ) ;
89
+
90
+ return ;
91
+ }
92
+
93
+ if ( this . setupSocketIfNeeded ( ) . wait ( ) ) {
94
+ this . liveEdit ( scriptFiles ) ;
95
+ this . reloadPage ( deviceAppData , otherFiles ) . wait ( ) ;
96
+ }
97
+ } ) . future < void > ( ) ( ) ;
98
+ }
99
+
100
+ private restartApplication ( deviceAppData : Mobile . IDeviceAppData ) : IFuture < void > {
41
101
let projectData : IProjectData = this . $injector . resolve ( "projectData" ) ;
42
102
return this . device . applicationManager . restartApplication ( deviceAppData . appIdentifier , projectData . projectName ) ;
43
103
}
44
104
45
- protected reloadPage ( deviceAppData : Mobile . IDeviceAppData ) : IFuture < void > {
105
+ private reloadPage ( deviceAppData : Mobile . IDeviceAppData , localToDevicePaths : Mobile . ILocalToDevicePathData [ ] ) : IFuture < void > {
46
106
return ( ( ) => {
47
- let timeout = 9000 ;
48
- if ( this . device . isEmulator ) {
49
- if ( ! this . socket ) {
50
- helpers . connectEventually ( ( ) => net . connect ( IOSLiveSyncService . BACKEND_PORT ) , ( socket : net . Socket ) => {
51
- this . socket = socket ;
52
- this . attachEventHandlersIfNecessary ( ) ;
53
- this . sendPageReloadMessage ( ) ;
54
- } ) ;
55
- } else {
56
- this . sendPageReloadMessage ( ) ;
57
- }
58
- this . $iOSEmulatorServices . postDarwinNotification ( this . $iOSNotification . attachRequest ) . wait ( ) ;
59
- } else {
60
- if ( ! this . socket ) {
61
- this . $iOSSocketRequestExecutor . executeAttachRequest ( this . device , timeout ) . wait ( ) ;
62
- this . socket = this . device . connectToPort ( IOSLiveSyncService . BACKEND_PORT ) ;
63
- this . attachEventHandlersIfNecessary ( ) ;
64
- }
65
- this . sendPageReloadMessage ( ) ;
107
+ if ( localToDevicePaths . length ) {
108
+ let message = JSON . stringify ( {
109
+ method : "Page.reload" ,
110
+ params : {
111
+ ignoreCache : false
112
+ } ,
113
+ id : ++ currentPageReloadId
114
+ } ) ;
115
+
116
+ this . sendMessage ( message ) . wait ( ) ;
66
117
}
67
118
} ) . future < void > ( ) ( ) ;
68
119
}
69
120
70
- private attachEventHandlersIfNecessary ( ) : void {
71
- if ( this . $options . watch ) {
72
- this . attachProcessExitHandlers ( ) ;
73
- this . attachSocketCloseEvent ( ) ;
74
- }
121
+ private liveEdit ( localToDevicePaths : Mobile . ILocalToDevicePathData [ ] ) {
122
+ return ( ( ) => {
123
+ _ . each ( localToDevicePaths , localToDevicePath => {
124
+ let content = this . $fs . readText ( localToDevicePath . getLocalPath ( ) ) . wait ( ) ;
125
+ let message = JSON . stringify ( {
126
+ method : "Debugger.setScriptSource" ,
127
+ params : {
128
+ scriptUrl : localToDevicePath . getDevicePath ( ) ,
129
+ scriptSource : content
130
+ } ,
131
+ id : ++ currentPageReloadId
132
+ } ) ;
133
+
134
+ this . sendMessage ( message ) . wait ( ) ;
135
+ } ) ;
136
+ } ) . future < void > ( ) ( ) ;
75
137
}
76
138
77
- private attachSocketCloseEvent ( ) : void {
139
+ private attachEventHandlers ( ) : void {
140
+ this . attachProcessExitHandlers ( ) ;
141
+
78
142
this . socket . on ( "close" , ( hadError : boolean ) => {
79
143
this . $logger . trace ( `Socket closed, hadError is ${ hadError } .` ) ;
80
144
this . socket = null ;
81
145
} ) ;
82
- }
83
146
84
- private sendPageReloadMessage ( ) : void {
85
- try {
86
- this . sendPageReloadMessageCore ( ) ;
87
- this . socket . on ( "data" , ( data : NodeBuffer | string ) => {
88
- this . $logger . trace ( `Socket sent data: ${ data . toString ( ) } ` ) ;
89
- this . destroySocketIfNecessary ( ) ;
90
- } ) ;
91
- } catch ( err ) {
92
- this . $logger . trace ( "Error while sending page reload:" , err ) ;
93
- this . destroySocketIfNecessary ( ) ;
94
- }
147
+ this . socket . on ( "error" , ( error : any ) => {
148
+ this . $logger . trace ( `Socket error received: ${ error } ` ) ;
149
+ } ) ;
150
+
151
+ this . socket . on ( "data" , ( data : NodeBuffer | string ) => {
152
+ this . $logger . trace ( `Socket sent data: ${ data . toString ( ) } ` ) ;
153
+ } ) ;
95
154
}
96
155
97
- private sendPageReloadMessageCore ( ) : void {
98
- let message = `{ "method":"Page.reload","params":{"ignoreCache":false},"id":${ ++ currentPageReloadId } }` ;
99
- let length = Buffer . byteLength ( message , "utf16le" ) ;
100
- let payload = new Buffer ( length + 4 ) ;
101
- payload . writeInt32BE ( length , 0 ) ;
102
- payload . write ( message , 4 , length , "utf16le" ) ;
103
- this . socket . write ( payload ) ;
156
+ private sendMessage ( message : string ) : IFuture < void > {
157
+ return ( ( ) => {
158
+ let socketWriteFuture = new Future < void > ( ) ;
159
+ try {
160
+ let length = Buffer . byteLength ( message , "utf16le" ) ;
161
+ let payload = new Buffer ( length + 4 ) ;
162
+ payload . writeInt32BE ( length , 0 ) ;
163
+ payload . write ( message , 4 , length , "utf16le" ) ;
164
+
165
+ this . socket . once ( "error" , ( error : Error ) => {
166
+ if ( ! socketWriteFuture . isResolved ( ) ) {
167
+ socketWriteFuture . throw ( error ) ;
168
+ }
169
+ } ) ;
170
+
171
+ this . socket . write ( payload , "utf16le" , ( ) => {
172
+ this . socket . removeAllListeners ( "error" ) ;
173
+
174
+ if ( ! socketWriteFuture . isResolved ( ) ) {
175
+ socketWriteFuture . return ( ) ;
176
+ }
177
+ } ) ;
178
+
179
+ socketWriteFuture . wait ( ) ;
180
+ } catch ( error ) {
181
+ this . $logger . trace ( "Error while sending message:" , error ) ;
182
+ this . destroySocket ( ) ;
183
+ }
184
+ } ) . future < void > ( ) ( ) ;
104
185
}
105
186
106
187
private attachProcessExitHandlers ( ) : void {
@@ -118,12 +199,6 @@ class IOSLiveSyncService extends DeviceLiveSyncServiceBase<Mobile.IiOSDevice> im
118
199
} ) ;
119
200
}
120
201
121
- private destroySocketIfNecessary ( ) : void {
122
- if ( ! this . $options . watch ) {
123
- this . destroySocket ( ) ;
124
- }
125
- }
126
-
127
202
private destroySocket ( ) : void {
128
203
if ( this . socket ) {
129
204
this . socket . destroy ( ) ;
0 commit comments