Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Example on how to make it work in a NX nextjs project in monorepo? #9

Open
victorcarvalhosp opened this issue Dec 15, 2023 · 7 comments

Comments

@victorcarvalhosp
Copy link

victorcarvalhosp commented Dec 15, 2023

Hi! First of all, thanks for developing this cool library, we're facing some hydration errors that are being hard to debug, and hopefully this lib will help with that! We're facing an issue though, we use a NX monorepo, and we're not being able to setup this lib, as soon as we add withHydrationOverlay to the next.config.js file our application stops working.

This is how our next.config.js looks by default:

const {withNx} = require('@nx/next/plugins/with-nx')
const {withSentryConfig} = require('@sentry/nextjs')
const path = require('path')

const isAnalyzingBundle = process.env['ANALYZE'] === 'true'

const regexEqual = (x, y) => {
  return (
    x instanceof RegExp &&
    y instanceof RegExp &&
    x.source === y.source &&
    x.global === y.global &&
    x.ignoreCase === y.ignoreCase &&
    x.multiline === y.multiline
  )
}

/**
 * @type {import('next').NextConfig}
 */
let nextConfig = {
  // We should not increase it, otherwise we run out of Vercel's limits.
  staticPageGenerationTimeout: 60,
  swcMinify: true,
  reactStrictMode: true,
  distDir: 'dist',
  pageExtensions: ['page.tsx', 'page.ts', 'api.tsx', 'api.ts'],

  /*
   Optional build-time configuration options
   https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/#extend-nextjs-configuration
  */
  sentry: {
    // This will be true by default on Sentry 8.0 so meanwhile we need to force its usage
    hideSourceMaps: true,
  },
  webpack: (config) => {
    // Adds rule in order to be able to use svgs using import, otherwise we get an error
    config.module.rules.push({
      test: /\.svg$/i,
      issuer: /\.[jt]sx?$/,
      resourceQuery: {not: [/url/]}, // exclude react component if *.svg?url
      use: ['@svgr/webpack'],
    })

    /*
     * Start of CSS modules classes output format configuration
     * This entire section is needed just to change CSS modules
     * classes output format as seen here:
     * https://stackoverflow.com/a/71450423/10504792
     */
    config.resolve.modules.push(path.resolve('./'))
    const oneOf = config.module.rules.find(
      (rule) => typeof rule.oneOf === 'object'
    )

    if (oneOf) {
      // Find the module which targets *.scss|*.sass files
      const moduleSassRule = oneOf.oneOf.find((rule) =>
        regexEqual(rule.test, /\.module\.(scss|sass)$/)
      )

      if (moduleSassRule) {
        // Get the config object for css-loader plugin
        const cssLoader = moduleSassRule.use.find(({loader}) =>
          loader.includes('css-loader')
        )

        if (cssLoader)
          cssLoader.options = {
            ...cssLoader.options,
            modules: {
              ...cssLoader.options.modules,
              localIdentName: '[local]--[hash:base64:5]',
              // You can also add other css-loader options here
            },
          }
      }
    }

    return config
  },
}

// Next js Bundle Analyzer https://www.npmjs.com/package/@next/bundle-analyzer
const withBundleAnalyzer = require('@next/bundle-analyzer')({
  enabled: true,
})

const plugins = []

if (isAnalyzingBundle) {
  plugins.push(withBundleAnalyzer)
}

module.exports = async (phase, context) => {
  let updatedConfig = plugins.reduce((acc, fn) => fn(acc), nextConfig)

  updatedConfig = await withNx(updatedConfig)(phase, context)
  updatedConfig = withSentryConfig(updatedConfig)

  return updatedConfig
}

We've tried to add the withHydrationOverlay in different ways, and all of them causes errors:

If we add withplugins.push(withHydrationOverlay) we get 404 for all pages.

If we add with

module.exports = async (phase, context) => {
  let updatedConfig = plugins.reduce((acc, fn) => fn(acc), nextConfig)

  updatedConfig = await withNx(updatedConfig)(phase, context)
  updatedConfig = withSentryConfig(updatedConfig)
  updatedConfig = withHydrationOverlay({})(updatedConfig)

  return updatedConfig
}

we receive this warning: Warning: React.jsx: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports. and nothing works with a 500 (Internal Server Error). Tried moving the withHydrationOverlay to different places without success as well.

I know this is probably something related to NX, but maybe it's something you're interested to take a look and have some documentation around it?

@samijaber
Copy link
Contributor

I am completely unfamiliar with the Nx plugin and how it impacts adding other things, so I'm not sure what to suggest here.

