1 | 'use strict';
|
2 |
|
3 | module.exports = hmrMiddleware;
|
4 |
|
5 | function 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 |
|
16 | function 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 |
|
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 |
|
44 |
|
45 |
|
46 |
|
47 |
|
48 | };
|
49 | middleware.publish = eventStream.publish;
|
50 | return middleware;
|
51 | }
|
52 |
|
53 | function 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 |
|
90 | function publishStats(action, statsResult, eventStream, log) {
|
91 |
|
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 |
|
98 |
|
99 |
|
100 |
|
101 |
|
102 |
|
103 |
|
104 |
|
105 |
|
106 |
|
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 |
|
132 | function extractBundles(stats) {
|
133 |
|
134 | if (stats.modules) {
|
135 | return [stats];
|
136 | }
|
137 |
|
138 |
|
139 | if (stats.children && stats.children.length) {
|
140 | return stats.children;
|
141 | }
|
142 |
|
143 |
|
144 | return [stats];
|
145 | }
|
146 |
|
147 | function 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 |