Skip to content

Commit 823420b

Browse files
refactor: code
1 parent 8f91c37 commit 823420b

File tree

1 file changed

+206
-54
lines changed

1 file changed

+206
-54
lines changed

lib/Server.js

Lines changed: 206 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -126,31 +126,60 @@ class Server {
126126
return path.resolve(dir, "node_modules/.cache/webpack-dev-server");
127127
}
128128

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+
);
133160

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;
135170
}
136171

137172
// eslint-disable-next-line class-methods-use-this
138-
async normalizeOptions() {
173+
normalizeOptions() {
139174
const { options } = this;
140175

141176
if (!this.logger) {
142177
this.logger = this.compiler.getInfrastructureLogger("webpack-dev-server");
143178
}
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 || {};
154183
const defaultOptionsForStatic = {
155184
directory: path.join(process.cwd(), "public"),
156185
staticOptions: {},
@@ -221,14 +250,9 @@ class Server {
221250

222251
// Respect infrastructureLogging.level
223252
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";
232256
}
233257
}
234258

@@ -504,13 +528,12 @@ class Server {
504528

505529
return level;
506530
};
507-
508-
const configWithDevServer =
509-
configArray.find((config) => config.devServer) || configArray[0];
510-
531+
511532
if (typeof proxyOptions.logLevel === "undefined") {
512533
proxyOptions.logLevel = getLogLevelForProxy(
513-
configWithDevServer.infrastructureLogging.level
534+
compilerOptions.infrastructureLogging
535+
? compilerOptions.infrastructureLogging.level
536+
: "info"
514537
);
515538
}
516539

@@ -1272,13 +1295,13 @@ class Server {
12721295
logStatus() {
12731296
const colorette = require("colorette");
12741297

1275-
const getColorsOption = (configArray) => {
1276-
const statsOption = this.getStatsOption(configArray);
1298+
const getColorsOption = (compilerOptions) => {
1299+
let colorsEnabled;
12771300

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;
12821305
}
12831306

12841307
return colorsEnabled;
@@ -1300,7 +1323,7 @@ class Server {
13001323
return msg;
13011324
},
13021325
};
1303-
const useColor = getColorsOption(this.getCompilerConfigArray());
1326+
const useColor = getColorsOption(this.getCompilerOptions());
13041327

13051328
if (this.options.ipc) {
13061329
this.logger.info(`Project is running at: "${this.server.address()}"`);
@@ -1432,31 +1455,160 @@ class Server {
14321455
}
14331456
}
14341457

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())
14471591
);
1592+
this.staticWatchers = [];
14481593

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+
}
14501604
}
14511605

14521606
getStats(statsObj) {
14531607
const stats = Server.DEFAULT_STATS;
1608+
const compilerOptions = this.getCompilerOptions();
14541609

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;
14601612
}
14611613

14621614
return statsObj.toJson(stats);

0 commit comments

Comments
 (0)