webpack: commonjs Modules Do Not Work Correctly With externalsType of 'promise'
Bug report
What is the current behavior?
EDIT: As per this comment below this behavior seems to be isolated to commonjs modules (and possibly other non-esm modules).
When using externalsType: 'promise'
, all known used exports get wrapped in an async
function, and their externals
dependencies are awaited. Modules marked as __unused_webpack_module
do not get wrapped with async
, and do not await
their dependencies.
This can be resolved by the upstream dependency doing a different thing in their package.json and exports
, but it seems unreasonable to expect that to be the case for all dependencies. Quite often upstream dependencies do have exports that are marked as unused, even if they actually are used. These exports will break when externalsType
is set to promise
.
If the current behavior is a bug, please provide the steps to reproduce.
- Pull down this repo: https://github.com/stevematney/webpack-promise-externals-bug-example
- Run
npm install && npm start
- Open
localhost:9000
in a browser. - Because we are not also externalizing
react-dom
, our build does not run.
Notice in the resulting webpack bundle we see react-dom
defined with this pattern:
/***/
"./node_modules/react-dom/cjs/react-dom.development.js": /*!*************************************************************!*\
!*** ./node_modules/react-dom/cjs/react-dom.development.js ***!
\*************************************************************/
/***/
((__unused_webpack_module,exports,__webpack_require__)=>{
"use strict";
/** @license React v17.0.2 ... */
Whereas our base module is defined with this pattern:
/***/
"./src/index.js": /*!**********************!*\
!*** ./src/index.js ***!
\**********************/
/***/
((module,__webpack_exports__,__webpack_require__)=>{
"use strict";
__webpack_require__.a(module, async(__webpack_handle_async_dependencies__,__webpack_async_result__)=>{
See that react-dom
is missing the async
setup. When it loads __webpack_require__('react')
, it gets a Promise which it does not await
, thus breaking when it looks for ReactSharedInternals.ReactCurrentDispatcher
. ReactSharedInternals
gets defined as undefined
instead of the property of the react
dependency that it should be.
What is the expected behavior?
All modules should correctly resolve external dependencies using the async setup pattern, whether or not these modules are unused.
Other relevant information:
webpack version: 5.75.0 Node.js version: 16.18.1 Operating System: macOS Ventura - 13.0.1 (22A400)
About this issue
- Original URL
- State: open
- Created 2 years ago
- Comments: 24 (11 by maintainers)
Commits related to this issue
- New: initial pass at unpromised externals working around webpack/webpack#16544 — committed to im-open/isolated-externals-plugin by stevematney a year ago
- New: initial pass at unpromised externals working around webpack/webpack#16544 — committed to im-open/isolated-externals-plugin by stevematney a year ago
- New: initial pass at unpromised externals working around webpack/webpack#16544 — committed to im-open/isolated-externals-plugin by stevematney a year ago
- New: initial pass at unpromised externals working around webpack/webpack#16544 — committed to im-open/isolated-externals-plugin by stevematney a year ago
Just an update: we worked around this issue in our plugin by using a special loader which gets applied if any of the parents of an external are non-esm. The loader preloads the externals into a synchronous object before running the original entry code. The downstream dependencies are overwritten to reference this synchronous object instead of the Promised externals.
Is it possible to reopen this without opening a new issue? This is definitely still a thing we are working around.
Yeah, I see … to be honest I don’t see a universal solution here… anyway give me time to think about it, hard solution is copy/paste externals plugin code and change code (but yes, I undestand, it is an unwanted solution)
hm, do you want to fix it or undestand why it doesn’t work with commonjs?
Solution - use
externals
as a function:It happens because you say webpack to wrap
react
inPromise.resolve(...)
, and module is commonjs format, due this you need to useawait
in soure code in this case (you can enable topLevelAwait), also you can look at source code and found that you don’t have__webpack_handle_async_dependencies__
in commonjsYou can also use: