diff --git a/packages/react-dev-utils/logger.js b/packages/react-dev-utils/logger.js new file mode 100644 index 00000000000..1080a0ae000 --- /dev/null +++ b/packages/react-dev-utils/logger.js @@ -0,0 +1,164 @@ +'use strict'; +const React = require('react'); +const ReactDOM = require('react-dom'); +const h = React.createElement; + +const id = 'create-react-app-logger'; + +let dom = document.getElementById(id); + +if (!dom) { + dom = document.createElement('div'); + dom.id = id; + document.body.appendChild(dom); +} + +const style = { + root: { + position: 'fixed', + bottom: '0', + left: '0', + right: '0', + padding: 10, + margin: 'auto', + transition: '250ms', + lineHeight: '24px', + borderTopLeftRadius: '2px', + borderTopRightRadius: '2px', + display: 'flex', + fontFamily: 'monospace', + color: '#293238', + }, + active: { + transform: 'translateY(0)', + }, + inactive: { + transform: 'translateY(100%)', + }, + link: { + padding: '0', + background: 'transparent', + border: '0', + fontSize: 'inherit', + lineHeight: 'inherit', + textTransform: 'uppercase', + textDecoration: 'none', + }, + button: { + padding: '0', + background: 'transparent', + border: '0', + color: 'inherit', + fontSize: 'inherit', + lineHeight: 'inherit', + textTransform: 'uppercase', + textDecoration: 'none', + cursor: 'pointer', + }, + text: { + flex: '1', + }, +}; + +class Logger extends React.Component { + constructor(...args) { + super(...args); + this.state = { + active: true, + }; + this.close = () => { + clearTimeout(this.timeout); + this.setState({ + active: false, + }); + }; + } + + componentDidMount() { + this.timeout = setTimeout(() => { + this.setState({ + active: false, + }); + }, this.props.timeout); + } + + componentWillUnmount() { + clearTimeout(this.timeout); + } + + render() { + return h( + 'div', + { + style: Object.assign( + {}, + style.root, + this.state.active ? style.active : style.inactive, + getColor(this.props.type) + ), + }, + h('span', { style: style.text }, this.props.children), + h('button', { style: style.button, onClick: this.close }, 'Dismiss') + ); + } +} + +Logger.defaultProps = { + timeout: 10000, +}; + +function getColor(type) { + switch (type) { + case 'info': + return { + background: '#61dafb', + }; + case 'warn': + return { + background: '#fbf5b4', + }; + case 'error': + return { + background: '#fccfcf', + }; + default: + return { + color: 'transparent', + }; + } +} + +function render(type, message) { + ReactDOM.render(h(Logger, { type }, message), dom); +} + +const logger = { + info(message) { + if (typeof console !== 'undefined' && typeof console.info === 'function') { + console.info(message); + } + render('info', message); + }, + error(message) { + if (typeof console !== 'undefined' && typeof console.error === 'function') { + console.error(message); + } + render('error', message); + }, + warn(message) { + if (typeof console !== 'undefined' && typeof console.warn === 'function') { + console.warn(message); + } + render('warn', message); + }, + clear() { + if (typeof console !== 'undefined' && typeof console.clear === 'function') { + console.clear(); + } + render('clear'); + }, +}; + +logger.clear(); + +module.exports = logger; diff --git a/packages/react-dev-utils/package.json b/packages/react-dev-utils/package.json index 48e1dc49d6a..e305bbf7c98 100644 --- a/packages/react-dev-utils/package.json +++ b/packages/react-dev-utils/package.json @@ -24,7 +24,8 @@ "inquirer.js", "InterpolateHtmlPlugin.js", "launchEditor.js", - "launchEditorEndpoint.js", + "logger.js", + "noopServiceWorkerMiddleware.js", "ModuleScopePlugin.js", "noopServiceWorkerMiddleware.js", "openBrowser.js", diff --git a/packages/react-dev-utils/webpackHotDevClient.js b/packages/react-dev-utils/webpackHotDevClient.js index 611fc6ba042..2c953eee76c 100644 --- a/packages/react-dev-utils/webpackHotDevClient.js +++ b/packages/react-dev-utils/webpackHotDevClient.js @@ -22,6 +22,7 @@ var url = require('url'); var launchEditorEndpoint = require('./launchEditorEndpoint'); var formatWebpackMessages = require('./formatWebpackMessages'); var ErrorOverlay = require('react-error-overlay'); +var logger = require('./logger'); // We need to keep track of if there has been a runtime error. // Essentially, we cannot guarantee application state was not corrupted by the @@ -57,14 +58,12 @@ var connection = new SockJS( ); // Unlike WebpackDevServer client, we won't try to reconnect -// to avoid spamming the console. Disconnect usually happens +// to avoid spamming the logger. 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.' - ); - } + logger.info( + 'The development server has disconnected.\nRefresh the page if necessary.' + ); }; // Remember some state related to hot module replacement. @@ -74,10 +73,8 @@ var hasCompileErrors = false; function clearOutdatedErrors() { // Clean up outdated compile errors, if any. - if (typeof console !== 'undefined' && typeof console.clear === 'function') { - if (hasCompileErrors) { - console.clear(); - } + if (hasCompileErrors) { + logger.clear(); } } @@ -108,23 +105,21 @@ function handleWarnings(warnings) { hasCompileErrors = false; function printWarnings() { - // Print warnings to the console. + // Print warnings to the logger. 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])); + for (var i = 0; i < formatted.warnings.length; i++) { + if (i === 5) { + logger.warn( + 'There were more warnings in other files.\n' + + 'You can find a complete log in the terminal.' + ); + break; } + logger.warn(stripAnsi(formatted.warnings[i])); } } @@ -160,11 +155,9 @@ function handleErrors(errors) { // 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])); - } + // Also log them to the logger. + for (var i = 0; i < formatted.errors.length; i++) { + logger.error(stripAnsi(formatted.errors[i])); } // Do not attempt to reload now. diff --git a/packages/react-scripts/template/src/App.js b/packages/react-scripts/template/src/App.js index 203067e4d75..03361cfd69f 100644 --- a/packages/react-scripts/template/src/App.js +++ b/packages/react-scripts/template/src/App.js @@ -18,4 +18,4 @@ class App extends Component { } } -export default App; +export default App; \ No newline at end of file