@@ -126,31 +126,60 @@ class Server {
126
126
return path . resolve ( dir , "node_modules/.cache/webpack-dev-server" ) ;
127
127
}
128
128
129
- getCompilerConfigArray ( ) {
130
- const compilers = this . compiler . compilers
131
- ? this . compiler . compilers
132
- : [ this . compiler ] ;
129
+ getCompilerOptions ( ) {
130
+ if ( typeof this . compiler . compilers !== "undefined" ) {
131
+ if ( this . compiler . compilers . length === 1 ) {
132
+ return this . compiler . compilers [ 0 ] . options ;
133
+ }
134
+
135
+ // Configuration with the `devServer` options
136
+ const compilerWithDevServer = this . compiler . compilers . find (
137
+ ( config ) => config . options . devServer
138
+ ) ;
139
+
140
+ if ( compilerWithDevServer ) {
141
+ return compilerWithDevServer . options ;
142
+ }
143
+
144
+ // Configuration with `web` preset
145
+ const compilerWithWebPreset = this . compiler . compilers . find (
146
+ ( config ) =>
147
+ ( config . options . externalsPresets &&
148
+ config . options . externalsPresets . web ) ||
149
+ [
150
+ "web" ,
151
+ "webworker" ,
152
+ "electron-preload" ,
153
+ "electron-renderer" ,
154
+ "node-webkit" ,
155
+ // eslint-disable-next-line no-undefined
156
+ undefined ,
157
+ null ,
158
+ ] . includes ( config . options . target )
159
+ ) ;
133
160
134
- return compilers . map ( ( compiler ) => compiler . options ) ;
161
+ if ( compilerWithWebPreset ) {
162
+ return compilerWithWebPreset . options ;
163
+ }
164
+
165
+ // Fallback
166
+ return this . compiler . compilers [ 0 ] . options ;
167
+ }
168
+
169
+ return this . compiler . options ;
135
170
}
136
171
137
172
// eslint-disable-next-line class-methods-use-this
138
- async normalizeOptions ( ) {
173
+ normalizeOptions ( ) {
139
174
const { options } = this ;
140
175
141
176
if ( ! this . logger ) {
142
177
this . logger = this . compiler . getInfrastructureLogger ( "webpack-dev-server" ) ;
143
178
}
144
-
145
- // TODO: improve this to not use .find for compiler watchOptions
146
- const configArray = this . getCompilerConfigArray ( ) ;
147
- const watchOptionsConfig = configArray . find (
148
- ( config ) => config . watch !== false && config . watchOptions
149
- ) ;
150
- const watchOptions = watchOptionsConfig
151
- ? watchOptionsConfig . watchOptions
152
- : { } ;
153
-
179
+
180
+ const compilerOptions = this . getCompilerOptions ( ) ;
181
+ // TODO remove `{}` after drop webpack v4 support
182
+ const watchOptions = compilerOptions . watchOptions || { } ;
154
183
const defaultOptionsForStatic = {
155
184
directory : path . join ( process . cwd ( ) , "public" ) ,
156
185
staticOptions : { } ,
@@ -221,14 +250,9 @@ class Server {
221
250
222
251
// Respect infrastructureLogging.level
223
252
if ( typeof options . client . logging === "undefined" ) {
224
- const configWithInfrastructureLogging =
225
- configArray . find (
226
- ( config ) =>
227
- config . infrastructureLogging && config . infrastructureLogging . level
228
- ) || configArray [ 0 ] ;
229
-
230
- options . client . logging =
231
- configWithInfrastructureLogging . infrastructureLogging . level ;
253
+ options . client . logging = compilerOptions . infrastructureLogging
254
+ ? compilerOptions . infrastructureLogging . level
255
+ : "info" ;
232
256
}
233
257
}
234
258
@@ -504,13 +528,12 @@ class Server {
504
528
505
529
return level ;
506
530
} ;
507
-
508
- const configWithDevServer =
509
- configArray . find ( ( config ) => config . devServer ) || configArray [ 0 ] ;
510
-
531
+
511
532
if ( typeof proxyOptions . logLevel === "undefined" ) {
512
533
proxyOptions . logLevel = getLogLevelForProxy (
513
- configWithDevServer . infrastructureLogging . level
534
+ compilerOptions . infrastructureLogging
535
+ ? compilerOptions . infrastructureLogging . level
536
+ : "info"
514
537
) ;
515
538
}
516
539
@@ -1272,13 +1295,13 @@ class Server {
1272
1295
logStatus ( ) {
1273
1296
const colorette = require ( "colorette" ) ;
1274
1297
1275
- const getColorsOption = ( configArray ) => {
1276
- const statsOption = this . getStatsOption ( configArray ) ;
1298
+ const getColorsOption = ( compilerOptions ) => {
1299
+ let colorsEnabled ;
1277
1300
1278
- let colorsEnabled = false ;
1279
-
1280
- if ( typeof statsOption === "object" && statsOption . colors ) {
1281
- colorsEnabled = statsOption . colors ;
1301
+ if ( typeof compilerOptions . stats . colors !== "undefined" ) {
1302
+ colorsEnabled = compilerOptions . stats ;
1303
+ } else {
1304
+ colorsEnabled = colorette . options . enabled ;
1282
1305
}
1283
1306
1284
1307
return colorsEnabled ;
@@ -1300,7 +1323,7 @@ class Server {
1300
1323
return msg ;
1301
1324
} ,
1302
1325
} ;
1303
- const useColor = getColorsOption ( this . getCompilerConfigArray ( ) ) ;
1326
+ const useColor = getColorsOption ( this . getCompilerOptions ( ) ) ;
1304
1327
1305
1328
if ( this . options . ipc ) {
1306
1329
this . logger . info ( `Project is running at: "${ this . server . address ( ) } "` ) ;
@@ -1432,31 +1455,160 @@ class Server {
1432
1455
}
1433
1456
}
1434
1457
1435
- // eslint-disable-next-line class-methods-use-this
1436
- getStatsOption ( configArray ) {
1437
- const isEmptyObject = ( val ) =>
1438
- typeof val === "object" && Object . keys ( val ) . length === 0 ;
1439
-
1440
- // in webpack@4 stats will not be defined if not provided,
1441
- // but in webpack@5 it will be an empty object
1442
- const statsConfig = configArray . find (
1443
- ( configuration ) =>
1444
- typeof configuration === "object" &&
1445
- configuration . stats &&
1446
- ! isEmptyObject ( configuration . stats )
1458
+ listen ( port , hostname , fn ) {
1459
+ this . logger = this . compiler . getInfrastructureLogger ( "webpack-dev-server" ) ;
1460
+ this . normalizeOptions ( this . options ) ;
1461
+
1462
+ if ( typeof port === "function" ) {
1463
+ fn = port ;
1464
+ }
1465
+
1466
+ if (
1467
+ typeof port !== "undefined" &&
1468
+ typeof this . options . port !== "undefined" &&
1469
+ port !== this . options . port
1470
+ ) {
1471
+ this . options . port = port ;
1472
+
1473
+ this . logger . warn (
1474
+ 'The "port" specified in options is different from the port passed as an argument. Will be used from arguments.'
1475
+ ) ;
1476
+ }
1477
+
1478
+ if ( ! this . options . port ) {
1479
+ this . options . port = port ;
1480
+ }
1481
+
1482
+ if (
1483
+ typeof hostname !== "undefined" &&
1484
+ typeof this . options . host !== "undefined" &&
1485
+ hostname !== this . options . host
1486
+ ) {
1487
+ this . options . host = hostname ;
1488
+
1489
+ this . logger . warn (
1490
+ 'The "host" specified in options is different from the host passed as an argument. Will be used from arguments.'
1491
+ ) ;
1492
+ }
1493
+
1494
+ if ( ! this . options . host ) {
1495
+ this . options . host = hostname ;
1496
+ }
1497
+
1498
+ this . options . host = Server . getHostname ( this . options . host ) ;
1499
+
1500
+ const resolveFreePortOrIPC = ( ) => {
1501
+ if ( this . options . ipc ) {
1502
+ return new Promise ( ( resolve , reject ) => {
1503
+ const net = require ( "net" ) ;
1504
+ const socket = new net . Socket ( ) ;
1505
+
1506
+ socket . on ( "error" , ( error ) => {
1507
+ if ( error . code === "ECONNREFUSED" ) {
1508
+ fs . unlinkSync ( this . options . ipc ) ;
1509
+
1510
+ resolve ( this . options . ipc ) ;
1511
+
1512
+ return ;
1513
+ } else if ( error . code === "ENOENT" ) {
1514
+ resolve ( this . options . ipc ) ;
1515
+
1516
+ return ;
1517
+ }
1518
+
1519
+ reject ( error ) ;
1520
+ } ) ;
1521
+
1522
+ socket . connect ( { path : this . options . ipc } , ( ) => {
1523
+ throw new Error ( `IPC "${ this . options . ipc } " is already used` ) ;
1524
+ } ) ;
1525
+ } ) ;
1526
+ }
1527
+
1528
+ return Server . getFreePort ( this . options . port ) . then ( ( foundPort ) => {
1529
+ this . options . port = foundPort ;
1530
+ } ) ;
1531
+ } ;
1532
+
1533
+ return resolveFreePortOrIPC ( )
1534
+ . then ( ( ) => {
1535
+ this . initialize ( ) ;
1536
+
1537
+ const listenOptions = this . options . ipc
1538
+ ? { path : this . options . ipc }
1539
+ : {
1540
+ host : this . options . host ,
1541
+ port : this . options . port ,
1542
+ } ;
1543
+
1544
+ return this . server . listen ( listenOptions , ( error ) => {
1545
+ if ( this . options . ipc ) {
1546
+ // chmod 666 (rw rw rw)
1547
+ const READ_WRITE = 438 ;
1548
+
1549
+ fs . chmodSync ( this . options . ipc , READ_WRITE ) ;
1550
+ }
1551
+
1552
+ if ( this . options . webSocketServer ) {
1553
+ try {
1554
+ this . createWebSocketServer ( ) ;
1555
+ } catch ( webSocketServerError ) {
1556
+ fn . call ( this . server , webSocketServerError ) ;
1557
+
1558
+ return ;
1559
+ }
1560
+ }
1561
+
1562
+ if ( this . options . bonjour ) {
1563
+ this . runBonjour ( ) ;
1564
+ }
1565
+
1566
+ this . logStatus ( ) ;
1567
+
1568
+ if ( fn ) {
1569
+ fn . call ( this . server , error ) ;
1570
+ }
1571
+
1572
+ if ( typeof this . options . onListening === "function" ) {
1573
+ this . options . onListening ( this ) ;
1574
+ }
1575
+ } ) ;
1576
+ } )
1577
+ . catch ( ( error ) => {
1578
+ if ( fn ) {
1579
+ fn . call ( this . server , error ) ;
1580
+ }
1581
+ } ) ;
1582
+ }
1583
+
1584
+ close ( callback ) {
1585
+ if ( this . webSocketServer ) {
1586
+ this . webSocketServer . implementation . close ( ) ;
1587
+ }
1588
+
1589
+ const prom = Promise . all (
1590
+ this . staticWatchers . map ( ( watcher ) => watcher . close ( ) )
1447
1591
) ;
1592
+ this . staticWatchers = [ ] ;
1448
1593
1449
- return statsConfig ? statsConfig . stats : { } ;
1594
+ if ( this . server ) {
1595
+ this . server . kill ( ( ) => {
1596
+ // watchers must be closed before closing middleware
1597
+ prom . then ( ( ) => {
1598
+ this . middleware . close ( callback ) ;
1599
+ } ) ;
1600
+ } ) ;
1601
+ } else if ( callback ) {
1602
+ callback ( ) ;
1603
+ }
1450
1604
}
1451
1605
1452
1606
getStats ( statsObj ) {
1453
1607
const stats = Server . DEFAULT_STATS ;
1608
+ const compilerOptions = this . getCompilerOptions ( ) ;
1454
1609
1455
- const configArray = this . getCompilerConfigArray ( ) ;
1456
- const statsOption = this . getStatsOption ( configArray ) ;
1457
-
1458
- if ( typeof statsOption === "object" && statsOption . warningsFilter ) {
1459
- stats . warningsFilter = statsOption . warningsFilter ;
1610
+ if ( compilerOptions . stats && compilerOptions . stats . warningsFilter ) {
1611
+ stats . warningsFilter = compilerOptions . stats . warningsFilter ;
1460
1612
}
1461
1613
1462
1614
return statsObj . toJson ( stats ) ;
0 commit comments