1- import { IS_DEV_MODE , READY_MESSAGE , RequestDetail } from '../common'
1+ import { READY_MESSAGE , RequestDetail } from '../common'
22import { type IncomingMessage } from 'http'
33import WebSocket from 'ws'
44import { ChildProcess , fork } from 'child_process'
55import { __dirname } from '../common'
66import { resolve as resolvePath } from 'path'
77import { RegisterOptions } from '../common'
88import fs from 'fs'
9+ import { sleep , checkMainProcessAlive } from '../utils/process'
10+ import { unlinkSafe } from '../utils/file'
11+ import { warn } from '../utils'
912
10- let hasLogError = false
13+ class ExpectError extends Error {
14+ constructor ( message : string ) {
15+ super ( message )
16+ }
17+ }
1118
1219export class MainProcess {
1320 private ws : Promise < WebSocket >
@@ -16,24 +23,34 @@ export class MainProcess {
1623
1724 constructor ( props : RegisterOptions & { key : string } ) {
1825 this . options = props
19- this . ws = new Promise < WebSocket > ( ( resolve , reject ) => {
26+ this . ws = new Promise < WebSocket > ( async ( resolve , reject ) => {
2027 const lockFilePath = resolvePath ( __dirname , `./${ props . key } ` )
2128 if ( fs . existsSync ( lockFilePath ) ) {
22- fs . watchFile ( lockFilePath , ( e ) => {
23- if ( ! fs . existsSync ( lockFilePath ) ) {
24- reject ( new Error ( 'MainProcess is already running' ) )
25- }
26- } )
27- return
29+ // 读取 lock 文件中的进程号
30+ const pid = fs . readFileSync ( lockFilePath , 'utf-8' )
31+ await sleep ( 800 )
32+
33+ // 检测该进程是否存活且 port 是否被占用
34+ const isProcessAlice = await checkMainProcessAlive ( pid , props . port ! )
35+ if ( isProcessAlice ) {
36+ warn ( `The main process with same options is already running, skip it.` )
37+ return
38+ }
39+ // 如果进程不存在:
40+ // 1. 热更新导致 process 重启
41+ // 2. 上一个进程未成功删除 lock
42+ // 都应该继续往下
43+ unlinkSafe ( lockFilePath )
2844 }
29- fs . writeFileSync ( lockFilePath , `LOCKED ` )
45+ fs . writeFileSync ( lockFilePath , `${ process . pid } ` )
3046 const socket = new WebSocket ( `ws://localhost:${ props . port } ` )
3147 socket . on ( 'open' , ( ) => {
48+ unlinkSafe ( lockFilePath )
3249 resolve ( socket )
3350 } )
3451 socket . on ( 'error' , ( ) => {
3552 this . openProcess ( ( ) => {
36- fs . unlinkSync ( lockFilePath )
53+ unlinkSafe ( lockFilePath )
3754 const socket = new WebSocket ( `ws://localhost:${ props . port } ` )
3855 socket . on ( 'open' , ( ) => {
3956 resolve ( socket )
@@ -49,14 +66,14 @@ export class MainProcess {
4966 } )
5067 } )
5168 . catch ( ( e ) => {
52- if ( ! hasLogError ) {
53- ! IS_DEV_MODE && ( hasLogError = true )
54- console . warn ( 'MainProcess Warning: ' , e )
69+ if ( e instanceof ExpectError ) {
70+ return
5571 }
72+ throw e
5673 } )
5774 }
5875
59- private openProcess ( callback ?: ( ) => void ) {
76+ private openProcess ( callback ?: ( cp : ChildProcess ) => void ) {
6077 const forkProcess = ( ) => {
6178 // fork a new process with options
6279 const cp = fork ( resolvePath ( __dirname , './fork' ) , {
@@ -67,7 +84,7 @@ export class MainProcess {
6784 } )
6885 const handleMsg = ( e : any ) => {
6986 if ( e === READY_MESSAGE ) {
70- callback && callback ( )
87+ callback && callback ( cp )
7188 cp . off ( 'message' , handleMsg )
7289 }
7390 }
@@ -81,13 +98,10 @@ export class MainProcess {
8198
8299 public async send ( data : any ) {
83100 const ws = await this . ws . catch ( ( err ) => {
84- if ( hasLogError ) {
85- // has error in main process or websocket
101+ if ( err instanceof ExpectError ) {
86102 return null
87103 }
88- ! IS_DEV_MODE && ( hasLogError = true )
89- console . warn ( 'MainProcess Websocket Error: ' , err )
90- return null
104+ throw err
91105 } )
92106 if ( ! ws ) return
93107 ws . send ( JSON . stringify ( data ) )
0 commit comments