UNPKG

7.93 kBJavaScriptView Raw
1/**
2 * Copyright (c) 2015-present, Facebook, Inc.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7
8'use strict';
9
10// This alternative WebpackDevServer combines the functionality of:
11// https://github.com/webpack/webpack-dev-server/blob/webpack-1/client/index.js
12// https://github.com/webpack/webpack/blob/webpack-1/hot/dev-server.js
13
14// It only supports their simplest configuration (hot updates on same server).
15// It makes some opinionated choices on top, like adding a syntax error overlay
16// that looks similar to our console output. The error overlay is inspired by:
17// https://github.com/glenjamin/webpack-hot-middleware
18
19var SockJS = require('sockjs-client');
20var stripAnsi = require('strip-ansi');
21var url = require('url');
22var launchEditorEndpoint = require('./launchEditorEndpoint');
23var formatWebpackMessages = require('./formatWebpackMessages');
24var ErrorOverlay = require('react-error-overlay');
25
26ErrorOverlay.setEditorHandler(function editorHandler(errorLocation) {
27 // Keep this sync with errorOverlayMiddleware.js
28 fetch(
29 launchEditorEndpoint +
30 '?fileName=' +
31 window.encodeURIComponent(errorLocation.fileName) +
32 '&lineNumber=' +
33 window.encodeURIComponent(errorLocation.lineNumber || 1)
34 );
35});
36
37// We need to keep track of if there has been a runtime error.
38// Essentially, we cannot guarantee application state was not corrupted by the
39// runtime error. To prevent confusing behavior, we forcibly reload the entire
40// application. This is handled below when we are notified of a compile (code
41// change).
42// See https://github.com/facebookincubator/create-react-app/issues/3096
43var hadRuntimeError = false;
44ErrorOverlay.startReportingRuntimeErrors({
45 onError: function() {
46 hadRuntimeError = true;
47 },
48 filename: '/static/js/bundle.js',
49});
50
51if (module.hot && typeof module.hot.dispose === 'function') {
52 module.hot.dispose(function() {
53 // TODO: why do we need this?
54 ErrorOverlay.stopReportingRuntimeErrors();
55 });
56}
57
58// Connect to WebpackDevServer via a socket.
59var connection = new SockJS(
60 url.format({
61 protocol: window.location.protocol,
62 hostname: window.location.hostname,
63 port: window.location.port,
64 // Hardcoded in WebpackDevServer
65 pathname: '/sockjs-node',
66 })
67);
68
69// Unlike WebpackDevServer client, we won't try to reconnect
70// to avoid spamming the console. Disconnect usually happens
71// when developer stops the server.
72connection.onclose = function() {
73 if (typeof console !== 'undefined' && typeof console.info === 'function') {
74 console.info(
75 'The development server has disconnected.\nRefresh the page if necessary.'
76 );
77 }
78};
79
80// Remember some state related to hot module replacement.
81var isFirstCompilation = true;
82var mostRecentCompilationHash = null;
83var hasCompileErrors = false;
84
85function clearOutdatedErrors() {
86 // Clean up outdated compile errors, if any.
87 if (typeof console !== 'undefined' && typeof console.clear === 'function') {
88 if (hasCompileErrors) {
89 console.clear();
90 }
91 }
92}
93
94// Successful compilation.
95function handleSuccess() {
96 clearOutdatedErrors();
97
98 var isHotUpdate = !isFirstCompilation;
99 isFirstCompilation = false;
100 hasCompileErrors = false;
101
102 // Attempt to apply hot updates or reload.
103 if (isHotUpdate) {
104 tryApplyUpdates(function onHotUpdateSuccess() {
105 // Only dismiss it when we're sure it's a hot update.
106 // Otherwise it would flicker right before the reload.
107 ErrorOverlay.dismissBuildError();
108 });
109 }
110}
111
112// Compilation with warnings (e.g. ESLint).
113function handleWarnings(warnings) {
114 clearOutdatedErrors();
115
116 var isHotUpdate = !isFirstCompilation;
117 isFirstCompilation = false;
118 hasCompileErrors = false;
119
120 function printWarnings() {
121 // Print warnings to the console.
122 var formatted = formatWebpackMessages({
123 warnings: warnings,
124 errors: [],
125 });
126
127 if (typeof console !== 'undefined' && typeof console.warn === 'function') {
128 for (var i = 0; i < formatted.warnings.length; i++) {
129 if (i === 5) {
130 console.warn(
131 'There were more warnings in other files.\n' +
132 'You can find a complete log in the terminal.'
133 );
134 break;
135 }
136 console.warn(stripAnsi(formatted.warnings[i]));
137 }
138 }
139 }
140
141 // Attempt to apply hot updates or reload.
142 if (isHotUpdate) {
143 tryApplyUpdates(function onSuccessfulHotUpdate() {
144 // Only print warnings if we aren't refreshing the page.
145 // Otherwise they'll disappear right away anyway.
146 printWarnings();
147 // Only dismiss it when we're sure it's a hot update.
148 // Otherwise it would flicker right before the reload.
149 ErrorOverlay.dismissBuildError();
150 });
151 } else {
152 // Print initial warnings immediately.
153 printWarnings();
154 }
155}
156
157// Compilation with errors (e.g. syntax error or missing modules).
158function handleErrors(errors) {
159 clearOutdatedErrors();
160
161 isFirstCompilation = false;
162 hasCompileErrors = true;
163
164 // "Massage" webpack messages.
165 var formatted = formatWebpackMessages({
166 errors: errors,
167 warnings: [],
168 });
169
170 // Only show the first error.
171 ErrorOverlay.reportBuildError(formatted.errors[0]);
172
173 // Also log them to the console.
174 if (typeof console !== 'undefined' && typeof console.error === 'function') {
175 for (var i = 0; i < formatted.errors.length; i++) {
176 console.error(stripAnsi(formatted.errors[i]));
177 }
178 }
179
180 // Do not attempt to reload now.
181 // We will reload on next success instead.
182}
183
184// There is a newer version of the code available.
185function handleAvailableHash(hash) {
186 // Update last known compilation hash.
187 mostRecentCompilationHash = hash;
188}
189
190// Handle messages from the server.
191connection.onmessage = function(e) {
192 var message = JSON.parse(e.data);
193 switch (message.type) {
194 case 'hash':
195 handleAvailableHash(message.data);
196 break;
197 case 'still-ok':
198 case 'ok':
199 handleSuccess();
200 break;
201 case 'content-changed':
202 // Triggered when a file from `contentBase` changed.
203 window.location.reload();
204 break;
205 case 'warnings':
206 handleWarnings(message.data);
207 break;
208 case 'errors':
209 handleErrors(message.data);
210 break;
211 default:
212 // Do nothing.
213 }
214};
215
216// Is there a newer version of this code available?
217function isUpdateAvailable() {
218 /* globals __webpack_hash__ */
219 // __webpack_hash__ is the hash of the current compilation.
220 // It's a global variable injected by Webpack.
221 return mostRecentCompilationHash !== __webpack_hash__;
222}
223
224// Webpack disallows updates in other states.
225function canApplyUpdates() {
226 return module.hot.status() === 'idle';
227}
228
229// Attempt to update code on the fly, fall back to a hard reload.
230function tryApplyUpdates(onHotUpdateSuccess) {
231 if (!module.hot) {
232 // HotModuleReplacementPlugin is not in Webpack configuration.
233 window.location.reload();
234 return;
235 }
236
237 if (!isUpdateAvailable() || !canApplyUpdates()) {
238 return;
239 }
240
241 function handleApplyUpdates(err, updatedModules) {
242 if (err || !updatedModules || hadRuntimeError) {
243 window.location.reload();
244 return;
245 }
246
247 if (typeof onHotUpdateSuccess === 'function') {
248 // Maybe we want to do something.
249 onHotUpdateSuccess();
250 }
251
252 if (isUpdateAvailable()) {
253 // While we were updating, there was a new update! Do it again.
254 tryApplyUpdates();
255 }
256 }
257
258 // https://webpack.github.io/docs/hot-module-replacement.html#check
259 var result = module.hot.check(/* autoApply */ true, handleApplyUpdates);
260
261 // // Webpack 2 returns a Promise instead of invoking a callback
262 if (result && result.then) {
263 result.then(
264 function(updatedModules) {
265 handleApplyUpdates(null, updatedModules);
266 },
267 function(err) {
268 handleApplyUpdates(err, null);
269 }
270 );
271 }
272}