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

Module Federation SSR is incompatible with webpack-node-externals #2662

Closed
5 tasks done
burnsdy opened this issue Jun 24, 2024 · 2 comments
Closed
5 tasks done

Module Federation SSR is incompatible with webpack-node-externals #2662

burnsdy opened this issue Jun 24, 2024 · 2 comments

Comments

@burnsdy
Copy link

burnsdy commented Jun 24, 2024

Describe the bug

webpack-node-externals is recommended for server-side Node.js applications (see docs). However, setting node_modules as externals in the host app server Webpack config causes issues when used with streaming SSR.

The minimal reproduction repo is based on the @module-federation/examples react-18-ssr repo, which has a shell app and remote app. The shell app performs React 18 streaming SSR using renderToPipeableStream, where UniversalFederationPlugin is used to handle server-side Module Federation. Running the app (yarn start) produces the following warnings/errors:

Warning: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.

TypeError: Cannot read properties of null (reading 'useState')
    at useState (/.../demo-react-18-ssr-externals-issue/node_modules/react/cjs/react.development.js:1622:21)
    at Content (__federation_expose_Content.js:22:76)
    at renderWithHooks (webpack://react-ssr_shell/../node_modules/react-dom/cjs/react-dom-server.node.development.js?:5724:16)
    at renderIndeterminateComponent (webpack://react-ssr_shell/../node_modules/react-dom/cjs/react-dom-server.node.development.js?:5798:15)
    at renderElement (webpack://react-ssr_shell/../node_modules/react-dom/cjs/react-dom-server.node.development.js?:6023:7)
    at renderLazyComponent (webpack://react-ssr_shell/../node_modules/react-dom/cjs/react-dom-server.node.development.js?:6013:3)
    at renderElement (webpack://react-ssr_shell/../node_modules/react-dom/cjs/react-dom-server.node.development.js?:6106:11)
    at renderNodeDestructiveImpl (webpack://react-ssr_shell/../node_modules/react-dom/cjs/react-dom-server.node.development.js?:6181:11)
    at renderNodeDestructive (webpack://react-ssr_shell/../node_modules/react-dom/cjs/react-dom-server.node.development.js?:6153:14)
    at retryTask (webpack://react-ssr_shell/../node_modules/react-dom/cjs/react-dom-server.node.development.js?:6605:5)

These errors are due to multiple instances of react and react-dom in the app, and they occur regardless of whether UniversalFederationPlugin is set to share react and react-dom (in the shell and remote apps).

  • If UniversalFederationPlugin dependency sharing is used, two instances of React dependencies are present: (1) separate shared dependency chunks in server-side bundle, and (2) in node_modules. So even if the react shared dependency chunk is shared to the remote, node_modules dependencies within the host may still use the node_modules React dependencies.
  • If UniversalFederationPlugin dependency sharing is not used, the remote has react and react-dom set as externals in its server Webpack config. In this case, it looks like those dependencies may be reinitialized by the remote? Otherwise I don't know how this error is produced.

This error isn't specific to webpack-node-externals, more generally it occurs when react and react-dom are set as externals within the host app server Webpack config.

Reproduction

https://github.com/burnsdy/demo-react-18-ssr-externals-issue

Used Package Manager

yarn

System Info

System:
    OS: macOS 14.2.1
    CPU: (16) x64 Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz
    Memory: 147.08 MB / 16.00 GB
    Shell: 5.9 - /bin/zsh
  Binaries:
    Node: 18.18.0 - /usr/local/bin/node
    Yarn: 1.22.21 - /usr/local/bin/yarn
    npm: 9.8.1 - /usr/local/bin/npm
  Browsers:
    Chrome: 126.0.6478.63
    Edge: 126.0.2592.68
    Safari: 17.2.1

Validations

@ScriptedAlchemy
Copy link
Member

yeah you cannot externalize shared modules since the module may not exist on the other end.
Anything exposed or shared must be bundled to ensure the dependency can be fetched when needed.

@burnsdy
Copy link
Author

burnsdy commented Jun 24, 2024

yeah you cannot externalize shared modules since the module may not exist on the other end. Anything exposed or shared must be bundled to ensure the dependency can be fetched when needed.

So Webpack externals are not allowed to be used in remote modules?

Is it impossible to use webpack-node-externals in a host app that performs streaming SSR then? This seems like a major limitation for larger apps since it results in many "the request of a dependency is an expression" errors from binary dependencies being included in the bundle.

Is there some Webpack mechanism for pointing transitive react dependencies in node_modules (i.e. other node_modules dependencies that require react or react-dom) to the bundled react shared dependency chunks instead of the node_modules react dependencies? This approach would ensure that the bundled shared dependency chunks are singletons, and that they are correctly shared to remotes without the use of externals.

This issue was closed.
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

2 participants