Skip to content

How do we completely reload/re-import modules in ES6? #2751

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

Closed
aqilc opened this issue Jun 3, 2020 · 19 comments
Closed

How do we completely reload/re-import modules in ES6? #2751

aqilc opened this issue Jun 3, 2020 · 19 comments
Labels

Comments

@aqilc
Copy link

aqilc commented Jun 3, 2020

  • Node.js Version: 14.4.0
  • OS: Windows 10 Pro

How do we do a complete reload of a module in ES6? I don't spot any cache we can access/delete and I haven't found anything helpful on the internet. This is crucial on some of my projects as it lets me re-import files I've edited without restarting the whole project.

import hello from "./some-file";

// . . . *edits hello*

// how to re-import?
import hey from "./some-file";

I have seen some other issues mentioning methods like "hooks" but there isn't much documentation on them and I don't know how to implement them.

@aqilc
Copy link
Author

aqilc commented Jun 4, 2020

Why won't anyone answer? 3 issues have been answered/closed after mine. Is there really no way?

@devsnek
Copy link
Member

devsnek commented Jun 4, 2020

The esm cache is not exposed, and even if it were, it is not mutable. What you want is hot-reload functionality, which has been mentioned in v8:10476.

@PoojaDurgad
Copy link

@AqilCont - is this issue resolved?

@aqilc
Copy link
Author

aqilc commented Oct 21, 2020

Not even close, there's no solution I could find for this project and I just went with complete restarts :<. If there is something new though, I would love some info.

@LaKing
Copy link

LaKing commented Feb 8, 2021

While researching the same subject, I came up with a hack, that works.
It is intended to be a method to develop a function within a running system, where full restarts are time consuming. ...

The key point is that once an import of a file is complete, the same file won't be imported again.
However, if you create a symlink to the file, and import that symlink, it will be considered as a new file, thus it can be imported.

Use dynamic import, and attach the exported function to a variable in your running code.
Once you edit the source code, create a symlink, and run the import again on that symlink, and replace the imported function on the variable.

@aqilc
Copy link
Author

aqilc commented Feb 8, 2021

That's actually a pretty cool solution! I might actually try it. I would always prefer a native solution though :<
The only problem is that I need dynamic imports for a lot of files, and doing this might make the app a lot laggier or it could get very wasteful, so I could have to work around it in the end anyways. Restarts so far haven't been too costly, and they act sort of as a garbage collection too. I'll see if I can make like a private package for this anyways.

@Barnie
Copy link

Barnie commented Apr 30, 2021

While researching the same subject, I came up with a hack, that works.
It is intended to be a method to develop a function within a running system, where full restarts are time consuming. ...

The key point is that once an import of a file is complete, the same file won't be imported again.
However, if you create a symlink to the file, and import that symlink, it will be considered as a new file, thus it can be imported.

Use dynamic import, and attach the exported function to a variable in your running code.
Once you edit the source code, create a symlink, and run the import again on that symlink, and replace the imported function on the variable.

It seems that there may be a problem that memory accumulates every time a changed file is reloaded.

@reignmaker
Copy link

reignmaker commented Mar 22, 2022

What about dynamic import with a searchParams in the module name? Something like this:

const handlers = {
  a: await import(`./sample.mjs`) // note the .mjs extension, the module format
}
handlers.a() // call an exported function, for example

if (someCondition) {
  handlers.a = await import(`./sample.mjs?version=${Number((new Date()))}`) // here we add a `searchParam` (could be anything) to force reimport, must change from the last inport to trigger reimport
}

It's implicitly described in the docs https://nodejs.org/api/modules.html#module-caching-caveats
Not sure of memory leaks/bloat, though.

@Teodor-Iancu
Copy link

Teodor-Iancu commented Apr 18, 2023

What about dynamic import with a searchParams in the module name? Something like this:

const handlers = {
  a: await import(`./sample.mjs`) // note the .mjs extension, the module format
}
handlers.a() // call an exported function, for example

