-
Notifications
You must be signed in to change notification settings - Fork 409
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: Fixed issue with CJS being imported as ESM #2168
Changes from 7 commits
5ca6060
0f12ddc
e33c410
1209698
161cc0e
e3a4227
b3e477c
b6c0484
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
/* | ||
* Copyright 2024 New Relic Corporation. All rights reserved. | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
'use strict' | ||
|
||
/** | ||
* Determines if a given string represents an absolute path to a module. | ||
* | ||
* @param {string} target Path to a module. | ||
* | ||
* @returns {boolean} True if it is an absolute path. | ||
*/ | ||
module.exports = function isAbsolutePath(target) { | ||
const leadChar = target.slice(0, 1) | ||
if (leadChar !== '.' && leadChar !== '/') { | ||
return false | ||
} | ||
|
||
const suffix = target.slice(-4) | ||
/* eslint-disable-next-line */ | ||
if (suffix.slice(-3) !== '.js' && suffix !== '.cjs' && suffix !== '.mjs') { | ||
return false | ||
} | ||
|
||
return true | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
/* | ||
* Copyright 2020 New Relic Corporation. All rights reserved. | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
'use strict' | ||
|
||
// A fake module that will be imported as ESM. Basically, the issue is that | ||
// CJS imported as ESM needs its exports proxied and our `shim.wrapExport` | ||
// needs to recognize the "original" export in order to pass it in to the | ||
// instrumentation. | ||
|
||
function foo() { | ||
return Object.create({ | ||
name() { | ||
return 'foo' | ||
} | ||
}) | ||
} | ||
|
||
// This triplet export replicates they way Fastify solves the CJS utilized in | ||
// ESM issue. It makes it possible to `import foo from './foo.cjs'` or to | ||
// `import { foo } from './foo.cjs'`. It also allows us to replicate the | ||
// issue at hand. | ||
module.exports = foo | ||
module.exports.default = foo | ||
module.exports.foo = foo |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,61 @@ | ||||||||||||||||||||||||
/* | ||||||||||||||||||||||||
* Copyright 2024 New Relic Corporation. All rights reserved. | ||||||||||||||||||||||||
* SPDX-License-Identifier: Apache-2.0 | ||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
import tap from 'tap' | ||||||||||||||||||||||||
import crypto from 'crypto' | ||||||||||||||||||||||||
import path from 'path' | ||||||||||||||||||||||||
import url from 'url' | ||||||||||||||||||||||||
import helper from '../../lib/agent_helper.js' | ||||||||||||||||||||||||
import shimmer from '../../../lib/shimmer.js' | ||||||||||||||||||||||||
import InstrumentationDescriptor from '../../../lib/instrumentation-descriptor.js' | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
let modPath | ||||||||||||||||||||||||
if (import.meta.dirname) { | ||||||||||||||||||||||||
modPath = path.join(import.meta.dirname, 'foo.cjs') | ||||||||||||||||||||||||
} else { | ||||||||||||||||||||||||
modPath = path.join(path.dirname(url.fileURLToPath(import.meta.url)), 'foo.cjs') | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
function instrumentation(shim, resolvedModule) { | ||||||||||||||||||||||||
shim.wrapExport(resolvedModule, function wrapModule(shim, fn) { | ||||||||||||||||||||||||
return function wrappedModule() { | ||||||||||||||||||||||||
// `fn` _should_ be the `foo()` function exported by the module. | ||||||||||||||||||||||||
// If it is anything else, i.e. the proxy object, then we have an error | ||||||||||||||||||||||||
// in our handling of CJS modules as ESM. | ||||||||||||||||||||||||
const foo = fn.apply(this, arguments) | ||||||||||||||||||||||||
const _name = foo.name | ||||||||||||||||||||||||
foo.name = () => { | ||||||||||||||||||||||||
const value = _name.call(foo) | ||||||||||||||||||||||||
return `wrapped: ${value}` | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
return foo | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
}) | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
tap.beforeEach(async (t) => { | ||||||||||||||||||||||||
shimmer.registerInstrumentation({ | ||||||||||||||||||||||||
type: InstrumentationDescriptor.TYPE_GENERIC, | ||||||||||||||||||||||||
moduleName: 'foo', | ||||||||||||||||||||||||
absolutePath: modPath, | ||||||||||||||||||||||||
onRequire: instrumentation | ||||||||||||||||||||||||
}) | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
const agent = helper.instrumentMockedAgent() | ||||||||||||||||||||||||
t.context.agent = agent | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
const { default: foo } = await import('./foo.cjs?v=' + crypto.randomBytes(16).toString('hex')) | ||||||||||||||||||||||||
t.context.mod = foo | ||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should you just import this and then access all 3 different exports and verify it works as expected? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think I understand the question. I have to import it after the "agent" is setup because otherwise the node-newrelic/test/lib/agent_helper.js Lines 180 to 190 in cb21d2c
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. oh so i meant importing the file to get at |
||||||||||||||||||||||||
}) | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
tap.afterEach((t) => { | ||||||||||||||||||||||||
helper.unloadAgent(t.context.agent) | ||||||||||||||||||||||||
}) | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
tap.test('CJS imported as ESM gets wrapped correctly', async (t) => { | ||||||||||||||||||||||||
bizob2828 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||
const { mod } = t.context | ||||||||||||||||||||||||
const instance = mod() | ||||||||||||||||||||||||
t.equal(instance.name(), 'wrapped: foo') | ||||||||||||||||||||||||
}) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
/* | ||
* Copyright 2024 New Relic Corporation. All rights reserved. | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
'use strict' | ||
|
||
const tap = require('tap') | ||
const isAbsolutePath = require('../../lib/is-absolute-path') | ||
|
||
tap.test('verifies paths correctly', async (t) => { | ||
const tests = [ | ||
['./foo/bar.js', true], | ||
['/foo/bar.cjs', true], | ||
['/foo.mjs', true], | ||
['foo', false], | ||
['foo.js', false] | ||
bizob2828 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
] | ||
|
||
for (const [input, expected] of tests) { | ||
t.equal(isAbsolutePath(input), expected) | ||
} | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i'd put this in
lib/util