From ab466510f3de25834aa8972d29fb3cb97e61e7e7 Mon Sep 17 00:00:00 2001 From: Kiwi Date: Wed, 15 May 2019 20:33:39 +0200 Subject: [PATCH] fix: use custom webpackHotDevClient with debounced reload and filter unwanted warnings (#2314) --- package-lock.json | 285 +++++++++++++++++++++++-- package.json | 4 + scripts/webpackHotDevClient.js | 287 ++++++++++++++++++++++++++ src/core/build/createWebpackConfig.ts | 2 +- src/core/build/paths.ts | 2 + 5 files changed, 561 insertions(+), 19 deletions(-) create mode 100644 scripts/webpackHotDevClient.js diff --git a/package-lock.json b/package-lock.json index e01d1ce4c..b0d35450b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5377,6 +5377,15 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", "dev": true + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } } } } @@ -7907,6 +7916,17 @@ "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", "strip-ansi": "^3.0.0" + }, + "dependencies": { + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + } } } } @@ -8223,6 +8243,17 @@ "requires": { "strip-ansi": "^3.0.0", "wcwidth": "^1.0.0" + }, + "dependencies": { + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + } } }, "combined-stream": { @@ -9292,6 +9323,15 @@ "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", "dev": true }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, "supports-color": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", @@ -14699,6 +14739,15 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", "dev": true + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } } } } @@ -14880,7 +14929,6 @@ "object-assign": "^4.1.0", "signal-exit": "^3.0.0", "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", "wide-align": "^1.1.0" } }, @@ -14976,8 +15024,7 @@ "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "requires": { "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" + "is-fullwidth-code-point": "^1.0.0" } } } @@ -15047,6 +15094,16 @@ "is-fullwidth-code-point": "^1.0.0", "strip-ansi": "^3.0.0" } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "optional": true, + "requires": { + "ansi-regex": "^2.0.0" + } } } }, @@ -16154,6 +16211,15 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", "dev": true + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } } } } @@ -16395,6 +16461,17 @@ "string-width": "^1.0.1", "strip-ansi": "^3.0.1", "wrap-ansi": "^2.0.0" + }, + "dependencies": { + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + } } }, "find-up": { @@ -16503,6 +16580,17 @@ "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", "strip-ansi": "^3.0.0" + }, + "dependencies": { + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + } } }, "strip-bom": { @@ -16528,6 +16616,17 @@ "requires": { "string-width": "^1.0.1", "strip-ansi": "^3.0.1" + }, + "dependencies": { + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + } } }, "y18n": { @@ -20846,6 +20945,15 @@ "requires": { "chalk": "^1.0.0" } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } } } }, @@ -22659,6 +22767,16 @@ "string-width": "^1.0.1", "strip-ansi": "^3.0.1", "wrap-ansi": "^2.0.0" + }, + "dependencies": { + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + } } }, "fsevents": { @@ -23189,6 +23307,16 @@ "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", "strip-ansi": "^3.0.0" + }, + "dependencies": { + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + } } }, "upath": { @@ -23204,6 +23332,16 @@ "requires": { "string-width": "^1.0.1", "strip-ansi": "^3.0.1" + }, + "dependencies": { + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + } } }, "y18n": { @@ -23552,12 +23690,12 @@ } }, "original": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/original/-/original-1.0.1.tgz", - "integrity": "sha512-IEvtB5vM5ULvwnqMxWBLxkS13JIEXbakizMSo3yoPNPCIWzg8TG3Usn/UhXoZFM/m+FuEA20KdzPSFq/0rS+UA==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz", + "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==", "dev": true, "requires": { - "url-parse": "~1.4.0" + "url-parse": "^1.4.3" } }, "os-browserify": { @@ -26500,6 +26638,15 @@ "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", "dev": true }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, "supports-color": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", @@ -28623,9 +28770,9 @@ } }, "react-error-overlay": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-5.1.5.tgz", - "integrity": "sha512-O9JRum1Zq/qCPFH5qVEvDDrVun8Jv9vbHtZXCR1EuRj9sKg1xJTlHxBzU6AkCzpvxRLuiY4OKImy3cDLQ+UTdg==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-5.1.6.tgz", + "integrity": "sha512-X1Y+0jR47ImDVr54Ab6V9eGk0Hnu7fVWGeHQSOXHf/C2pF9c6uy3gef8QUeuUiWlNb0i08InPSE5a/KJzNzw1Q==", "dev": true }, "react-feather": { @@ -29361,6 +29508,15 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", "dev": true + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } } } }, @@ -29385,6 +29541,15 @@ "is-fullwidth-code-point": "^1.0.0", "strip-ansi": "^3.0.0" } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } } } }, @@ -29479,6 +29644,15 @@ "is-fullwidth-code-point": "^1.0.0", "strip-ansi": "^3.0.0" } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } } } }, @@ -29734,6 +29908,17 @@ "htmlparser2": "~3.3.0", "strip-ansi": "^3.0.0", "utila": "^0.4.0" + }, + "dependencies": { + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + } } }, "repeat-element": { @@ -31208,11 +31393,20 @@ } }, "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, "requires": { - "ansi-regex": "^2.0.0" + "ansi-regex": "^4.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + } } }, "strip-bom": { @@ -32928,6 +33122,15 @@ "is-fullwidth-code-point": "^1.0.0", "strip-ansi": "^3.0.0" } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } } } }, @@ -33609,6 +33812,15 @@ "is-fullwidth-code-point": "^1.0.0", "strip-ansi": "^3.0.0" } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } } } }, @@ -34166,13 +34378,21 @@ } }, "url-parse": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.3.tgz", - "integrity": "sha512-rh+KuAW36YKo0vClhQzLLveoj8FwPJNu65xLb7Mrt+eZht0IPT0IXgSv8gcMegZ6NvjJUALf6Mf25POlMwD1Fw==", + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.7.tgz", + "integrity": "sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==", "dev": true, "requires": { - "querystringify": "^2.0.0", + "querystringify": "^2.1.1", "requires-port": "^1.0.0" + }, + "dependencies": { + "querystringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.1.1.tgz", + "integrity": "sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==", + "dev": true + } } }, "url-regex": { @@ -35158,6 +35378,16 @@ "has-ansi": "^2.0.0", "strip-ansi": "^3.0.0", "supports-color": "^2.0.0" + }, + "dependencies": { + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + } } }, "domelementtype": { @@ -36471,6 +36701,15 @@ "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", "dev": true }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, "supports-color": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", @@ -36805,6 +37044,16 @@ "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", "strip-ansi": "^3.0.0" + }, + "dependencies": { + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + } } } } diff --git a/package.json b/package.json index 09ce80367..f6c124a7d 100644 --- a/package.json +++ b/package.json @@ -289,6 +289,7 @@ "react-copy-to-clipboard": "^5.0.1", "react-dev-utils": "^9.0.0", "react-dom": "^16.8.6", + "react-error-overlay": "^5.1.6", "react-final-form": "4.0.2", "react-popper": "^1.3.2", "react-relay": "^1.7.0-rc.1", @@ -307,6 +308,8 @@ "simplemde": "^1.11.2", "simulant": "^0.2.2", "sinon": "^7.3.2", + "sockjs-client": "^1.3.0", + "strip-ansi": "^5.2.0", "style-loader": "^0.23.1", "terser-webpack-plugin": "^1.2.3", "thread-loader": "^2.1.2", @@ -325,6 +328,7 @@ "typeface-source-sans-pro": "^0.0.54", "typescript": "3.3.4000", "typescript-snapshots-plugin": "^1.6.0", + "url": "^0.11.0", "wait-for-expect": "^1.1.1", "webpack": "^4.30.0", "webpack-assets-manifest": "^3.1.1", diff --git a/scripts/webpackHotDevClient.js b/scripts/webpackHotDevClient.js new file mode 100644 index 000000000..999856d26 --- /dev/null +++ b/scripts/webpackHotDevClient.js @@ -0,0 +1,287 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +"use strict"; + +// tslint:disable:no-console + +// This alternative WebpackDevServer combines the functionality of: +// https://github.com/webpack/webpack-dev-server/blob/webpack-1/client/index.js +// https://github.com/webpack/webpack/blob/webpack-1/hot/dev-server.js + +// It only supports their simplest configuration (hot updates on same server). +// It makes some opinionated choices on top, like adding a syntax error overlay +// that looks similar to our console output. The error overlay is inspired by: +// https://github.com/glenjamin/webpack-hot-middleware + +var SockJS = require("sockjs-client"); +var stripAnsi = require("strip-ansi"); +var url = require("url"); +var launchEditorEndpoint = require("react-dev-utils/launchEditorEndpoint"); +var formatWebpackMessages = require("react-dev-utils/formatWebpackMessages"); +var ErrorOverlay = require("react-error-overlay"); +var { debounce } = require("lodash"); + +ErrorOverlay.setEditorHandler(function editorHandler(errorLocation) { + // Keep this sync with errorOverlayMiddleware.js + fetch( + launchEditorEndpoint + + "?fileName=" + + window.encodeURIComponent(errorLocation.fileName) + + "&lineNumber=" + + window.encodeURIComponent(errorLocation.lineNumber || 1) + + "&colNumber=" + + window.encodeURIComponent(errorLocation.colNumber || 1) + ); +}); + +// We need to keep track of if there has been a runtime error. +// Essentially, we cannot guarantee application state was not corrupted by the +// runtime error. To prevent confusing behavior, we forcibly reload the entire +// application. This is handled below when we are notified of a compile (code +// change). +// See https://github.com/facebook/create-react-app/issues/3096 +var hadRuntimeError = false; +ErrorOverlay.startReportingRuntimeErrors({ + onError: function() { + hadRuntimeError = true; + }, + filename: "/static/js/bundle.js", +}); + +if (module.hot && typeof module.hot.dispose === "function") { + module.hot.dispose(function() { + // TODO: why do we need this? + ErrorOverlay.stopReportingRuntimeErrors(); + }); +} + +// Connect to WebpackDevServer via a socket. +var connection = new SockJS( + url.format({ + protocol: window.location.protocol, + hostname: window.location.hostname, + port: window.location.port, + // Hardcoded in WebpackDevServer + pathname: "/sockjs-node", + }) +); + +// Unlike WebpackDevServer client, we won't try to reconnect +// to avoid spamming the console. Disconnect usually happens +// when developer stops the server. +connection.onclose = function() { + if (typeof console !== "undefined" && typeof console.info === "function") { + console.info( + "The development server has disconnected.\nRefresh the page if necessary." + ); + } +}; + +// Remember some state related to hot module replacement. +var isFirstCompilation = true; +var mostRecentCompilationHash = null; +var hasCompileErrors = false; + +function clearOutdatedErrors() { + // Clean up outdated compile errors, if any. + if (typeof console !== "undefined" && typeof console.clear === "function") { + if (hasCompileErrors) { + console.clear(); + } + } +} + +// Successful compilation. +function handleSuccess() { + clearOutdatedErrors(); + + var isHotUpdate = !isFirstCompilation; + isFirstCompilation = false; + hasCompileErrors = false; + + // Attempt to apply hot updates or reload. + if (isHotUpdate) { + tryApplyUpdates(function onHotUpdateSuccess() { + // Only dismiss it when we're sure it's a hot update. + // Otherwise it would flicker right before the reload. + tryDismissErrorOverlay(); + }); + } +} + +// Compilation with warnings (e.g. ESLint). +function handleWarnings(warnings) { + clearOutdatedErrors(); + + var isHotUpdate = !isFirstCompilation; + isFirstCompilation = false; + hasCompileErrors = false; + + // TODO: remove this workaround when we can upgrade to WebpackDevServer >= v3.3.0, + // which includes proper `warningsFilter` support. + warnings = warnings.filter(w => !/export .* was not found in/.test(w)); + + function printWarnings() { + // Print warnings to the console. + var formatted = formatWebpackMessages({ + warnings: warnings, + errors: [], + }); + + if (typeof console !== "undefined" && typeof console.warn === "function") { + for (var i = 0; i < formatted.warnings.length; i++) { + if (i === 5) { + console.warn( + "There were more warnings in other files.\n" + + "You can find a complete log in the terminal." + ); + break; + } + console.warn(stripAnsi(formatted.warnings[i])); + } + } + } + + printWarnings(); + + // Attempt to apply hot updates or reload. + if (isHotUpdate) { + tryApplyUpdates(function onSuccessfulHotUpdate() { + // Only dismiss it when we're sure it's a hot update. + // Otherwise it would flicker right before the reload. + tryDismissErrorOverlay(); + }); + } +} + +// Compilation with errors (e.g. syntax error or missing modules). +function handleErrors(errors) { + clearOutdatedErrors(); + + isFirstCompilation = false; + hasCompileErrors = true; + + // "Massage" webpack messages. + var formatted = formatWebpackMessages({ + errors: errors, + warnings: [], + }); + + // Only show the first error. + ErrorOverlay.reportBuildError(formatted.errors[0]); + + // Also log them to the console. + if (typeof console !== "undefined" && typeof console.error === "function") { + for (var i = 0; i < formatted.errors.length; i++) { + console.error(stripAnsi(formatted.errors[i])); + } + } + + // Do not attempt to reload now. + // We will reload on next success instead. +} + +function tryDismissErrorOverlay() { + if (!hasCompileErrors) { + ErrorOverlay.dismissBuildError(); + } +} + +// There is a newer version of the code available. +function handleAvailableHash(hash) { + // Update last known compilation hash. + mostRecentCompilationHash = hash; +} + +const debouncedReload = debounce(() => { + window.location.reload(); +}, 1000); + +// Handle messages from the server. +connection.onmessage = function(e) { + var message = JSON.parse(e.data); + switch (message.type) { + case "hash": + handleAvailableHash(message.data); + break; + case "still-ok": + case "ok": + handleSuccess(); + break; + case "content-changed": + // Triggered when a file from `contentBase` changed. + debouncedReload(); + break; + case "warnings": + handleWarnings(message.data); + break; + case "errors": + handleErrors(message.data); + break; + default: + // Do nothing. + } +}; + +// Is there a newer version of this code available? +function isUpdateAvailable() { + /* globals __webpack_hash__ */ + // __webpack_hash__ is the hash of the current compilation. + // It's a global variable injected by Webpack. + return mostRecentCompilationHash !== __webpack_hash__; +} + +// Webpack disallows updates in other states. +function canApplyUpdates() { + return module.hot.status() === "idle"; +} + +// Attempt to update code on the fly, fall back to a hard reload. +function tryApplyUpdates(onHotUpdateSuccess) { + if (!module.hot) { + // HotModuleReplacementPlugin is not in Webpack configuration. + debouncedReload(); + return; + } + + if (!isUpdateAvailable() || !canApplyUpdates()) { + return; + } + + function handleApplyUpdates(err, updatedModules) { + if (err || !updatedModules || hadRuntimeError) { + debouncedReload(); + return; + } + + if (typeof onHotUpdateSuccess === "function") { + // Maybe we want to do something. + onHotUpdateSuccess(); + } + + if (isUpdateAvailable()) { + // While we were updating, there was a new update! Do it again. + tryApplyUpdates(); + } + } + + // https://webpack.github.io/docs/hot-module-replacement.html#check + var result = module.hot.check(/* autoApply */ true, handleApplyUpdates); + + // // Webpack 2 returns a Promise instead of invoking a callback + if (result && result.then) { + result.then( + function(updatedModules) { + handleApplyUpdates(null, updatedModules); + }, + function(err) { + handleApplyUpdates(err, null); + } + ); + } +} diff --git a/src/core/build/createWebpackConfig.ts b/src/core/build/createWebpackConfig.ts index b5b4711ef..6c92e87b0 100644 --- a/src/core/build/createWebpackConfig.ts +++ b/src/core/build/createWebpackConfig.ts @@ -573,7 +573,7 @@ export default function createWebpackConfig( // the line below with these two lines if you prefer the stock client: // require.resolve('webpack-dev-server/client') + '?/', // require.resolve('webpack/hot/dev-server'), - require.resolve("react-dev-utils/webpackHotDevClient"), + paths.appWebpackHotDevClient, ]; return [ diff --git a/src/core/build/paths.ts b/src/core/build/paths.ts index e96507278..50dbf736d 100644 --- a/src/core/build/paths.ts +++ b/src/core/build/paths.ts @@ -55,4 +55,6 @@ export default { appPublic: resolveApp("public"), appPackageJson: resolveApp("package.json"), appNodeModules: resolveApp("node_modules"), + + appWebpackHotDevClient: resolveApp("scripts/webpackHotDevClient.js"), };