UNPKG

2.56 kBJavaScriptView Raw
1const http = require('http');
2const https = require('https');
3const WebSocket = require('ws');
4const prettyError = require('./utils/prettyError');
5const generateCertificate = require('./utils/generateCertificate');
6const getCertificate = require('./utils/getCertificate');
7const logger = require('./Logger');
8
9class HMRServer {
10 async start(options = {}) {
11 await new Promise(async resolve => {
12 if (!options.https) {
13 this.server = http.createServer();
14 } else if (typeof options.https === 'boolean') {
15 this.server = https.createServer(generateCertificate(options));
16 } else {
17 this.server = https.createServer(await getCertificate(options.https));
18 }
19
20 this.wss = new WebSocket.Server({server: this.server});
21 this.server.listen(options.hmrPort, resolve);
22 });
23
24 this.wss.on('connection', ws => {
25 ws.onerror = this.handleSocketError;
26 if (this.unresolvedError) {
27 ws.send(JSON.stringify(this.unresolvedError));
28 }
29 });
30
31 this.wss.on('error', this.handleSocketError);
32
33 return this.wss._server.address().port;
34 }
35
36 stop() {
37 this.wss.close();
38 this.server.close();
39 }
40
41 emitError(err) {
42 let {message, stack} = prettyError(err);
43
44 // store the most recent error so we can notify new connections
45 // and so we can broadcast when the error is resolved
46 this.unresolvedError = {
47 type: 'error',
48 error: {
49 message,
50 stack
51 }
52 };
53
54 this.broadcast(this.unresolvedError);
55 }
56
57 emitUpdate(assets) {
58 if (this.unresolvedError) {
59 this.unresolvedError = null;
60 this.broadcast({
61 type: 'error-resolved'
62 });
63 }
64
65 const containsHtmlAsset = assets.some(asset => asset.type === 'html');
66 if (containsHtmlAsset) {
67 this.broadcast({
68 type: 'reload'
69 });
70 } else {
71 this.broadcast({
72 type: 'update',
73 assets: assets.map(asset => {
74 let deps = {};
75 for (let [dep, depAsset] of asset.depAssets) {
76 deps[dep.name] = depAsset.id;
77 }
78
79 return {
80 id: asset.id,
81 generated: asset.generated,
82 deps: deps
83 };
84 })
85 });
86 }
87 }
88
89 handleSocketError(err) {
90 if (err.error.code === 'ECONNRESET') {
91 // This gets triggered on page refresh, ignore this
92 return;
93 }
94 logger.warn(err);
95 }
96
97 broadcast(msg) {
98 const json = JSON.stringify(msg);
99 for (let ws of this.wss.clients) {
100 ws.send(json);
101 }
102 }
103}
104
105module.exports = HMRServer;