question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

HMR is broken with non-universal import()

See original GitHub issue

Hello there,

I’m not 100% sure that my issue is related to the babel plugin or RUC.

I created the following repository (React SSR app) to demonstrate the issue when executing an import() which is not handled by RUC (ie. as an universal component), that is breaking HMR. I use this import to polyfill the client if required.

Steps to reproduce the issue

  1. git clone https://github.com/cdoublev/react-ssr-debug-hmr
  2. npm start and open your browser
  3. Open src/universal/hello.js in your editor and change the text: no hot update
  4. Open src/index.js in your editor and remove/comment L#5
  5. Change the text of src/universal/hello.js again: hot update

I spend about 3 hours on this issue but I didn’t look at the sources of this plugin, and it’s been a long time since I read the posts from faceyspacey, explaining what it does and how chunks ids are saved and shared between server and client.

I will try to look deeper soon, but as I can easily remove this import() in dev mode, I will first continue the work I was on. But any advice/comments would be appreciated.

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Comments:13

github_iconTop GitHub Comments

1reaction
ScriptedAlchemycommented, Aug 23, 2020

Can you make a PR?

1reaction
cdoublevcommented, Aug 23, 2020

Hello again,

The following change in babel-plugin-universal-import/index.js fixes this HMR issue.

+ function isUniversalImport(p) {
+   const universalCallee = p.findParent(path => path.node.type === 'CallExpression' && path.node.callee.name === 'universal')
+   if (!universalCallee) return false
+   const program = universalCallee.findParent(path => path.node.type === 'Program')
+   return program.node.body.find(node =>
+     node.type === 'ImportDeclaration'
+       && node.specifiers.find(node => node.type === 'ImportDefaultSpecifier')
+       && node.source.value === 'react-universal-component')
+ }
// ...
      visitor: {
       Import(p) {
         if (p[visited]) return
         p[visited] = true
 
+        if(!isUniversalImport(p)) return
// ...

It prevents transforming this:

// src/index.js
import('./notUniversalModule').then(/*...*/))

into this:

babel_plugin_universal_import_universalImport__WEBPACK_IMPORTED_MODULE_2___default()({
    id: "./notUniversalModule",
    load: function load() {
        return Promise.all([
            __webpack_require__.e(/*! import() | notUniversalModule */ "notUniversalModule")
                .then(__webpack_require__.bind(null, /*! ./notUniversalModule */ "./src/notUniversalModule.js"))
            ]).then(function (proms) {
                return proms[0];
            });
    },
    path() {
        return path__WEBPACK_IMPORTED_MODULE_1___default.a.join(__dirname, './notUniversalModule');
    },
    resolve() {
        return /*require.resolve*/(/*! ./notUniversalModule*/ "./src/notUniversalModule.js");
    },
    chunkName() {
        return "notUniversalModule";
    }
}).then(/* ... */);
/* WEBPACK VAR INJECTION */}.call(this, "/"))

The latter will make src/notUniversalModule.js a module.parents of babel-plugin-universal-import/universalImport.js, which is not wanted, and prevents hot reload of all modules (even the modules wrapped with universal) with an unaccepted update.

Adding a unit test would involve updating all tests by adding import universal from 'react-universal-component. Also note that traversing the tree upwards for each import statement will obviously have a performance impact.

As a reminder, the goal of a dynamic import only client side, not handled by this plugin, is for example loading a polyfill. Its “universal” loading isn’t required as it will not change the HTML, and its dynamic loading before running the application (hydration) IS a requirement.

Waiting for your comments.

EDIT: the user could rename the default export of react-universal-component to something else than universal, therefore my suggested fix should handle this (sorry!).

Read more comments on GitHub >

github_iconTop Results From Across the Web

HMR is broken in examples when using components #17
Description After updating to latest, the minimal example and others do not hot update properly when updating an imported component.
Read more >
HMR of Webpack is broken - Stack Overflow
I followed the steps to add webpack-hot-middleware on my SSR React + Express APP but it's not working. server.js import express from ...
Read more >
Development: Hot Module Replacement
import { hmrPlugin } from '@open-wc/dev-server-hmr'; export default { plugins: ... assumptions you can normally make about how your code runs is broken....
Read more >
Code Splitting - webpack
Dynamic Imports: Split code via inline function calls within modules. ... The first and recommended approach is to use the import() syntax that...
Read more >
HMR + Fast Refresh - Snowpack
Hot Module Replacement (HMR) is the ability to push file updates to the browser ... HMR Code Snippet Example if (undefined /* [snowpack]...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found