1
1
import * as fs from 'fs-extra' ;
2
2
import * as path from 'path' ;
3
+ import semver from 'semver' ;
3
4
import debug from 'debug' ;
4
5
import extract from 'extract-zip' ;
5
6
import { EventEmitter } from 'events' ;
@@ -37,6 +38,11 @@ export interface Mirrors {
37
38
electronNightlyMirror : string ;
38
39
}
39
40
41
+ export interface ElectronBinary {
42
+ path : string ;
43
+ alreadyExtracted : boolean ; // to check if it's kept as zipped or not
44
+ }
45
+
40
46
interface InstallerParams {
41
47
progressCallback : ( progress : ProgressObject ) => void ;
42
48
mirror : Mirrors ;
@@ -124,7 +130,23 @@ export class Installer extends EventEmitter {
124
130
try {
125
131
for ( const file of fs . readdirSync ( this . paths . electronDownloads ) ) {
126
132
const match = reg . exec ( file ) ;
127
- if ( match ) this . setState ( match [ 1 ] , InstallState . downloaded ) ;
133
+ if ( match ) {
134
+ this . setState ( match [ 1 ] , InstallState . downloaded ) ;
135
+ } else {
136
+ // Case when the download path already has the unzipped electron version
137
+ const versionFile = path . join (
138
+ this . paths . electronDownloads ,
139
+ file ,
140
+ 'version' ,
141
+ ) ;
142
+
143
+ if ( fs . existsSync ( versionFile ) ) {
144
+ const version = fs . readFileSync ( versionFile , 'utf8' ) ;
145
+ if ( semver . valid ( version ) ) {
146
+ this . setState ( version , InstallState . downloaded ) ;
147
+ }
148
+ }
149
+ }
128
150
}
129
151
} catch {
130
152
// no download directory yet
@@ -174,7 +196,11 @@ export class Installer extends EventEmitter {
174
196
this . paths . electronDownloads ,
175
197
getZipName ( version ) ,
176
198
) ;
199
+ // Or, maybe the version was already installed and kept in file system
200
+ const preInstalledPath = path . join ( this . paths . electronDownloads , version ) ;
201
+
177
202
const isZipDeleted = await rerunner ( zipPath , binaryCleaner ) ;
203
+ const isPathDeleted = await rerunner ( preInstalledPath , binaryCleaner ) ;
178
204
179
205
// maybe uninstall it
180
206
if ( this . installedVersion === version ) {
@@ -187,7 +213,7 @@ export class Installer extends EventEmitter {
187
213
isBinaryDeleted = true ;
188
214
}
189
215
190
- if ( isZipDeleted && isBinaryDeleted ) {
216
+ if ( ( isZipDeleted || isPathDeleted ) && isBinaryDeleted ) {
191
217
this . setState ( version , InstallState . missing ) ;
192
218
} else {
193
219
// Ideally the execution shouldn't reach this point
@@ -235,12 +261,23 @@ export class Installer extends EventEmitter {
235
261
private async ensureDownloadedImpl (
236
262
version : string ,
237
263
opts ?: Partial < InstallerParams > ,
238
- ) : Promise < string > {
264
+ ) : Promise < ElectronBinary > {
239
265
const d = debug ( `fiddle-core:Installer:${ version } :ensureDownloadedImpl` ) ;
240
266
const { electronDownloads } = this . paths ;
241
267
const zipFile = path . join ( electronDownloads , getZipName ( version ) ) ;
242
268
243
269
const state = this . state ( version ) ;
270
+
271
+ if ( state === InstallState . downloaded ) {
272
+ const preInstalledPath = path . join ( electronDownloads , version ) ;
273
+ if ( ! fs . existsSync ( zipFile ) && fs . existsSync ( preInstalledPath ) ) {
274
+ return {
275
+ path : preInstalledPath ,
276
+ alreadyExtracted : true ,
277
+ } ;
278
+ }
279
+ }
280
+
244
281
if ( state === InstallState . missing ) {
245
282
d ( `"${ zipFile } " does not exist; downloading now` ) ;
246
283
this . setState ( version , InstallState . downloading ) ;
@@ -253,16 +290,19 @@ export class Installer extends EventEmitter {
253
290
d ( `"${ zipFile } " exists; no need to download` ) ;
254
291
}
255
292
256
- return zipFile ;
293
+ return {
294
+ path : zipFile ,
295
+ alreadyExtracted : false ,
296
+ } ;
257
297
}
258
298
259
299
/** map of version string to currently-running active Promise */
260
- private downloading = new Map < string , Promise < string > > ( ) ;
300
+ private downloading = new Map < string , Promise < ElectronBinary > > ( ) ;
261
301
262
302
public async ensureDownloaded (
263
303
version : string ,
264
304
opts ?: Partial < InstallerParams > ,
265
- ) : Promise < string > {
305
+ ) : Promise < ElectronBinary > {
266
306
const { downloading : promises } = this ;
267
307
let promise = promises . get ( version ) ;
268
308
if ( promise ) return promise ;
@@ -297,24 +337,38 @@ export class Installer extends EventEmitter {
297
337
if ( installedVersion === version ) {
298
338
d ( `already installed` ) ;
299
339
} else {
300
- const zipFile = await this . ensureDownloaded ( version , opts ) ;
301
- this . setState ( version , InstallState . installing ) ;
302
- d ( `installing from "${ zipFile } "` ) ;
303
- await fs . emptyDir ( electronInstall ) ;
304
- // FIXME(anyone) is there a less awful way to wrangle asar
305
- // @ts -ignore: yes, I know noAsar isn't defined in process
306
- const { noAsar } = process ;
307
- try {
308
- // @ts -ignore: yes, I know noAsar isn't defined in process
309
- process . noAsar = true ;
310
- await extract ( zipFile , { dir : electronInstall } ) ;
311
- } finally {
312
- // @ts -ignore: yes, I know noAsar isn't defined in process
313
- process . noAsar = noAsar ; // eslint-disable-line
340
+ const { path : zipFile , alreadyExtracted } = await this . ensureDownloaded (
341
+ version ,
342
+ opts ,
343
+ ) ;
344
+
345
+ // An unzipped version already exists at `electronDownload` path
346
+ if ( alreadyExtracted ) {
347
+ await this . installVersionImpl ( version , zipFile , ( ) => {
348
+ // Simply copy over the files from preinstalled version to `electronInstall`
349
+ // @ts -ignore
350
+ const { noAsar } = process ;
351
+ // @ts -ignore
352
+ process . noAsar = true ;
353
+ fs . copySync ( zipFile , electronInstall ) ;
354
+ // @ts -ignore
355
+ process . noAsar = noAsar ; // eslint-disable-line
356
+ } ) ;
357
+ } else {
358
+ await this . installVersionImpl ( version , zipFile , async ( ) => {
359
+ // FIXME(anyone) is there a less awful way to wrangle asar
360
+ // @ts -ignore: yes, I know noAsar isn't defined in process
361
+ const { noAsar } = process ;
362
+ try {
363
+ // @ts -ignore: yes, I know noAsar isn't defined in process
364
+ process . noAsar = true ;
365
+ await extract ( zipFile , { dir : electronInstall } ) ;
366
+ } finally {
367
+ // @ts -ignore: yes, I know noAsar isn't defined in process
368
+ process . noAsar = noAsar ; // eslint-disable-line
369
+ }
370
+ } ) ;
314
371
}
315
- if ( installedVersion )
316
- this . setState ( installedVersion , InstallState . downloaded ) ;
317
- this . setState ( version , InstallState . installed ) ;
318
372
}
319
373
320
374
this . installing . delete ( version ) ;
@@ -323,4 +377,30 @@ export class Installer extends EventEmitter {
323
377
d ( inspect ( { electronExec, version } ) ) ;
324
378
return electronExec ;
325
379
}
380
+
381
+ private async installVersionImpl (
382
+ version : string ,
383
+ zipFile : string ,
384
+ installCallback : ( ) => Promise < void > | void ,
385
+ ) : Promise < void > {
386
+ const {
387
+ paths : { electronInstall } ,
388
+ installedVersion,
389
+ } = this ;
390
+ const d = debug ( `fiddle-core:Installer:${ version } :install` ) ;
391
+
392
+ this . setState ( version , InstallState . installing ) ;
393
+ d ( `installing from "${ zipFile } "` ) ;
394
+ await fs . emptyDir ( electronInstall ) ;
395
+
396
+ // Call the user defined callback which unzips/copies files content
397
+ if ( installCallback ) {
398
+ await installCallback ( ) ;
399
+ }
400
+
401
+ if ( installedVersion ) {
402
+ this . setState ( installedVersion , InstallState . downloaded ) ;
403
+ }
404
+ this . setState ( version , InstallState . installed ) ;
405
+ }
326
406
}
0 commit comments