2017-01-12 01:58:20 +00:00
|
|
|
import React from 'react'
|
|
|
|
import ansiHTML from 'ansi-html'
|
|
|
|
import Head from './head'
|
2018-04-18 16:18:06 +00:00
|
|
|
import {applySourcemaps} from '../client/source-map-support'
|
2017-01-12 01:58:20 +00:00
|
|
|
|
2018-04-18 16:18:06 +00:00
|
|
|
// On the client side the error can come from multiple places for example react-hot-loader or client/index.js
|
|
|
|
// `componentDidCatch` doesn't support asynchronous execution, so we have to handle sourcemap support here
|
|
|
|
export class ClientDebug extends React.Component {
|
|
|
|
state = {
|
|
|
|
mappedError: null
|
|
|
|
}
|
|
|
|
componentDidMount () {
|
|
|
|
const {error} = this.props
|
|
|
|
|
|
|
|
// If sourcemaps were already applied there is no need to set the state
|
|
|
|
if (error.sourceMapsApplied) {
|
|
|
|
return
|
2017-01-12 01:58:20 +00:00
|
|
|
}
|
|
|
|
|
2018-04-18 16:18:06 +00:00
|
|
|
// Since componentDidMount doesn't handle errors we use then/catch here
|
|
|
|
applySourcemaps(error).then(() => {
|
|
|
|
this.setState({mappedError: error})
|
|
|
|
}).catch(console.error)
|
|
|
|
}
|
|
|
|
|
|
|
|
render () {
|
|
|
|
const {mappedError} = this.state
|
|
|
|
const {error} = this.props
|
|
|
|
if (!error.sourceMapsApplied && mappedError === null) {
|
|
|
|
return <div style={styles.errorDebug}>
|
|
|
|
<h1 style={styles.heading}>Loading stacktrace...</h1>
|
|
|
|
</div>
|
|
|
|
}
|
|
|
|
|
|
|
|
return <ErrorDebug error={error} />
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// On the server side the error has sourcemaps already applied, so `ErrorDebug` is rendered directly.
|
|
|
|
export default function ErrorDebug ({error}) {
|
|
|
|
const { name, message, module } = error
|
|
|
|
return (
|
|
|
|
<div style={styles.errorDebug}>
|
|
|
|
<Head>
|
|
|
|
<meta name='viewport' content='width=device-width, initial-scale=1.0' />
|
|
|
|
</Head>
|
|
|
|
{module ? <h1 style={styles.heading}>Error in {module.rawRequest}</h1> : null}
|
|
|
|
{
|
|
|
|
name === 'ModuleBuildError'
|
|
|
|
? <pre style={styles.stack} dangerouslySetInnerHTML={{ __html: ansiHTML(encodeHtml(message)) }} />
|
|
|
|
: <StackTrace error={error} />
|
|
|
|
}
|
|
|
|
</div>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
const StackTrace = ({ error: { name, message, stack, info } }) => (
|
2017-01-15 16:17:29 +00:00
|
|
|
<div>
|
2017-03-22 23:13:23 +00:00
|
|
|
<div style={styles.heading}>{message || name}</div>
|
|
|
|
<pre style={styles.stack}>
|
2017-01-15 16:17:29 +00:00
|
|
|
{stack}
|
|
|
|
</pre>
|
2018-04-18 16:18:06 +00:00
|
|
|
{info && <pre style={styles.stack}>
|
|
|
|
{info.componentStack}
|
|
|
|
</pre>}
|
2017-01-15 16:17:29 +00:00
|
|
|
</div>
|
|
|
|
)
|
|
|
|
|
2017-01-12 01:58:20 +00:00
|
|
|
const styles = {
|
|
|
|
errorDebug: {
|
2017-06-09 18:50:58 +00:00
|
|
|
background: '#0e0d0d',
|
2017-01-12 01:58:20 +00:00
|
|
|
boxSizing: 'border-box',
|
|
|
|
overflow: 'auto',
|
2017-06-09 18:50:58 +00:00
|
|
|
padding: '24px',
|
2017-01-12 01:58:20 +00:00
|
|
|
position: 'fixed',
|
|
|
|
left: 0,
|
|
|
|
right: 0,
|
|
|
|
top: 0,
|
|
|
|
bottom: 0,
|
2018-04-18 16:18:06 +00:00
|
|
|
zIndex: 9999,
|
|
|
|
color: '#b3adac'
|
2017-01-12 01:58:20 +00:00
|
|
|
},
|
|
|
|
|
2017-03-22 23:13:23 +00:00
|
|
|
stack: {
|
2017-01-22 02:04:41 +00:00
|
|
|
fontFamily: '"SF Mono", "Roboto Mono", "Fira Mono", consolas, menlo-regular, monospace',
|
2017-01-22 02:12:27 +00:00
|
|
|
fontSize: '13px',
|
2017-06-09 18:50:58 +00:00
|
|
|
lineHeight: '18px',
|
|
|
|
color: '#b3adac',
|
2017-01-12 01:58:20 +00:00
|
|
|
margin: 0,
|
|
|
|
whiteSpace: 'pre-wrap',
|
2017-03-22 23:13:23 +00:00
|
|
|
wordWrap: 'break-word',
|
2017-06-09 18:50:58 +00:00
|
|
|
marginTop: '16px'
|
2017-01-12 01:58:20 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
heading: {
|
2017-03-29 14:55:26 +00:00
|
|
|
fontFamily: '-apple-system, system-ui, BlinkMacSystemFont, Roboto, "Segoe UI", "Fira Sans", Avenir, "Helvetica Neue", "Lucida Grande", sans-serif',
|
2017-06-09 18:50:58 +00:00
|
|
|
fontSize: '20px',
|
|
|
|
fontWeight: '400',
|
|
|
|
lineHeight: '28px',
|
|
|
|
color: '#fff',
|
|
|
|
marginBottom: '0px',
|
|
|
|
marginTop: '0px'
|
2017-01-12 01:58:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const encodeHtml = str => {
|
|
|
|
return str.replace(/</g, '<').replace(/>/g, '>')
|
|
|
|
}
|
|
|
|
|
|
|
|
// see color definitions of babel-code-frame:
|
|
|
|
// https://github.com/babel/babel/blob/master/packages/babel-code-frame/src/index.js
|
|
|
|
|
|
|
|
ansiHTML.setColors({
|
2017-06-09 18:50:58 +00:00
|
|
|
reset: ['6F6767', '0e0d0d'],
|
|
|
|
darkgrey: '6F6767',
|
|
|
|
yellow: '6F6767',
|
|
|
|
green: 'ebe7e5',
|
|
|
|
magenta: 'ebe7e5',
|
|
|
|
blue: 'ebe7e5',
|
|
|
|
cyan: 'ebe7e5',
|
|
|
|
red: 'ff001f'
|
2017-01-12 01:58:20 +00:00
|
|
|
})
|