BuilderIO / hydration-overlay

Overlay for hydration errors with explicit diff between renders.
https://www.builder.io/blog/announcing-react-hydration-overlay
MIT License
500 stars 17 forks source link

Not compatible with Webpack's splitChunks.cacheGroups config #36

Closed mlmmn closed 10 months ago

mlmmn commented 10 months ago

hydration-overlay-initializer.js relies on presence of react-dom string in event filename, namely const isReactDomError = event.filename.includes("react-dom");. This won't work when chunk that contains ReactDOM lib is renamed. Below is an extract from a real world Webpack config:

const vendorNames = [
    'react',
    'react-dom',
    'react-router-dom',
    'react-router',
    'react-helmet-async'
];

const webpackConfig = {
    optimization: {
        splitChunks: {
            chunks: 'all',
            cacheGroups: {
                vendor: {
                    test: new RegExp(
                        `[\\/]node_modules[\\/](${vendorNames.join('|')})[\\/]`
                    ),
                    name: 'vendor',
                    chunks: 'all',
                    enforce: true
                }
            }
        }
    }
};

This config puts listed dependencies inside vendor.chunk.js module which obviously does not satisfy aforementioned condition. To mitigate this issue within workbase that I'm working on, I copied the contents of hydration-overlay-initializer.js script and modified it like so:

const HYDRATION_ERROR_REGEX_LIST = [
    /^Uncaught Error: Hydration failed/,
    /^Uncaught Error: There was an error while hydrating/
];

window.BUILDER_HYDRATION_OVERLAY = {};
window.addEventListener('error', event => {
    // Removed `react-dom` condition entirely
    const isHydrationMsg = HYDRATION_ERROR_REGEX_LIST.some(regexp => regexp.test(event.message));

    if (isHydrationMsg) {
        window.BUILDER_HYDRATION_OVERLAY.ERROR = true;
        const appRootEl = document.querySelector('#hydrate-root');
        if (appRootEl) {
            window.BUILDER_HYDRATION_OVERLAY.CSR_HTML = appRootEl.innerHTML;
        }
    }
});
const BUILDER_HYDRATION_OVERLAY_ELEMENT = document.querySelector('#hydrate-root');
if (BUILDER_HYDRATION_OVERLAY_ELEMENT) {
    window.BUILDER_HYDRATION_OVERLAY.SSR_HTML = BUILDER_HYDRATION_OVERLAY_ELEMENT.innerHTML;
}

Then this module is simply imported within bootstrap code. I believe this is not an ideal solution, but I hope it helps to shed some light on the issue as well as iterating to find a decent solution :v:

samijaber commented 10 months ago

You're right. I think we can get away with removing the filename check completely. Any error message with "hydration" or "hydrating" should be good enough of a condition IMO. Would you be interested in putting up a PR? Should be a one-liner 😃

mlmmn commented 10 months ago

@samijaber just opened a PR :v: