UNPKG

webpack-hot-client

Version:

A client for enabling, and interacting with, webpack Hot Module Replacement

152 lines (122 loc) 3.8 kB
const stringify = require('json-stringify-safe'); const strip = require('strip-ansi'); const WebSocket = require('ws'); function getServer(options) { const { host, log, port, server } = options; const wssOptions = server ? { server } : { host: host.server, port: port.server }; if (server && !server.listening) { server.listen(port.server, host.server); } const wss = new WebSocket.Server(wssOptions); onConnection(wss, options); onError(wss, options); onListening(wss, options); const broadcast = (data) => { wss.clients.forEach((client) => { if (client.readyState === WebSocket.OPEN) { client.send(data); } }); }; const ogClose = wss.close; const close = (callback) => { try { ogClose.call(wss, callback); } catch (err) { /* istanbul ignore next */ log.error(err); } }; const send = sendData.bind(null, wss, options); return Object.assign(wss, { broadcast, close, send }); } function onConnection(server, options) { const { log } = options; server.on('connection', (socket) => { log.info('WebSocket Client Connected'); socket.on('error', (err) => { /* istanbul ignore next */ if (err.errno !== 'ECONNRESET') { log.warn('client socket error', JSON.stringify(err)); } }); socket.on('message', (data) => { const message = JSON.parse(data); if (message.type === 'broadcast') { for (const client of server.clients) { if (client.readyState === WebSocket.OPEN) { client.send(stringify(message.data)); } } } }); // only send stats to newly connected clients, if no previous clients have // connected and stats has been modified by webpack if (options.stats && options.stats.toJson && server.clients.size === 1) { const jsonStats = options.stats.toJson(options.stats); /* istanbul ignore if */ if (!jsonStats) { options.log.error('Client Connection: `stats` is undefined'); } server.send(jsonStats); } }); } function onError(server, options) { const { log } = options; server.on('error', (err) => { /* istanbul ignore next */ log.error('WebSocket Server Error', err); }); } function onListening(server, options) { /* eslint-disable no-underscore-dangle, no-param-reassign */ const { host, log } = options; if (options.server && options.server.listening) { const addr = options.server.address(); server.host = addr.address; server.port = addr.port; log.info(`WebSocket Server Attached to ${addr.address}:${addr.port}`); } else { server.on('listening', () => { const { address, port } = server._server.address(); server.host = address; server.port = port; // a port.client value of 0 will be falsy, so it should pull the server port options.webSocket.port = options.port.client || port; log.info(`WebSocket Server Listening on ${host.server}:${port}`); }); } } function payload(type, data) { return stringify({ type, data }); } function sendData(server, options, stats) { const send = (type, data) => { server.broadcast(payload(type, data)); }; if (stats.errors && stats.errors.length > 0) { if (options.send.errors) { const errors = [].concat(stats.errors).map((error) => strip(error)); send('errors', { errors }); } return; } if (stats.assets && stats.assets.every((asset) => !asset.emitted)) { send('no-change'); return; } const { hash, warnings } = stats; send('hash', { hash }); if (warnings.length > 0) { if (options.send.warnings) { send('warnings', { warnings }); } } else { send('ok', { hash }); } } module.exports = { getServer, payload };