@@ -4,18 +4,26 @@ import { ip } from 'address';
4
4
5
5
const debug = debuglog ( 'detect-port' ) ;
6
6
7
- type DetectPortCallback = ( err : Error | null , port ?: number ) => void ;
7
+ export type DetectPortCallback = ( err : Error | null , port ?: number ) => void ;
8
8
9
- interface PortConfig {
9
+ export interface PortConfig {
10
10
port ?: number | string ;
11
11
hostname ?: string | undefined ;
12
12
callback ?: DetectPortCallback ;
13
13
}
14
14
15
- export default function detectPort ( port ?: number | PortConfig | string ) : Promise < number > ;
16
- export default function detectPort ( callback : DetectPortCallback ) : void ;
17
- export default function detectPort ( port : number | PortConfig | string | undefined , callback : DetectPortCallback ) : void ;
18
- export default function detectPort ( port ?: number | string | PortConfig | DetectPortCallback , callback ?: DetectPortCallback ) {
15
+ export class IPAddressNotAvailableError extends Error {
16
+ constructor ( options ?: ErrorOptions ) {
17
+ super ( 'The IP address is not available on this machine' , options ) ;
18
+ this . name = this . constructor . name ;
19
+ Error . captureStackTrace ( this , this . constructor ) ;
20
+ }
21
+ }
22
+
23
+ export function detectPort ( port ?: number | PortConfig | string ) : Promise < number > ;
24
+ export function detectPort ( callback : DetectPortCallback ) : void ;
25
+ export function detectPort ( port : number | PortConfig | string | undefined , callback : DetectPortCallback ) : void ;
26
+ export function detectPort ( port ?: number | string | PortConfig | DetectPortCallback , callback ?: DetectPortCallback ) {
19
27
let hostname : string | undefined = '' ;
20
28
21
29
if ( port && typeof port === 'object' ) {
@@ -36,99 +44,102 @@ export default function detectPort(port?: number | string | PortConfig | DetectP
36
44
}
37
45
debug ( 'detect free port between [%s, %s)' , port , maxPort ) ;
38
46
if ( typeof callback === 'function' ) {
39
- return tryListen ( port , maxPort , hostname , callback ) ;
47
+ return tryListen ( port , maxPort , hostname )
48
+ . then ( port => callback ( null , port ) )
49
+ . catch ( callback ) ;
40
50
}
41
51
// promise
42
- return new Promise ( resolve => {
43
- tryListen ( port as number , maxPort , hostname , ( _ , realPort ) => {
44
- resolve ( realPort ) ;
45
- } ) ;
46
- } ) ;
52
+ return tryListen ( port as number , maxPort , hostname ) ;
47
53
}
48
54
49
- function tryListen ( port : number , maxPort : number , hostname : string | undefined , callback : DetectPortCallback ) {
50
- function handleError ( ) {
51
- port ++ ;
52
- if ( port >= maxPort ) {
53
- debug ( 'port: %s >= maxPort: %s, give up and use random port' , port , maxPort ) ;
54
- port = 0 ;
55
- maxPort = 0 ;
56
- }
57
- tryListen ( port , maxPort , hostname , callback ) ;
55
+ async function handleError ( port : number , maxPort : number , hostname ?: string ) {
56
+ if ( port >= maxPort ) {
57
+ debug ( 'port: %s >= maxPort: %s, give up and use random port' , port , maxPort ) ;
58
+ port = 0 ;
59
+ maxPort = 0 ;
58
60
}
61
+ return await tryListen ( port , maxPort , hostname ) ;
62
+ }
59
63
64
+ async function tryListen ( port : number , maxPort : number , hostname ?: string ) : Promise < number > {
60
65
// use user hostname
61
66
if ( hostname ) {
62
- listen ( port , hostname , ( err , realPort ) => {
63
- if ( err ) {
64
- if ( ( err as any ) . code === 'EADDRNOTAVAIL' ) {
65
- return callback ( new Error ( 'The IP address is not available on this machine' ) ) ;
66
- }
67
- return handleError ( ) ;
67
+ try {
68
+ return await listen ( port , hostname ) ;
69
+ } catch ( err : any ) {
70
+ if ( err . code === 'EADDRNOTAVAIL' ) {
71
+ throw new IPAddressNotAvailableError ( { cause : err } ) ;
68
72
}
73
+ return await handleError ( ++ port , maxPort , hostname ) ;
74
+ }
75
+ }
69
76
70
- callback ( null , realPort ) ;
71
- } ) ;
72
- } else {
73
- // 1. check null
74
- listen ( port , void 0 , ( err , realPort ) => {
75
- // ignore random listening
76
- if ( port === 0 ) {
77
- return callback ( err , realPort ) ;
78
- }
77
+ // 1. check null / undefined
78
+ try {
79
+ await listen ( port ) ;
80
+ } catch ( err ) {
81
+ // ignore random listening
82
+ if ( port === 0 ) {
83
+ throw err ;
84
+ }
85
+ return await handleError ( ++ port , maxPort , hostname ) ;
86
+ }
79
87
80
- if ( err ) {
81
- return handleError ( ) ;
82
- }
88
+ // 2. check 0.0.0.0
89
+ try {
90
+ await listen ( port , '0.0.0.0' ) ;
91
+ } catch ( err ) {
92
+ return await handleError ( ++ port , maxPort , hostname ) ;
93
+ }
83
94
84
- // 2. check 0.0.0.0
85
- listen ( port , '0.0.0.0' , err => {
86
- if ( err ) {
87
- return handleError ( ) ;
88
- }
89
-
90
- // 3. check localhost
91
- listen ( port , 'localhost' , err => {
92
- // if localhost refer to the ip that is not unkonwn on the machine, you will see the error EADDRNOTAVAIL
93
- // https://stackoverflow.com/questions/10809740/listen-eaddrnotavail-error-in-node-js
94
- if ( err && ( err as any ) . code !== 'EADDRNOTAVAIL' ) {
95
- return handleError ( ) ;
96
- }
97
-
98
- // 4. check current ip
99
- listen ( port , ip ( ) , ( err , realPort ) => {
100
- if ( err ) {
101
- return handleError ( ) ;
102
- }
103
-
104
- callback ( null , realPort ) ;
105
- } ) ;
106
- } ) ;
107
- } ) ;
108
- } ) ;
95
+ // 3. check 127.0.0.1
96
+ try {
97
+ await listen ( port , '127.0.0.1' ) ;
98
+ } catch ( err ) {
99
+ return await handleError ( ++ port , maxPort , hostname ) ;
100
+ }
101
+
102
+ // 4. check localhost
103
+ try {
104
+ await listen ( port , 'localhost' ) ;
105
+ } catch ( err : any ) {
106
+ // if localhost refer to the ip that is not unknown on the machine, you will see the error EADDRNOTAVAIL
107
+ // https://stackoverflow.com/questions/10809740/listen-eaddrnotavail-error-in-node-js
108
+ if ( err . code !== 'EADDRNOTAVAIL' ) {
109
+ return await handleError ( ++ port , maxPort , hostname ) ;
110
+ }
111
+ }
112
+
113
+ // 5. check current ip
114
+ try {
115
+ return await listen ( port , ip ( ) ) ;
116
+ } catch ( err ) {
117
+ return await handleError ( ++ port , maxPort , hostname ) ;
109
118
}
110
119
}
111
120
112
- function listen ( port : number , hostname : string | undefined , callback : DetectPortCallback ) {
121
+ function listen ( port : number , hostname ? : string ) {
113
122
const server = createServer ( ) ;
114
123
115
- server . once ( 'error' , err => {
116
- debug ( 'listen %s:%s error: %s' , hostname , port , err ) ;
117
- server . close ( ) ;
124
+ return new Promise < number > ( ( resolve , reject ) => {
125
+ server . once ( 'error' , err => {
126
+ debug ( 'listen %s:%s error: %s' , hostname , port , err ) ;
127
+ server . close ( ) ;
118
128
119
- if ( ( err as any ) . code === 'ENOTFOUND' ) {
120
- debug ( 'ignore dns ENOTFOUND error, get free %s:%s' , hostname , port ) ;
121
- return callback ( null , port ) ;
122
- }
129
+ if ( ( err as any ) . code === 'ENOTFOUND' ) {
130
+ debug ( 'ignore dns ENOTFOUND error, get free %s:%s' , hostname , port ) ;
131
+ return resolve ( port ) ;
132
+ }
123
133
124
- return callback ( err ) ;
125
- } ) ;
134
+ return reject ( err ) ;
135
+ } ) ;
126
136
127
- debug ( 'try listen %d on %s' , port , hostname ) ;
128
- server . listen ( port , hostname , ( ) => {
129
- port = ( server . address ( ) as AddressInfo ) . port ;
130
- debug ( 'get free %s:%s' , hostname , port ) ;
131
- server . close ( ) ;
132
- return callback ( null , port ) ;
137
+ debug ( 'try listen %d on %s' , port , hostname ) ;
138
+ server . listen ( port , hostname , ( ) => {
139
+ port = ( server . address ( ) as AddressInfo ) . port ;
140
+ debug ( 'get free %s:%s' , hostname , port ) ;
141
+ server . close ( ) ;
142
+ return resolve ( port ) ;
143
+ } ) ;
133
144
} ) ;
134
145
}
0 commit comments