@@ -53,70 +53,61 @@ export abstract class Emulator {
53
53
return Promise . resolve ( ) ;
54
54
}
55
55
56
+ const { name : tempDir } = tmp . dirSync ( { unsafeCleanup : true } ) ;
57
+ const filepath = path . resolve ( tempDir , this . binaryName ) ;
56
58
return new Promise < void > ( ( resolve , reject ) => {
57
- tmp . dir ( ( err : Error | null , dir : string ) => {
58
- if ( err ) reject ( err ) ;
59
- console . log ( `Created temporary directory at [${ dir } ].` ) ;
60
- const filepath : string = path . resolve ( dir , this . binaryName ) ;
61
- const buf : any [ ] = [ ] ;
62
- console . log ( `Downloading emulator from [${ this . binaryUrl } ] ...` ) ;
63
- // Map the DOM's fetch Reader to node's streaming file system
64
- // operations. We will need to access class members `binaryPath` and `copyToCache` after the
65
- // download completes. It's a compilation error to pass `this` into the named function
66
- // `readChunk`, so the download operation is wrapped in a promise that we wait upon.
67
- console . log ( process . memoryUsage ( ) . heapTotal ) ;
68
- const downloadPromise = new Promise < void > (
69
- ( downloadComplete , downloadFailed ) => {
70
- fetch ( this . binaryUrl )
71
- . then ( resp => {
72
- if ( resp . status !== 200 || resp . body === null ) {
73
- console . log ( 'Download of emulator failed: ' , resp . statusText ) ;
74
- downloadFailed ( ) ;
75
- } else {
76
- const reader = resp . body . getReader ( ) ;
77
- reader . read ( ) . then ( function readChunk ( { done, value } ) : any {
78
- if ( done ) {
79
- console . log ( 'done download. buffer length:' , buf . length ) ;
80
- downloadComplete ( ) ;
81
- } else {
82
- buf . push ( ...value ) ;
83
- return reader . read ( ) . then ( readChunk ) ;
84
- }
85
- } ) ;
59
+ // We want access to `this.binaryPath` after the download is finished, but we can't since in
60
+ // `readChunk`, `this` is not inherited from the parent (since it's a named function expression).
61
+ // To work around this, we wrap the fetch in a promise, then once it's resolved we can access `this` in the callback arrow function.
62
+ const downloadPromise = new Promise < Uint8Array > (
63
+ ( downloadComplete , downloadFailed ) => {
64
+ fetch ( this . binaryUrl )
65
+ . then ( resp => {
66
+ if ( ! resp . ok || resp . body === null ) {
67
+ return downloadFailed (
68
+ `Failed to download emulator: [${ resp . status } ] ${ resp . statusText } `
69
+ ) ;
70
+ }
71
+
72
+ const buf = new Uint8Array ( 2 ** 25 ) ; // 32Mb
73
+ let cur = 0 ;
74
+ const reader = resp . body . getReader ( ) ;
75
+ reader . read ( ) . then ( function readChunk ( { done, value } ) : any {
76
+ if ( done ) {
77
+ return downloadComplete ( buf ) ;
86
78
}
87
- } )
88
- . catch ( e => {
89
- console . log ( `Download of emulator failed: ${ e } ` ) ;
90
- downloadFailed ( ) ;
79
+
80
+ if ( ! value ) {
81
+ return downloadFailed (
82
+ 'Did not receive chunk in response body'
83
+ ) ;
84
+ }
85
+
86
+ buf . set ( value , cur ) ;
87
+ cur += value . length ;
88
+ return reader . read ( ) . then ( readChunk ) ;
91
89
} ) ;
92
- }
93
- ) ;
90
+ } )
91
+ . catch ( err => downloadFailed ( err ) ) ;
92
+ }
93
+ ) ;
94
94
95
- downloadPromise . then (
96
- ( ) => {
97
- console . log ( 'Download complete' ) ;
98
- // Change emulator binary file permission to 'rwxr-xr-x'.
99
- // The execute permission is required for it to be able to start
100
- // with 'java -jar'.
101
- fs . writeFileSync ( filepath , new Uint8Array ( buf ) ) ;
102
-
103
- fs . chmod ( filepath , 0o755 , err => {
104
- if ( err ) {
105
- reject ( err ) ;
106
- }
95
+ downloadPromise . then ( buf => {
96
+ fs . writeFileSync ( filepath , buf ) ;
97
+ fs . chmod ( filepath , 0o755 , err => {
98
+ if ( err ) {
99
+ return reject ( err ) ;
100
+ }
107
101
108
- console . log ( `Changed emulator file permissions to 'rwxr-xr-x'.` ) ;
109
- this . binaryPath = filepath ;
110
- if ( this . copyToCache ( ) ) {
111
- console . log ( `Cached emulator at ${ this . cacheBinaryPath } ` ) ;
112
- }
113
- resolve ( ) ;
114
- } ) ;
115
- } ,
116
- ( ) => {
117
- reject ( ) ;
102
+ console . log ( `Changed emulator file permissions to 'rwxr-xr-x'.` ) ;
103
+ // Since we are now in an arrow function, `this` is inherited from the `download()` method, so it is the Emulator object
104
+ this . binaryPath = filepath ;
105
+ if ( this . copyToCache ( ) ) {
106
+ console . log ( `Cached emulator at ${ this . cacheBinaryPath } ` ) ;
118
107
}
119
- ) ;
108
+
109
+ resolve ( ) ;
110
+ } ) ;
120
111
} ) ;
121
112
} ) ;
122
113
}
@@ -128,14 +119,11 @@ export abstract class Emulator {
128
119
}
129
120
let promise : ChildProcessPromise < SpawnPromiseResult > ;
130
121
if ( this . isDataConnect ) {
131
- promise = spawn (
132
- this . binaryPath ,
133
- [
134
- 'dev' ,
135
- '--local_connection_string' ,
136
- "'postgresql://postgres:secretpassword@localhost:5432/postgres?sslmode=disable'"
137
- ]
138
- ) ;
122
+ promise = spawn ( this . binaryPath , [
123
+ 'dev' ,
124
+ '--local_connection_string' ,
125
+ "'postgresql://postgres:secretpassword@localhost:5432/postgres?sslmode=disable'"
126
+ ] ) ;
139
127
} else {
140
128
promise = spawn (
141
129
'java' ,
0 commit comments