help icon indicating copy to clipboard operation
help copied to clipboard

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

Open aqilc opened this issue 5 years ago • 15 comments

  • 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 avatar Jun 03 '20 17:06 aqilc

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

aqilc avatar Jun 04 '20 14:06 aqilc

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.

devsnek avatar Jun 04 '20 14:06 devsnek

@AqilCont - is this issue resolved?

PoojaDurgad avatar Oct 21 '20 06:10 PoojaDurgad

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.

aqilc avatar Oct 21 '20 06:10 aqilc

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.

LaKing avatar Feb 08 '21 05:02 LaKing

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.

aqilc avatar Feb 08 '21 13:02 aqilc

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.

Barnie avatar Apr 30 '21 23:04 Barnie

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.

reignmaker avatar Mar 22 '22 19:03 reignmaker

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

Teodor-Iancu avatar Apr 18 '23 11:04 Teodor-Iancu

`./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?

codespearhead avatar Jun 12 '23 05:06 codespearhead

@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.

aqilc avatar Jun 14 '23 09:06 aqilc

@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

tkrotoff avatar Oct 24 '23 21:10 tkrotoff

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

kpeters-cbsi avatar Jan 04 '24 00:01 kpeters-cbsi

@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

mastondzn avatar Jan 15 '24 12:01 mastondzn

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 https://github.com/nodejs/node/pull/51977#issuecomment-2093972575

icetbr avatar Jun 04 '24 02:06 icetbr

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[bot] avatar Dec 02 '24 01:12 github-actions[bot]

using query params with dynamic imports stopped working once i updated to node v22.10.0. anyone got any new ideas?

dh049 avatar Dec 06 '24 00:12 dh049

Honestly, everyone, this is so much easier and a much better experience in competing environments like Bunjs. I have long given up on this outdated engine that refuses to innovate and match others in speed and reliability. Hope all your projects go well though!

aqilc avatar Apr 29 '25 19:04 aqilc

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!

avivkeller avatar Apr 29 '25 19:04 avivkeller