/* eslint-disable no-underscore-dangle */ // see https://github.com/nodejs/node/blob/master/lib/module.js import Module from 'module' // the module in which the require() call originated let requireCaller // all custom registered resolvers let pathResolvers = [] // keep original Module._resolveFilename // @ts-ignore const originalResolveFilename = Module._resolveFilename // override Module._resolveFilename // @ts-ignore Module._resolveFilename = function _resolveFilename(...parameters) { const parent = parameters[1] // store require() caller (the module in which this require() call originated) requireCaller = parent return originalResolveFilename.apply(this, parameters) } // keep original Module._findPath // @ts-ignore const originalFindPath = Module._findPath // override Module._findPath // @ts-ignore Module._findPath = function _findPath(...parameters) { const request = parameters[0] // try to resolve the path with custom resolvers for (const resolve of pathResolvers) { const resolved = resolve(request, requireCaller) if (typeof resolved !== 'undefined') { return resolved } } // and when none found try to resolve path with original resolver const filename = originalFindPath.apply(this, parameters) if (filename !== false) { return filename } return false } export default function registerRequireHook( dotExt: string, resolve: ( path: string, parent: Module ) => { path: string | null; source: string | null } ) { // cache source code after resolving to avoid another access to the fs const sourceCache = {} // store all files that were affected by this hook const affectedFiles = {} const resolvePath = (path, parent) => { // get CommonJS module source code for this require() call const { path: resolvedPath, source } = resolve(path, parent) // if no CommonJS module source code returned - skip this require() hook if (resolvedPath == null) { return undefined } // flush require() cache delete require.cache[resolvedPath] // put the CommonJS module source code into the hash sourceCache[resolvedPath] = source // return the path to be require()d in order to get the CommonJS module source code return resolvedPath } const resolveSource = path => { const source = sourceCache[path] delete sourceCache[path] return source } pathResolvers.push(resolvePath) // keep original extension loader // @ts-ignore const originalLoader = Module._extensions[dotExt] // override extension loader // @ts-ignore Module._extensions[dotExt] = (module, filename) => { const source = resolveSource(filename) if (typeof source === 'undefined') { // load the file with the original loader // @ts-ignore ;(originalLoader || Module._extensions['.js'])(module, filename) return } affectedFiles[filename] = true // compile javascript module from its source module._compile(source, filename) } return function unmout() { pathResolvers = pathResolvers.filter(r => r !== resolvePath) // @ts-ignore Module._extensions[dotExt] = originalLoader Object.keys(affectedFiles).forEach(path => { delete require.cache[path] delete sourceCache[path] delete affectedFiles[path] }) } }