if (someCondition) {
  handlers.a = await import(`./sample.mjs?version=${Number((new Date()))}`) // here we add a `searchParam` (could be anything) to force reimport, must change from the last inport to trigger reimport
}

It's implicitly described in the docs https://nodejs.org/api/modules.html#module-caching-caveats Not sure of memory leaks/bloat, though.

Nice hack, Thank you!
Here my two cents:
Instead of rename files to *.mjs we can use npm install esm to install esm package, which allows to use ES6 modules in Node.js
Then add "start": "node -r esm app.js" to your package.json file. (where app.js is your script you whant to run at start)
Then npm start

@codespearhead
Copy link

`./sample.mjs?version=${Number((new Date()))}`

Conversely, you can just delete the cached module after have used it:

[1] delete require.cache[require.resolve("././sample.js")]

The imported module doesn't even have to be renamed to *.mjs. (Source)

I'm currently using this technique in the entrypoint of this project to leverage hot module replacement.

@aqilc Does [1] solve your problem? If so, would you mind closing this issue?

@aqilc
Copy link
Author

aqilc commented Jun 14, 2023

@codespearhead That project and solution is irrelevant to the problem presented here. "require" is not a global support by ESM, and the project you referenced does not use ESM. Typescript supports ESM imports, but it converts them to CommonJS in compilation. I'm talking about projects strictly with "type": "module" in their package.json. The reason this problem isn't exactly solved is because the main workaround causes a memory leak(albeit a small one), and still doesn't offer a proper solution to resetting the cache of a module, like how you could do in previous versions of Node.

@tkrotoff
Copy link

tkrotoff commented Oct 24, 2023

@reignmaker or simply:

await import(`./sample.mjs?version=${Date.now()}`);

Date.now() returns the number of milliseconds since January 1, 1970, UTC, example: 1698183378232. It's shorter and more readable than Number((new Date())).

Note: this is strange: ?version=... works, ?u=... works, ?X=... works, but ?v=... doesn't work

@kpeters-cbsi
Copy link

FWIW, I tried @tkrotoff 's solution in Typescript and it didn't work. I got an error: Cannot find module './index.ts?version=1704325253966'

@mastondzn
Copy link

mastondzn commented Jan 15, 2024

@kpeters-cbsi works for me, i use tsx to start the node process (with type: "module" ofc), my import looks like this:

import(`./module?version=${Date.now()}`)

note that i am not using a file extension for the import, i tested it with .ts .js .mjs and none worked here, dynamic module works too (also without extension!):

import(`./${file}?version=${Date.now()}`);

not sure about the impacts on memory yet

@icetbr
Copy link

icetbr commented Jun 4, 2024

This landed on node 22: --experimental-require-module. It allows mocha --watch to work with ESM modules, which was broken feature due to require.cache.

Here is the person who found the "fix"
https://joyeecheung.github.io/blog/2024/03/18/require-esm-in-node-js/

The docs
https://nodejs.org/api/modules.html#loading-ecmascript-modules-using-require

Here is where I first saw the merge
nodejs/node#51977 (comment)

Copy link

github-actions bot commented Dec 2, 2024

It seems there has been no activity on this issue for a while, and it is being closed in 30 days. If you believe this issue should remain open, please leave a comment.
If you need further assistance or have questions, you can also search for similar issues on Stack Overflow.
Make sure to look at the README file for the most updated links.

@github-actions github-actions bot added the stale label Dec 2, 2024
@dh049

This comment has been minimized.

@aqilc

This comment has been minimized.

@avivkeller
Copy link
Member

avivkeller commented Apr 29, 2025

Hi @aqilc! I'm sorry you feel that way!

I closed this issue because of inactivity, however, if you are still experiencing problems, I'll be happy to re open.

That being said, I've hidden your comment as it does not regard the Node.js runtime, and is frankly quite rude.

If you have specific ideas or suggestions to enhance the reliability and or speed of the project, please let us know!

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

No branches or pull requests