aralroca / next-translate

Next.js plugin + i18n API for Next.js 🌍 - Load page translations and use them in an easy way!
MIT License
2.64k stars 207 forks source link

An error occurred when used with sentry and next-source-maps #439

Open thanhtoan1196 opened 3 years ago

thanhtoan1196 commented 3 years ago

next.config.js

const withPlugins = require('next-compose-plugins');
const withSourceMaps = require('@zeit/next-source-maps')();
const nextTranslate = require('next-translate')
translate = nextTranslate({
    webpack: (config, { isServer, webpack }) => {
        return config;
    }
})
const sourceMaps = withSourceMaps({
    serverRuntimeConfig: {
        rootDir: __dirname
    },
    webpack: (config, options) => {
        // In `pages/_app.js`, Sentry is imported from @sentry/browser. While
        // @sentry/node will run in a Node.js environment. @sentry/node will use
        // Node.js-only APIs to catch even more unhandled exceptions.
        //
        // This works well when Next.js is SSRing your page on a server with
        // Node.js, but it is not what we want when your client-side bundle is being
        // executed by a browser.
        //
        // Luckily, Next.js will call this webpack function twice, once for the
        // server and once for the client. Read more:
        // https://nextjs.org/docs/api-reference/next.config.js/custom-webpack-config
        //
        // So ask Webpack to replace @sentry/node imports with @sentry/browser when
        // building the browser's bundle
        if (!options.isServer) {
            config.resolve.alias['@sentry/node'] = '@sentry/browser';
        }

        // When all the Sentry configuration env variables are available/configured
        // The Sentry webpack plugin gets pushed to the webpack plugins to build
        // and upload the source maps to sentry.
        // This is an alternative to manually uploading the source maps
        // Note: This is disabled in development mode.
        if (
            SENTRY_DSN &&
            SENTRY_ORG &&
            SENTRY_PROJECT &&
            SENTRY_AUTH_TOKEN &&
            COMMIT_SHA &&
            NODE_ENV === 'production'
        ) {
            config.plugins.push(
                new SentryWebpackPlugin({
                    include: '.next',
                    ignore: ['node_modules'],
                    urlPrefix: '~/_next'
                    // release: COMMIT_SHA
                })
            );
        }

        return config;
    }
});

// case 1
module.exports = withPlugins([translate, sourceMaps, withBundleAnalyzer, [optimizedImages, {
        optimizeImages: false
    }]], {});

// case 2
module.exports = withPlugins([sourceMaps, translate, withBundleAnalyzer, [optimizedImages, {
        optimizeImages: false
    }]], {});

More information on package.json

"dependencies": {
    "@sentry/browser": "5.22.3",
    "@sentry/integrations": "5.22.3",
    "@sentry/node": "5.22.3",
    "@sentry/webpack-plugin": "1.12.1",
    "@zeit/next-source-maps": "0.0.4-canary.1",
    "axios": "0.19.2",
    "cogo-toast": "4.2.3",
    "dayjs": "1.9.6",
    "lazysizes": "5.2.2",
    "lodash.clonedeep": "^4.5.0",
    "next": "10.0.5",
    "next-compose-plugins": "2.2.0",
    "next-optimized-images": "2.6.2",
    "next-seo": "4.17.0",
    "next-translate": "^1.0.1",
    "nprogress": "0.2.0",
    "postcss-flexbugs-fixes": "4.2.1",
    "postcss-preset-env": "6.7.0",
    "react": "17.0.1",
    "react-animated-css": "^1.2.1",
    "react-copy-to-clipboard": "^5.0.2",
    "react-dom": "17.0.1",
    "react-facebook": "https://github.com/thanhtoan1196/react-facebook/tarball/0688995daceda6bf8fa33789a26b578c762ac616",
    "react-hotkeys-hook": "2.2.2",
    "react-scroll-up": "1.3.5",
    "react-share": "^4.3.1",
    "react-social-login": "3.4.10",
    "react-social-login-buttons": "3.1.0",
    "swr": "0.3.0",
    "tailwindcss": "2.0.1",
    "video-react": "^0.14.1",
    "video.js": "^7.10.2"
  },
  "devDependencies": {
    "@next/bundle-analyzer": "10.0.3",
    "@trivago/prettier-plugin-sort-imports": "1.3.0",
    "@types/node": "14.0.27",
    "@types/nprogress": "0.2.0",
    "@types/numeral": "0.0.28",
    "@types/react": "16.9.44",
    "@types/react-copy-to-clipboard": "^5.0.0",
    "@types/video.js": "^7.3.11",
    "@typescript-eslint/eslint-plugin": "4.11.0",
    "@typescript-eslint/parser": "4.1.1",
    "@welldone-software/why-did-you-render": "6.0.3",
    "eslint": "7.16.0",
    "eslint-config-prettier": "7.1.0",
    "eslint-plugin-import": "2.22.1",
    "eslint-plugin-jsx-a11y": "6.4.1",
    "eslint-plugin-prettier": "3.3.0",
    "eslint-plugin-react": "7.21.5",
    "eslint-plugin-react-hooks": "4.2.0",
    "eslint-plugin-simple-import-sort": "5.0.3",
    "husky": "4.3.0",
    "lint-staged": "10.3.0",
    "postcss": "8.1.8",
    "postcss-cli": "8.3.1",
    "postcss-prefixwrap": "1.16.0",
    "prettier": "2.2.1",
    "raw-loader": "4.0.2",
    "tailwindcss-debug-screens": "1.1.0",
    "typescript": "3.9.7"
  }
