testdouble / quibble

Makes it easy to replace require'd dependencies.
93 stars 26 forks source link

exports are empty when ESM loader loads a cjs package which uses a quibbled module #111

Open stixx200 opened 5 months ago

stixx200 commented 5 months ago

I want to fake the fs package using memfs. My production code uses fs-extra.

The require-stack looks like: app.js (ESM) -> fs-extra (CJS) -> graceful-fs (CJS) -> fs (Built-In)

Reproduction

Example:

import quibble from "quibble";
import { vol, fs as fsm } from "memfs";

vol.fromJSON({
    "foo.txt": "foo",
});

quibble("fs", { ...fsm });

const fse = (await import("fs-extra")).default;
await fse.stat("foo.txt");

In this use case, the ESM-Loader loads the fs-extra module with it's internal cjs loader. Because quibble restores the Module._cache entry, the cjs loader only returns an empty object for the exports: TypeError: fse.stat is not a function

Hint:

If I remove the line delete Module._cache[filename] (in doAndRestoreCache) my use-case works. I think the ESM Loader reads the exports from the cached value after quibble deleted it.

If I use require to load fs-extra, there is no problem:

const require = createRequire(import.meta.url);
const fse = require("fs-extra");
// ...