Reputation: 3519
Problem
I'm adding an error boundary to my client-side React app. In development, I want to display the error with a stack trace in the browser window, similar to create-react-app's or nextjs's error overlays. Using webpack's devtool
option, I'm able to generate a stack trace with the correct filename, but the wrong line number.
// This is what renders in the browser window
Error: You got an error!
at ProjectPage (webpack-internal:///./src/pages/ProjectPage.tsx:96:11) // <-- 96 is the wrong line
// This is what shows up in the console
Uncaught Error: You got an error!
at ProjectPage (ProjectPage.tsx?8371:128) // <-- 128 is the correct line
What I've tried
devtool
settings, but none of the ones I've tried provide correct line numbers.retainLines
babel setting in
webpack, but I'm not using babel to transpile my code, I'm using
ts-loader. Also, the babel docs suggest this option is a workaround for people not using source maps, which shouldn't be an issue here.babel-plugin-transform-react-jsx-source
but again, I'm not using babel to transpile my code. Should I be?I'm not sure if this is a problem with ts-loader, webpack, or some other fundamental step I'm not understanding about source mapping. Setting a debugger in componentDidCatch
and inspecting the error gives me the wrong line number, but when it gets logged to the console it's correct. It seems that the console has an additional step to map the correct line numbers; is this something I need to do manually?
ErrorBoundary.tsx
class ErrorBoundary extends React.Component {
state = {
error: null,
};
static getDerivedStateFromError(error) {
return {
error,
};
}
componentDidCatch(error, errorInfo) {
// Line numbers are wrong when inspecting in the function, but correct when logged to the console.
console.log(error, errorInfo);
}
render() {
return this.state.error ?
<ErrorPage error={this.state.error} /> :
this.props.children;
}
}
ErrorPage.tsx
const ErrorPage = ({ error }) => {
if (__DEV__) {
return (
<Layout title={error.name}>
<h1>{error.name}: {error.message}</h1>
<pre>{error.stack}</pre>
</Layout>
);
}
// Display a nicer page in production.
};
tsconfig.json
{
"compilerOptions": {
"allowJs": true,
"esModuleInterop": true,
"jsx": "react",
"lib": ["es2015", "dom"],
"module": "commonjs",
"sourceMap": true,
"target": "es6"
}
}
webpack.config.js
module.exports = (env, argv) => {
return {
mode: isProduction ? 'production' : 'development',
output: {
path: path.join(__dirname, env.output_path),
filename: 'app.bundle.js',
},
resolve: {
extensions: ['.ts', '.tsx', '.js', '.jsx'],
},
devtool: isProduction ? 'source-map' : 'eval-source-map',
entry: ['./src/index.tsx'],
module: {
rules: [
{
test: /\.ts(x?)$/,
exclude: /node_modules/,
loader: 'ts-loader',
},
...
],
},
devServer: {
contentBase: path.join(__dirname, env.output_path),
disableHostCheck: true,
historyApiFallback: true,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': '*',
},
},
};
};
Upvotes: 7
Views: 3509
Reputation: 3519
I ended up using error-overlay-webpack-plugin to solve my problem. While it didn't solve the low-level issue in my question (why aren't the line numbers correct in the stack trace?), it did solve my meta-issue (display a nice stack trace in dev) in a much simpler way. Maybe others will find this solution useful as well:
// webpack.config.js
const ErrorOverlayPlugin = require('error-overlay-webpack-plugin');
const plugins = [
...
];
if (!isProduction) {
plugins.push(new ErrorOverlayPlugin());
}
return {
...
plugins,
};
Upvotes: 5