Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: ipc #3479

Merged
merged 31 commits into from
Jun 28, 2021
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
35ef2f5
feat: ipc
alexander-akait Jun 23, 2021
a13a388
docs: update readme
alexander-akait Jun 23, 2021
90c90bd
refactor: code
alexander-akait Jun 23, 2021
b31a216
test: fix snapshots
alexander-akait Jun 23, 2021
68b19a7
test: fix
alexander-akait Jun 23, 2021
0223e4f
test: fix
alexander-akait Jun 23, 2021
d6eb45b
test: fix
alexander-akait Jun 23, 2021
4df834e
test: fix
alexander-akait Jun 23, 2021
fbd9fd3
fix: windows supports
alexander-akait Jun 23, 2021
1668629
test: fix cli
alexander-akait Jun 23, 2021
2fd5fa2
refactor: code and test
alexander-akait Jun 24, 2021
f2247af
test: fix
alexander-akait Jun 24, 2021
eb46756
test: fix
alexander-akait Jun 24, 2021
abcc980
fix: compatibility with libuv
alexander-akait Jun 24, 2021
6dd7b9a
test: debug
alexander-akait Jun 24, 2021
ab7152b
test: debug
alexander-akait Jun 24, 2021
c115054
test: debug speedup
alexander-akait Jun 24, 2021
1781b2e
test: debug speedup
alexander-akait Jun 24, 2021
ce53763
test: debug
alexander-akait Jun 24, 2021
49f50e9
test: debug
alexander-akait Jun 24, 2021
48d8761
test: debug
alexander-akait Jun 25, 2021
7aed664
test: debug
alexander-akait Jun 25, 2021
a452602
test: debug
alexander-akait Jun 25, 2021
bd97148
test: fix
alexander-akait Jun 25, 2021
4d1ed7a
test: timeout
alexander-akait Jun 25, 2021
50548e3
test: timeout
alexander-akait Jun 25, 2021
7dc85f4
test: fix
alexander-akait Jun 25, 2021
73dc4cc
test: fix
alexander-akait Jun 25, 2021
a9972bf
test: fix
alexander-akait Jun 25, 2021
9e96927
test: fix
alexander-akait Jun 25, 2021
1eac0f9
test: fix
alexander-akait Jun 25, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/nodejs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ jobs:
npm link webpack-dev-server

- name: Run tests for webpack version ${{ matrix.webpack-version }}
run: npm run test:coverage -- --ci
run: node_modules/.bin/jest test/e2e/ipc.test.js --ci

