-
Notifications
You must be signed in to change notification settings - Fork 17
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
How will hot reloading be accomplished? #22
Comments
Not totally sure, but this article that explains how I implemented "proxyquire" like functionality for ESM may help. Mocking libraries such as these need to enable importing different module implementations on every |
Thanks for the response - the approach in the quibble article is similar to what I've experimented with that adds query strings to specifiers during the resolve hook. However, the problem with that approach is that, as far as I know, there's no current way of replacing a module in the nodejs registry - when you add or modify the query parameter during resolve, it just instantiates a new module rather than replacing the module at the file path. So Quibble has to expose a global module api since using the nodejs module loader isn't sufficient. The use case I've been exploring is a project similar to nodemon or node-dev, except hot reloading the ES modules that changed rather than restarting the nodejs process. For that, I think that an api would be needed to tell the nodejs module loader that it should re-instantiate the module (including calling getSource and transformSource again for it, and then updating live bindings for the exports to those of the reinstantiated module rather than the original module). In other words, the internal ModuleMap would set a new Module object for a URL. The other part of it would be exposing an api for individual modules to clean up any memory when they are deleted from the module map. The import.meta.hot proposal is similar to this, except I believe (not sure) that it's intended use case is to update live bindings for a module without replacing it in the ModuleMap, rather than for cleaning up memory. I'm sure there are lots of use cases to consider and lots of varying approaches that could be proposed. And I'm not sure if a full replacement of the Module within the ModuleMap would be feasible (eg its unclear what it would do if the new module has different exports than the previous one). If the approach I suggested above is interesting to you, I'm happy to propose an API for it. And if not, I am interested to hear others thoughts on what would be a good api. |
I’m currently working on a new Node.js server framework for the Small Web that is based on ESM loaders and so I’m also interested in this. Just like everyone else, I’m currently just overloading modules using a random query string fragment but this is, of course, a memory leak. While this is perhaps tolerable during development, I’d also like to explore hot reloading in production where updates of the app are made possible without entirely restarting the server. I’m pretty sure you’re far more aware of the considerations around this than I am but if there’s any feedback or assistance I can provide, just let me know and I’d be happy to. |
there is an upcoming PR that implements this functionality and has a test case that allows replacing the global specifiers resolution nodejs/node#39240 ; the JS spec itself does not allow for replacement of values, only new values so indeed you must generate new module source texts and cannot remove references to old source texts. |
@bmeck I read through nodejs/node#39240, but it seems related more to communication between loaders and mocking, rather than with hot reloading? It's quite possible I'm missing something or that we're using different definitions of hot reloading.
I wasn't aware of this - could you share a link to the part of the spec that forbids this? I was under the impression that an API for manipulating the module registry was mostly left undefined rather than forbidden by current specs. |
@aral this is also what I would look to solve for. I think that a module should be modifiable / replaceable without restarting the NodeJS process, since the query string approach results in memory leaks and also requires reloading all dependent modules (not just the changed module) so that they get the new version of the hot reloaded module. Do you have any ideas for an API that would allow for this? Here are some of the thoughts I had when thinking about possible solutions: // One idea
import.meta.reload('./some-file.js').then(() => {
console.log("All loader hooks, including getSource/transformSource were re-run. The resulting module replaced the old module entirely. Live bindings for dependent modules have been updated")
});
// Another idea
import.meta.delete('./some-file.js')
import('./some-file.js').then(() => {
console.log("All loader hooks, including getSource/transformSource were re-run. The resulting module replaced the old module entirely. Live bindings for dependent modules have been updated")
})
// A rough idea of how modules could fully clean up memory when being hot reloaded
import.meta.hot.dispose = function () {
console.log("This module is about to be deleted from the registry and unlinked! Time to clean up any memory")
} |
@joeldenning maybe I'm missing something but doesn't the query string approach fail to reload dependent modules since they are cached? My use case is to reload a local module (along with its dependencies) every time it or its dependencies are modified. While this works for direct changes to the module, I'm not sure how to handle modifications to one of the module's local dependencies (simply re-importing the module doesn't work since it's dependencies are cached, and I don't want to have to require all imports of the module to use query strings for cache busting). Basically I have code that looks like this:
This works for direct edits to Once chained loaders are implemented maybe I can use something like https://github.com/vinsonchuong/hot-esm to solve this |
Yes, the dependent modules are not reinstantiated unless you add a query string to them, too. This is part of why I do not consider the query string approach to be hot reloading - it's just adding more modules to the registry without reloading existing modules. As far as I know, there is no current method of achieving real hot reloading where a module and its dependencies are updated. |
Hi @joeldenning,
Thank you for organizing a set of use-cases as you have. I have not had a moment to review all of what you have collected, but I have another loader (loader339) that I have been meaning to transfer to the @nodejs organization after some improvements are made. It is intended to support the APM use case. We have sort of been using it to help guide the business use-cases (just made it public for your viewing pleasure, but might keep it public since it is already receiving stars in private).
You may need to make your own module graph. That is not really a case I have been working on, but thanks for bringing it up — surely someone will want to get that solved as well. |
I wanted to link a hot-reloading loader which has been sufficient for my use case: https://github.com/vinsonchuong/hot-esm |
I implemented a full-featured hot reloading loader: It supports live bindings like Webpack, and the full |
I see in the use cases documentation that it mentions hot reloading as a use case that you're hoping to support - is there a proposal for how that would be accomplished?
I am interested in it for the various node loaders that I help maintain at https://github.com/node-loader, such as an import maps loader, babel loader, http loader, postcss loader, etc. I've been using a user-land implementation of loader chaining to combine these, but am interested in also hot reloading them. I explored it a bit with adding query parameters to the module urls during
resolve()
, which succeeds in reinstantiating the module but does not delete or replace the older, existing module.I also help maintain SystemJS, which has its own loader implementation, so I'm interested to see if the nodejs implementation has any similarities.
The text was updated successfully, but these errors were encountered: