jest-resolve
Advanced tools
Comparing version 28.0.0-alpha.3 to 28.0.0-alpha.4
@@ -6,3 +6,3 @@ 'use strict'; | ||
}); | ||
exports.default = defaultResolver; | ||
exports.default = void 0; | ||
@@ -53,3 +53,3 @@ function _path() { | ||
*/ | ||
function defaultResolver(path, options) { | ||
const defaultResolver = (path, options) => { | ||
// Yarn 2 adds support to `resolve` automatically so the pnpResolver is only | ||
@@ -77,3 +77,5 @@ // needed for Yarn 1 which implements version 1 of the pnp spec | ||
return (0, _fileWalkers.realpathSync)(result); | ||
} | ||
}; | ||
var _default = defaultResolver; | ||
/* | ||
@@ -83,2 +85,4 @@ * helper functions | ||
exports.default = _default; | ||
function readPackageSync(_, file) { | ||
@@ -85,0 +89,0 @@ return (0, _fileWalkers.readPackageCached)(file); |
@@ -64,2 +64,6 @@ /** | ||
): string | null; | ||
static findNodeModuleAsync( | ||
path: string, | ||
options: FindNodeModuleConfig, | ||
): Promise<string | null>; | ||
static unstable_shouldLoadAsEsm: typeof cachedShouldLoadAsEsm; | ||
@@ -71,2 +75,7 @@ resolveModuleFromDirIfExists( | ||
): string | null; | ||
resolveModuleFromDirIfExistsAsync( | ||
dirname: string, | ||
moduleName: string, | ||
options?: ResolveModuleConfig, | ||
): Promise<string | null>; | ||
resolveModule( | ||
@@ -77,2 +86,18 @@ from: string, | ||
): string; | ||
resolveModuleAsync( | ||
from: string, | ||
moduleName: string, | ||
options?: ResolveModuleConfig, | ||
): Promise<string>; | ||
/** | ||
* _prepareForResolution is shared between the sync and async module resolution | ||
* methods, to try to keep them as DRY as possible. | ||
*/ | ||
private _prepareForResolution; | ||
/** | ||
* _getHasteModulePath attempts to return the path to a haste module. | ||
*/ | ||
private _getHasteModulePath; | ||
private _throwModNotFoundError; | ||
private _getMapModuleName; | ||
private _isAliasModule; | ||
@@ -84,2 +109,3 @@ isCoreModule(moduleName: string): boolean; | ||
getMockModule(from: string, name: string): string | null; | ||
getMockModuleAsync(from: string, name: string): Promise<string | null>; | ||
getModulePaths(from: string): Array<string>; | ||
@@ -92,8 +118,22 @@ getModuleID( | ||
): string; | ||
getModuleIDAsync( | ||
virtualMocks: Map<string, boolean>, | ||
from: string, | ||
moduleName?: string, | ||
options?: ResolveModuleConfig, | ||
): Promise<string>; | ||
private _getModuleType; | ||
private _getAbsolutePath; | ||
private _getAbsolutePathAsync; | ||
private _getMockPath; | ||
private _getMockPathAsync; | ||
private _getVirtualMockPath; | ||
private _getVirtualMockPathAsync; | ||
private _isModuleResolved; | ||
private _isModuleResolvedAsync; | ||
resolveStubModuleName(from: string, moduleName: string): string | null; | ||
resolveStubModuleNameAsync( | ||
from: string, | ||
moduleName: string, | ||
): Promise<string | null>; | ||
} | ||
@@ -100,0 +140,0 @@ export default Resolver; |
@@ -178,5 +178,11 @@ 'use strict'; | ||
static findNodeModule(path, options) { | ||
const resolver = options.resolver | ||
? require(options.resolver) | ||
: _defaultResolver.default; | ||
const resolverModule = loadResolver(options.resolver); | ||
let resolver = _defaultResolver.default; | ||
if (typeof resolverModule === 'function') { | ||
resolver = resolverModule; | ||
} else if (typeof resolverModule.sync === 'function') { | ||
resolver = resolverModule.sync; | ||
} | ||
const paths = options.paths; | ||
@@ -202,30 +208,53 @@ | ||
return null; | ||
} // unstable as it should be replaced by https://github.com/nodejs/modules/issues/393, and we don't want people to use it | ||
} | ||
static unstable_shouldLoadAsEsm = _shouldLoadAsEsm.default; | ||
static async findNodeModuleAsync(path, options) { | ||
const resolverModule = loadResolver(options.resolver); | ||
let resolver = _defaultResolver.default; | ||
resolveModuleFromDirIfExists(dirname, moduleName, options) { | ||
const paths = | ||
(options === null || options === void 0 ? void 0 : options.paths) || | ||
this._options.modulePaths; | ||
const moduleDirectory = this._options.moduleDirectories; | ||
const stringifiedOptions = options ? JSON.stringify(options) : ''; | ||
const key = dirname + path().delimiter + moduleName + stringifiedOptions; | ||
const defaultPlatform = this._options.defaultPlatform; | ||
if (typeof resolverModule === 'function') { | ||
resolver = resolverModule; | ||
} else if ( | ||
typeof resolverModule.async === 'function' || | ||
typeof resolverModule.sync === 'function' | ||
) { | ||
const asyncOrSync = resolverModule.async || resolverModule.sync; | ||
const extensions = this._options.extensions.slice(); | ||
if (asyncOrSync == null) { | ||
throw new Error(`Unable to load resolver at ${options.resolver}`); | ||
} | ||
let module; | ||
resolver = asyncOrSync; | ||
} | ||
if (this._supportsNativePlatform) { | ||
extensions.unshift( | ||
...this._options.extensions.map(ext => '.' + NATIVE_PLATFORM + ext) | ||
); | ||
const paths = options.paths; | ||
try { | ||
const result = await resolver(path, { | ||
basedir: options.basedir, | ||
browser: options.browser, | ||
conditions: options.conditions, | ||
defaultResolver: _defaultResolver.default, | ||
extensions: options.extensions, | ||
moduleDirectory: options.moduleDirectory, | ||
paths: paths ? (nodePaths || []).concat(paths) : nodePaths, | ||
rootDir: options.rootDir | ||
}); | ||
return result; | ||
} catch (e) { | ||
if (options.throwIfNotFound) { | ||
throw e; | ||
} | ||
} | ||
if (defaultPlatform) { | ||
extensions.unshift( | ||
...this._options.extensions.map(ext => '.' + defaultPlatform + ext) | ||
); | ||
} // 1. If we have already resolved this module for this directory name, | ||
return null; | ||
} // unstable as it should be replaced by https://github.com/nodejs/modules/issues/393, and we don't want people to use it | ||
static unstable_shouldLoadAsEsm = _shouldLoadAsEsm.default; | ||
resolveModuleFromDirIfExists(dirname, moduleName, options) { | ||
const {extensions, key, moduleDirectory, paths, skipResolution} = | ||
this._prepareForResolution(dirname, moduleName, options); | ||
let module; // 1. If we have already resolved this module for this directory name, | ||
// return a value from the cache. | ||
@@ -252,5 +281,2 @@ | ||
const skipResolution = | ||
options && options.skipNodeResolution && !moduleName.includes(path().sep); | ||
const resolveNodeModule = (name, throwIfNotFound = false) => { | ||
@@ -285,15 +311,88 @@ if (this.isCoreModule(name)) { | ||
const parts = moduleName.split('/'); | ||
const hastePackage = this.getPackage(parts.shift()); | ||
try { | ||
const hasteModulePath = this._getHasteModulePath(moduleName); | ||
if (hastePackage) { | ||
try { | ||
const module = path().join.apply( | ||
path(), | ||
[path().dirname(hastePackage)].concat(parts) | ||
); // try resolving with custom resolver first to support extensions, | ||
if (hasteModulePath) { | ||
// try resolving with custom resolver first to support extensions, | ||
// then fallback to require.resolve | ||
const resolvedModule = | ||
resolveNodeModule(hasteModulePath) || | ||
require.resolve(hasteModulePath); | ||
this._moduleNameCache.set(key, resolvedModule); | ||
return resolvedModule; | ||
} | ||
} catch {} | ||
return null; | ||
} | ||
async resolveModuleFromDirIfExistsAsync(dirname, moduleName, options) { | ||
const {extensions, key, moduleDirectory, paths, skipResolution} = | ||
this._prepareForResolution(dirname, moduleName, options); | ||
let module; // 1. If we have already resolved this module for this directory name, | ||
// return a value from the cache. | ||
const cacheResult = this._moduleNameCache.get(key); | ||
if (cacheResult) { | ||
return cacheResult; | ||
} // 2. Check if the module is a haste module. | ||
module = this.getModule(moduleName); | ||
if (module) { | ||
this._moduleNameCache.set(key, module); | ||
return module; | ||
} // 3. Check if the module is a node module and resolve it based on | ||
// the node module resolution algorithm. If skipNodeResolution is given we | ||
// ignore all modules that look like node modules (ie. are not relative | ||
// requires). This enables us to speed up resolution when we build a | ||
// dependency graph because we don't have to look at modules that may not | ||
// exist and aren't mocked. | ||
const resolveNodeModule = async (name, throwIfNotFound = false) => { | ||
if (this.isCoreModule(name)) { | ||
return name; | ||
} | ||
return await Resolver.findNodeModuleAsync(name, { | ||
basedir: dirname, | ||
conditions: | ||
options === null || options === void 0 ? void 0 : options.conditions, | ||
extensions, | ||
moduleDirectory, | ||
paths, | ||
resolver: this._options.resolver, | ||
rootDir: this._options.rootDir, | ||
throwIfNotFound | ||
}); | ||
}; | ||
if (!skipResolution) { | ||
module = await resolveNodeModule( | ||
moduleName, | ||
Boolean(process.versions.pnp) | ||
); | ||
if (module) { | ||
this._moduleNameCache.set(key, module); | ||
return module; | ||
} | ||
} // 4. Resolve "haste packages" which are `package.json` files outside of | ||
// `node_modules` folders anywhere in the file system. | ||
try { | ||
const hasteModulePath = this._getHasteModulePath(moduleName); | ||
if (hasteModulePath) { | ||
// try resolving with custom resolver first to support extensions, | ||
// then fallback to require.resolve | ||
const resolvedModule = | ||
resolveNodeModule(module) || require.resolve(module); | ||
(await resolveNodeModule(hasteModulePath)) || // QUESTION: should this be async? | ||
require.resolve(hasteModulePath); | ||
@@ -303,4 +402,4 @@ this._moduleNameCache.set(key, resolvedModule); | ||
return resolvedModule; | ||
} catch {} | ||
} | ||
} | ||
} catch {} | ||
@@ -319,2 +418,77 @@ return null; | ||
this._throwModNotFoundError(from, moduleName); | ||
} | ||
async resolveModuleAsync(from, moduleName, options) { | ||
const dirname = path().dirname(from); | ||
const module = | ||
(await this.resolveStubModuleNameAsync(from, moduleName)) || | ||
(await this.resolveModuleFromDirIfExistsAsync( | ||
dirname, | ||
moduleName, | ||
options | ||
)); | ||
if (module) return module; // 5. Throw an error if the module could not be found. `resolve` only | ||
// produces an error based on the dirname but we have the actual current | ||
// module name available. | ||
this._throwModNotFoundError(from, moduleName); | ||
} | ||
/** | ||
* _prepareForResolution is shared between the sync and async module resolution | ||
* methods, to try to keep them as DRY as possible. | ||
*/ | ||
_prepareForResolution(dirname, moduleName, options) { | ||
const paths = | ||
(options === null || options === void 0 ? void 0 : options.paths) || | ||
this._options.modulePaths; | ||
const moduleDirectory = this._options.moduleDirectories; | ||
const stringifiedOptions = options ? JSON.stringify(options) : ''; | ||
const key = dirname + path().delimiter + moduleName + stringifiedOptions; | ||
const defaultPlatform = this._options.defaultPlatform; | ||
const extensions = this._options.extensions.slice(); | ||
if (this._supportsNativePlatform) { | ||
extensions.unshift( | ||
...this._options.extensions.map(ext => '.' + NATIVE_PLATFORM + ext) | ||
); | ||
} | ||
if (defaultPlatform) { | ||
extensions.unshift( | ||
...this._options.extensions.map(ext => '.' + defaultPlatform + ext) | ||
); | ||
} | ||
const skipResolution = | ||
options && options.skipNodeResolution && !moduleName.includes(path().sep); | ||
return { | ||
extensions, | ||
key, | ||
moduleDirectory, | ||
paths, | ||
skipResolution | ||
}; | ||
} | ||
/** | ||
* _getHasteModulePath attempts to return the path to a haste module. | ||
*/ | ||
_getHasteModulePath(moduleName) { | ||
const parts = moduleName.split('/'); | ||
const hastePackage = this.getPackage(parts.shift()); | ||
if (hastePackage) { | ||
return path().join.apply( | ||
path(), | ||
[path().dirname(hastePackage)].concat(parts) | ||
); | ||
} | ||
return null; | ||
} | ||
_throwModNotFoundError(from, moduleName) { | ||
const relativePath = | ||
@@ -329,2 +503,12 @@ (0, _slash().default)(path().relative(this._options.rootDir, from)) || | ||
_getMapModuleName(matches) { | ||
return matches | ||
? moduleName => | ||
moduleName.replace( | ||
/\$([0-9]+)/g, | ||
(_, index) => matches[parseInt(index, 10)] | ||
) | ||
: moduleName => moduleName; | ||
} | ||
_isAliasModule(moduleName) { | ||
@@ -390,2 +574,18 @@ const moduleNameMapper = this._options.moduleNameMapper; | ||
async getMockModuleAsync(from, name) { | ||
const mock = this._moduleMap.getMockModule(name); | ||
if (mock) { | ||
return mock; | ||
} else { | ||
const moduleName = await this.resolveStubModuleNameAsync(from, name); | ||
if (moduleName) { | ||
return this.getModule(moduleName) || moduleName; | ||
} | ||
} | ||
return null; | ||
} | ||
getModulePaths(from) { | ||
@@ -447,2 +647,37 @@ const cachedModule = this._modulePathCache.get(from); | ||
async getModuleIDAsync(virtualMocks, from, moduleName = '', options) { | ||
const stringifiedOptions = options ? JSON.stringify(options) : ''; | ||
const key = from + path().delimiter + moduleName + stringifiedOptions; | ||
const cachedModuleID = this._moduleIDCache.get(key); | ||
if (cachedModuleID) { | ||
return cachedModuleID; | ||
} | ||
if (moduleName.startsWith('data:')) { | ||
return moduleName; | ||
} | ||
const moduleType = this._getModuleType(moduleName); | ||
const absolutePath = await this._getAbsolutePathAsync( | ||
virtualMocks, | ||
from, | ||
moduleName, | ||
options | ||
); | ||
const mockPath = await this._getMockPathAsync(from, moduleName); | ||
const sep = path().delimiter; | ||
const id = | ||
moduleType + | ||
sep + | ||
(absolutePath ? absolutePath + sep : '') + | ||
(mockPath ? mockPath + sep : ''); | ||
this._moduleIDCache.set(key, id); | ||
return id; | ||
} | ||
_getModuleType(moduleName) { | ||
@@ -466,2 +701,25 @@ return this.isCoreModule(moduleName) ? 'node' : 'user'; | ||
async _getAbsolutePathAsync(virtualMocks, from, moduleName, options) { | ||
if (this.isCoreModule(moduleName)) { | ||
return moduleName; | ||
} | ||
if (moduleName.startsWith('data:')) { | ||
return moduleName; | ||
} | ||
const isModuleResolved = await this._isModuleResolvedAsync( | ||
from, | ||
moduleName | ||
); | ||
return isModuleResolved | ||
? this.getModule(moduleName) | ||
: await this._getVirtualMockPathAsync( | ||
virtualMocks, | ||
from, | ||
moduleName, | ||
options | ||
); | ||
} | ||
_getMockPath(from, moduleName) { | ||
@@ -473,2 +731,8 @@ return !this.isCoreModule(moduleName) | ||
async _getMockPathAsync(from, moduleName) { | ||
return !this.isCoreModule(moduleName) | ||
? await this.getMockModuleAsync(from, moduleName) | ||
: null; | ||
} | ||
_getVirtualMockPath(virtualMocks, from, moduleName, options) { | ||
@@ -483,2 +747,11 @@ const virtualMockPath = this.getModulePath(from, moduleName); | ||
async _getVirtualMockPathAsync(virtualMocks, from, moduleName, options) { | ||
const virtualMockPath = this.getModulePath(from, moduleName); | ||
return virtualMocks.get(virtualMockPath) | ||
? virtualMockPath | ||
: moduleName | ||
? await this.resolveModuleAsync(from, moduleName, options) | ||
: from; | ||
} | ||
_isModuleResolved(from, moduleName) { | ||
@@ -490,25 +763,81 @@ return !!( | ||
async _isModuleResolvedAsync(from, moduleName) { | ||
return !!( | ||
this.getModule(moduleName) || | ||
(await this.getMockModuleAsync(from, moduleName)) | ||
); | ||
} | ||
resolveStubModuleName(from, moduleName) { | ||
const dirname = path().dirname(from); | ||
const paths = this._options.modulePaths; | ||
const extensions = this._options.extensions.slice(); | ||
const {extensions, moduleDirectory, paths} = this._prepareForResolution( | ||
dirname, | ||
moduleName | ||
); | ||
const moduleDirectory = this._options.moduleDirectories; | ||
const moduleNameMapper = this._options.moduleNameMapper; | ||
const resolver = this._options.resolver; | ||
const defaultPlatform = this._options.defaultPlatform; | ||
if (this._supportsNativePlatform) { | ||
extensions.unshift( | ||
...this._options.extensions.map(ext => '.' + NATIVE_PLATFORM + ext) | ||
); | ||
} | ||
if (moduleNameMapper) { | ||
for (const {moduleName: mappedModuleName, regex} of moduleNameMapper) { | ||
if (regex.test(moduleName)) { | ||
// Note: once a moduleNameMapper matches the name, it must result | ||
// in a module, or else an error is thrown. | ||
const matches = moduleName.match(regex); | ||
if (defaultPlatform) { | ||
extensions.unshift( | ||
...this._options.extensions.map(ext => '.' + defaultPlatform + ext) | ||
); | ||
const mapModuleName = this._getMapModuleName(matches); | ||
const possibleModuleNames = Array.isArray(mappedModuleName) | ||
? mappedModuleName | ||
: [mappedModuleName]; | ||
let module = null; | ||
for (const possibleModuleName of possibleModuleNames) { | ||
const updatedName = mapModuleName(possibleModuleName); | ||
module = | ||
this.getModule(updatedName) || | ||
Resolver.findNodeModule(updatedName, { | ||
basedir: dirname, | ||
extensions, | ||
moduleDirectory, | ||
paths, | ||
resolver, | ||
rootDir: this._options.rootDir | ||
}); | ||
if (module) { | ||
break; | ||
} | ||
} | ||
if (!module) { | ||
throw createNoMappedModuleFoundError( | ||
moduleName, | ||
mapModuleName, | ||
mappedModuleName, | ||
regex, | ||
resolver | ||
); | ||
} | ||
return module; | ||
} | ||
} | ||
} | ||
return null; | ||
} | ||
async resolveStubModuleNameAsync(from, moduleName) { | ||
const dirname = path().dirname(from); | ||
const {extensions, moduleDirectory, paths} = this._prepareForResolution( | ||
dirname, | ||
moduleName | ||
); | ||
const moduleNameMapper = this._options.moduleNameMapper; | ||
const resolver = this._options.resolver; | ||
if (moduleNameMapper) { | ||
@@ -520,9 +849,5 @@ for (const {moduleName: mappedModuleName, regex} of moduleNameMapper) { | ||
const matches = moduleName.match(regex); | ||
const mapModuleName = matches | ||
? moduleName => | ||
moduleName.replace( | ||
/\$([0-9]+)/g, | ||
(_, index) => matches[parseInt(index, 10)] | ||
) | ||
: moduleName => moduleName; | ||
const mapModuleName = this._getMapModuleName(matches); | ||
const possibleModuleNames = Array.isArray(mappedModuleName) | ||
@@ -537,3 +862,3 @@ ? mappedModuleName | ||
this.getModule(updatedName) || | ||
Resolver.findNodeModule(updatedName, { | ||
(await Resolver.findNodeModuleAsync(updatedName, { | ||
basedir: dirname, | ||
@@ -545,3 +870,3 @@ extensions, | ||
rootDir: this._options.rootDir | ||
}); | ||
})); | ||
@@ -605,1 +930,28 @@ if (module) { | ||
}; | ||
function loadResolver(resolver) { | ||
if (resolver == null) { | ||
return _defaultResolver.default; | ||
} | ||
const loadedResolver = require(resolver); | ||
if (loadedResolver == null) { | ||
throw new Error(`Resolver located at ${resolver} does not export anything`); | ||
} | ||
if (typeof loadedResolver === 'function') { | ||
return loadedResolver; | ||
} | ||
if ( | ||
typeof loadedResolver === 'object' && | ||
(loadedResolver.sync != null || loadedResolver.async != null) | ||
) { | ||
return loadedResolver; | ||
} | ||
throw new Error( | ||
`Resolver located at ${resolver} does not export a function or an object with "sync" and "async" props` | ||
); | ||
} |
{ | ||
"name": "jest-resolve", | ||
"version": "28.0.0-alpha.3", | ||
"version": "28.0.0-alpha.4", | ||
"repository": { | ||
@@ -22,6 +22,6 @@ "type": "git", | ||
"graceful-fs": "^4.2.9", | ||
"jest-haste-map": "^28.0.0-alpha.3", | ||
"jest-haste-map": "^28.0.0-alpha.4", | ||
"jest-pnp-resolver": "^1.2.2", | ||
"jest-util": "^28.0.0-alpha.3", | ||
"jest-validate": "^28.0.0-alpha.3", | ||
"jest-util": "^28.0.0-alpha.4", | ||
"jest-validate": "^28.0.0-alpha.4", | ||
"resolve": "^1.20.0", | ||
@@ -41,3 +41,3 @@ "resolve.exports": "^1.1.0", | ||
}, | ||
"gitHead": "fc30b27bd94bb7ebeaadc72626ebbdba535150d2" | ||
"gitHead": "c13dab19491ba6b57c2d703e7d7c4b20189e1e17" | ||
} |
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
59615
1854
6
Updatedjest-util@^28.0.0-alpha.4