UNPKG

dd-trace

Version:

Datadog APM tracing client for JavaScript

121 lines (99 loc) 3.44 kB
'use strict' const dc = require('diagnostics_channel') const path = require('path') const semver = require('semver') const iitm = require('../../../dd-trace/src/iitm') const ritm = require('../../../dd-trace/src/ritm') const parse = require('module-details-from-path') const requirePackageJson = require('../../../dd-trace/src/require-package-json') const { AsyncResource } = require('async_hooks') const pathSepExpr = new RegExp(`\\${path.sep}`, 'g') const channelMap = {} exports.channel = function channel (name) { const maybe = channelMap[name] if (maybe) return maybe const ch = dc.channel(name) channelMap[name] = ch return ch } exports.addHook = function addHook ({ name, versions, file }, hook) { file = filename(name, file) const loaderHook = (moduleExports, moduleName, moduleBaseDir) => { moduleName = moduleName.replace(pathSepExpr, '/') const moduleVersion = getVersion(moduleBaseDir) if (moduleName !== file || !matchVersion(moduleVersion, versions)) { return moduleExports } return hook(moduleExports) } ritm([name], loaderHook) cjsPostLoad({ name, versions, file }, hook) iitm([name], loaderHook) } function matchVersion (version, ranges) { return !version || (ranges && ranges.some(range => semver.satisfies(semver.coerce(version), range))) } function getVersion (moduleBaseDir) { if (moduleBaseDir) { return requirePackageJson(moduleBaseDir, module).version } } function filename (name, file) { return [name, file].filter(val => val).join('/') } // TODO this is basically Loader#_getModules + running the hook. DRY up. function cjsPostLoad (instrumentation, hook) { const ids = Object.keys(require.cache) let pkg for (let i = 0, l = ids.length; i < l; i++) { if (ids[i] === instrumentation.name) { hook(require.cache[ids[i]].exports) continue } const id = ids[i].replace(pathSepExpr, '/') if (!id.includes(`/node_modules/${instrumentation.name}/`)) continue if (instrumentation.file) { if (!id.endsWith(`/node_modules/${filename(instrumentation)}`)) continue const basedir = getBasedir(ids[i]) pkg = requirePackageJson(basedir, module) } else { const basedir = getBasedir(ids[i]) pkg = requirePackageJson(basedir, module) const mainFile = path.posix.normalize(pkg.main || 'index.js') if (!id.endsWith(`/node_modules/${instrumentation.name}/${mainFile}`)) continue } if (!matchVersion(pkg.version, instrumentation.versions)) continue hook(require.cache[ids[i]].exports) } } function getBasedir (id) { return parse(id).basedir.replace(pathSepExpr, '/') } if (semver.satisfies(process.versions.node, '>=16.0.0')) { exports.AsyncResource = AsyncResource } else { exports.AsyncResource = class extends AsyncResource { static bind (fn, type, thisArg) { type = type || fn.name return (new exports.AsyncResource(type || 'bound-anonymous-fn')).bind(fn, thisArg) } bind (fn, thisArg = this) { const ret = this.runInAsyncScope.bind(this, fn, thisArg) Object.defineProperties(ret, { 'length': { configurable: true, enumerable: false, value: fn.length, writable: false }, 'asyncResource': { configurable: true, enumerable: true, value: this, writable: true } }) return ret } } }