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': '*',
},
},
};
};
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,
};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With