diff --git a/.gitignore b/.gitignore index ba2a97b..280d31c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ node_modules coverage +.snapshots \ No newline at end of file diff --git a/test/example-deps/lib/node_modules/pkg-3/lib/foo.cjs b/test/example-deps/lib/node_modules/pkg-3/lib/foo.cjs new file mode 100644 index 0000000..32dce8e --- /dev/null +++ b/test/example-deps/lib/node_modules/pkg-3/lib/foo.cjs @@ -0,0 +1,8 @@ +class Foo { + constructor() {} + bar() { + return 'baz!' + } +} + +module.exports = Foo \ No newline at end of file diff --git a/test/example-deps/lib/node_modules/pkg-3/lib/foo.js b/test/example-deps/lib/node_modules/pkg-3/lib/foo.js new file mode 100644 index 0000000..fd7338a --- /dev/null +++ b/test/example-deps/lib/node_modules/pkg-3/lib/foo.js @@ -0,0 +1,8 @@ +class Foo { + constructor() {} + bar() { + return 'baz!' + } +} + +export default Foo \ No newline at end of file diff --git a/test/example-deps/lib/node_modules/pkg-3/package.json b/test/example-deps/lib/node_modules/pkg-3/package.json new file mode 100644 index 0000000..c11d3e5 --- /dev/null +++ b/test/example-deps/lib/node_modules/pkg-3/package.json @@ -0,0 +1,14 @@ +{ + "name": "custom-pkg-3-name", + "version": "1.0.0", + "exports": { + "./foo": { + "require": { + "default": "./lib/foo.cjs" + }, + "import": { + "default": "./lib/foo.js" + } + } + } +} \ No newline at end of file diff --git a/test/hook.test.mjs b/test/hook.test.mjs index c0a42fc..aeca709 100644 --- a/test/hook.test.mjs +++ b/test/hook.test.mjs @@ -3,31 +3,32 @@ import test from 'node:test' import assert from 'node:assert' import path from 'node:path' import { readFileSync } from 'node:fs' +import { createRequire } from 'node:module' import Snap from '@matteo.collina/snap' test.beforeEach(async (t) => { const esmLoaderRewriter = await import('../hook.mjs') esmLoaderRewriter.initialize({ instrumentations: [ - { - channelName: 'unitTestEsm', - module: { name: 'esm-pkg', versionRange: '>=1', filePath: 'foo.js' }, - functionQuery: { - className: 'Foo', - methodName: 'doStuff', - kind: 'Async' - } - }, - { - channelName: 'unitTestCjs', - module: { name: 'pkg-1', versionRange: '>=1', filePath: 'foo.js' }, - functionQuery: { - className: 'Foo', - methodName: 'doStuff', - kind: 'Async' - } + { + channelName: 'unitTestEsm', + module: { name: 'esm-pkg', versionRange: '>=1', filePath: 'foo.js' }, + functionQuery: { + className: 'Foo', + methodName: 'doStuff', + kind: 'Async' } - ] + }, + { + channelName: 'unitTestCjs', + module: { name: 'pkg-1', versionRange: '>=1', filePath: 'foo.js' }, + functionQuery: { + className: 'Foo', + methodName: 'doStuff', + kind: 'Async' + } + } + ] }) const snap = Snap(`${import.meta.url}/${t.name}`) @@ -61,7 +62,7 @@ test('should rewrite code if it matches a subscriber and esm module', async (t) }) test('should not rewrite code if it does not match a subscriber and a esm module', async (t) => { - const { esmLoaderRewriter, snap } = t.ctx + const { esmLoaderRewriter, snap } = t.ctx const esmPath = path.join(import.meta.dirname, './example-deps/lib/node_modules/esm-pkg-2/index.js') async function resolveFn() { return { url: `file://${esmPath}` } @@ -152,16 +153,16 @@ test('should not rewrite code if a function query does not exist in file', async const { esmLoaderRewriter, snap } = t.ctx esmLoaderRewriter.initialize({ instrumentations: [ - { - channelName: 'unitTestEsm', - module: { name: 'esm-pkg', versionRange: '>=1', filePath: 'foo.js' }, - functionQuery: { - className: 'Foo', - methodName: 'nonExistentMethod', - kind: 'Async' - } + { + channelName: 'unitTestEsm', + module: { name: 'esm-pkg', versionRange: '>=1', filePath: 'foo.js' }, + functionQuery: { + className: 'Foo', + methodName: 'nonExistentMethod', + kind: 'Async' } - ] + } + ] }) const esmPath = path.join(import.meta.dirname, './example-deps/lib/node_modules/esm-pkg/foo.js') async function resolveFn() { @@ -203,3 +204,85 @@ test('should default initialization to not crash if not defined', async (t) => { const snapshot = await snap(result.source) assert.deepEqual(result.source, snapshot) }) + +test('should rewrite code with conditional exports, cjs', async (t) => { + const { esmLoaderRewriter, snap } = t.ctx + + esmLoaderRewriter.initialize({ + instrumentations: [ + { + channelName: 'unitTestConditional', + module: { name: 'pkg-3', versionRange: '>=1', filePath: 'lib/foo.cjs' }, + functionQuery: { + className: 'Foo', + methodName: 'bar', + kind: 'Sync' + } + } + ] + }) + + const pkgDir = path.join(import.meta.dirname, './example-deps/lib/node_modules') + const require = createRequire(path.join(pkgDir, 'pkg-3', 'package.json')) + + async function resolveFn(specifier, context) { + try { + const resolved = require.resolve(specifier) + return { url: `file://${resolved}` } + } catch (err) { + throw new Error(`Cannot resolve ${specifier}: ${err.message}`) + } + } + async function nextLoad(url, context) { + const filePath = url.startsWith('file://') ? fileURLToPath(url) : url + const data = readFileSync(filePath, 'utf8') + return { + format: 'commonjs', + source: data + } + } + + const url = await esmLoaderRewriter.resolve('pkg-3/foo', {}, resolveFn) + const result = await esmLoaderRewriter.load(url.url, {}, nextLoad) + assert.equal(result.format, 'commonjs') + assert.equal(result.shortCircuit, true) + const snapshot = await snap(result.source) + assert.deepEqual(result.source, snapshot) +}) + +test('should rewrite code with conditional exports, esm', async (t) => { + const { esmLoaderRewriter, snap } = t.ctx + + esmLoaderRewriter.initialize({ + instrumentations: [ + { + channelName: 'unitTestConditional', + module: { name: 'pkg-3', versionRange: '>=1', filePath: 'lib/foo.js' }, + functionQuery: { + className: 'Foo', + methodName: 'bar', + kind: 'Sync' + } + } + ] + }) + + const esmPath = path.join(import.meta.dirname, './example-deps/lib/node_modules/pkg-3/lib/foo.js') + async function resolveFn() { + return { url: `file://${esmPath}` } + } + async function nextLoad(url, context) { + const data = readFileSync(esmPath, 'utf8') + return { + format: 'module', + source: data + } + } + + const url = await esmLoaderRewriter.resolve('pkg-3/foo', {}, resolveFn) + const result = await esmLoaderRewriter.load(url.url, {}, nextLoad) + assert.equal(result.format, 'module') + assert.equal(result.shortCircuit, true) + const snapshot = await snap(result.source) + assert.deepEqual(result.source, snapshot) +}) \ No newline at end of file