aralroca commented 3 years ago

@thanhtoan1196 we will have to investigate, apparently next-translate allows another plugin to overwrite the webpack configuration. Thanks to report it

MatthieuVeillon commented 3 years ago

out of curiosity @thanhtoan1196 - how did you end up implementing sentry in _error.tsx ?

All the implementation I see (i.e on the next example repo ) make use of getInitialProps but it leads to this issue https://github.com/vercel/next.js/discussions/18396

Btw @aralroca your library is very nice to play with. Thanks for having thought about the logging part - it makes translation management much easier

thanhtoan1196 commented 3 years ago

@MatthieuVeillon my _error.tsx

import NextErrorComponent from 'next/error';
import * as Sentry from '@sentry/node';
import * as React from "react";
import dynamic from "next/dynamic";
const MyImage = dynamic(import('components/MyImage'));
const BackButton = dynamic(import('components/BackButton'));

const MyError = ({ statusCode, hasGetInitialPropsRun, err }) => {
    if (!hasGetInitialPropsRun && err) {
        // getInitialProps is not called in case of
        // https://github.com/vercel/next.js/issues/8592. As a workaround, we pass
        // err via _app.js so it can be captured
        Sentry.captureException(err);
    }

    return <section className="py-8 text-center h-screen bg-white">
        <div className="mx-auto h-full flex items-center justify-center flex-col pb-16">
            <MyImage
                width="300"
                height="250,46"
                className="mb-8 mx-auto"
                     src={require('res/svg/error.svg')}
                     alt="Không hoạt động" />
            <h2 className="text-3xl mb-2 font-heading mb-4">Không hoạt động</h2>
            <p className="mb-8 text-gray-500 text-xl px-6">
                Trang này hiện tại không truy cập được, <br className="inline sm:hidden"/>vui lòng thử lại sau
            </p>
            <BackButton />
        </div>
    </section>;
};

MyError.getInitialProps = async ({ res, err, asPath }) => {
    const errorInitialProps = await NextErrorComponent.getInitialProps({
        res,
        err
    });

    // Workaround for https://github.com/vercel/next.js/issues/8592, mark when
    // getInitialProps has run
    errorInitialProps.hasGetInitialPropsRun = true;

    // Running on the server, the response object (`res`) is available.
    //
    // Next.js will pass an err on the server if a page's data fetching methods
    // threw or returned a Promise that rejected
    //
    // Running on the client (browser), Next.js will provide an err if:
    //
    //  - a page's `getInitialProps` threw or returned a Promise that rejected
    //  - an exception was thrown somewhere in the React lifecycle (render,
    //    componentDidMount, etc) that was caught by Next.js's React Error
    //    Boundary. Read more about what types of exceptions are caught by Error
    //    Boundaries: https://reactjs.org/docs/error-boundaries.html

    if (res?.statusCode === 404) {
        // Opinionated: do not record an exception in Sentry for 404
        return { statusCode: 404 };
    }
    if (err) {
        Sentry.captureException(err);
        await Sentry.flush(2000);
        return errorInitialProps;
    }

    // If this point is reached, getInitialProps was called without any
    // information about what the error might be. This is unexpected and may
    // indicate a bug introduced in Next.js, so record it in Sentry
    Sentry.captureException(new Error(`_error.js getInitialProps missing data at path: ${asPath}`));
    await Sentry.flush(2000);

    return errorInitialProps;
};

export default MyError;
aralroca commented 3 years ago

we move it to 1.0.3 to avoid blocking 1.0.2