API/api.medcify.app/node_modules/require-in-the-middle/index.js

176 lines
6.4 KiB
JavaScript
Raw Normal View History

2022-09-26 06:11:44 +00:00
'use strict'
const path = require('path')
const Module = require('module')
const resolve = require('resolve')
const debug = require('debug')('require-in-the-middle')
const parse = require('module-details-from-path')
module.exports = Hook
const builtins = Module.builtinModules
const isCore = builtins
? (filename) => builtins.includes(filename)
// Fallback in case `builtins` isn't available in the current Node.js
// version. This isn't as acurate, as some core modules contain slashes, but
// all modern versions of Node.js supports `buildins`, so it shouldn't affect
// many people.
: (filename) => filename.includes(path.sep) === false
// 'foo/bar.js' or 'foo/bar/index.js' => 'foo/bar'
const normalize = /([/\\]index)?(\.js)?$/
function Hook (modules, options, onrequire) {
if ((this instanceof Hook) === false) return new Hook(modules, options, onrequire)
if (typeof modules === 'function') {
onrequire = modules
modules = null
options = null
} else if (typeof options === 'function') {
onrequire = options
options = null
}
if (typeof Module._resolveFilename !== 'function') {
console.error('Error: Expected Module._resolveFilename to be a function (was: %s) - aborting!', typeof Module._resolveFilename)
console.error('Please report this error as an issue related to Node.js %s at %s', process.version, require('./package.json').bugs.url)
return
}
this.cache = new Map()
this._unhooked = false
this._origRequire = Module.prototype.require
const self = this
const patching = new Set()
const internals = options ? options.internals === true : false
const hasWhitelist = Array.isArray(modules)
debug('registering require hook')
this._require = Module.prototype.require = function (id) {
if (self._unhooked === true) {
// if the patched require function could not be removed because
// someone else patched it after it was patched here, we just
// abort and pass the request onwards to the original require
debug('ignoring require call - module is soft-unhooked')
return self._origRequire.apply(this, arguments)
}
const filename = Module._resolveFilename(id, this)
const core = isCore(filename)
let moduleName, basedir
debug('processing %s module require(\'%s\'): %s', core === true ? 'core' : 'non-core', id, filename)
// return known patched modules immediately
if (self.cache.has(filename) === true) {
debug('returning already patched cached module: %s', filename)
return self.cache.get(filename)
}
// Check if this module has a patcher in-progress already.
// Otherwise, mark this module as patching in-progress.
const isPatching = patching.has(filename)
if (isPatching === false) {
patching.add(filename)
}
const exports = self._origRequire.apply(this, arguments)
// If it's already patched, just return it as-is.
if (isPatching === true) {
debug('module is in the process of being patched already - ignoring: %s', filename)
return exports
}
// The module has already been loaded,
// so the patching mark can be cleaned up.
patching.delete(filename)
if (core === true) {
if (hasWhitelist === true && modules.includes(filename) === false) {
debug('ignoring core module not on whitelist: %s', filename)
return exports // abort if module name isn't on whitelist
}
moduleName = filename
} else if (hasWhitelist === true && modules.includes(filename)) {
// whitelist includes the absolute path to the file including extension
const parsedPath = path.parse(filename)
moduleName = parsedPath.name
basedir = parsedPath.dir
} else {
const stat = parse(filename)
if (stat === undefined) {
debug('could not parse filename: %s', filename)
return exports // abort if filename could not be parsed
}
moduleName = stat.name
basedir = stat.basedir
const fullModuleName = resolveModuleName(stat)
debug('resolved filename to module: %s (id: %s, resolved: %s, basedir: %s)', moduleName, id, fullModuleName, basedir)
// Ex: require('foo/lib/../bar.js')
// moduleName = 'foo'
// fullModuleName = 'foo/bar'
if (hasWhitelist === true && modules.includes(moduleName) === false) {
if (modules.includes(fullModuleName) === false) return exports // abort if module name isn't on whitelist
// if we get to this point, it means that we're requiring a whitelisted sub-module
moduleName = fullModuleName
} else {
// figure out if this is the main module file, or a file inside the module
let res
try {
res = resolve.sync(moduleName, { basedir })
} catch (e) {
debug('could not resolve module: %s', moduleName)
return exports // abort if module could not be resolved (e.g. no main in package.json and no index.js file)
}
if (res !== filename) {
// this is a module-internal file
if (internals === true) {
// use the module-relative path to the file, prefixed by original module name
moduleName = moduleName + path.sep + path.relative(basedir, filename)
debug('preparing to process require of internal file: %s', moduleName)
} else {
debug('ignoring require of non-main module file: %s', res)
return exports // abort if not main module file
}
}
}
}
// only call onrequire the first time a module is loaded
if (self.cache.has(filename) === false) {
// ensure that the cache entry is assigned a value before calling
// onrequire, in case calling onrequire requires the same module.
self.cache.set(filename, exports)
debug('calling require hook: %s', moduleName)
self.cache.set(filename, onrequire(exports, moduleName, basedir))
}
debug('returning module: %s', moduleName)
return self.cache.get(filename)
}
}
Hook.prototype.unhook = function () {
this._unhooked = true
if (this._require === Module.prototype.require) {
Module.prototype.require = this._origRequire
debug('unhook successful')
} else {
debug('unhook unsuccessful')
}
}
function resolveModuleName (stat) {
const normalizedPath = path.sep !== '/' ? stat.path.split(path.sep).join('/') : stat.path
return path.posix.join(stat.name, normalizedPath).replace(normalize, '')
}