ericgio
ericgio

Reputation: 3519

Wrong line numbers in component stack trace [TS + React]

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

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

Answers (1)

ericgio
ericgio

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

Related Questions