preactjs / preset-vite

Preset for using Preact with the vite bundler
https://npm.im/@preact/preset-vite
MIT License
260 stars 26 forks source link

HMR not working when using react -> @preact/compat override in package.json #76

Closed bhallstein closed 2 months ago

bhallstein commented 1 year ago

Edit: I initially thought it was a dynamic backend that was causing this issue, this turned out to be wrong. Skip to my next comment as what I wrote here turned out to be a red herring.

Our project uses a dynamic backend. I'm trying to switch it to use preact and @preact/preset-vite is looking like almost a drop-in replacement, which is fairly amazing.

However, HMR isn't working. On https://vitejs.dev/guide/backend-integration.html for HMR with react we have this:


<!-- if development -->
<script type="module" src="http://localhost:5173/@vite/client"></script>
<script type="module" src="http://localhost:5173/main.js"></script>
...

Note if you are using React with @vitejs/plugin-react, you'll also need to add this before the above scripts, since the plugin is not able to modify the HTML you are serving:

html
<script type="module">
  import RefreshRuntime from 'http://localhost:5173/@react-refresh'
  RefreshRuntime.injectIntoGlobalHook(window)
  window.$RefreshReg$ = () => {}
  window.$RefreshSig$ = () => (type) => type
  window.__vite_plugin_react_preamble_installed__ = true
</script>

(When the above is included in our served HTML with this preset in use and react removed, it errors in the browser console, so I assume it is not to be included when using this preset, though I'm not entirely confident in that.)

This leads me to wonder if there is an equivalent piece of code that is missing from our dynamically served backend when using this preset?

marvinhagemeister commented 1 year ago

Thanks for filing an issue. Can you share code or the steps with which we can reproduce the issue? Happy to look into it.

bhallstein commented 1 year ago

Thanks so much for your reply. I tried to reproduce, found I couldn't, then spent a few hours looking into this.

Rather, it turned out that:

  1. I had used overrides in package.json to get around the react-in-peer-deps issue:
"overrides": {
  "react": "npm:@preact/compat@17.1.2",
  "react-dom": "npm:@preact/compat@17.1.2"
}
  1. Because I was converting a previously-react codebase, our top-level jsx file had import {render} from 'react'. 'react' was being resolved by vite to /node_modules/react-dom, which returned a copy of @preact/compat, which had been installed in that folder due to the use of the overrides above.

  2. Changing the import to import {render} from 'preact' caused hmr to start working 🎉

  3. Strangely, before making this change, hmr would work, but only in the top-level file, not in any files imported by it.

I'm not sure how to explain all the above. I suspect that when importing render from react, resolving to @preact/compat due to package.overrides, this contained a slightly different code path compared to when it is imported from preact; and that this was for some reason affecting the top level file and every other file in different ways. I'm noting that this preset aliases react-dom to preact/compat (without the @), it seems the overrides npm option as above interacts badly with this.

That's a bit vague for my liking but I'm not sure I can pin it down any further with only limited knowledge about preact, @preact/compat, preset-vite, and vite itself.

This also leaves me unsure if using overrides as above is a bad approach to have preact installed (and also prevent react being installed) in any other respect. I don't know if there's a recommended way, if so I wonder if it could be that the readme for this plugin could mention?

(I've updated the title and description in this issue to make clear it turned out not to have anything to do with the presence of a dynamic backend.)

Edit: Just removing overrides from package.json and using npm install --legacy-peer-deps did the trick — feel free to close issue.

rschristian commented 2 months ago

Ended up finally tracking this down, after being plagued by it myself.

It seems that Vite will only optimize directly referenced deps in user code, and for some reason, if preact isn't optimized, HMR goes a bit wonky (only in the top level, can be broken if you clear Vite's cache, etc). Adding an import from preact anywhere in your app would fix it, but alternatively, we can make sure in the preset that it's always optimized.