UNPKG

3.99 kBJavaScriptView Raw
1'use strict';
2
3module.exports = hmrMiddleware;
4
5function pathMatch(url, path) {
6 if (url == path) {
7 return true;
8 }
9 var q = url.indexOf('?');
10 if (q == -1) {
11 return false;
12 }
13 return url.substring(0, q) == path;
14}
15
16function hmrMiddleware(compiler, opts) {
17 opts = opts || {};
18 opts.log = typeof opts.log == 'undefined' ? console.log.bind(console) : opts.log;
19 opts.path = opts.path || '/__webpack_hmr';
20 opts.heartbeat = opts.heartbeat || 10 * 1000;
21
22 var eventStream = createEventStream(opts.heartbeat);
23 var latestStats = null;
24
25 compiler.plugin('compile', function () {
26 latestStats = null;
27 if (opts.log) {
28 opts.log('webpack building...');
29 }
30 eventStream.publish({ type: 'building' });
31 });
32 compiler.plugin('done', function (statsResult) {
33 // Keep hold of latest stats so they can be propagated to new clients
34 latestStats = statsResult;
35
36 publishStats('built', latestStats, eventStream, opts.log);
37 });
38 var middleware = function middleware(req, res, next) {
39 if (!pathMatch(req.url, opts.path)) {
40 return next();
41 }
42 eventStream.handler(req, res);
43 // if (latestStats) {
44 // // Explicitly not passing in `log` fn as we don't want to log again on
45 // // the server
46 // // publishStats('sync', latestStats, eventStream);
47 // }
48 };
49 middleware.publish = eventStream.publish;
50 return middleware;
51}
52
53function createEventStream(heartbeat) {
54 var clientId = 0;
55 var clients = {};
56 function everyClient(fn) {
57 Object.keys(clients).forEach(function (id) {
58 fn(clients[id]);
59 });
60 }
61 setInterval(function heartbeatTick() {
62 everyClient(function (client) {
63 client.write('data: ' + JSON.stringify({ type: 'heartbeat' }) + '\n\n');
64 });
65 }, heartbeat).unref();
66 return {
67 handler: function handler(req, res) {
68 req.socket.setKeepAlive(true);
69 res.writeHead(200, {
70 'Access-Control-Allow-Origin': '*',
71 'Content-Type': 'text/event-stream;charset=utf-8',
72 'Cache-Control': 'no-cache, no-transform',
73 Connection: 'keep-alive'
74 });
75 res.write('\n');
76 var id = clientId++;
77 clients[id] = res;
78 req.on('close', function () {
79 delete clients[id];
80 });
81 },
82 publish: function publish(payload) {
83 everyClient(function (client) {
84 client.write('data: ' + JSON.stringify(payload) + '\n\n');
85 });
86 }
87 };
88}
89
90function publishStats(action, statsResult, eventStream, log) {
91 // For multi-compiler, stats will be an object with a 'children' array of stats
92 var bundles = extractBundles(statsResult.toJson({ errorDetails: false }));
93 bundles.forEach(function (stats) {
94 if (log) {
95 log('webpack built ' + (stats.name ? stats.name + ' ' : '') + stats.hash + ' in ' + stats.time + 'ms');
96 }
97 // if (
98 // // !force &&
99 // action !== 'sync' &&
100 // stats &&
101 // (!stats.errors || stats.errors.length === 0) &&
102 // stats.assets &&
103 // stats.assets.every(asset => !asset.emitted)
104 // ) {
105 // eventStream.publish({
106 // type: 'still-ok'
107 // });
108 // }
109 eventStream.publish({
110 type: 'hash',
111 data: stats.hash
112 });
113
114 if (stats.errors.length > 0) {
115 eventStream.publish({
116 type: 'errors',
117 data: stats.errors
118 });
119 } else if (stats.warnings.length > 0) {
120 eventStream.publish({
121 type: 'warnings',
122 data: stats.warnings
123 });
124 } else {
125 eventStream.publish({
126 type: 'content-changed'
127 });
128 }
129 });
130}
131
132function extractBundles(stats) {
133 // Stats has modules, single bundle
134 if (stats.modules) {
135 return [stats];
136 }
137
138 // Stats has children, multiple bundles
139 if (stats.children && stats.children.length) {
140 return stats.children;
141 }
142
143 // Not sure, assume single
144 return [stats];
145}
146
147function buildModuleMap(modules) {
148 var map = {};
149 modules.forEach(function (module) {
150 map[module.id] = module.name;
151 });
152 return map;
153}
\No newline at end of file