mock-require-lazy
Version:
Simple, intuitive mocking of Node.js modules. Fork of mock-require adding lazy require and is a drop in replacement for mock-require
89 lines (88 loc) • 3.38 kB
JavaScript
import getCallerFile from 'get-caller-file';
import _Module from 'module';
import normalize from 'normalize-path';
import { dirname, join, resolve, sep } from 'path';
const Module = _Module;
let mockExports = {};
let pendingMockExports = {};
// biome-ignore lint/suspicious/noShadowRestrictedNames: Legacy
const hasOwnProperty = {}.hasOwnProperty;
const startsWith = (string, check)=>string.lastIndexOf(check, 0) === 0;
const originalLoader = Module._load;
Module._load = function(request, parent) {
// biome-ignore lint/complexity/noArguments: Apply arguments
if (!parent) return originalLoader.apply(this, arguments);
const fullFilePath = getFullPathNormalized(request, parent.filename);
if (hasOwnProperty.call(pendingMockExports, fullFilePath)) {
const pending = pendingMockExports[fullFilePath];
const mockExport = pending.lazy ? pending.mockExport() : pending.mockExport;
mockExports[fullFilePath] = typeof mockExport === 'string' ? require(getFullPathNormalized(mockExport, pending.calledFrom)) : mockExport;
delete pendingMockExports[fullFilePath];
}
// biome-ignore lint/complexity/noArguments: Apply arguments
return hasOwnProperty.call(mockExports, fullFilePath) ? mockExports[fullFilePath] : originalLoader.apply(this, arguments);
};
function startMocking(path, mockExport, lazy) {
const calledFrom = getCallerFile();
pendingMockExports[getFullPathNormalized(path, calledFrom)] = {
mockExport: mockExport,
calledFrom: calledFrom,
lazy: lazy
};
}
function stopMocking(path) {
const calledFrom = getCallerFile();
const fullPath = getFullPathNormalized(path, calledFrom);
delete pendingMockExports[fullPath];
delete mockExports[fullPath];
}
function stopMockingAll() {
mockExports = {};
pendingMockExports = {};
}
function reRequire(path) {
const module1 = getFullPathNormalized(path, getCallerFile());
delete require.cache[require.resolve(module1)];
return require(module1);
}
function isInNodePath(resolvedPath) {
if (!resolvedPath) return false;
return Module.globalPaths.map((nodePath)=>resolve(process.cwd(), nodePath) + sep).some((fullNodePath)=>startsWith(resolvedPath, fullNodePath));
}
function getFullPath(path, calledFrom) {
let resolvedPath;
try {
resolvedPath = require.resolve(path);
} catch (_e) {
// do nothing
}
const isLocalModule = /^\.{1,2}[/\\]?/.test(path);
const isInPath = isInNodePath(resolvedPath);
const isExternal = !isLocalModule && /[/\\]node_modules[/\\]/.test(resolvedPath);
const isSystemModule = resolvedPath === path;
if (isExternal || isSystemModule || isInPath) {
return resolvedPath;
}
if (!isLocalModule) {
return path;
}
const localModuleName = join(dirname(calledFrom), path);
try {
return Module._resolveFilename(localModuleName);
} catch (e) {
if (isModuleNotFoundError(e)) {
return localModuleName;
}
throw e;
}
}
function getFullPathNormalized(path, calledFrom) {
return normalize(getFullPath(path, calledFrom));
}
function isModuleNotFoundError(e) {
return e.code && e.code === 'MODULE_NOT_FOUND';
}
module.exports = startMocking;
module.exports.stop = stopMocking;
module.exports.stopAll = stopMockingAll;
module.exports.reRequire = reRequire;