diff --git a/packages/react-dev-utils/failFast.js b/packages/react-dev-utils/failFast.js new file mode 100644 index 00000000000..9d3d9f1fbb2 --- /dev/null +++ b/packages/react-dev-utils/failFast.js @@ -0,0 +1,77 @@ +(function() { + const ErrorStackParser = require('error-stack-parser') + + const overlayStyle = { + position: 'fixed', + 'box-sizing': 'border-box', + top: '0px', left: '0px', + bottom: '0px', right: '0px', + width: '100vw', height: '100vh', + 'background-color': 'rgb(200, 0, 0)', + padding: '2rem', + 'z-index': 1337, + 'font-family': 'Menlo, Consolas, monospace', + color: 'rgb(232, 232, 232)', + 'white-space': 'pre-wrap' + } + + const headerStyle = { + 'font-size': 'larger', + 'font-weight': 'bold' + } + + const traceStyle = { + 'font-size': '1rem' + } + + function applyStyles(element, styles) { + element.setAttribute('style', '') + for (const key in styles) { + if (!styles.hasOwnProperty(key)) continue + element.style[key] = styles[key].toString() + } + } + + function render(name, message, frames) { + // Create container + const overlay = document.createElement('div') + applyStyles(overlay, overlayStyle) + + // Create header + const header = document.createElement('div') + applyStyles(header, headerStyle) + header.appendChild(document.createTextNode(`${name}: ${message}`)) + overlay.appendChild(header) + + // Show trace + const trace = document.createElement('div') + applyStyles(trace, traceStyle) + for (const frame of frames) { + const { source } = frame + const elem = document.createElement('div') + elem.appendChild(document.createTextNode(source)) + trace.appendChild(elem) + } + overlay.appendChild(trace) + + // Mount + document.body.appendChild(overlay) + } + + function crash(error) { + let frames = [] + try { + frames = ErrorStackParser.parse(error) + } catch (e) { + } + render(error.name, error.message, frames) + } + + window.onerror = function(messageOrEvent, source, lineno, colno, error) { + if (error == null || !(error instanceof Error) || messageOrEvent.indexOf('Script error') !== -1) { + crash(new Error('Unknown script error.'))// TODO: more helpful message + } else { + crash(error) + } + } +})() diff --git a/packages/react-dev-utils/package.json b/packages/react-dev-utils/package.json index c9f2fac2dcf..fdf1b3dad5f 100644 --- a/packages/react-dev-utils/package.json +++ b/packages/react-dev-utils/package.json @@ -11,8 +11,9 @@ "node": ">=4" }, "files": [ - "clearConsole.js", "checkRequiredFiles.js", + "clearConsole.js", + "failFast.js", "formatWebpackMessages.js", "getProcessForPort.js", "InterpolateHtmlPlugin.js", @@ -25,6 +26,7 @@ "dependencies": { "ansi-html": "0.0.5", "chalk": "1.1.3", + "error-stack-parser": "1.3.6", "escape-string-regexp": "1.0.5", "html-entities": "1.2.0", "opn": "4.0.2", diff --git a/packages/react-scripts/config/webpack.config.dev.js b/packages/react-scripts/config/webpack.config.dev.js index 8e264d40286..b18f662f3a3 100644 --- a/packages/react-scripts/config/webpack.config.dev.js +++ b/packages/react-scripts/config/webpack.config.dev.js @@ -53,6 +53,8 @@ module.exports = { require.resolve('react-dev-utils/webpackHotDevClient'), // We ship a few polyfills by default: require.resolve('./polyfills'), + // In development, we want to fail fast + require.resolve('react-dev-utils/failFast'), // Finally, this is your app's code: paths.appIndexJs // We include the app code last so that if there is a runtime error during