Since the only changes made by the plugin is altering the webpack config, we could potentially extract a withHydrationOverlayWebpack(webpackConfig) function that you can provide inside of your webpack configuration directly.

samijaber added a commit that referenced this issue Dec 15, 2023
Should maybe help with #9
@samijaber
Copy link
Contributor

In case having a webpack wrapper adds more flexibility: I released this https://github.com/BuilderIO/hydration-overlay/releases/tag/%40builder.io%2Freact-hydration-overlay%400.0.5

So you might be able to call that directly inside of your webpack config and hopefully that'll work?

@keisha-rw
Copy link

Hi @samijaber! First off, major kudos to you for working on this! 🎉 Hydration errors are so frustrating to debug and I'm excited to use this library.

We also use an Nx monorepo with our Next.js app, and we ran into the same issue that Victor mentioned. We tried using the webpack wrapper, but it seems like the initial setup in hydration-overlay-initializer.js isn't happening soon enough, because we get this error:
image

Overlay line 46 is trying to access window.BUILDER_HYDRATION_OVERLAY.SSR_HTML, but it's undefined, which is why I'm assuming that setup script isn't running soon enough (though I could definitely be wrong there):
image

Our next config has the webpack plugin imported and it's being called in our webpack config section:
image

image

Any thoughts on what might be missing?

Thanks again for all your work! I'll keep poking at this but I'd appreciate any insight you might have!

@samijaber
Copy link
Contributor

Unfortunately, hard to debug this since it seems to be specific to Nx. Can you provide a small repro?

My instinct is that the initializer script is not running, or not running at the right time.

You can confirm that by adding a console.log to this file: node_modules/@builder.io/react-hydration-overlay/dist/hydration-overlay-initializer.js:

+ console.log('initializer script ran');

window.BUILDER_HYDRATION_OVERLAY = {};
window.addEventListener("error", (event)=>{
    const msg = event.message.toLowerCase();
    const isReactDomError = event.filename.includes("react-dom");
    const isHydrationMsg = msg.includes("hydration") || msg.includes("hydrating");
    if (isReactDomError && isHydrationMsg) {
        window.BUILDER_HYDRATION_OVERLAY.ERROR = true;
        let appRootEl = document.querySelector(window.BUILDER_HYDRATION_OVERLAY.APP_ROOT_SELECTOR);
        if (appRootEl) {
            window.BUILDER_HYDRATION_OVERLAY.CSR_HTML = appRootEl.innerHTML;
        }
    }
});
let BUILDER_HYDRATION_OVERLAY_ELEMENT = document.querySelector(window.BUILDER_HYDRATION_OVERLAY.APP_ROOT_SELECTOR);
if (BUILDER_HYDRATION_OVERLAY_ELEMENT) {
    window.BUILDER_HYDRATION_OVERLAY.SSR_HTML = BUILDER_HYDRATION_OVERLAY_ELEMENT.innerHTML;
}

If this script doesn't run, then it means the webpack plugin is not adding the script correctly.

@keisha-rw
Copy link

I dug into this a bit more today, and I noticed this in my console:

error - ../node_modules/@builder.io/react-hydration-overlay/dist/hydration-overlay-initializer.js (2:0) @ eval
error - unhandledRejection: ReferenceError: window is not defined
    at eval (webpack-internal:///../../node_modules/@builder.io/react-hydration-overlay/dist/hydration-overlay-initializer.js:2:1)
    at ../../node_modules/@builder.io/react-hydration-overlay/dist/hydration-overlay-initializer.js (/Users/H726042/git/eks-test-nsk-app/apps/ui/dist/.next/server/node_modules_builder_io_react-hydration-overlay_dist_hydration-overlay-initializer_js.js:20:1)
    at __webpack_require__ (/Users/H726042/git/eks-test-nsk-app/apps/ui/dist/.next/server/webpack-runtime.js:33:42)
    at __webpack_require__.t (/Users/H726042/git/eks-test-nsk-app/apps/ui/dist/.next/server/webpack-runtime.js:139:38)
null

It appears that the initializer script is indeed running, but on the server instead of the client.

@samijaber
Copy link
Contributor

Huh. I am not familiar enough with Nextjs internals to know why this is happening (ONLY for Nx). We should add a check for window in the script to avoid running on the server, but I am not sure if it will run on the client afterwards.

Would you be able to create a minimum reproduction codebase using Nx + Next + react-hydration-overlay that showcases your issue? To make sure it isn't caused by some other dependency in your project and add it to this project to make sure there are no regressions.

@samijaber
Copy link
Contributor

Could your issue be the same as #25 (comment) ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants