-
-
Notifications
You must be signed in to change notification settings - Fork 6.2k
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
feat(hmr): improve circular import updates #14867
Conversation
Run & review this pull request in StackBlitz Codeflow. |
This comment was marked as outdated.
This comment was marked as outdated.
/ecosystem-ci run |
📝 Ran ecosystem CI on
|
Amazing work on HMR all around for Vite 5! It looks good to me, but there is a new fail in plugin react that may be related: https://github.com/vitejs/vite-ecosystem-ci/actions/runs/6783049879/job/18436527990. It would be good if @ArnaudBarre gets some time to review this one, and then maybe we could ping Evan to speed up merging it as part of the major. |
I ran the plugin-react's test locally and it's working fine. Could be a fluke 🤔 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great!
@@ -0,0 +1,3 @@ | |||
import { value as _value } from './mod-c' | |||
|
|||
export const value = `mod-b -> ${_value}` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I add import.meta.hot?.accept(() => {})
here, the full refresh starts happening.
index.js (boundary) -> mod-a.js -> mod-b.js (boundary) -> mod-c.js -> mod-a.js
It might be good to improve this case as well in future.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah this one is tricky currently since the two boundary could start fetching at the same time, causing potential execution order issues. We could detect and only refresh index.js
as the boundary, but I think it's also nice that this triggers a page reload + HMR debug logs so they can fix this and get fine-grained HMR in the first place.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's also nice that this triggers a page reload + HMR debug logs so they can fix this and get fine-grained HMR in the first place.
Ah, that's a good point.
Co-authored-by: 翠 / green <green@sapphi.red>
For the code, great, very clear thanks! For the feature, I will just say that I don't think we're helping people in the long term to add a patch for circular imports. People using circular imports are also adding really strong requirement on bundlers, and they will miss perf improvement on that side because new bundlers will not always have the edge cases right (ex. esbuild). |
Oops, I forgot about this comment and merged this one. 😅
@patak-dev Do you think we should wait for Evan? or was it because of the plugin-react's (flaky?) fail? |
@ArnaudBarre I think we should still add more nudges in the docs for people to avoid circular imports (maybe in the performance or troubleshooting guide?). The PR doesn't feel like a patch though complexity-wise, so I think it was good merging it. @sapphi-red we are good 👍🏼 |
Yeah I think the PR is leaning towards the real fix for the problem. The previous reload and circular detection was more of a patch that made HMR not ideal for those case. I agree though that circular imports is not great in general, and hopefully the explicit reloads compared to the silent fails before will bring more attention to the issue. |
Thanks for improving this. Is there a canary release where we can test this out? |
The latest Vite 5 beta should already include this |
@bluwy I just tried 5.0 out an a large codebase with a lot of circular imports. Unfortunately we can end up in an infinite loop when changing certain files – vite becomes irresponsive and the only solution is to kill vite. I'll try to reproduce this on a smaller example that I can share. But maybe this can already now guide us on the right track to fix this, by adding a log line for one of our files function isNodeWithinCircularImports(node, nodeChain, currentChain = [node], recursion=0) {
if (node.url === '/src/vite/index.tsx') {
console.log("isNodeWithinCircularImports 0", node.url, {nodeChainLength: nodeChain.length, currentChainLength: currentChain.length, recursion});
} Then I basically see the same file being analyzed with varying currentChain length but a stable nodeChain length. I also instrumented the recursion level but that doesn't explode – we just seem to be cycling around in the same nodes for some reason. Let me know how I can help to fix this. |
This is expected that the nodeChain is stable for a given node and then all the rest of the graph is explored. There is probably an issue with barrel exports where a long part of the graph is explored repetitively. Can you try to update your patch with this? function isNodeWithinCircularImports(node, nodeChain, currentChain = [node], traversedModules = new Set()) {
if (traversedModules.has(node)) return false
traversedModules.add(node)
// [...]
isNodeWithinCircularImports(importer, nodeChain, currentChain.concat(importer), traversedModules) |
@ArnaudBarre that works as expected, thanks! PR up to fix this: #15034 |
Description
fix #10208
fix #7893
fix #3033
fix #10118 (not quite an exact fix but I think close enough, the issue has a lot of circular imports and fast refresh warnings that's causing things to update slowly)
When propagating HMR updates, we only propagate until we reach HMR-accepted modules, or the root (which we do a full reload.
A HMR-accepted module doesn't need a full reload as it can update itself. However, if the accepted module is within an import loop, updating itself can break the execution order of the import loop. This PR detects this and forces a full reload instead.
Any other import loops without a HMR-accepted module are fine. For example,
If we update
b.js
andApp.vue
accepts HMR,App.vue
will refetcha.js?t=123
and sincea.js -> b.js -> c.js -> a.js
is its own self-contained loop, the execution order and state should be fine.If
b.js
(or any within the loop) accepts HMR, we can't guarantee the execution order anymore, e.g. it will refetchb.js?t=123
, causingb.js -> c.js -> a.js
(import order matters)Additional context
Tests ported from #1477. The commit that fixes that issue introduces the full reload, but another the issue was that the leaf of circular imports wasn't properly invalidated. It is now properly invalidated, but I don't think we always need a full reload.
What is the purpose of this pull request?
Before submitting the PR, please make sure you do the following
fixes #123
).