- name: Submit coverage data to codecov
uses: codecov/codecov-action@v1
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ Options:
--https-key <value> Path to an SSL key.
--https-pfx <value> Path to an SSL pfx file.
--https-cert <value> Path to an SSL certificate.
--ipc [value] Listen to a unix socket. https://webpack.js.org/configuration/dev-server/#devserveripc
--live-reload Enables reload/refresh the page(s) when file changes are detected (enabled by default).
https://webpack.js.org/configuration/dev-server/#devserverlivereload
--no-live-reload Negative 'live-reload' option.
Expand Down
23 changes: 23 additions & 0 deletions bin/cli-flags.js
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,29 @@ module.exports = {
simpleType: 'string',
multiple: false,
},
ipc: {
configs: [
{
type: 'string',
multiple: false,
description:
'Listen to a unix socket. https://webpack.js.org/configuration/dev-server/#devserveripc',
path: 'ipc',
},
{
type: 'enum',
values: [true],
multiple: false,
description:
'Listen to a unix socket. https://webpack.js.org/configuration/dev-server/#devserveripc',
path: 'ipc',
},
],
description:
'Listen to a unix socket. https://webpack.js.org/configuration/dev-server/#devserveripc',
simpleType: 'string',
multiple: false,
},
'live-reload': {
configs: [
{
Expand Down
250 changes: 155 additions & 95 deletions lib/Server.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
'use strict';

const net = require('net');
const path = require('path');
const fs = require('fs');
const url = require('url');
Expand Down Expand Up @@ -759,94 +760,105 @@ class Server {
};

const useColor = getColorsOption(getCompilerConfigArray(this.compiler));
const protocol = this.options.https ? 'https' : 'http';
const { address, port } = this.server.address();
const prettyPrintUrl = (newHostname) =>
url.format({ protocol, hostname: newHostname, port, pathname: '/' });

let server;
let localhost;
let loopbackIPv4;
let loopbackIPv6;
let networkUrlIPv4;
let networkUrlIPv6;

if (this.options.host) {
if (this.options.host === 'localhost') {
localhost = prettyPrintUrl('localhost');
} else {
let isIP;

try {
isIP = ipaddr.parse(this.options.host);
} catch (error) {
// Ignore
}
if (this.options.ipc) {
this.logger.info(`Project is running at: "${this.server.address()}"`);
} else {
const protocol = this.options.https ? 'https' : 'http';
const { address, port } = this.server.address();
const prettyPrintUrl = (newHostname) =>
url.format({ protocol, hostname: newHostname, port, pathname: '/' });

let server;
let localhost;
let loopbackIPv4;
let loopbackIPv6;
let networkUrlIPv4;
let networkUrlIPv6;

if (this.options.host) {
if (this.options.host === 'localhost') {
localhost = prettyPrintUrl('localhost');
} else {
let isIP;

if (!isIP) {
server = prettyPrintUrl(this.options.host);
try {
isIP = ipaddr.parse(this.options.host);
} catch (error) {
// Ignore
}

if (!isIP) {
server = prettyPrintUrl(this.options.host);
}
}
}
}

const parsedIP = ipaddr.parse(address);
const parsedIP = ipaddr.parse(address);

if (parsedIP.range() === 'unspecified') {
localhost = prettyPrintUrl('localhost');
if (parsedIP.range() === 'unspecified') {
localhost = prettyPrintUrl('localhost');

const networkIPv4 = internalIp.v4.sync();
const networkIPv4 = internalIp.v4.sync();

if (networkIPv4) {
networkUrlIPv4 = prettyPrintUrl(networkIPv4);
}
if (networkIPv4) {
networkUrlIPv4 = prettyPrintUrl(networkIPv4);
}

const networkIPv6 = internalIp.v6.sync();
const networkIPv6 = internalIp.v6.sync();

if (networkIPv6) {
networkUrlIPv6 = prettyPrintUrl(networkIPv6);
}
} else if (parsedIP.range() === 'loopback') {
if (parsedIP.kind() === 'ipv4') {
loopbackIPv4 = prettyPrintUrl(parsedIP.toString());
} else if (parsedIP.kind() === 'ipv6') {
loopbackIPv6 = prettyPrintUrl(parsedIP.toString());
if (networkIPv6) {
networkUrlIPv6 = prettyPrintUrl(networkIPv6);
}
} else if (parsedIP.range() === 'loopback') {
if (parsedIP.kind() === 'ipv4') {
loopbackIPv4 = prettyPrintUrl(parsedIP.toString());
} else if (parsedIP.kind() === 'ipv6') {
loopbackIPv6 = prettyPrintUrl(parsedIP.toString());
}
} else {
networkUrlIPv4 =
parsedIP.kind() === 'ipv6' && parsedIP.isIPv4MappedAddress()
? prettyPrintUrl(parsedIP.toIPv4Address().toString())
: prettyPrintUrl(address);

if (parsedIP.kind() === 'ipv6') {
networkUrlIPv6 = prettyPrintUrl(address);
}
}
} else {
networkUrlIPv4 =
parsedIP.kind() === 'ipv6' && parsedIP.isIPv4MappedAddress()
? prettyPrintUrl(parsedIP.toIPv4Address().toString())
: prettyPrintUrl(address);

if (parsedIP.kind() === 'ipv6') {
networkUrlIPv6 = prettyPrintUrl(address);
this.logger.info('Project is running at:');

if (server) {
this.logger.info(`Server: ${colors.info(useColor, server)}`);
}
}

this.logger.info('Project is running at:');
if (localhost || loopbackIPv4 || loopbackIPv6) {
const loopbacks = []
.concat(localhost ? [colors.info(useColor, localhost)] : [])
.concat(loopbackIPv4 ? [colors.info(useColor, loopbackIPv4)] : [])
.concat(loopbackIPv6 ? [colors.info(useColor, loopbackIPv6)] : []);

if (server) {
this.logger.info(`Server: ${colors.info(useColor, server)}`);
}
this.logger.info(`Loopback: ${loopbacks.join(', ')}`);
}

if (localhost || loopbackIPv4 || loopbackIPv6) {
const loopbacks = []
.concat(localhost ? [colors.info(useColor, localhost)] : [])
.concat(loopbackIPv4 ? [colors.info(useColor, loopbackIPv4)] : [])
.concat(loopbackIPv6 ? [colors.info(useColor, loopbackIPv6)] : []);
if (networkUrlIPv4) {
this.logger.info(
`On Your Network (IPv4): ${colors.info(useColor, networkUrlIPv4)}`
);
}

this.logger.info(`Loopback: ${loopbacks.join(', ')}`);
}
if (networkUrlIPv6) {
this.logger.info(
`On Your Network (IPv6): ${colors.info(useColor, networkUrlIPv6)}`
);
}

if (networkUrlIPv4) {
this.logger.info(
`On Your Network (IPv4): ${colors.info(useColor, networkUrlIPv4)}`
);
}
if (this.options.open) {
const openTarget = prettyPrintUrl(this.options.host || 'localhost');

if (networkUrlIPv6) {
this.logger.info(
`On Your Network (IPv6): ${colors.info(useColor, networkUrlIPv6)}`
);
this.openBrowser(openTarget);
}
}

if (this.options.static && this.options.static.length > 0) {
Expand Down Expand Up @@ -877,15 +889,13 @@ class Server {
`Broadcasting "${bonjourProtocol}" with subtype of "webpack" via ZeroConf DNS (Bonjour)`
);
}

if (this.options.open) {
const openTarget = prettyPrintUrl(this.options.host || 'localhost');

this.openBrowser(openTarget);
}
}

listen(port, hostname, fn) {
if (typeof port === 'function') {
fn = port;
}

if (
typeof port !== 'undefined' &&
typeof this.options.port !== 'undefined' &&
Expand Down Expand Up @@ -920,35 +930,85 @@ class Server {

this.options.host = Server.getHostname(this.options.host);

return Server.getFreePort(this.options.port)
.then((foundPort) => {
const resolveFreePortOrIPC = () => {
if (this.options.ipc) {
return new Promise((resolve, reject) => {
const socket = new net.Socket();

socket.on('error', (error) => {
// eslint-disable-next-line no-console
console.log('TEST ERROR', error);

if (error.code === 'ECONNREFUSED') {
fs.unlinkSync(this.options.ipc);

resolve(this.options.ipc);

return;
} else if (error.code === 'ENOENT') {
resolve(this.options.ipc);

return;
}

reject(error);
});

socket.connect({ path: this.options.ipc }, () => {
throw new Error(`IPC "${this.options.ipc}" is already used`);
});
});
}

return Server.getFreePort(this.options.port).then((foundPort) => {
this.options.port = foundPort;
});
};

return resolveFreePortOrIPC()
.then(() => {
this.initialize();

return this.server.listen(
this.options.port,
this.options.host,
(error) => {
if (Boolean(this.options.hot) || this.options.liveReload) {
this.createWebSocketServer();
}
const listenOptions = this.options.ipc
? { path: this.options.ipc }
: {
host: this.options.host,
port: this.options.port,
};

// eslint-disable-next-line no-console
console.log(listenOptions);

return this.server.listen(listenOptions, (error) => {
// eslint-disable-next-line no-console
console.log('LISTEN');
if (this.options.ipc) {
// chmod 666 (rw rw rw)
const READ_WRITE = 438;

// eslint-disable-next-line no-console
console.log('CHANGE CHMOD');
fs.chmodSync(this.options.ipc, READ_WRITE);
}

if (this.options.bonjour) {
this.runBonjour();
}
if (Boolean(this.options.hot) || this.options.liveReload) {
this.createWebSocketServer();
}

this.logStatus();
if (this.options.bonjour) {
this.runBonjour();
}

if (fn) {
fn.call(this.server, error);
}
this.logStatus();

if (typeof this.options.onListening === 'function') {
this.options.onListening(this);
}
if (fn) {
fn.call(this.server, error);
}
);

if (typeof this.options.onListening === 'function') {
this.options.onListening(this);
}
});
})
.catch((error) => {
if (fn) {
Expand Down
17 changes: 16 additions & 1 deletion lib/options.json
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,19 @@
],
"description": "Enables Hot Module Replacement. https://webpack.js.org/configuration/dev-server/#devserverhot"
},

"IPC": {
"anyOf": [
{
"type": "string",
"minLength": 1
},
{
"type": "boolean",
"enum": [true]
}
],
"description": "Listen to a unix socket. https://webpack.js.org/configuration/dev-server/#devserveripc"
},
"LiveReload": {
"type": "boolean",
"description": "Enables reload/refresh the page(s) when file changes are detected (enabled by default). https://webpack.js.org/configuration/dev-server/#devserverlivereload"
Expand Down Expand Up @@ -692,6 +704,9 @@
"https": {
"$ref": "#/definitions/HTTPS"
},
"ipc": {
"$ref": "#/definitions/IPC"
},
"liveReload": {
"$ref": "#/definitions/LiveReload"
},
Expand Down