Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
# Unreleased

* Fix CommonJS modules loaded with `import()` having empty exports after
`quibble()` was called from the importing file (Node 20.6–24.17 and 25.x)
[#111](https://github.com/testdouble/quibble/issues/111). As a side effect,
if a file that called `quibble()` is part of a circular `require()`, it now
gets the partially loaded module, as it would without quibble, instead of
a second copy

# 0.12.1

* Fix mocking an ES module with a named export whose name isn't an identifier
Expand Down
5 changes: 5 additions & 0 deletions lib/quibble.js
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,11 @@ const requireWasCalledFromAFileThatHasQuibbledStuff = function () {
const doWithoutCache = function (request, parent, thingToDo) {
const filename = Module._resolveFilename(request, parent)
if (Object.prototype.hasOwnProperty.call(Module._cache, filename)) {
if (Module._cache[filename]?.loaded === false) {
// Node's ESM loader caches a CommonJS module before loading it, then
// reads the exports off that cache entry, so load it where it is
return thingToDo()
}
return doAndRestoreCache(filename, thingToDo)
} else {
return doAndDeleteCache(filename, thingToDo)
Expand Down
1 change: 1 addition & 0 deletions test/esm-fixtures/a-cjs-dependency.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = 'the real dependency'
3 changes: 3 additions & 0 deletions test/esm-fixtures/a-cjs-module-with-a-dependency.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// Only ever imported by the test that quibbles its dependency, so that
// import() is what first loads it
module.exports = { dependency: require('./a-cjs-dependency.cjs') }
8 changes: 8 additions & 0 deletions test/esm-lib/quibble-esm.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,14 @@ export default {
assert.equal(result[name], 'replacement')
}
},
'import() a CommonJS module that requires a quibbled module': async function () {
// Node's ESM loader caches a not-yet-loaded CommonJS module before
// requiring it, then reads the exports off that same cache entry
quibble('../esm-fixtures/a-cjs-dependency.cjs', 'a fake dependency')

const { default: result } = await import('../esm-fixtures/a-cjs-module-with-a-dependency.cjs')
assert.equal(result.dependency, 'a fake dependency')
},
'mock a module after it is used unmocked': async function () {
const result1 = await import('../esm-fixtures/a-module.mjs')
assert.deepEqual({ ...result1 }, {
Expand Down
Loading