createRequire does not respect deleting module from require.cache
Version
v23.10.0
Platform
Microsoft Windows NT 10.0.26100.0 x64
Subsystem
No response
What steps will reproduce the bug?
mod.js
console.log('in mod.ts')
testcase.js
import { createRequire } from 'module'
const require = createRequire(import.meta.url)
require('./mod.js?1')
const key = Object.keys(require.cache)[0]
delete require.cache[key]
require('./mod.js?2')
How often does it reproduce? Is there a required condition?
Every time.
What is the expected behavior? Why is that the expected behavior?
in mod.ts
in mod.ts
What do you see instead?
in mod.ts
Additional information
I traced it to this line, which just fully ignores source.
https://github.com/nodejs/node/blob/eab0fe264bf5e7e105827651bbb6a895354aa9c3/lib/internal/modules/esm/loader.js#L390-L391
For context, this is after I provided source via a custom sync loader which had both resolve and load hooks. I traced the source all the way to this point, and Node.js does not take into account that I had provided a custom source. That's why I consider this a bug.
Also, I'm using a cache busting query already, but it gets stripped away a few functions up when turned into a filename for the sake of requiring it via CJS resolution, so it's apparently ignored.
require only takes file paths or bare specifiers, not URLs, so no query strings would make sense there.
@ljharb right but it's a weird hybrid of require and import, because it's in an ESM project and it's an ESM file being imported, but require is called via createRequire, which goes through the hassle of resolving it as if required, but skips require's actual resolution which would use require.cache (Module._cache), and ultimately finds an ESM module, sees that it's already been resolved, and uses that one's exports. Ironically, it's really only using require-logic to skip the cache busting, but in every other area in this code it's acting as if it's ESM. So it's inconsistent. Either it should fully use ESM resolution and see the cache busting query, or it should fully use require-logic and see that I deleted it from require.cache.
I’m confused, if you want to skip the cache busting just import it normally. The only place it makes any sense to require ESM is inside a CJS file.
@ljharb In this case, I'm using createRequire because it's a sync ESM module (no tlawait) and I'd like to avoid poisining all sync methods involved
I’m still confused - what do you mean by poisoning?
https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/
Interesting, that article gives me a new respect for Go.
ahhh this is something that can't be a static import at the root of the file?
@ljharb Right, it's dynamically imported/required in a sync context.
require('./foo.mjs') also does not honor cache invalidation.
Anyone have a workaround? I'm trying to migrate a module to ESM while providing backward compat and this is breaking tests.