Open Filipoliko opened 1 month ago
Adding console.log({ parentURL })
to esmockLoader's resolve
function normally logs every module that is loaded, however in the reproduction, the conole.log is never called again when changelog-parser is loaded.
This is un-expected because the resolve
function is a hook function called directly by node.js.
I've tried various things but with no success. Tried specifying type: "module"
at the reproduction package.json, tried importing changelog-parser indirectly through an additional "import-then-export-change-parser.js" file, tried removing the content of changelog-parser/index.js and replacing it with something that imports "fs" and calls fs.read, tried updating the test to only mock node:fs... no luck.
The full tests from a fresh cloned esmock are passing here, so the module loader hooks are working normally for many tests there.
Not sure what the solution is but I will keep thinking about it.
Thanks for opening the issue and creating the reproduction sample.
I added the test inside the existing esmock test files and it fails there. I removed the contents of changlog-parser's index.js to be sure that the readline package is not interfering with the terminal output in some way.
Added this inside the "load" hook function and when changelog-parser is loaded, the first the console.log appears in the process output, but the subsequent calls are not seen...
console.log('PASS1', { url })
console.log('PASS2', { url }) // not logged
console.log('PASS3', { url }) // not logged
My intuition is a simplified test could probably be made to demonstrate this as a node.js bug, but I don't have bandwidth at the moment to make one. Perhaps, under certain conditions, node.js wrongly continues the stack before module-loader hooks have returned values.
If you don't mind using unstable newer releases of node, you could try this new module mocking functionality https://github.com/nodejs/node/pull/52848
Thank you for your quick reply, I tried using the native mock.module
, but it has some other issues for a change 😃
https://github.com/nodejs/node/issues/49442#issuecomment-2393456121
Thanks for the update I hope a solution will appear
readline imports fs inside of an iffe and that may be what causes it to early-load fs before it is processed by the module-loader
(function () {
var fs = require('fs')
// ...
}());
The very same use-case seems to be happening with graceful-fs library. I also cannot mock the fs
dependency inside for some reason.
import { test, mock } from 'node:test';
import assert from 'node:assert';
import esmock from 'esmock';
test('graceful-fs', async () => {
const content = 'content';
const fs = await esmock('graceful-fs', {}, {
fs: {
readFileSync: mock.fn(() => content),
},
});
assert.equal(fs.readFileSync(), content);
});
Not sure if patching changelog-parser
will solve the issue as this seems to be happening in more than one dependency. But thank you for trying to solve this, really appreciate it and I am amazed by your activity. The problem seems to be mostly in older libraries with little maintenance, so maybe it is time to let the old ways die and focus on the new stuff :D
I noticed graceful-fs does not require/import readFileSync
, so I tried to to test that with readFile
which is required by graceful-fs, however, my attempt also failed.
Maybe I hold a misunderstanding about the way the moduleLoader works or maybe this is a bug in the nodejs runtime... the next step should be to make a smaller test isolating the inability of the 'resolve' and 'load' hooks to indertict require usages.
There are many esmock tests and it is surprising they are missing whatever is happening here.
This single file mocks the fs
module using a similar flow esmock should follow, but no success updating esmock to do this
For anyone who encounters this issue, you should be fine if your tests apply mocks to ESM targets Per the description, esmock provides: "ESM import and globals mocking for unit tests" but sample tests associated with this issue attempt to mock to import trees that are entirely commonjs.
Many subtle time-consuming adjustments are needed for esmock to possibly handle this one and there's no certainty they would be succesful.
resolve
hook is not always called for commonjs modules. To trigger the resolve
hook for the 'graceful-fs' module, it was necessary for esmock's load
hook to read that module file and to return a source string from the load
hook similar to what is done in the single-file example attached to the previous comment. More testing is needed to confirm details. Simply returning nextLoad(url, context)
from the load
hook does not trigger the resolve
hook.TypeError [Error]: Cannot read properties of undefined (reading 'exports')
at require (node:internal/modules/esm/translators:248:33)
at Object.<anonymous> (/home/bumble/soft/esdock-bug/node_modules/graceful-fs/graceful-fs.js:2:10)
const log = (...args) => (
writeSync(1, JSON.stringify(args, null, ' ').slice(2, -1)))
Hi,
thank you for this great library, thanks to this, we were able to actually use native node test runner in some of our projects!
I ran into an issue, but maybe it is expected behaviour. When I am trying to mock a dependency of a dependency, it does not really work. Maybe the issue is that both dependencies are commonjs modules?
I prepared a reproduction repository - https://github.com/Filipoliko/esmock-bug - but the use-case is quite straightforward. Ultimately, I am trying to mock
fs
module globally (even within dependencies likechangelog-parser
), but I am failing to do so. Being desperate, I tried to mockline-reader
module, which is the one using thefs
module in my case, but this failed also. This is a simplified example, where I was unable to mock the 3rd party dependency of a dependency.Code
Output (Node 22.9.0)
The result is, that there is no mocked
fs
, norline-reader
and the test really tries to openfake
file, which fails.I am trying to use memfs as a replacement for native node
fs
, since we are trying to create some sort of an integration test.