From 4427f0acbbbec6dc64719eef1119e7b6753760a1 Mon Sep 17 00:00:00 2001 From: chris Date: Thu, 12 Oct 2023 14:15:29 -0700 Subject: [PATCH] add support for custom resolvers --- src/esmockModule.js | 14 ++++++- tests/local/customResolverChild.js | 7 ++++ tests/local/customResolverParent.js | 5 +++ .../esmock.node.loader-custom.test.js | 39 +++++++++++++++++++ tests/tests-node/esmock.node.test.js | 30 +++++++++++++- 5 files changed, 92 insertions(+), 3 deletions(-) create mode 100644 tests/local/customResolverChild.js create mode 100644 tests/local/customResolverParent.js create mode 100644 tests/tests-node/esmock.node.loader-custom.test.js diff --git a/src/esmockModule.js b/src/esmockModule.js index 6f47d054..938cab66 100644 --- a/src/esmockModule.js +++ b/src/esmockModule.js @@ -108,6 +108,14 @@ const esmockModuleCreate = async (treeid, def, id, fileURL, opt) => { return mockModuleKey } +const esmockCustomResolvers = (id, parent, resolvers) => { + if (!Array.isArray(resolvers) || !resolvers.length) + return null + + return resolvers[0](id, parent) + || esmockCustomResolvers(id, parent, resolvers.slice(1)) +} + const esmockModuleId = async (parent, treeid, defs, ids, opt, mocks, id) => { ids = ids || Object.keys(defs) id = ids[0] @@ -115,7 +123,8 @@ const esmockModuleId = async (parent, treeid, defs, ids, opt, mocks, id) => { if (!id) return mocks - const fileURL = resolvewith(id, parent) + const fileURL = esmockCustomResolvers(id, parent, opt.resolvers) + || resolvewith(id, parent) if (!fileURL && opt.isModuleNotFoundError !== false && id !== 'import') throw esmockErr.errModuleIdNotFound(id, parent) @@ -125,7 +134,8 @@ const esmockModuleId = async (parent, treeid, defs, ids, opt, mocks, id) => { } const esmockModule = async (moduleId, parent, defs, gdefs, opt) => { - const moduleFileURL = resolvewith(moduleId, parent) + const moduleFileURL = esmockCustomResolvers(moduleId, parent, opt.resolvers) + || resolvewith(moduleId, parent) if (!moduleFileURL) throw esmockErr.errModuleIdNotFound(moduleId, parent) diff --git a/tests/local/customResolverChild.js b/tests/local/customResolverChild.js new file mode 100644 index 00000000..15c80096 --- /dev/null +++ b/tests/local/customResolverChild.js @@ -0,0 +1,7 @@ +const isMocked = false +const isCustomResolverChild = true + +export default { + isCustomResolverChild, + isMocked +} diff --git a/tests/local/customResolverParent.js b/tests/local/customResolverParent.js new file mode 100644 index 00000000..32f1c7f9 --- /dev/null +++ b/tests/local/customResolverParent.js @@ -0,0 +1,5 @@ +import child from 'RESOLVECUSTOM' + +export { + child +} diff --git a/tests/tests-node/esmock.node.loader-custom.test.js b/tests/tests-node/esmock.node.loader-custom.test.js new file mode 100644 index 00000000..a918609a --- /dev/null +++ b/tests/tests-node/esmock.node.loader-custom.test.js @@ -0,0 +1,39 @@ +import path from 'path' +import test from 'node:test' +import assert from 'node:assert/strict' +import module from 'node:module' +import esmock from 'esmock' + +function resolverCustom (moduleId, parent) { + return /RESOLVECUSTOM$/.test(moduleId) && new URL( + path.resolve( + new URL(parent).pathname, + '../../local/customResolverChild.js'), + parent + ).href +} + +async function resolve (specifier, context, next) { + return next( + specifier === 'RESOLVECUSTOM' + ? resolverCustom(specifier, context.parentURL) + : specifier, context) +} + +module.register(` +data:text/javascript, +import path from 'node:path'; +${encodeURIComponent(resolverCustom)} +export ${encodeURIComponent(resolve)}`.slice(1)) + +test('should use custom resolver', async () => { + const customResolverParent = await esmock( + '../local/customResolverParent.js', {}, { + RESOLVECUSTOM: ({ isMocked: true }) + }, { + resolvers: [resolverCustom] + }) + + assert.ok(customResolverParent.child.isCustomResolverChild) + assert.ok(customResolverParent.child.isMocked) +}) diff --git a/tests/tests-node/esmock.node.test.js b/tests/tests-node/esmock.node.test.js index bd502ebb..dc8ff3af 100644 --- a/tests/tests-node/esmock.node.test.js +++ b/tests/tests-node/esmock.node.test.js @@ -4,7 +4,7 @@ import assert from 'node:assert/strict' import esmock from 'esmock' import sinon from 'sinon' import esmockCache from '../../src/esmockCache.js' - +/* test('should mock node:process', async () => { // has direct and in-direct calls to `process.cwd()` const thingBeingTested = await esmock('../local/usesNodeProcess.js', {}, { @@ -536,3 +536,31 @@ test('should mock scoped package, @aws-sdk/client-s3 (deep)', async () => { assert.strictEqual(scopedClientS3.mocked, 'mock client') }) +*/ +test('should use custom resolver', async () => { + const customResolverParent = await esmock( + '../local/customResolverParent.js', {}, { + 'form-urlencoded': ({ isMocked: true }) + }, { + resolvers: [ + (moduleId, parent) => { + const pathParent = new URL(parent).pathname + const pathChild = path.resolve( + pathParent, '../../local/customResolverChild.js') + + if (/CUSTOMLOADER$/.test(moduleId)) { + console.log('resolved path', new URL(pathChild, parent).href) + } + + return /CUSTOMLOADER$/.test(moduleId) + ? new URL(pathChild, parent).href + // ? path.join(path.dirname(parent), '../local/customResolverChild.js') + : null + } + ] + }) + + console.log('child', customResolverParent.child) + assert.ok(customResolverParent.child.isCustomResolverChild) + assert.ok(customResolverParent.child.isMocked) +})