import * as fs from 'fs-extra' ; import * as path from 'path' ; import extract from 'extract-zip' ; import { RunnableVersion , Version , VersionSource , VersionState , } from '../interfaces' ; import { USER_DATA_PATH } from './constants' ; import { download as electronDownload } from '@electron/get' ; const downloading = new Map ( ) ; const unzipping = new Set ( ) ; param { Version } version returns { VersionState } export function getVersionState ( ver ) { const { localPath , version } = ver ; if ( downloading . has ( version ) ) { return VersionState . downloading ; } if ( unzipping . has ( version ) ) { return VersionState . unzipping ; } const dir = localPath || getDownloadPath ( version ) ; const exec = path . join ( dir , execSubpath ( ) ) ; return fs . existsSync ( exec ) ? VersionState . ready : VersionState . unknown ; } param { RunnableVersion } ver returns { Promise < void > } export function setupBinary ( ver ) { const { version } = ver ; if ( ver . source !== VersionSource . remote ) { return Promise . resolve ( ) ; } let pending = downloading . get ( version ) ; if ( ! pending ) { pending = downloadBinary ( ver ) ; downloading . set ( version , pending ) ; } return pending ; } async function downloadBinary ( ver ) { const { version } = ver ; ver . state = VersionState . downloading ; console . log ( `Binary: Downloading Electron ${ version }` ) ; let zipPath ; try { zipPath = await download ( ver ) ; } catch ( error ) { console . warn ( `Binary: Failure to download ${ version }` , error ) ; ver . state = VersionState . unknown ; downloading . delete ( version ) ; return ; } const extractPath = getDownloadPath ( version ) ; console . log ( `Binary: Unpacking ${ version } to ${ extractPath }` ) ; try { unzipping . add ( version ) ; downloading . delete ( version ) ; ver . state = VersionState . unzipping ; await fs . mkdirp ( extractPath ) ; await fs . emptyDir ( extractPath ) ; await unzip ( zipPath , extractPath ) ; console . log ( `Binary: Unzipped ${ version }` ) ; ver . state = VersionState . ready ; } catch ( error ) { console . warn ( `Binary: Failure while unzipping ${ version }` , error ) ; ver . state = VersionState . unknown ; downloading . delete ( version ) ; } finally { unzipping . delete ( version ) ; } } param { RunnableVersion } ver returns { Promise < void > } export async function removeBinary ( ver ) { const { version } = ver ; let isDeleted = false ; const rerunner = async ( func , counter = 1 ) => { try { await func ( ) ; } catch ( error ) { console . warn ( `Binary Manager: failed to run ${ func . name } for ${ version }, but failed` , error ) ; if ( counter < 4 ) { console . log ( `Binary Manager: Trying again to run ${ func . name }` ) ; await rerunner ( func , counter + 1 ) ; } } } ; const binaryCleaner = async ( ) => { if ( getIsDownloaded ( version ) ) { process . noAsar = true ; await fs . remove ( getDownloadPath ( version ) ) ; process . noAsar = false ; isDeleted = true ; } } ; const typeDefsCleaner = async ( ) => { window . ElectronFiddle . app . electronTypes . uncache ( ver ) ; } ; await rerunner ( binaryCleaner ) ; if ( isDeleted ) { ver . state = VersionState . unknown ; await rerunner ( typeDefsCleaner ) ; } } export function getIsDownloaded ( version , dir ? ) { const expectedPath = getElectronBinaryPath ( version , dir ) ; return fs . existsSync ( expectedPath ) ; } function execSubpath ( ) { const { platform } = process ; switch ( platform ) { case 'darwin' : return 'Electron.app/Contents/MacOS/Electron' ; case 'linux' : case 'freebsd' : return 'electron' ; case 'win32' : return 'electron.exe' ; default : throw new Error ( `Electron builds are not available for ${ platform }` ) ; } } param { string } version param { string } dir returns { string } export function getElectronBinaryPath ( version , dir = getDownloadPath ( version ) ) { return path . join ( dir , execSubpath ( ) ) ; } param { RunnableVersion } ver returns { Promise < string > } async function download ( ver ) { const { version } = ver ; const getProgressCallback = ( progress ) => { const percent = Math . round ( progress . percent * 100 ) / 100 ; if ( ver . downloadProgress !== percent ) { ver . downloadProgress = percent ; console . info ( `Binary: Version ${ version } download progress: ${ percent }` ) ; } } ; const zipFilePath = await electronDownload ( version , { downloadOptions : { quiet : true , getProgressCallback , timeout : { lookup : 15000 , connect : 15000 , response : 15000 , socket : 15000 , } , } , } ) ; return zipFilePath ; } param { string } version returns { string } function getDownloadPath ( version ) { return path . join ( USER_DATA_PATH , 'electron-bin' , version ) ; } param { string } zipPath param { string } dir returns { Promise < void > } async function unzip ( zipPath , dir ) { process . noAsar = true ; try { await extract ( zipPath , { dir } ) ; console . log ( `Binary: Unpacked!` ) ; } finally { process . noAsar = false ; } } interface Progress { percent : number ; transferred : number ; total : number ; }