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

TypeError: Cannot read property 'exports' of undefined with experiments.topLevelAwait enabled #432

Closed
wojtekmaj opened this issue Jun 22, 2021 · 9 comments · Fixed by #435 or #493
Closed

Comments

@wojtekmaj
Copy link
Contributor

wojtekmaj commented Jun 22, 2021

When launching the app with @pmmmwh/react-refresh-webpack-plugin, it crashed for me. Interestingly, this happened when I enabled experiments.topLevelAwait in Webpack and introduced one module which uses top level await. Enabling this option and NOT adding this one module does NOT cause the app to crash.

Uncaught (in promise) TypeError: Cannot read property 'exports' of undefined
    at Object.getModuleExports (RefreshUtils.js:10)
    at eval (index.jsx:38)
getModuleExports@ RefreshUtils.js:10
eval @ index.jsx:38
Promise.then (async)
__webpack_require__.a @ src-p.7546d993.js:398
eval @ index.jsx:1
./index.jsx @ src-h.cb557749.js:117
options.factory @ src-p.7546d993.js:981
__webpack_require__ @ src-p.7546d993.js:305
checkDeferredModulesImpl @ src-p.7546d993.js:1609
__webpack_require__.x @ src-p.7546d993.js:1622
__webpack_require__.x @ src-p.7546d993.js:1695
(anonymous) @ src-p.7546d993.js:1705
(anonymous) @ src-p.7546d993.js:1707
12:13:13.659 

I found out that getModuleExports was called with moduleId being undefined. This undefined came from:

const $ReactRefreshModuleId$ = __webpack_require__.$Refresh$.moduleId;
const $ReactRefreshCurrentExports$ = __react_refresh_utils__.getModuleExports(
	$ReactRefreshModuleId$
);

where the entire $Refresh$ object looks like so:

obraz

And here's the same object before this topLevelAwait change:

obraz

I figured out it must be caused by:

  • __webpack_require__.$Refresh$.setup() not being called for some reason
  • __webpack_require__.$Refresh$.cleanup() being called too early for some reason

And it looks like the latter is the cause. I have seen .cleanup() being executed before getModuleExports.

I suppose this has something to do with AsyncModuleRuntimeModule using Promises.

@wojtekmaj wojtekmaj changed the title TypeError: Cannot read property 'exports' of undefined TypeError: Cannot read property 'exports' of undefined with experiments.topLevelAwait enabled Jun 22, 2021
@wojtekmaj
Copy link
Contributor Author

wojtekmaj commented Jun 22, 2021

I don't know how reliable this can prove to be, but changing:

'} finally {',
webpack.Template.indent(`${refreshGlobal}.cleanup(options.id);`),
'}',

to:

                '} finally {',
                '  Promise.resolve().then(() => {',
                webpack.Template.indent(`${refreshGlobal}.cleanup(options.id);`),
                '  });',
                '}',

fixed the crash. However, for some reason, I'm now getting:

react_devtools_backend.js:2560 [HMR] Cannot apply update. Need to do a full reload!

@pmmmwh
Copy link
Owner

pmmmwh commented Jun 27, 2021

Hey, thanks for testing out Top Level Await. Unfortunately to support this feature properly basically the whole runtime needs to be re-written/extended to support full asynchronous behaviour and probably wouldn't land soon (at least not in 0.5.0).

