mirror of
https://github.com/zhigang1992/create-react-app.git
synced 2026-01-13 09:00:30 +08:00
As a lot of [people](https://hackernoon.com/why-i-no-longer-use-typescript-with-react-and-why-you-shouldnt-either-e744d27452b4) is complaining about TypeScript performance in CRA, I decided to enable `async` mode in TypeScript checker. These changes basically brings the JS compilation times to TS projects. So, recompilation took less than 1 second instead of 3 seconds in medium size project. The problem with async mode is that type-errors are reported after Webpack ends up recompilation as TypeScript could be slower than Babel. PR allows to emit files compiled by Babel immediately and then wait for TS and show type errors in terminal later. Also, if there was no compilation errors and any type error occurs, we trigger a hot-reload with new errors to show error overlay in browser. Also, I wanted to start a discussion about `skipLibCheck: false` option in default `tsconfig.json`. This makes recompilations really slow and we should consider to set it to `true` or at least give users a big warning to let them know that it could be really slow. The following video is showing the updated workflow with a forced 2.5 second delay for type-check to give you an idea how it works.  I'm pretty sure that PR needs some polishing and improvements but it should works as it is. Especially a "hack" with reloading the browser after type-check looks ugly to me. cc @brunolemos as he is an initiator of an original TypeScript PR. Should fix https://github.com/facebook/create-react-app/issues/5820
277 lines
7.7 KiB
JavaScript
277 lines
7.7 KiB
JavaScript
/**
|
|
* 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';
|
|
|
|
// 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('./launchEditorEndpoint');
|
|
var formatWebpackMessages = require('./formatWebpackMessages');
|
|
var ErrorOverlay = require('react-error-overlay');
|
|
|
|
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;
|
|
|
|
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;
|
|
}
|
|
|
|
// 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.
|
|
window.location.reload();
|
|
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.
|
|
window.location.reload();
|
|
return;
|
|
}
|
|
|
|
if (!isUpdateAvailable() || !canApplyUpdates()) {
|
|
return;
|
|
}
|
|
|
|
function handleApplyUpdates(err, updatedModules) {
|
|
if (err || !updatedModules || hadRuntimeError) {
|
|
window.location.reload();
|
|
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);
|
|
}
|
|
);
|
|
}
|
|
}
|