-
-
Save guybedford/e6687e77d60739f27d1b5449a2872bf4 to your computer and use it in GitHub Desktop.
const mocks = Object.create(null); | |
global.mock = function (_mocks) { | |
Object.assign(mocks, _mocks); | |
}; | |
export async function resolve (specifier, context, parentResolve) { | |
if (mocks[specifier]) { | |
return { url: 'mock:' + specifier }; | |
} | |
return parentResolve(specifier, context); | |
} | |
export async function getFormat(url, context, parentGetFormat) { | |
if (url.startsWith('mock:')) { | |
return { format: 'dynamic' }; | |
} | |
return parentGetFormat(url, context, parentGetFormat); | |
} | |
export async function dynamicInstantiate (url) { | |
const obj = mocks[url.slice(5)]; | |
const exports = Object.keys(obj); | |
return { | |
exports, | |
execute (ns) { | |
for (const key of exports) | |
ns[key].set(obj[key]); | |
} | |
} | |
} |
node --loader ./mock-loader.mjs test.mjs | |
# Alternatively, locate at node_modules/mock-loader/loader.mjs with a package.json "exports": "./loader.mjs" set | |
# Then use something like: | |
# NODE_OPTIONS="--loader mock-loader" node test.mjs |
mock({ | |
express: { | |
custom: 'express' | |
} | |
}); | |
import('express').then(x => console.log(x)); |
Will the loader and the main process share two different set of globals? How could the same API keep working if so? The important part is the the fact that any closures that are passed through mock() are retained.
They can't! Rather, the main thread mock
call would use a unique identifier that can serialize to the loader thread, that then coordinates that the binding run through the mock
call is used for the main thread module instance. That is, the binding is still created, setup and shared on the main thread, but the loader on the other thread has to coordinate that through a serialization interface over having access to the binding explicitly.
My biggest concern is that without mocking capabilities, the only viable approach to testing esm is to use heavy transpilation similar to what jest does, which is not really esm anyway. (The distance of what jest executes vs what node executes is significant).
If you consider the mocking example above enough capability, then I can guarantee that the capability will remain possible despite any future API changes. The API just won't necessarily be stable. It should be possible for a userland mocking API to be created that provides a stable interface to users working over all the unstable versions. Happy to discuss collaboration further there too if you like.
The distance of what jest executes vs what node executes is significant
Can you clarify your point here further?
Can you clarify your point here further?
Jest mocks rely on transpiling instead of using any proper real-time esm mechanism. Essentially they are fundamentally incompatibile with Node.js esm implementation - they sidestep it completey as they are fully transpiling everything.
Will the loader and the main process share two different set of globals? How could the same API keep working if so? The important part is the the fact that any closures that are passed through
mock()
are retained.My biggest concern is that without mocking capabilities, the only viable approach to testing esm is to use heavy transpilation similar to what jest does, which is not really esm anyway. (The distance of what jest executes vs what node executes is significant).