A very brief explanation on why this is hard
  • Top Level Await is implemented as a dangling Promise within the module factory (that would be assigned to module.exports after the whole factory has executed
  • The only reliable way to check whether the module is async or not hence need to be within the finally block which in my opinion is a bit clumsy but at least would work
  • However, because module.exports now carry the Promise for TLA to resolve, the whole HMR module runtime portion needs to be fully async as well, breaking the current approach of doing a static access to the module cache
  • This also might potentially break other things that we've not uncovered yet 😬

Edit:

If you don't mind, can you try the branch here: #435?

@tim-ethitrade
Copy link

@pmmmwh Can confirm using #435 solves this issue (for me at least) 👍

@wojtekmaj
Copy link
Contributor Author

wojtekmaj commented Jul 20, 2021

I tested it on 0.5.0-rc.2 and it's not working for me. I enabled top level await and included one module using it. Then, I did a simple text change in one of my other, synchronous JSX files.

08:38:44.582 index.js:3210 [webpack-dev-server] App updated. Recompiling...
08:38:45.061 index.js:3210 [webpack-dev-server] App updated. Recompiling...
08:38:45.876 index.js:3210 [webpack-dev-server] App hot update...
08:38:45.877 log.js:24 [HMR] Checking for updates on the server...
08:38:45.918 log.js:24 [HMR] Updated modules:
08:38:45.918 log.js:24 [HMR]  - ./components/pages/form/1_privacy/index.jsx
08:38:45.918 log.js:24 [HMR] App is up to date.

Logs looked correct, but the component wasn't actually updated. Tried editing this file again, and:

08:38:50.415 index.js:3210 [webpack-dev-server] App updated. Recompiling...
08:38:50.682 index.js:3210 [webpack-dev-server] App updated. Recompiling...
08:38:51.424 index.js:3210 [webpack-dev-server] App hot update...

and it got stuck on this message.

Removing top level await support makes the update work again just fine.

@pmmmwh
Copy link
Owner

pmmmwh commented Jul 20, 2021

@wojtekmaj Is it possible to provide a reproducible example?

@wojtekmaj
Copy link
Contributor Author

Sure! Sorry for the delay. Reducing my repo to repro repo was quite a task.

repro.zip

@Jack-Works
Copy link
Contributor

Is TLA support stable? I used asyncWebAssembly and had an error too. It generated the following code:

/* provided dependency */ var __react_refresh_utils__ = __webpack_require__("../../node_modules/@pmmmwh/react-refresh-webpack-plugin/lib/runtime/RefreshUtils.js");
var __webpack_async_dependencies__ = __webpack_handle_async_dependencies__([_crypto__WEBPACK_IMPORTED_MODULE_4__]);
_crypto__WEBPACK_IMPORTED_MODULE_4__ = (__webpack_async_dependencies__.then ? await __webpack_async_dependencies__ : __webpack_async_dependencies__)[0];

__webpack_require__.$Refresh$.runtime = /*#__PURE__*/ (....);

where you can see _crypto__WEBPACK_IMPORTED_MODULE_4__ is used before it has been defined. I don't know if it is related to this plugin though.

@pmmmwh
Copy link
Owner

pmmmwh commented Dec 10, 2021

Is TLA support stable? I used asyncWebAssembly and had an error too. It generated the following code:

/* provided dependency */ var __react_refresh_utils__ = __webpack_require__("../../node_modules/@pmmmwh/react-refresh-webpack-plugin/lib/runtime/RefreshUtils.js");
var __webpack_async_dependencies__ = __webpack_handle_async_dependencies__([_crypto__WEBPACK_IMPORTED_MODULE_4__]);
_crypto__WEBPACK_IMPORTED_MODULE_4__ = (__webpack_async_dependencies__.then ? await __webpack_async_dependencies__ : __webpack_async_dependencies__)[0];

__webpack_require__.$Refresh$.runtime = /*#__PURE__*/ (....);

where you can see _crypto__WEBPACK_IMPORTED_MODULE_4__ is used before it has been defined. I don't know if it is related to this plugin though.

crypto seems to be unrelated - at least on our side TLA should be stable but it is not on Webpack's side?

@Jack-Works
Copy link
Contributor

crypto seems to be unrelated - at least on our side TLA should be stable but it is not on Webpack's side?

Oh, crypto is not the Node crypto module. It is just a normal module in our codebase called crypto. I'll try to reproduce it since it is not a known issue.

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