UNPKG

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