import * as BluebirdPromise from "bluebird" ; import chalk from "chalk" ; import { spawn } from "child_process" ; import { readdir , remove } from "fs-extra" ; import * as path from "path" ; import "source-map-support/register" ; import webpack , { Compiler } from "webpack" ; import { getElectronWebpackConfiguration , getPackageMetadata } from "../config" ; import { HmrServer } from "../electron-main-hmr/HmrServer" ; import { configure } from "../main" ; import { getFreePort , orNullIfFileNotExist } from "../util" ; import { DelayedFunction , getCommonEnv , logError , logProcess , logProcessErrorOutput } from "./devUtil" ; import { startRenderer } from "./WebpackDevServerManager" ; const projectDir = process . cwd ( ) ; let socketPath = null ; const debug = require ( "debug" ) ( "electron-webpack" ) ; async function getOutDir ( ) { const electronWebpackConfig = await getElectronWebpackConfiguration ( { projectDir , packageMetadata : getPackageMetadata ( projectDir ) , } ) ; return path . join ( electronWebpackConfig . commonDistDirectory ! ! , "main" ) ; } async function emptyMainOutput ( ) { const outDir = await getOutDir ( ) ; const files = await orNullIfFileNotExist ( readdir ( outDir ) ) ; if ( files == null ) { return ; } await BluebirdPromise . map ( files . filter ( it => ! it . startsWith ( "." ) && it !== "main.js" ) , it => remove ( outDir + path . sep + it ) ) ; } class DevRunner { async start ( ) { const wdsHost = "localhost" ; const wdsPort = await getFreePort ( wdsHost , 9080 ) ; const env = { ... getCommonEnv ( ) , ELECTRON_WEBPACK_WDS_HOST : wdsHost , ELECTRON_WEBPACK_WDS_PORT : wdsPort , } ; const hmrServer = new HmrServer ( ) ; await Promise . all ( [ startRenderer ( projectDir , env ) , hmrServer . listen ( ) . then ( it => { socketPath = it ; } ) , emptyMainOutput ( ) . then ( ( ) => this . startMainCompilation ( hmrServer ) ) , ] ) ; hmrServer . ipc . on ( "error" , ( error ) => { logError ( "Main" , error ) ; } ) ; const electronArgs = process . env . ELECTRON_ARGS ; const outDir = await getOutDir ( ) ; const args = electronArgs != null && electronArgs . length > 0 ? JSON . parse ( electronArgs ) : [ `--inspect=${ await getFreePort ( "127.0.0.1" , 5858 ) }` ] ; args . push ( path . join ( outDir , "/main.js" ) ) ; args . push ( ... process . argv . slice ( 3 ) ) ; startElectron ( args , env ) ; } async startMainCompilation ( hmrServer ) { const mainConfig = await configure ( "main" , { production : false , autoClean : false , forkTsCheckerLogger : { info : ( ) => { } , warn : ( message ) => { logProcess ( "Main" , message , chalk . yellow ) ; } , error : ( message ) => { logProcess ( "Main" , message , chalk . red ) ; } , } , } ) ; await new Promise ( ( resolve , reject ) => { const compiler = webpack ( mainConfig ! ! ) ; const printCompilingMessage = new DelayedFunction ( ( ) => { logProcess ( "Main" , "Compiling..." , chalk . yellow ) ; } ) ; compiler . hooks . compile . tap ( "electron-webpack-dev-runner" , ( ) => { hmrServer . beforeCompile ( ) ; printCompilingMessage . schedule ( ) ; } ) ; let watcher = compiler . watch ( { } , ( error , stats ) => { printCompilingMessage . cancel ( ) ; if ( watcher == null ) { return ; } if ( error != null ) { if ( reject == null ) { logError ( "Main" , error ) ; } else { reject ( error ) ; reject = null ; } return ; } logProcess ( "Main" , stats . toString ( { colors : true , } ) , chalk . yellow ) ; if ( resolve != null ) { resolve ( ) ; resolve = null ; return ; } hmrServer . built ( stats ) ; } ) ; require ( "async-exit-hook" ) ( ( callback ) => { debug ( `async-exit-hook: ${ callback == null }` ) ; const w = watcher ; if ( w == null ) { return ; } watcher = null ; w . close ( ( ) => callback ( ) ) ; } ) ; } ) ; } } async function main ( ) { const devRunner = new DevRunner ( ) ; await devRunner . start ( ) ; } main ( ) . catch ( error => { console . error ( error ) ; } ) ; function startElectron ( electronArgs , env ) { const electronProcess = spawn ( require ( "electron" ) . toString ( ) , electronArgs , { env : { ... env , ELECTRON_HMR_SOCKET_PATH : socketPath , } } ) ; require ( "async-exit-hook" ) ( ( ) => { electronProcess . kill ( "SIGINT" ) ; } ) ; let queuedData = null ; electronProcess . stdout . on ( "data" , data => { data = data . toString ( ) ; if ( data . trim ( ) === "[HMR] Updated modules:" ) { queuedData = data ; return ; } if ( queuedData != null ) { data = queuedData + data ; queuedData = null ; } logProcess ( "Electron" , data , chalk . blue ) ; } ) ; logProcessErrorOutput ( "Electron" , electronProcess ) ; electronProcess . on ( "close" , exitCode => { debug ( `Electron exited with exit code ${ exitCode }` ) ; if ( exitCode === 100 ) { setImmediate ( ( ) => { startElectron ( electronArgs , env ) ; } ) ; } else { ( process as any ) . emit ( "message" , "shutdown" ) ; } } ) ; }