From 7347e9293ab4234b252d190013052a8a9422d56a Mon Sep 17 00:00:00 2001 From: Ian VanSchooten Date: Mon, 7 Jun 2021 23:41:02 -0400 Subject: [PATCH 01/39] WIP add async defaultResolver --- packages/jest-resolve/src/defaultResolver.ts | 129 +++++++++++++++++-- packages/jest-resolve/src/types.ts | 6 + 2 files changed, 123 insertions(+), 12 deletions(-) diff --git a/packages/jest-resolve/src/defaultResolver.ts b/packages/jest-resolve/src/defaultResolver.ts index 79ea2976a848..8ca43e924096 100644 --- a/packages/jest-resolve/src/defaultResolver.ts +++ b/packages/jest-resolve/src/defaultResolver.ts @@ -7,15 +7,22 @@ import * as fs from 'graceful-fs'; import pnpResolver from 'jest-pnp-resolver'; -import {Opts as ResolveOpts, sync as resolveSync} from 'resolve'; +import { + AsyncOpts, + Opts as ResolveOpts, + SyncOpts, + sync as resolveSync, +} from 'resolve'; +import resolveAsync = require('resolve'); import type {Config} from '@jest/types'; import {tryRealpath} from 'jest-util'; +import type {PackageMeta} from './types'; interface ResolverOptions extends ResolveOpts { basedir: Config.Path; browser?: boolean; conditions?: Array; - defaultResolver: typeof defaultResolver; + defaultResolver: typeof defaultResolverSync; extensions?: Array; rootDir?: Config.Path; } @@ -29,7 +36,7 @@ declare global { } } -export default function defaultResolver( +export default function defaultResolverSync( path: Config.Path, options: ResolverOptions, ): Config.Path { @@ -39,18 +46,63 @@ export default function defaultResolver( return pnpResolver(path, options); } - const result = resolveSync(path, { + const result = resolveSync(path, getSyncResolveOptions(options)); + + // Dereference symlinks to ensure we don't create a separate + // module instance depending on how it was referenced. + return realpathSync(result); +} + +export function defaultResolverAsync( + path: Config.Path, + options: ResolverOptions, +): Promise<{path: Config.Path; meta?: PackageMeta}> { + // Yarn 2 adds support to `resolve` automatically so the pnpResolver is only + // needed for Yarn 1 which implements version 1 of the pnp spec + if (process.versions.pnp === '1') { + // QUESTION: do we need an async version of pnpResolver? + return Promise.resolve({path: pnpResolver(path, options)}); + } + + return new Promise((resolve, reject) => { + function resolveCb(err: Error | null, result?: string, meta?: PackageMeta) { + if (err) { + reject(err); + } + if (result) { + resolve({meta, path: realpathSync(result)}); + } + } + resolveAsync(path, getAsyncResolveOptions(options), resolveCb); + }); +} + +/** + * getSyncResolveOptions returns resolution options that are used synchronously. + */ +function getSyncResolveOptions(options: ResolverOptions): SyncOpts { + return { ...options, - isDirectory, - isFile, + isDirectory: isDirectorySync, + isFile: isFileSync, preserveSymlinks: false, readPackageSync, realpathSync, - }); + }; +} - // Dereference symlinks to ensure we don't create a separate - // module instance depending on how it was referenced. - return realpathSync(result); +/** + * getAsyncResolveOptions returns resolution options that are used asynchronously. + */ +function getAsyncResolveOptions(options: ResolverOptions): AsyncOpts { + return { + ...options, + isDirectory: isDirectoryAsync, + isFile: isFileAsync, + preserveSymlinks: false, + readPackage: readPackageAsync, + realpath: realpathAsync, + }; } export function clearDefaultResolverCache(): void { @@ -134,18 +186,71 @@ function readPackageCached(path: Config.Path): PkgJson { /* * helper functions */ -function isFile(file: Config.Path): boolean { +function isFileSync(file: Config.Path): boolean { return statSyncCached(file) === IPathType.FILE; } -function isDirectory(dir: Config.Path): boolean { +function isFileAsync( + file: Config.Path, + cb: (err: Error | null, isFile?: boolean) => void, +): void { + try { + // QUESTION: do we need an async version of statSyncCached? + const isFile = statSyncCached(file) === IPathType.FILE; + cb(null, isFile); + } catch (err) { + cb(err); + } +} + +function isDirectorySync(dir: Config.Path): boolean { return statSyncCached(dir) === IPathType.DIRECTORY; } +function isDirectoryAsync( + dir: Config.Path, + cb: (err: Error | null, isDir?: boolean) => void, +): void { + try { + // QUESTION: do we need an async version of statSyncCached? + const isDir = statSyncCached(dir) === IPathType.DIRECTORY; + cb(null, isDir); + } catch (err) { + cb(err); + } +} + function realpathSync(file: Config.Path): Config.Path { return realpathCached(file); } +function realpathAsync( + file: string, + cb: (err: Error | null, resolved?: string) => void, +): void { + try { + // QUESTION: do we need an async version of realpathCached? + const resolved = realpathCached(file); + cb(null, resolved); + } catch (err) { + cb(err); + } +} + function readPackageSync(_: unknown, file: Config.Path): PkgJson { return readPackageCached(file); } + +function readPackageAsync( + _: unknown, + pkgfile: string, + cb: (err: Error | null, pkgJson?: Record) => void, +): void { + try { + // QUESTION: do we need an async version of readPackageCached? + const pkgJson = readPackageCached(pkgfile); + cb(null, pkgJson); + } catch (err) { + cb(err); + } +} diff --git a/packages/jest-resolve/src/types.ts b/packages/jest-resolve/src/types.ts index cbe7666ac21f..0535e021c0cb 100644 --- a/packages/jest-resolve/src/types.ts +++ b/packages/jest-resolve/src/types.ts @@ -23,3 +23,9 @@ type ModuleNameMapperConfig = { regex: RegExp; moduleName: string | Array; }; + +export interface PackageMeta { + name: string; + version: string; + [key: string]: any; +} From 74f1fc5d67ad99ec8033e42e740ec91231e291fc Mon Sep 17 00:00:00 2001 From: Ian VanSchooten Date: Tue, 8 Jun 2021 15:12:57 -0400 Subject: [PATCH 02/39] Create ResolverAsync class --- packages/jest-resolve/src/resolver.ts | 660 ++++++++++++++++++++------ packages/jest-resolve/src/types.ts | 1 + 2 files changed, 503 insertions(+), 158 deletions(-) diff --git a/packages/jest-resolve/src/resolver.ts b/packages/jest-resolve/src/resolver.ts index 315537ae4fcd..2cfe756593d2 100644 --- a/packages/jest-resolve/src/resolver.ts +++ b/packages/jest-resolve/src/resolver.ts @@ -14,7 +14,10 @@ import type {Config} from '@jest/types'; import type {IModuleMap} from 'jest-haste-map'; import {tryRealpath} from 'jest-util'; import ModuleNotFoundError from './ModuleNotFoundError'; -import defaultResolver, {clearDefaultResolverCache} from './defaultResolver'; +import defaultResolver, { + clearDefaultResolverCache, + defaultResolverAsync, +} from './defaultResolver'; import isBuiltinModule from './isBuiltinModule'; import nodeModulesPaths from './nodeModulesPaths'; import shouldLoadAsEsm, {clearCachedLookups} from './shouldLoadAsEsm'; @@ -28,6 +31,7 @@ type FindNodeModuleConfig = { moduleDirectory?: Array; paths?: Array; resolver?: Config.Path | null; + asyncResolver?: Config.Path | null; rootDir?: Config.Path; throwIfNotFound?: boolean; }; @@ -50,11 +54,11 @@ const nodePaths = NODE_PATH .map(p => path.resolve(resolvedCwd, p)) : undefined; -export default class Resolver { - private readonly _options: ResolverConfig; - private readonly _moduleMap: IModuleMap; - private readonly _moduleIDCache: Map; - private readonly _moduleNameCache: Map; +class BaseResolver { + protected readonly _options: ResolverConfig; + protected readonly _moduleMap: IModuleMap; + protected readonly _moduleIDCache: Map; + protected readonly _moduleNameCache: Map; private readonly _modulePathCache: Map>; private readonly _supportsNativePlatform: boolean; @@ -68,7 +72,6 @@ export default class Resolver { moduleNameMapper: options.moduleNameMapper, modulePaths: options.modulePaths, platforms: options.platforms, - resolver: options.resolver, rootDir: options.rootDir, }; this._supportsNativePlatform = options.platforms @@ -102,49 +105,33 @@ export default class Resolver { clearCachedLookups(); } - static findNodeModule( - path: Config.Path, - options: FindNodeModuleConfig, - ): Config.Path | null { - const resolver: typeof defaultResolver = options.resolver - ? require(options.resolver) - : defaultResolver; - const paths = options.paths; + // 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; - try { - return resolver(path, { - basedir: options.basedir, - browser: options.browser, - conditions: options.conditions, - defaultResolver, - extensions: options.extensions, - moduleDirectory: options.moduleDirectory, - paths: paths ? (nodePaths || []).concat(paths) : nodePaths, - rootDir: options.rootDir, - }); - } catch (e) { - if (options.throwIfNotFound) { - throw e; - } + private _isAliasModule(moduleName: string): boolean { + const moduleNameMapper = this._options.moduleNameMapper; + if (!moduleNameMapper) { + return false; } - 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; + return moduleNameMapper.some(({regex}) => regex.test(moduleName)); + } - resolveModuleFromDirIfExists( + /** + * _prepareForResolution is shared between the sync and async module resolution + * methods, to try to keep them as DRY as possible. + */ + protected _prepareForResolution( dirname: Config.Path, moduleName: string, options?: ResolveModuleConfig, - ): Config.Path | null { + ) { const paths = 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(); - let module; if (this._supportsNativePlatform) { extensions.unshift( @@ -157,91 +144,28 @@ export default class Resolver { ); } - // 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 skipResolution = options && options.skipNodeResolution && !moduleName.includes(path.sep); - const resolveNodeModule = (name: Config.Path, throwIfNotFound = false) => { - if (this.isCoreModule(name)) { - return name; - } - - return Resolver.findNodeModule(name, { - basedir: dirname, - conditions: options?.conditions, - extensions, - moduleDirectory, - paths, - resolver: this._options.resolver, - rootDir: this._options.rootDir, - throwIfNotFound, - }); - }; - - if (!skipResolution) { - module = resolveNodeModule(moduleName, Boolean(process.versions.pnp)); - - if (module) { - this._moduleNameCache.set(key, module); - return module; - } - } + return {extensions, key, moduleDirectory, paths, skipResolution}; + } - // 4. Resolve "haste packages" which are `package.json` files outside of - // `node_modules` folders anywhere in the file system. + /** + * _getHasteModulePath attempts to return the path to a haste module. + */ + protected _getHasteModulePath(moduleName: string) { const parts = moduleName.split('/'); const hastePackage = this.getPackage(parts.shift()!); if (hastePackage) { - try { - const module = path.join.apply( - path, - [path.dirname(hastePackage)].concat(parts), - ); - // try resolving with custom resolver first to support extensions, - // then fallback to require.resolve - const resolvedModule = - resolveNodeModule(module) || require.resolve(module); - this._moduleNameCache.set(key, resolvedModule); - return resolvedModule; - } catch {} + return path.join.apply(path, [path.dirname(hastePackage)].concat(parts)); } - return null; } - resolveModule( + protected _throwModNotFoundError( from: Config.Path, moduleName: string, - options?: ResolveModuleConfig, - ): Config.Path { - const dirname = path.dirname(from); - const module = - this.resolveStubModuleName(from, moduleName) || - this.resolveModuleFromDirIfExists(dirname, moduleName, options); - if (module) return module; - - // 5. Throw an error if the module could not be found. `resolve.sync` only - // produces an error based on the dirname but we have the actual current - // module name available. + ): never { const relativePath = slash(path.relative(this._options.rootDir, from)) || '.'; @@ -251,13 +175,22 @@ export default class Resolver { ); } - private _isAliasModule(moduleName: string): boolean { - const moduleNameMapper = this._options.moduleNameMapper; - if (!moduleNameMapper) { - return false; - } + protected _getModuleType(moduleName: string): 'node' | 'user' { + return this.isCoreModule(moduleName) ? 'node' : 'user'; + } - return moduleNameMapper.some(({regex}) => regex.test(moduleName)); + protected _getMapModuleName(matches: RegExpMatchArray | null) { + return matches + ? (moduleName: string) => + moduleName.replace( + /\$([0-9]+)/g, + (_, index) => matches[parseInt(index, 10)], + ) + : (moduleName: string) => moduleName; + } + + setResolver(resolver?: Config.Path | null) { + this._options.resolver = resolver; } isCoreModule(moduleName: string): boolean { @@ -293,19 +226,6 @@ export default class Resolver { ); } - getMockModule(from: Config.Path, name: string): Config.Path | null { - const mock = this._moduleMap.getMockModule(name); - if (mock) { - return mock; - } else { - const moduleName = this.resolveStubModuleName(from, name); - if (moduleName) { - return this.getModule(moduleName) || moduleName; - } - } - return null; - } - getModulePaths(from: Config.Path): Array { const cachedModule = this._modulePathCache.get(from); if (cachedModule) { @@ -321,13 +241,151 @@ export default class Resolver { this._modulePathCache.set(from, paths); return paths; } +} - getModuleID( +export class ResolverAsync extends BaseResolver { + constructor(moduleMap: IModuleMap, options: ResolverConfig) { + super(moduleMap, options); + this.setResolver(options.asyncResolver); + } + + private async _getAbsolutePathAsync( + virtualMocks: Map, + from: Config.Path, + moduleName: string, + options?: ResolveModuleConfig, + ): Promise { + if (this.isCoreModule(moduleName)) { + return moduleName; + } + const isModuleResolved = await this._isModuleResolvedAsync( + from, + moduleName, + ); + return isModuleResolved + ? this.getModule(moduleName) + : await this._getVirtualMockPathAsync( + virtualMocks, + from, + moduleName, + options, + ); + } + + private async _getMockPathAsync( + from: Config.Path, + moduleName: string, + ): Promise { + return !this.isCoreModule(moduleName) + ? await this.getMockModuleAsync(from, moduleName) + : null; + } + + private async _getVirtualMockPathAsync( + virtualMocks: Map, + from: Config.Path, + moduleName: string, + options?: ResolveModuleConfig, + ): Promise { + const virtualMockPath = this.getModulePath(from, moduleName); + return virtualMocks.get(virtualMockPath) + ? virtualMockPath + : moduleName + ? await this.resolveModuleAsync(from, moduleName, options) + : from; + } + + private async _isModuleResolvedAsync( + from: Config.Path, + moduleName: string, + ): Promise { + return !!( + this.getModule(moduleName) || + (await this.getMockModuleAsync(from, moduleName)) + ); + } + + async resolveStubModuleNameAsync( + from: Config.Path, + moduleName: string, + ): Promise { + const dirname = path.dirname(from); + + const {extensions, moduleDirectory, paths} = this._prepareForResolution( + dirname, + moduleName, + ); + const moduleNameMapper = this._options.moduleNameMapper; + const asyncResolver = this._options.asyncResolver; + + 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); + const mapModuleName = this._getMapModuleName(matches); + const possibleModuleNames = Array.isArray(mappedModuleName) + ? mappedModuleName + : [mappedModuleName]; + let module: string | null = null; + for (const possibleModuleName of possibleModuleNames) { + const updatedName = mapModuleName(possibleModuleName); + + module = + this.getModule(updatedName) || + (await ResolverAsync.findNodeModuleAsync(updatedName, { + asyncResolver, + basedir: dirname, + extensions, + moduleDirectory, + paths, + rootDir: this._options.rootDir, + })); + + if (module) { + break; + } + } + + if (!module) { + throw createNoMappedModuleFoundError( + moduleName, + mapModuleName, + mappedModuleName, + regex, + asyncResolver, + ); + } + return module; + } + } + } + return null; + } + + async getMockModuleAsync( + from: Config.Path, + name: string, + ): Promise { + 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; + } + + async getModuleIDAsync( virtualMocks: Map, from: Config.Path, moduleName = '', options?: ResolveModuleConfig, - ): string { + ): Promise { const stringifiedOptions = options ? JSON.stringify(options) : ''; const key = from + path.delimiter + moduleName + stringifiedOptions; const cachedModuleID = this._moduleIDCache.get(key); @@ -336,13 +394,13 @@ export default class Resolver { } const moduleType = this._getModuleType(moduleName); - const absolutePath = this._getAbsolutePath( + const absolutePath = await this._getAbsolutePathAsync( virtualMocks, from, moduleName, options, ); - const mockPath = this._getMockPath(from, moduleName); + const mockPath = await this._getMockPathAsync(from, moduleName); const sep = path.delimiter; const id = @@ -356,8 +414,143 @@ export default class Resolver { return id; } - private _getModuleType(moduleName: string): 'node' | 'user' { - return this.isCoreModule(moduleName) ? 'node' : 'user'; + static async findNodeModuleAsync( + path: Config.Path, + options: FindNodeModuleConfig, + ): Promise { + const resolver: typeof defaultResolverAsync = options.asyncResolver + ? require(options.asyncResolver) + : defaultResolverAsync; + const paths = options.paths; + + try { + const result = await resolver(path, { + basedir: options.basedir, + browser: options.browser, + defaultResolver, + extensions: options.extensions, + moduleDirectory: options.moduleDirectory, + paths: paths ? (nodePaths || []).concat(paths) : nodePaths, + rootDir: options.rootDir, + }); + return result.path; + } catch (e) { + if (options.throwIfNotFound) { + throw e; + } + } + return null; + } + + async resolveModuleFromDirIfExistsAsync( + dirname: Config.Path, + moduleName: string, + options?: ResolveModuleConfig, + ): Promise { + 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: Config.Path, + throwIfNotFound = false, + ) => { + if (this.isCoreModule(name)) { + return name; + } + + return await ResolverAsync.findNodeModuleAsync(name, { + asyncResolver: this._options.asyncResolver, + basedir: dirname, + conditions: options?.conditions, + extensions, + moduleDirectory, + paths, + 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 = + (await resolveNodeModule(hasteModulePath)) || + // QUESTION: should this be async? + require.resolve(hasteModulePath); + this._moduleNameCache.set(key, resolvedModule); + return resolvedModule; + } + } catch {} + + return null; + } + + /* eslint-disable-next-line consistent-return */ + async resolveModuleAsync( + from: Config.Path, + moduleName: string, + options?: ResolveModuleConfig, + ): Promise { + 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.sync` only + // produces an error based on the dirname but we have the actual current + // module name available. + this._throwModNotFoundError(from, moduleName); + } +} + +export default class Resolver extends BaseResolver { + constructor(moduleMap: IModuleMap, options: ResolverConfig) { + super(moduleMap, options); + this.setResolver(options.resolver); } private _getAbsolutePath( @@ -408,24 +601,13 @@ export default class Resolver { moduleName: string, ): Config.Path | null { const dirname = path.dirname(from); - const paths = this._options.modulePaths; - const extensions = this._options.extensions.slice(); - const moduleDirectory = this._options.moduleDirectories; + + const {extensions, moduleDirectory, paths} = this._prepareForResolution( + dirname, + moduleName, + ); 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 (defaultPlatform) { - extensions.unshift( - ...this._options.extensions.map(ext => '.' + defaultPlatform + ext), - ); - } if (moduleNameMapper) { for (const {moduleName: mappedModuleName, regex} of moduleNameMapper) { @@ -433,14 +615,7 @@ export default class Resolver { // Note: once a moduleNameMapper matches the name, it must result // in a module, or else an error is thrown. const matches = moduleName.match(regex); - const mapModuleName = matches - ? (moduleName: string) => - moduleName.replace( - /\$([0-9]+)/g, - (_, index) => matches[parseInt(index, 10)], - ) - : (moduleName: string) => moduleName; - + const mapModuleName = this._getMapModuleName(matches); const possibleModuleNames = Array.isArray(mappedModuleName) ? mappedModuleName : [mappedModuleName]; @@ -479,6 +654,175 @@ export default class Resolver { } return null; } + + getMockModule(from: Config.Path, name: string): Config.Path | null { + const mock = this._moduleMap.getMockModule(name); + if (mock) { + return mock; + } else { + const moduleName = this.resolveStubModuleName(from, name); + if (moduleName) { + return this.getModule(moduleName) || moduleName; + } + } + return null; + } + + getModuleID( + virtualMocks: Map, + from: Config.Path, + _moduleName?: string, + options?: ResolveModuleConfig, + ): string { + const moduleName = _moduleName || ''; + + const stringifiedOptions = options ? JSON.stringify(options) : ''; + const key = from + path.delimiter + moduleName + stringifiedOptions; + const cachedModuleID = this._moduleIDCache.get(key); + if (cachedModuleID) { + return cachedModuleID; + } + + const moduleType = this._getModuleType(moduleName); + const absolutePath = this._getAbsolutePath( + virtualMocks, + from, + moduleName, + options, + ); + const mockPath = this._getMockPath(from, moduleName); + + const sep = path.delimiter; + const id = + moduleType + + sep + + (absolutePath ? absolutePath + sep : '') + + (mockPath ? mockPath + sep : '') + + (stringifiedOptions ? stringifiedOptions + sep : ''); + + this._moduleIDCache.set(key, id); + return id; + } + + static findNodeModule( + path: Config.Path, + options: FindNodeModuleConfig, + ): Config.Path | null { + const resolver: typeof defaultResolver = options.resolver + ? require(options.resolver) + : defaultResolver; + const paths = options.paths; + + try { + return resolver(path, { + basedir: options.basedir, + browser: options.browser, + conditions: options.conditions, + defaultResolver, + extensions: options.extensions, + moduleDirectory: options.moduleDirectory, + paths: paths ? (nodePaths || []).concat(paths) : nodePaths, + rootDir: options.rootDir, + }); + } catch (e) { + if (options.throwIfNotFound) { + throw e; + } + } + return null; + } + + resolveModuleFromDirIfExists( + dirname: Config.Path, + moduleName: string, + options?: ResolveModuleConfig, + ): Config.Path | null { + 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 = (name: Config.Path, throwIfNotFound = false) => { + if (this.isCoreModule(name)) { + return name; + } + + return Resolver.findNodeModule(name, { + basedir: dirname, + conditions: options?.conditions, + extensions, + moduleDirectory, + paths, + resolver: this._options.resolver, + rootDir: this._options.rootDir, + throwIfNotFound, + }); + }; + + if (!skipResolution) { + module = 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(hasteModulePath) || + require.resolve(hasteModulePath); + this._moduleNameCache.set(key, resolvedModule); + return resolvedModule; + } + } catch {} + + return null; + } + + /* eslint-disable-next-line consistent-return */ + resolveModule( + from: Config.Path, + moduleName: string, + options?: ResolveModuleConfig, + ): Config.Path { + const dirname = path.dirname(from); + const module = + this.resolveStubModuleName(from, moduleName) || + this.resolveModuleFromDirIfExists(dirname, moduleName, options); + if (module) return module; + + // 5. Throw an error if the module could not be found. `resolve.sync` only + // produces an error based on the dirname but we have the actual current + // module name available. + this._throwModNotFoundError(from, moduleName); + } } const createNoMappedModuleFoundError = ( diff --git a/packages/jest-resolve/src/types.ts b/packages/jest-resolve/src/types.ts index 0535e021c0cb..58501784b335 100644 --- a/packages/jest-resolve/src/types.ts +++ b/packages/jest-resolve/src/types.ts @@ -16,6 +16,7 @@ export type ResolverConfig = { modulePaths?: Array; platforms?: Array; resolver?: Config.Path | null; + asyncResolver?: Config.Path | null; rootDir: Config.Path; }; From 66c6356873b5df4a15b70bb953cac2627883bdff Mon Sep 17 00:00:00 2001 From: Ian VanSchooten Date: Tue, 8 Jun 2021 17:09:06 -0400 Subject: [PATCH 03/39] Forget about meta --- packages/jest-resolve/src/defaultResolver.ts | 12 ++++++------ packages/jest-resolve/src/resolver.ts | 2 +- packages/jest-resolve/src/types.ts | 6 ------ 3 files changed, 7 insertions(+), 13 deletions(-) diff --git a/packages/jest-resolve/src/defaultResolver.ts b/packages/jest-resolve/src/defaultResolver.ts index 8ca43e924096..a231b6e5c91e 100644 --- a/packages/jest-resolve/src/defaultResolver.ts +++ b/packages/jest-resolve/src/defaultResolver.ts @@ -16,7 +16,6 @@ import { import resolveAsync = require('resolve'); import type {Config} from '@jest/types'; import {tryRealpath} from 'jest-util'; -import type {PackageMeta} from './types'; interface ResolverOptions extends ResolveOpts { basedir: Config.Path; @@ -56,24 +55,25 @@ export default function defaultResolverSync( export function defaultResolverAsync( path: Config.Path, options: ResolverOptions, -): Promise<{path: Config.Path; meta?: PackageMeta}> { +): Promise { // Yarn 2 adds support to `resolve` automatically so the pnpResolver is only // needed for Yarn 1 which implements version 1 of the pnp spec if (process.versions.pnp === '1') { // QUESTION: do we need an async version of pnpResolver? - return Promise.resolve({path: pnpResolver(path, options)}); + return Promise.resolve(pnpResolver(path, options)); } return new Promise((resolve, reject) => { - function resolveCb(err: Error | null, result?: string, meta?: PackageMeta) { + function resolveCb(err: Error | null, result?: string) { if (err) { reject(err); } if (result) { - resolve({meta, path: realpathSync(result)}); + resolve(realpathSync(result)); } } - resolveAsync(path, getAsyncResolveOptions(options), resolveCb); + const opts = getAsyncResolveOptions(options); + resolveAsync(path, opts, resolveCb); }); } diff --git a/packages/jest-resolve/src/resolver.ts b/packages/jest-resolve/src/resolver.ts index 2cfe756593d2..07a1638379a9 100644 --- a/packages/jest-resolve/src/resolver.ts +++ b/packages/jest-resolve/src/resolver.ts @@ -433,7 +433,7 @@ export class ResolverAsync extends BaseResolver { paths: paths ? (nodePaths || []).concat(paths) : nodePaths, rootDir: options.rootDir, }); - return result.path; + return result; } catch (e) { if (options.throwIfNotFound) { throw e; diff --git a/packages/jest-resolve/src/types.ts b/packages/jest-resolve/src/types.ts index 58501784b335..e2c672925fe4 100644 --- a/packages/jest-resolve/src/types.ts +++ b/packages/jest-resolve/src/types.ts @@ -24,9 +24,3 @@ type ModuleNameMapperConfig = { regex: RegExp; moduleName: string | Array; }; - -export interface PackageMeta { - name: string; - version: string; - [key: string]: any; -} From 3916bafd4010e0a8423ba59e6a252c32a2c51509 Mon Sep 17 00:00:00 2001 From: Ian VanSchooten Date: Tue, 8 Jun 2021 17:09:48 -0400 Subject: [PATCH 04/39] Set asyncResolver properly --- packages/jest-resolve/src/resolver.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/jest-resolve/src/resolver.ts b/packages/jest-resolve/src/resolver.ts index 07a1638379a9..814f413aa61f 100644 --- a/packages/jest-resolve/src/resolver.ts +++ b/packages/jest-resolve/src/resolver.ts @@ -193,6 +193,10 @@ class BaseResolver { this._options.resolver = resolver; } + setAsyncResolver(asyncResolver?: Config.Path | null) { + this._options.asyncResolver = asyncResolver; + } + isCoreModule(moduleName: string): boolean { return ( this._options.hasCoreModules && @@ -246,7 +250,7 @@ class BaseResolver { export class ResolverAsync extends BaseResolver { constructor(moduleMap: IModuleMap, options: ResolverConfig) { super(moduleMap, options); - this.setResolver(options.asyncResolver); + this.setAsyncResolver(options.asyncResolver); } private async _getAbsolutePathAsync( From fa67da8a6e8a58b7ded6359e46f18b4ba70ffb47 Mon Sep 17 00:00:00 2001 From: Ian VanSchooten Date: Tue, 8 Jun 2021 17:10:10 -0400 Subject: [PATCH 05/39] Add async tests Copied from the sync tests, and modified --- .../src/__mocks__/userResolverAsync.d.ts | 16 + .../src/__mocks__/userResolverAsync.js | 12 + .../src/__tests__/resolveAsync.test.ts | 333 ++++++++++++++++++ 3 files changed, 361 insertions(+) create mode 100644 packages/jest-resolve/src/__mocks__/userResolverAsync.d.ts create mode 100644 packages/jest-resolve/src/__mocks__/userResolverAsync.js create mode 100644 packages/jest-resolve/src/__tests__/resolveAsync.test.ts diff --git a/packages/jest-resolve/src/__mocks__/userResolverAsync.d.ts b/packages/jest-resolve/src/__mocks__/userResolverAsync.d.ts new file mode 100644 index 000000000000..b292cb17a62e --- /dev/null +++ b/packages/jest-resolve/src/__mocks__/userResolverAsync.d.ts @@ -0,0 +1,16 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import {defaultResolverAsync} from '../defaultResolver'; + +// todo: can be replaced with jest.MockedFunction +declare const userResolver: jest.MockInstance< + ReturnType, + Parameters +>; + +export default userResolver; diff --git a/packages/jest-resolve/src/__mocks__/userResolverAsync.js b/packages/jest-resolve/src/__mocks__/userResolverAsync.js new file mode 100644 index 000000000000..61dff6635904 --- /dev/null +++ b/packages/jest-resolve/src/__mocks__/userResolverAsync.js @@ -0,0 +1,12 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +'use strict'; + +module.exports = function userResolver(path, options) { + return Promise.resolve('module'); +}; diff --git a/packages/jest-resolve/src/__tests__/resolveAsync.test.ts b/packages/jest-resolve/src/__tests__/resolveAsync.test.ts new file mode 100644 index 000000000000..40a66a84b7a2 --- /dev/null +++ b/packages/jest-resolve/src/__tests__/resolveAsync.test.ts @@ -0,0 +1,333 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + */ + +import * as path from 'path'; +import * as fs from 'graceful-fs'; +import {sync as resolveSync} from 'resolve'; +import {ModuleMap} from 'jest-haste-map'; +import userResolverAsync from '../__mocks__/userResolverAsync'; +import defaultResolver from '../defaultResolver'; +import nodeModulesPaths from '../nodeModulesPaths'; +import {ResolverAsync} from '../resolver'; +import type {ResolverConfig} from '../types'; + +jest.mock('../__mocks__/userResolverAsync'); + +// Do not fully mock `resolve` because it is used by Jest. Doing it will crash +// in very strange ways. Instead just spy on the method `sync`. +jest.mock('resolve', () => { + const originalModule = jest.requireActual('resolve'); + return { + ...originalModule, + sync: jest.spyOn(originalModule, 'sync'), + }; +}); + +const mockResolveSync = < + jest.Mock, Parameters> +>resolveSync; + +beforeEach(() => { + userResolverAsync.mockClear(); + mockResolveSync.mockClear(); +}); + +describe('isCoreModule', () => { + it('returns false if `hasCoreModules` is false.', () => { + const moduleMap = ModuleMap.create('/'); + const resolver = new ResolverAsync(moduleMap, { + hasCoreModules: false, + } as ResolverConfig); + const isCore = resolver.isCoreModule('assert'); + expect(isCore).toEqual(false); + }); + + it('returns true if `hasCoreModules` is true and `moduleName` is a core module.', () => { + const moduleMap = ModuleMap.create('/'); + const resolver = new ResolverAsync(moduleMap, {} as ResolverConfig); + const isCore = resolver.isCoreModule('assert'); + expect(isCore).toEqual(true); + }); + + it('returns false if `hasCoreModules` is true and `moduleName` is not a core module.', () => { + const moduleMap = ModuleMap.create('/'); + const resolver = new ResolverAsync(moduleMap, {} as ResolverConfig); + const isCore = resolver.isCoreModule('not-a-core-module'); + expect(isCore).toEqual(false); + }); + + it('returns false if `hasCoreModules` is true and `moduleNameMapper` alias a module same name with core module', () => { + const moduleMap = ModuleMap.create('/'); + const resolver = new ResolverAsync(moduleMap, { + moduleNameMapper: [ + { + moduleName: '$1', + regex: /^constants$/, + }, + ], + } as ResolverConfig); + const isCore = resolver.isCoreModule('constants'); + expect(isCore).toEqual(false); + }); +}); + +describe('findNodeModuleAsync', () => { + it('is possible to override the default resolver', async () => { + const cwd = process.cwd(); + const resolvedCwd = fs.realpathSync(cwd) || cwd; + const nodePaths = process.env.NODE_PATH + ? process.env.NODE_PATH.split(path.delimiter) + .filter(Boolean) + .map(p => path.resolve(resolvedCwd, p)) + : null; + + userResolverAsync.mockImplementation(() => Promise.resolve('module')); + + const newPath = await ResolverAsync.findNodeModuleAsync('test', { + basedir: '/', + browser: true, + extensions: ['js'], + moduleDirectory: ['node_modules'], + paths: ['/something'], + asyncResolver: require.resolve('../__mocks__/userResolverAsync'), + }); + + expect(newPath).toBe('module'); + expect(userResolverAsync.mock.calls[0][0]).toBe('test'); + expect(userResolverAsync.mock.calls[0][1]).toStrictEqual({ + basedir: '/', + browser: true, + defaultResolver, + extensions: ['js'], + moduleDirectory: ['node_modules'], + paths: (nodePaths || []).concat(['/something']), + rootDir: undefined, + }); + }); + + it('passes packageFilter to the resolve module when using the default resolver', async () => { + const packageFilter = jest.fn(); + + // A resolver that delegates to defaultResolver with a packageFilter implementation + userResolverAsync.mockImplementation((request, opts) => + Promise.resolve(opts.defaultResolver(request, {...opts, packageFilter})), + ); + + await ResolverAsync.findNodeModuleAsync('test', { + basedir: '/', + asyncResolver: require.resolve('../__mocks__/userResolverAsync'), + }); + + expect(mockResolveSync).toHaveBeenCalledWith( + 'test', + expect.objectContaining({ + packageFilter, + }), + ); + }); +}); + +describe('resolveModuleAsync', () => { + let moduleMap: ModuleMap; + beforeEach(() => { + moduleMap = ModuleMap.create('/'); + }); + + it('is possible to resolve node modules', async () => { + const resolver = new ResolverAsync(moduleMap, { + extensions: ['.js'], + } as ResolverConfig); + const src = require.resolve('../'); + const resolved = await resolver.resolveModuleAsync( + src, + './__mocks__/mockJsDependency', + ); + expect(resolved).toBe(require.resolve('../__mocks__/mockJsDependency.js')); + }); + + it('is possible to resolve node modules with custom extensions', async () => { + const resolver = new ResolverAsync(moduleMap, { + extensions: ['.js', '.jsx'], + } as ResolverConfig); + const src = require.resolve('../'); + const resolvedJsx = await resolver.resolveModuleAsync( + src, + './__mocks__/mockJsxDependency', + ); + expect(resolvedJsx).toBe( + require.resolve('../__mocks__/mockJsxDependency.jsx'), + ); + }); + + it('is possible to resolve node modules with custom extensions and platforms', async () => { + const resolver = new ResolverAsync(moduleMap, { + extensions: ['.js', '.jsx'], + platforms: ['native'], + } as ResolverConfig); + const src = require.resolve('../'); + const resolvedJsx = await resolver.resolveModuleAsync( + src, + './__mocks__/mockJsxDependency', + ); + expect(resolvedJsx).toBe( + require.resolve('../__mocks__/mockJsxDependency.native.jsx'), + ); + }); + + it('is possible to resolve node modules by resolving their realpath', async () => { + const resolver = new ResolverAsync(moduleMap, { + extensions: ['.js'], + } as ResolverConfig); + const src = path.join( + path.resolve(__dirname, '../../src/__mocks__/bar/node_modules/'), + 'foo/index.js', + ); + const resolved = await resolver.resolveModuleAsync(src, 'dep'); + expect(resolved).toBe( + require.resolve('../../src/__mocks__/foo/node_modules/dep/index.js'), + ); + }); + + it('is possible to specify custom resolve paths', async () => { + const resolver = new ResolverAsync(moduleMap, { + extensions: ['.js'], + } as ResolverConfig); + const src = require.resolve('../'); + const resolved = await resolver.resolveModuleAsync( + src, + 'mockJsDependency', + { + paths: [ + path.resolve(__dirname, '../../src/__tests__'), + path.resolve(__dirname, '../../src/__mocks__'), + ], + }, + ); + expect(resolved).toBe(require.resolve('../__mocks__/mockJsDependency.js')); + }); + + it('does not confuse directories with files', async () => { + const resolver = new ResolverAsync(moduleMap, { + extensions: ['.js'], + } as ResolverConfig); + const mocksDirectory = path.resolve(__dirname, '../__mocks__'); + const fooSlashFoo = path.join(mocksDirectory, 'foo/foo.js'); + const fooSlashIndex = path.join(mocksDirectory, 'foo/index.js'); + + const resolvedWithSlash = await resolver.resolveModuleAsync( + fooSlashFoo, + './', + ); + const resolvedWithDot = await resolver.resolveModuleAsync(fooSlashFoo, '.'); + expect(resolvedWithSlash).toBe(fooSlashIndex); + expect(resolvedWithSlash).toBe(resolvedWithDot); + }); +}); + +describe('getMockModuleAsync', () => { + it.only('is possible to use custom resolver to resolve deps inside mock modules with moduleNameMapper', async () => { + userResolverAsync.mockImplementation(() => Promise.resolve('module')); + + const moduleMap = ModuleMap.create('/'); + const resolver = new ResolverAsync(moduleMap, { + extensions: ['.js'], + moduleNameMapper: [ + { + moduleName: '$1', + regex: /(.*)/, + }, + ], + asyncResolver: require.resolve('../__mocks__/userResolverAsync'), + } as ResolverConfig); + const src = require.resolve('../'); + + await resolver.resolveModuleAsync(src, 'dependentModule'); + + expect(userResolverAsync).toHaveBeenCalled(); + expect(userResolverAsync.mock.calls[0][0]).toBe('dependentModule'); + expect(userResolverAsync.mock.calls[0][1]).toHaveProperty( + 'basedir', + path.dirname(src), + ); + }); +}); + +describe('nodeModulesPaths', () => { + it('provides custom module paths after node_modules', () => { + const src = require.resolve('../'); + const result = nodeModulesPaths(src, {paths: ['./customFolder']}); + expect(result[result.length - 1]).toBe('./customFolder'); + }); +}); + +describe('Resolver.getModulePaths() -> nodeModulesPaths()', () => { + const _path = path; + let moduleMap: ModuleMap; + + beforeEach(() => { + jest.resetModules(); + + moduleMap = ModuleMap.create('/'); + + // Mocking realpath to function the old way, where it just looks at + // pathstrings instead of actually trying to access the physical directory. + // This test suite won't work otherwise, since we cannot make assumptions + // about the test environment when it comes to absolute paths. + jest.doMock('graceful-fs', () => ({ + ...jest.requireActual('graceful-fs'), + realPathSync: { + native: (dirInput: string) => dirInput, + }, + })); + }); + + afterAll(() => { + jest.resetModules(); + jest.dontMock('path'); + }); + + it('can resolve node modules relative to absolute paths in "moduleDirectories" on Windows platforms', () => { + jest.doMock('path', () => _path.win32); + const path = require('path'); + const Resolver = require('../').default; + + const cwd = 'D:\\temp\\project'; + const src = 'C:\\path\\to\\node_modules'; + const resolver = new Resolver(moduleMap, { + moduleDirectories: [src, 'node_modules'], + }); + const dirs_expected = [ + src, + cwd + '\\node_modules', + path.dirname(cwd) + '\\node_modules', + 'D:\\node_modules', + ]; + const dirs_actual = resolver.getModulePaths(cwd); + expect(dirs_actual).toEqual(expect.arrayContaining(dirs_expected)); + }); + + it('can resolve node modules relative to absolute paths in "moduleDirectories" on Posix platforms', () => { + jest.doMock('path', () => _path.posix); + const path = require('path'); + const Resolver = require('../').default; + + const cwd = '/temp/project'; + const src = '/path/to/node_modules'; + const resolver = new Resolver(moduleMap, { + moduleDirectories: [src, 'node_modules'], + }); + const dirs_expected = [ + src, + cwd + '/node_modules', + path.dirname(cwd) + '/node_modules', + '/node_modules', + ]; + const dirs_actual = resolver.getModulePaths(cwd); + expect(dirs_actual).toEqual(expect.arrayContaining(dirs_expected)); + }); +}); From f2e9bb0f47ca572c6d6e60280ff5829aeb03ee9b Mon Sep 17 00:00:00 2001 From: Ian VanSchooten Date: Tue, 8 Jun 2021 22:00:52 -0400 Subject: [PATCH 06/39] Update snapshot tests --- .../circusDeclarationErrors.test.ts.snap | 8 ++++---- .../moduleNameMapper.test.ts.snap | 4 ++-- .../resolveNoFileExtensions.test.ts.snap | 2 +- .../logDebugMessages.test.ts.snap | 2 -- .../src/__tests__/resolveAsync.test.ts | 8 ++++---- .../ScriptTransformer.test.ts.snap | 19 ++++++++----------- 6 files changed, 19 insertions(+), 24 deletions(-) diff --git a/e2e/__tests__/__snapshots__/circusDeclarationErrors.test.ts.snap b/e2e/__tests__/__snapshots__/circusDeclarationErrors.test.ts.snap index 36c675798378..e073a8300d49 100644 --- a/e2e/__tests__/__snapshots__/circusDeclarationErrors.test.ts.snap +++ b/e2e/__tests__/__snapshots__/circusDeclarationErrors.test.ts.snap @@ -16,7 +16,7 @@ FAIL __tests__/asyncDefinition.test.js 14 | }); 15 | }); - at eventHandler (../../packages/jest-circus/build/eventHandler.js:190:11) + at eventHandler (../../packages/jest-circus/build/eventHandler.js:146:11) at test (__tests__/asyncDefinition.test.js:12:5) ● Test suite failed to run @@ -31,7 +31,7 @@ FAIL __tests__/asyncDefinition.test.js 15 | }); 16 | - at eventHandler (../../packages/jest-circus/build/eventHandler.js:158:11) + at eventHandler (../../packages/jest-circus/build/eventHandler.js:114:11) at afterAll (__tests__/asyncDefinition.test.js:13:5) ● Test suite failed to run @@ -46,7 +46,7 @@ FAIL __tests__/asyncDefinition.test.js 20 | }); 21 | - at eventHandler (../../packages/jest-circus/build/eventHandler.js:190:11) + at eventHandler (../../packages/jest-circus/build/eventHandler.js:146:11) at test (__tests__/asyncDefinition.test.js:18:3) ● Test suite failed to run @@ -60,6 +60,6 @@ FAIL __tests__/asyncDefinition.test.js 20 | }); 21 | - at eventHandler (../../packages/jest-circus/build/eventHandler.js:158:11) + at eventHandler (../../packages/jest-circus/build/eventHandler.js:114:11) at afterAll (__tests__/asyncDefinition.test.js:19:3) `; diff --git a/e2e/__tests__/__snapshots__/moduleNameMapper.test.ts.snap b/e2e/__tests__/__snapshots__/moduleNameMapper.test.ts.snap index c73d54ca2d2e..b1d74dc76bde 100644 --- a/e2e/__tests__/__snapshots__/moduleNameMapper.test.ts.snap +++ b/e2e/__tests__/__snapshots__/moduleNameMapper.test.ts.snap @@ -41,7 +41,7 @@ FAIL __tests__/index.js 12 | module.exports = () => 'test'; 13 | - at createNoMappedModuleFoundError (../../packages/jest-resolve/build/resolver.js:577:17) + at createNoMappedModuleFoundError (../../packages/jest-resolve/build/resolver.js:873:17) at Object.require (index.js:10:1) `; @@ -70,6 +70,6 @@ FAIL __tests__/index.js 12 | module.exports = () => 'test'; 13 | - at createNoMappedModuleFoundError (../../packages/jest-resolve/build/resolver.js:577:17) + at createNoMappedModuleFoundError (../../packages/jest-resolve/build/resolver.js:873:17) at Object.require (index.js:10:1) `; diff --git a/e2e/__tests__/__snapshots__/resolveNoFileExtensions.test.ts.snap b/e2e/__tests__/__snapshots__/resolveNoFileExtensions.test.ts.snap index 0abfba5f3a54..33975c445b5e 100644 --- a/e2e/__tests__/__snapshots__/resolveNoFileExtensions.test.ts.snap +++ b/e2e/__tests__/__snapshots__/resolveNoFileExtensions.test.ts.snap @@ -37,6 +37,6 @@ FAIL __tests__/test.js | ^ 9 | - at Resolver.resolveModule (../../packages/jest-resolve/build/resolver.js:322:11) + at Resolver._throwModNotFoundError (../../packages/jest-resolve/build/resolver.js:249:11) at Object.require (index.js:8:18) `; diff --git a/packages/jest-core/src/lib/__tests__/__snapshots__/logDebugMessages.test.ts.snap b/packages/jest-core/src/lib/__tests__/__snapshots__/logDebugMessages.test.ts.snap index bf5d5881d7f1..7b74de72bd44 100644 --- a/packages/jest-core/src/lib/__tests__/__snapshots__/logDebugMessages.test.ts.snap +++ b/packages/jest-core/src/lib/__tests__/__snapshots__/logDebugMessages.test.ts.snap @@ -41,7 +41,6 @@ exports[`prints the config object 1`] = ` "skipFilter": false, "skipNodeResolution": false, "slowTestThreshold": 5, - "snapshotFormat": {}, "snapshotSerializers": [], "testEnvironment": "node", "testEnvironmentOptions": {}, @@ -95,7 +94,6 @@ exports[`prints the config object 1`] = ` "runTestsByPath": false, "silent": false, "skipFilter": false, - "snapshotFormat": {}, "testFailureExitCode": 1, "testNamePattern": "", "testPathPattern": "", diff --git a/packages/jest-resolve/src/__tests__/resolveAsync.test.ts b/packages/jest-resolve/src/__tests__/resolveAsync.test.ts index 40a66a84b7a2..40e2097ff01d 100644 --- a/packages/jest-resolve/src/__tests__/resolveAsync.test.ts +++ b/packages/jest-resolve/src/__tests__/resolveAsync.test.ts @@ -89,12 +89,12 @@ describe('findNodeModuleAsync', () => { userResolverAsync.mockImplementation(() => Promise.resolve('module')); const newPath = await ResolverAsync.findNodeModuleAsync('test', { + asyncResolver: require.resolve('../__mocks__/userResolverAsync'), basedir: '/', browser: true, extensions: ['js'], moduleDirectory: ['node_modules'], paths: ['/something'], - asyncResolver: require.resolve('../__mocks__/userResolverAsync'), }); expect(newPath).toBe('module'); @@ -119,8 +119,8 @@ describe('findNodeModuleAsync', () => { ); await ResolverAsync.findNodeModuleAsync('test', { - basedir: '/', asyncResolver: require.resolve('../__mocks__/userResolverAsync'), + basedir: '/', }); expect(mockResolveSync).toHaveBeenCalledWith( @@ -230,11 +230,12 @@ describe('resolveModuleAsync', () => { }); describe('getMockModuleAsync', () => { - it.only('is possible to use custom resolver to resolve deps inside mock modules with moduleNameMapper', async () => { + it('is possible to use custom resolver to resolve deps inside mock modules with moduleNameMapper', async () => { userResolverAsync.mockImplementation(() => Promise.resolve('module')); const moduleMap = ModuleMap.create('/'); const resolver = new ResolverAsync(moduleMap, { + asyncResolver: require.resolve('../__mocks__/userResolverAsync'), extensions: ['.js'], moduleNameMapper: [ { @@ -242,7 +243,6 @@ describe('getMockModuleAsync', () => { regex: /(.*)/, }, ], - asyncResolver: require.resolve('../__mocks__/userResolverAsync'), } as ResolverConfig); const src = require.resolve('../'); diff --git a/packages/jest-transform/src/__tests__/__snapshots__/ScriptTransformer.test.ts.snap b/packages/jest-transform/src/__tests__/__snapshots__/ScriptTransformer.test.ts.snap index ffa97cef1c0b..30afd6905c65 100644 --- a/packages/jest-transform/src/__tests__/__snapshots__/ScriptTransformer.test.ts.snap +++ b/packages/jest-transform/src/__tests__/__snapshots__/ScriptTransformer.test.ts.snap @@ -55,7 +55,6 @@ exports[`ScriptTransformer in async mode, passes expected transform options to g "skipFilter": false, "skipNodeResolution": false, "slowTestThreshold": 5, - "snapshotFormat": Object {}, "snapshotResolver": undefined, "snapshotSerializers": Array [], "testEnvironment": "node", @@ -84,7 +83,7 @@ exports[`ScriptTransformer in async mode, passes expected transform options to g "unmockedModulePathPatterns": undefined, "watchPathIgnorePatterns": Array [], }, - "configString": "{\\"automock\\":false,\\"cache\\":true,\\"cacheDirectory\\":\\"/cache/\\",\\"clearMocks\\":false,\\"coveragePathIgnorePatterns\\":[],\\"cwd\\":\\"/test_root_dir/\\",\\"detectLeaks\\":false,\\"detectOpenHandles\\":false,\\"errorOnDeprecated\\":false,\\"extensionsToTreatAsEsm\\":[],\\"extraGlobals\\":[],\\"forceCoverageMatch\\":[],\\"globals\\":{},\\"haste\\":{},\\"injectGlobals\\":true,\\"moduleDirectories\\":[],\\"moduleFileExtensions\\":[\\"js\\"],\\"moduleLoader\\":\\"/test_module_loader_path\\",\\"moduleNameMapper\\":[],\\"modulePathIgnorePatterns\\":[],\\"modulePaths\\":[],\\"name\\":\\"test\\",\\"prettierPath\\":\\"prettier\\",\\"resetMocks\\":false,\\"resetModules\\":false,\\"restoreMocks\\":false,\\"rootDir\\":\\"/\\",\\"roots\\":[],\\"runner\\":\\"jest-runner\\",\\"setupFiles\\":[],\\"setupFilesAfterEnv\\":[],\\"skipFilter\\":false,\\"skipNodeResolution\\":false,\\"slowTestThreshold\\":5,\\"snapshotFormat\\":{},\\"snapshotSerializers\\":[],\\"testEnvironment\\":\\"node\\",\\"testEnvironmentOptions\\":{},\\"testLocationInResults\\":false,\\"testMatch\\":[],\\"testPathIgnorePatterns\\":[],\\"testRegex\\":[\\"\\\\\\\\.test\\\\\\\\.js$\\"],\\"testRunner\\":\\"jest-circus/runner\\",\\"testURL\\":\\"http://localhost\\",\\"timers\\":\\"real\\",\\"transform\\":[[\\"\\\\\\\\.js$\\",\\"test_preprocessor\\",{\\"configKey\\":\\"configValue\\"}]],\\"transformIgnorePatterns\\":[\\"/node_modules/\\"],\\"watchPathIgnorePatterns\\":[]}", + "configString": "{\\"automock\\":false,\\"cache\\":true,\\"cacheDirectory\\":\\"/cache/\\",\\"clearMocks\\":false,\\"coveragePathIgnorePatterns\\":[],\\"cwd\\":\\"/test_root_dir/\\",\\"detectLeaks\\":false,\\"detectOpenHandles\\":false,\\"errorOnDeprecated\\":false,\\"extensionsToTreatAsEsm\\":[],\\"extraGlobals\\":[],\\"forceCoverageMatch\\":[],\\"globals\\":{},\\"haste\\":{},\\"injectGlobals\\":true,\\"moduleDirectories\\":[],\\"moduleFileExtensions\\":[\\"js\\"],\\"moduleLoader\\":\\"/test_module_loader_path\\",\\"moduleNameMapper\\":[],\\"modulePathIgnorePatterns\\":[],\\"modulePaths\\":[],\\"name\\":\\"test\\",\\"prettierPath\\":\\"prettier\\",\\"resetMocks\\":false,\\"resetModules\\":false,\\"restoreMocks\\":false,\\"rootDir\\":\\"/\\",\\"roots\\":[],\\"runner\\":\\"jest-runner\\",\\"setupFiles\\":[],\\"setupFilesAfterEnv\\":[],\\"skipFilter\\":false,\\"skipNodeResolution\\":false,\\"slowTestThreshold\\":5,\\"snapshotSerializers\\":[],\\"testEnvironment\\":\\"node\\",\\"testEnvironmentOptions\\":{},\\"testLocationInResults\\":false,\\"testMatch\\":[],\\"testPathIgnorePatterns\\":[],\\"testRegex\\":[\\"\\\\\\\\.test\\\\\\\\.js$\\"],\\"testRunner\\":\\"jest-circus/runner\\",\\"testURL\\":\\"http://localhost\\",\\"timers\\":\\"real\\",\\"transform\\":[[\\"\\\\\\\\.js$\\",\\"test_preprocessor\\",{\\"configKey\\":\\"configValue\\"}]],\\"transformIgnorePatterns\\":[\\"/node_modules/\\"],\\"watchPathIgnorePatterns\\":[]}", "coverageProvider": "babel", "instrument": true, "supportsDynamicImport": false, @@ -110,7 +109,7 @@ exports[`ScriptTransformer in async mode, uses the supplied async preprocessor 1 const TRANSFORMED = { filename: '/fruits/banana.js', script: 'module.exports = "banana";', - config: '{"collectCoverage":false,"collectCoverageFrom":[],"coverageProvider":"babel","supportsDynamicImport":false,"supportsExportNamespaceFrom":false,"supportsStaticESM":false,"supportsTopLevelAwait":false,"instrument":false,"cacheFS":{},"config":{"automock":false,"cache":true,"cacheDirectory":"/cache/","clearMocks":false,"coveragePathIgnorePatterns":[],"cwd":"/test_root_dir/","detectLeaks":false,"detectOpenHandles":false,"errorOnDeprecated":false,"extensionsToTreatAsEsm":[],"extraGlobals":[],"forceCoverageMatch":[],"globals":{},"haste":{},"injectGlobals":true,"moduleDirectories":[],"moduleFileExtensions":["js"],"moduleLoader":"/test_module_loader_path","moduleNameMapper":[],"modulePathIgnorePatterns":[],"modulePaths":[],"name":"test","prettierPath":"prettier","resetMocks":false,"resetModules":false,"restoreMocks":false,"rootDir":"/","roots":[],"runner":"jest-runner","setupFiles":[],"setupFilesAfterEnv":[],"skipFilter":false,"skipNodeResolution":false,"slowTestThreshold":5,"snapshotFormat":{},"snapshotSerializers":[],"testEnvironment":"node","testEnvironmentOptions":{},"testLocationInResults":false,"testMatch":[],"testPathIgnorePatterns":[],"testRegex":["\\\\.test\\\\.js$"],"testRunner":"jest-circus/runner","testURL":"http://localhost","timers":"real","transform":[["\\\\.js$","test_async_preprocessor",{}]],"transformIgnorePatterns":["/node_modules/"],"watchPathIgnorePatterns":[]},"configString":"{\\"automock\\":false,\\"cache\\":true,\\"cacheDirectory\\":\\"/cache/\\",\\"clearMocks\\":false,\\"coveragePathIgnorePatterns\\":[],\\"cwd\\":\\"/test_root_dir/\\",\\"detectLeaks\\":false,\\"detectOpenHandles\\":false,\\"errorOnDeprecated\\":false,\\"extensionsToTreatAsEsm\\":[],\\"extraGlobals\\":[],\\"forceCoverageMatch\\":[],\\"globals\\":{},\\"haste\\":{},\\"injectGlobals\\":true,\\"moduleDirectories\\":[],\\"moduleFileExtensions\\":[\\"js\\"],\\"moduleLoader\\":\\"/test_module_loader_path\\",\\"moduleNameMapper\\":[],\\"modulePathIgnorePatterns\\":[],\\"modulePaths\\":[],\\"name\\":\\"test\\",\\"prettierPath\\":\\"prettier\\",\\"resetMocks\\":false,\\"resetModules\\":false,\\"restoreMocks\\":false,\\"rootDir\\":\\"/\\",\\"roots\\":[],\\"runner\\":\\"jest-runner\\",\\"setupFiles\\":[],\\"setupFilesAfterEnv\\":[],\\"skipFilter\\":false,\\"skipNodeResolution\\":false,\\"slowTestThreshold\\":5,\\"snapshotFormat\\":{},\\"snapshotSerializers\\":[],\\"testEnvironment\\":\\"node\\",\\"testEnvironmentOptions\\":{},\\"testLocationInResults\\":false,\\"testMatch\\":[],\\"testPathIgnorePatterns\\":[],\\"testRegex\\":[\\"\\\\\\\\.test\\\\\\\\.js$\\"],\\"testRunner\\":\\"jest-circus/runner\\",\\"testURL\\":\\"http://localhost\\",\\"timers\\":\\"real\\",\\"transform\\":[[\\"\\\\\\\\.js$\\",\\"test_async_preprocessor\\",{}]],\\"transformIgnorePatterns\\":[\\"/node_modules/\\"],\\"watchPathIgnorePatterns\\":[]}","transformerConfig":{}}', + config: '{"collectCoverage":false,"collectCoverageFrom":[],"coverageProvider":"babel","supportsDynamicImport":false,"supportsExportNamespaceFrom":false,"supportsStaticESM":false,"supportsTopLevelAwait":false,"instrument":false,"cacheFS":{},"config":{"automock":false,"cache":true,"cacheDirectory":"/cache/","clearMocks":false,"coveragePathIgnorePatterns":[],"cwd":"/test_root_dir/","detectLeaks":false,"detectOpenHandles":false,"errorOnDeprecated":false,"extensionsToTreatAsEsm":[],"extraGlobals":[],"forceCoverageMatch":[],"globals":{},"haste":{},"injectGlobals":true,"moduleDirectories":[],"moduleFileExtensions":["js"],"moduleLoader":"/test_module_loader_path","moduleNameMapper":[],"modulePathIgnorePatterns":[],"modulePaths":[],"name":"test","prettierPath":"prettier","resetMocks":false,"resetModules":false,"restoreMocks":false,"rootDir":"/","roots":[],"runner":"jest-runner","setupFiles":[],"setupFilesAfterEnv":[],"skipFilter":false,"skipNodeResolution":false,"slowTestThreshold":5,"snapshotSerializers":[],"testEnvironment":"node","testEnvironmentOptions":{},"testLocationInResults":false,"testMatch":[],"testPathIgnorePatterns":[],"testRegex":["\\\\.test\\\\.js$"],"testRunner":"jest-circus/runner","testURL":"http://localhost","timers":"real","transform":[["\\\\.js$","test_async_preprocessor",{}]],"transformIgnorePatterns":["/node_modules/"],"watchPathIgnorePatterns":[]},"configString":"{\\"automock\\":false,\\"cache\\":true,\\"cacheDirectory\\":\\"/cache/\\",\\"clearMocks\\":false,\\"coveragePathIgnorePatterns\\":[],\\"cwd\\":\\"/test_root_dir/\\",\\"detectLeaks\\":false,\\"detectOpenHandles\\":false,\\"errorOnDeprecated\\":false,\\"extensionsToTreatAsEsm\\":[],\\"extraGlobals\\":[],\\"forceCoverageMatch\\":[],\\"globals\\":{},\\"haste\\":{},\\"injectGlobals\\":true,\\"moduleDirectories\\":[],\\"moduleFileExtensions\\":[\\"js\\"],\\"moduleLoader\\":\\"/test_module_loader_path\\",\\"moduleNameMapper\\":[],\\"modulePathIgnorePatterns\\":[],\\"modulePaths\\":[],\\"name\\":\\"test\\",\\"prettierPath\\":\\"prettier\\",\\"resetMocks\\":false,\\"resetModules\\":false,\\"restoreMocks\\":false,\\"rootDir\\":\\"/\\",\\"roots\\":[],\\"runner\\":\\"jest-runner\\",\\"setupFiles\\":[],\\"setupFilesAfterEnv\\":[],\\"skipFilter\\":false,\\"skipNodeResolution\\":false,\\"slowTestThreshold\\":5,\\"snapshotSerializers\\":[],\\"testEnvironment\\":\\"node\\",\\"testEnvironmentOptions\\":{},\\"testLocationInResults\\":false,\\"testMatch\\":[],\\"testPathIgnorePatterns\\":[],\\"testRegex\\":[\\"\\\\\\\\.test\\\\\\\\.js$\\"],\\"testRunner\\":\\"jest-circus/runner\\",\\"testURL\\":\\"http://localhost\\",\\"timers\\":\\"real\\",\\"transform\\":[[\\"\\\\\\\\.js$\\",\\"test_async_preprocessor\\",{}]],\\"transformIgnorePatterns\\":[\\"/node_modules/\\"],\\"watchPathIgnorePatterns\\":[]}","transformerConfig":{}}', }; `; @@ -120,7 +119,7 @@ exports[`ScriptTransformer in async mode, uses the supplied preprocessor 1`] = ` const TRANSFORMED = { filename: '/fruits/banana.js', script: 'module.exports = "banana";', - config: '{"collectCoverage":false,"collectCoverageFrom":[],"coverageProvider":"babel","supportsDynamicImport":false,"supportsExportNamespaceFrom":false,"supportsStaticESM":false,"supportsTopLevelAwait":false,"instrument":false,"cacheFS":{},"config":{"automock":false,"cache":true,"cacheDirectory":"/cache/","clearMocks":false,"coveragePathIgnorePatterns":[],"cwd":"/test_root_dir/","detectLeaks":false,"detectOpenHandles":false,"errorOnDeprecated":false,"extensionsToTreatAsEsm":[],"extraGlobals":[],"forceCoverageMatch":[],"globals":{},"haste":{},"injectGlobals":true,"moduleDirectories":[],"moduleFileExtensions":["js"],"moduleLoader":"/test_module_loader_path","moduleNameMapper":[],"modulePathIgnorePatterns":[],"modulePaths":[],"name":"test","prettierPath":"prettier","resetMocks":false,"resetModules":false,"restoreMocks":false,"rootDir":"/","roots":[],"runner":"jest-runner","setupFiles":[],"setupFilesAfterEnv":[],"skipFilter":false,"skipNodeResolution":false,"slowTestThreshold":5,"snapshotFormat":{},"snapshotSerializers":[],"testEnvironment":"node","testEnvironmentOptions":{},"testLocationInResults":false,"testMatch":[],"testPathIgnorePatterns":[],"testRegex":["\\\\.test\\\\.js$"],"testRunner":"jest-circus/runner","testURL":"http://localhost","timers":"real","transform":[["\\\\.js$","test_preprocessor",{}]],"transformIgnorePatterns":["/node_modules/"],"watchPathIgnorePatterns":[]},"configString":"{\\"automock\\":false,\\"cache\\":true,\\"cacheDirectory\\":\\"/cache/\\",\\"clearMocks\\":false,\\"coveragePathIgnorePatterns\\":[],\\"cwd\\":\\"/test_root_dir/\\",\\"detectLeaks\\":false,\\"detectOpenHandles\\":false,\\"errorOnDeprecated\\":false,\\"extensionsToTreatAsEsm\\":[],\\"extraGlobals\\":[],\\"forceCoverageMatch\\":[],\\"globals\\":{},\\"haste\\":{},\\"injectGlobals\\":true,\\"moduleDirectories\\":[],\\"moduleFileExtensions\\":[\\"js\\"],\\"moduleLoader\\":\\"/test_module_loader_path\\",\\"moduleNameMapper\\":[],\\"modulePathIgnorePatterns\\":[],\\"modulePaths\\":[],\\"name\\":\\"test\\",\\"prettierPath\\":\\"prettier\\",\\"resetMocks\\":false,\\"resetModules\\":false,\\"restoreMocks\\":false,\\"rootDir\\":\\"/\\",\\"roots\\":[],\\"runner\\":\\"jest-runner\\",\\"setupFiles\\":[],\\"setupFilesAfterEnv\\":[],\\"skipFilter\\":false,\\"skipNodeResolution\\":false,\\"slowTestThreshold\\":5,\\"snapshotFormat\\":{},\\"snapshotSerializers\\":[],\\"testEnvironment\\":\\"node\\",\\"testEnvironmentOptions\\":{},\\"testLocationInResults\\":false,\\"testMatch\\":[],\\"testPathIgnorePatterns\\":[],\\"testRegex\\":[\\"\\\\\\\\.test\\\\\\\\.js$\\"],\\"testRunner\\":\\"jest-circus/runner\\",\\"testURL\\":\\"http://localhost\\",\\"timers\\":\\"real\\",\\"transform\\":[[\\"\\\\\\\\.js$\\",\\"test_preprocessor\\",{}]],\\"transformIgnorePatterns\\":[\\"/node_modules/\\"],\\"watchPathIgnorePatterns\\":[]}","transformerConfig":{}}', + config: '{"collectCoverage":false,"collectCoverageFrom":[],"coverageProvider":"babel","supportsDynamicImport":false,"supportsExportNamespaceFrom":false,"supportsStaticESM":false,"supportsTopLevelAwait":false,"instrument":false,"cacheFS":{},"config":{"automock":false,"cache":true,"cacheDirectory":"/cache/","clearMocks":false,"coveragePathIgnorePatterns":[],"cwd":"/test_root_dir/","detectLeaks":false,"detectOpenHandles":false,"errorOnDeprecated":false,"extensionsToTreatAsEsm":[],"extraGlobals":[],"forceCoverageMatch":[],"globals":{},"haste":{},"injectGlobals":true,"moduleDirectories":[],"moduleFileExtensions":["js"],"moduleLoader":"/test_module_loader_path","moduleNameMapper":[],"modulePathIgnorePatterns":[],"modulePaths":[],"name":"test","prettierPath":"prettier","resetMocks":false,"resetModules":false,"restoreMocks":false,"rootDir":"/","roots":[],"runner":"jest-runner","setupFiles":[],"setupFilesAfterEnv":[],"skipFilter":false,"skipNodeResolution":false,"slowTestThreshold":5,"snapshotSerializers":[],"testEnvironment":"node","testEnvironmentOptions":{},"testLocationInResults":false,"testMatch":[],"testPathIgnorePatterns":[],"testRegex":["\\\\.test\\\\.js$"],"testRunner":"jest-circus/runner","testURL":"http://localhost","timers":"real","transform":[["\\\\.js$","test_preprocessor",{}]],"transformIgnorePatterns":["/node_modules/"],"watchPathIgnorePatterns":[]},"configString":"{\\"automock\\":false,\\"cache\\":true,\\"cacheDirectory\\":\\"/cache/\\",\\"clearMocks\\":false,\\"coveragePathIgnorePatterns\\":[],\\"cwd\\":\\"/test_root_dir/\\",\\"detectLeaks\\":false,\\"detectOpenHandles\\":false,\\"errorOnDeprecated\\":false,\\"extensionsToTreatAsEsm\\":[],\\"extraGlobals\\":[],\\"forceCoverageMatch\\":[],\\"globals\\":{},\\"haste\\":{},\\"injectGlobals\\":true,\\"moduleDirectories\\":[],\\"moduleFileExtensions\\":[\\"js\\"],\\"moduleLoader\\":\\"/test_module_loader_path\\",\\"moduleNameMapper\\":[],\\"modulePathIgnorePatterns\\":[],\\"modulePaths\\":[],\\"name\\":\\"test\\",\\"prettierPath\\":\\"prettier\\",\\"resetMocks\\":false,\\"resetModules\\":false,\\"restoreMocks\\":false,\\"rootDir\\":\\"/\\",\\"roots\\":[],\\"runner\\":\\"jest-runner\\",\\"setupFiles\\":[],\\"setupFilesAfterEnv\\":[],\\"skipFilter\\":false,\\"skipNodeResolution\\":false,\\"slowTestThreshold\\":5,\\"snapshotSerializers\\":[],\\"testEnvironment\\":\\"node\\",\\"testEnvironmentOptions\\":{},\\"testLocationInResults\\":false,\\"testMatch\\":[],\\"testPathIgnorePatterns\\":[],\\"testRegex\\":[\\"\\\\\\\\.test\\\\\\\\.js$\\"],\\"testRunner\\":\\"jest-circus/runner\\",\\"testURL\\":\\"http://localhost\\",\\"timers\\":\\"real\\",\\"transform\\":[[\\"\\\\\\\\.js$\\",\\"test_preprocessor\\",{}]],\\"transformIgnorePatterns\\":[\\"/node_modules/\\"],\\"watchPathIgnorePatterns\\":[]}","transformerConfig":{}}', }; `; @@ -183,7 +182,6 @@ exports[`ScriptTransformer passes expected transform options to getCacheKey 1`] "skipFilter": false, "skipNodeResolution": false, "slowTestThreshold": 5, - "snapshotFormat": Object {}, "snapshotResolver": undefined, "snapshotSerializers": Array [], "testEnvironment": "node", @@ -212,7 +210,7 @@ exports[`ScriptTransformer passes expected transform options to getCacheKey 1`] "unmockedModulePathPatterns": undefined, "watchPathIgnorePatterns": Array [], }, - "configString": "{\\"automock\\":false,\\"cache\\":true,\\"cacheDirectory\\":\\"/cache/\\",\\"clearMocks\\":false,\\"coveragePathIgnorePatterns\\":[],\\"cwd\\":\\"/test_root_dir/\\",\\"detectLeaks\\":false,\\"detectOpenHandles\\":false,\\"errorOnDeprecated\\":false,\\"extensionsToTreatAsEsm\\":[],\\"extraGlobals\\":[],\\"forceCoverageMatch\\":[],\\"globals\\":{},\\"haste\\":{},\\"injectGlobals\\":true,\\"moduleDirectories\\":[],\\"moduleFileExtensions\\":[\\"js\\"],\\"moduleLoader\\":\\"/test_module_loader_path\\",\\"moduleNameMapper\\":[],\\"modulePathIgnorePatterns\\":[],\\"modulePaths\\":[],\\"name\\":\\"test\\",\\"prettierPath\\":\\"prettier\\",\\"resetMocks\\":false,\\"resetModules\\":false,\\"restoreMocks\\":false,\\"rootDir\\":\\"/\\",\\"roots\\":[],\\"runner\\":\\"jest-runner\\",\\"setupFiles\\":[],\\"setupFilesAfterEnv\\":[],\\"skipFilter\\":false,\\"skipNodeResolution\\":false,\\"slowTestThreshold\\":5,\\"snapshotFormat\\":{},\\"snapshotSerializers\\":[],\\"testEnvironment\\":\\"node\\",\\"testEnvironmentOptions\\":{},\\"testLocationInResults\\":false,\\"testMatch\\":[],\\"testPathIgnorePatterns\\":[],\\"testRegex\\":[\\"\\\\\\\\.test\\\\\\\\.js$\\"],\\"testRunner\\":\\"jest-circus/runner\\",\\"testURL\\":\\"http://localhost\\",\\"timers\\":\\"real\\",\\"transform\\":[[\\"\\\\\\\\.js$\\",\\"test_preprocessor\\",{\\"configKey\\":\\"configValue\\"}]],\\"transformIgnorePatterns\\":[\\"/node_modules/\\"],\\"watchPathIgnorePatterns\\":[]}", + "configString": "{\\"automock\\":false,\\"cache\\":true,\\"cacheDirectory\\":\\"/cache/\\",\\"clearMocks\\":false,\\"coveragePathIgnorePatterns\\":[],\\"cwd\\":\\"/test_root_dir/\\",\\"detectLeaks\\":false,\\"detectOpenHandles\\":false,\\"errorOnDeprecated\\":false,\\"extensionsToTreatAsEsm\\":[],\\"extraGlobals\\":[],\\"forceCoverageMatch\\":[],\\"globals\\":{},\\"haste\\":{},\\"injectGlobals\\":true,\\"moduleDirectories\\":[],\\"moduleFileExtensions\\":[\\"js\\"],\\"moduleLoader\\":\\"/test_module_loader_path\\",\\"moduleNameMapper\\":[],\\"modulePathIgnorePatterns\\":[],\\"modulePaths\\":[],\\"name\\":\\"test\\",\\"prettierPath\\":\\"prettier\\",\\"resetMocks\\":false,\\"resetModules\\":false,\\"restoreMocks\\":false,\\"rootDir\\":\\"/\\",\\"roots\\":[],\\"runner\\":\\"jest-runner\\",\\"setupFiles\\":[],\\"setupFilesAfterEnv\\":[],\\"skipFilter\\":false,\\"skipNodeResolution\\":false,\\"slowTestThreshold\\":5,\\"snapshotSerializers\\":[],\\"testEnvironment\\":\\"node\\",\\"testEnvironmentOptions\\":{},\\"testLocationInResults\\":false,\\"testMatch\\":[],\\"testPathIgnorePatterns\\":[],\\"testRegex\\":[\\"\\\\\\\\.test\\\\\\\\.js$\\"],\\"testRunner\\":\\"jest-circus/runner\\",\\"testURL\\":\\"http://localhost\\",\\"timers\\":\\"real\\",\\"transform\\":[[\\"\\\\\\\\.js$\\",\\"test_preprocessor\\",{\\"configKey\\":\\"configValue\\"}]],\\"transformIgnorePatterns\\":[\\"/node_modules/\\"],\\"watchPathIgnorePatterns\\":[]}", "coverageProvider": "babel", "instrument": true, "supportsDynamicImport": false, @@ -289,7 +287,6 @@ exports[`ScriptTransformer passes expected transform options to getCacheKeyAsync "skipFilter": false, "skipNodeResolution": false, "slowTestThreshold": 5, - "snapshotFormat": Object {}, "snapshotResolver": undefined, "snapshotSerializers": Array [], "testEnvironment": "node", @@ -318,7 +315,7 @@ exports[`ScriptTransformer passes expected transform options to getCacheKeyAsync "unmockedModulePathPatterns": undefined, "watchPathIgnorePatterns": Array [], }, - "configString": "{\\"automock\\":false,\\"cache\\":true,\\"cacheDirectory\\":\\"/cache/\\",\\"clearMocks\\":false,\\"coveragePathIgnorePatterns\\":[],\\"cwd\\":\\"/test_root_dir/\\",\\"detectLeaks\\":false,\\"detectOpenHandles\\":false,\\"errorOnDeprecated\\":false,\\"extensionsToTreatAsEsm\\":[],\\"extraGlobals\\":[],\\"forceCoverageMatch\\":[],\\"globals\\":{},\\"haste\\":{},\\"injectGlobals\\":true,\\"moduleDirectories\\":[],\\"moduleFileExtensions\\":[\\"js\\"],\\"moduleLoader\\":\\"/test_module_loader_path\\",\\"moduleNameMapper\\":[],\\"modulePathIgnorePatterns\\":[],\\"modulePaths\\":[],\\"name\\":\\"test\\",\\"prettierPath\\":\\"prettier\\",\\"resetMocks\\":false,\\"resetModules\\":false,\\"restoreMocks\\":false,\\"rootDir\\":\\"/\\",\\"roots\\":[],\\"runner\\":\\"jest-runner\\",\\"setupFiles\\":[],\\"setupFilesAfterEnv\\":[],\\"skipFilter\\":false,\\"skipNodeResolution\\":false,\\"slowTestThreshold\\":5,\\"snapshotFormat\\":{},\\"snapshotSerializers\\":[],\\"testEnvironment\\":\\"node\\",\\"testEnvironmentOptions\\":{},\\"testLocationInResults\\":false,\\"testMatch\\":[],\\"testPathIgnorePatterns\\":[],\\"testRegex\\":[\\"\\\\\\\\.test\\\\\\\\.js$\\"],\\"testRunner\\":\\"jest-circus/runner\\",\\"testURL\\":\\"http://localhost\\",\\"timers\\":\\"real\\",\\"transform\\":[[\\"\\\\\\\\.js$\\",\\"test_async_preprocessor\\",{\\"configKey\\":\\"configValue\\"}]],\\"transformIgnorePatterns\\":[\\"/node_modules/\\"],\\"watchPathIgnorePatterns\\":[]}", + "configString": "{\\"automock\\":false,\\"cache\\":true,\\"cacheDirectory\\":\\"/cache/\\",\\"clearMocks\\":false,\\"coveragePathIgnorePatterns\\":[],\\"cwd\\":\\"/test_root_dir/\\",\\"detectLeaks\\":false,\\"detectOpenHandles\\":false,\\"errorOnDeprecated\\":false,\\"extensionsToTreatAsEsm\\":[],\\"extraGlobals\\":[],\\"forceCoverageMatch\\":[],\\"globals\\":{},\\"haste\\":{},\\"injectGlobals\\":true,\\"moduleDirectories\\":[],\\"moduleFileExtensions\\":[\\"js\\"],\\"moduleLoader\\":\\"/test_module_loader_path\\",\\"moduleNameMapper\\":[],\\"modulePathIgnorePatterns\\":[],\\"modulePaths\\":[],\\"name\\":\\"test\\",\\"prettierPath\\":\\"prettier\\",\\"resetMocks\\":false,\\"resetModules\\":false,\\"restoreMocks\\":false,\\"rootDir\\":\\"/\\",\\"roots\\":[],\\"runner\\":\\"jest-runner\\",\\"setupFiles\\":[],\\"setupFilesAfterEnv\\":[],\\"skipFilter\\":false,\\"skipNodeResolution\\":false,\\"slowTestThreshold\\":5,\\"snapshotSerializers\\":[],\\"testEnvironment\\":\\"node\\",\\"testEnvironmentOptions\\":{},\\"testLocationInResults\\":false,\\"testMatch\\":[],\\"testPathIgnorePatterns\\":[],\\"testRegex\\":[\\"\\\\\\\\.test\\\\\\\\.js$\\"],\\"testRunner\\":\\"jest-circus/runner\\",\\"testURL\\":\\"http://localhost\\",\\"timers\\":\\"real\\",\\"transform\\":[[\\"\\\\\\\\.js$\\",\\"test_async_preprocessor\\",{\\"configKey\\":\\"configValue\\"}]],\\"transformIgnorePatterns\\":[\\"/node_modules/\\"],\\"watchPathIgnorePatterns\\":[]}", "coverageProvider": "babel", "instrument": true, "supportsDynamicImport": false, @@ -648,7 +645,7 @@ exports[`ScriptTransformer uses mixture of sync/async preprocessors 1`] = ` const TRANSFORMED = { filename: '/fruits/banana.js', script: 'module.exports = "banana";', - config: '{"collectCoverage":false,"collectCoverageFrom":[],"coverageProvider":"babel","supportsDynamicImport":false,"supportsExportNamespaceFrom":false,"supportsStaticESM":false,"supportsTopLevelAwait":false,"instrument":false,"cacheFS":{},"config":{"automock":false,"cache":true,"cacheDirectory":"/cache/","clearMocks":false,"coveragePathIgnorePatterns":[],"cwd":"/test_root_dir/","detectLeaks":false,"detectOpenHandles":false,"errorOnDeprecated":false,"extensionsToTreatAsEsm":[],"extraGlobals":[],"forceCoverageMatch":[],"globals":{},"haste":{},"injectGlobals":true,"moduleDirectories":[],"moduleFileExtensions":["js"],"moduleLoader":"/test_module_loader_path","moduleNameMapper":[],"modulePathIgnorePatterns":[],"modulePaths":[],"name":"test","prettierPath":"prettier","resetMocks":false,"resetModules":false,"restoreMocks":false,"rootDir":"/","roots":[],"runner":"jest-runner","setupFiles":[],"setupFilesAfterEnv":[],"skipFilter":false,"skipNodeResolution":false,"slowTestThreshold":5,"snapshotFormat":{},"snapshotSerializers":[],"testEnvironment":"node","testEnvironmentOptions":{},"testLocationInResults":false,"testMatch":[],"testPathIgnorePatterns":[],"testRegex":["\\\\.test\\\\.js$"],"testRunner":"jest-circus/runner","testURL":"http://localhost","timers":"real","transform":[["\\\\.js$","test_async_preprocessor",{}],["\\\\.css$","css-preprocessor",{}]],"transformIgnorePatterns":["/node_modules/"],"watchPathIgnorePatterns":[]},"configString":"{\\"automock\\":false,\\"cache\\":true,\\"cacheDirectory\\":\\"/cache/\\",\\"clearMocks\\":false,\\"coveragePathIgnorePatterns\\":[],\\"cwd\\":\\"/test_root_dir/\\",\\"detectLeaks\\":false,\\"detectOpenHandles\\":false,\\"errorOnDeprecated\\":false,\\"extensionsToTreatAsEsm\\":[],\\"extraGlobals\\":[],\\"forceCoverageMatch\\":[],\\"globals\\":{},\\"haste\\":{},\\"injectGlobals\\":true,\\"moduleDirectories\\":[],\\"moduleFileExtensions\\":[\\"js\\"],\\"moduleLoader\\":\\"/test_module_loader_path\\",\\"moduleNameMapper\\":[],\\"modulePathIgnorePatterns\\":[],\\"modulePaths\\":[],\\"name\\":\\"test\\",\\"prettierPath\\":\\"prettier\\",\\"resetMocks\\":false,\\"resetModules\\":false,\\"restoreMocks\\":false,\\"rootDir\\":\\"/\\",\\"roots\\":[],\\"runner\\":\\"jest-runner\\",\\"setupFiles\\":[],\\"setupFilesAfterEnv\\":[],\\"skipFilter\\":false,\\"skipNodeResolution\\":false,\\"slowTestThreshold\\":5,\\"snapshotFormat\\":{},\\"snapshotSerializers\\":[],\\"testEnvironment\\":\\"node\\",\\"testEnvironmentOptions\\":{},\\"testLocationInResults\\":false,\\"testMatch\\":[],\\"testPathIgnorePatterns\\":[],\\"testRegex\\":[\\"\\\\\\\\.test\\\\\\\\.js$\\"],\\"testRunner\\":\\"jest-circus/runner\\",\\"testURL\\":\\"http://localhost\\",\\"timers\\":\\"real\\",\\"transform\\":[[\\"\\\\\\\\.js$\\",\\"test_async_preprocessor\\",{}],[\\"\\\\\\\\.css$\\",\\"css-preprocessor\\",{}]],\\"transformIgnorePatterns\\":[\\"/node_modules/\\"],\\"watchPathIgnorePatterns\\":[]}","transformerConfig":{}}', + config: '{"collectCoverage":false,"collectCoverageFrom":[],"coverageProvider":"babel","supportsDynamicImport":false,"supportsExportNamespaceFrom":false,"supportsStaticESM":false,"supportsTopLevelAwait":false,"instrument":false,"cacheFS":{},"config":{"automock":false,"cache":true,"cacheDirectory":"/cache/","clearMocks":false,"coveragePathIgnorePatterns":[],"cwd":"/test_root_dir/","detectLeaks":false,"detectOpenHandles":false,"errorOnDeprecated":false,"extensionsToTreatAsEsm":[],"extraGlobals":[],"forceCoverageMatch":[],"globals":{},"haste":{},"injectGlobals":true,"moduleDirectories":[],"moduleFileExtensions":["js"],"moduleLoader":"/test_module_loader_path","moduleNameMapper":[],"modulePathIgnorePatterns":[],"modulePaths":[],"name":"test","prettierPath":"prettier","resetMocks":false,"resetModules":false,"restoreMocks":false,"rootDir":"/","roots":[],"runner":"jest-runner","setupFiles":[],"setupFilesAfterEnv":[],"skipFilter":false,"skipNodeResolution":false,"slowTestThreshold":5,"snapshotSerializers":[],"testEnvironment":"node","testEnvironmentOptions":{},"testLocationInResults":false,"testMatch":[],"testPathIgnorePatterns":[],"testRegex":["\\\\.test\\\\.js$"],"testRunner":"jest-circus/runner","testURL":"http://localhost","timers":"real","transform":[["\\\\.js$","test_async_preprocessor",{}],["\\\\.css$","css-preprocessor",{}]],"transformIgnorePatterns":["/node_modules/"],"watchPathIgnorePatterns":[]},"configString":"{\\"automock\\":false,\\"cache\\":true,\\"cacheDirectory\\":\\"/cache/\\",\\"clearMocks\\":false,\\"coveragePathIgnorePatterns\\":[],\\"cwd\\":\\"/test_root_dir/\\",\\"detectLeaks\\":false,\\"detectOpenHandles\\":false,\\"errorOnDeprecated\\":false,\\"extensionsToTreatAsEsm\\":[],\\"extraGlobals\\":[],\\"forceCoverageMatch\\":[],\\"globals\\":{},\\"haste\\":{},\\"injectGlobals\\":true,\\"moduleDirectories\\":[],\\"moduleFileExtensions\\":[\\"js\\"],\\"moduleLoader\\":\\"/test_module_loader_path\\",\\"moduleNameMapper\\":[],\\"modulePathIgnorePatterns\\":[],\\"modulePaths\\":[],\\"name\\":\\"test\\",\\"prettierPath\\":\\"prettier\\",\\"resetMocks\\":false,\\"resetModules\\":false,\\"restoreMocks\\":false,\\"rootDir\\":\\"/\\",\\"roots\\":[],\\"runner\\":\\"jest-runner\\",\\"setupFiles\\":[],\\"setupFilesAfterEnv\\":[],\\"skipFilter\\":false,\\"skipNodeResolution\\":false,\\"slowTestThreshold\\":5,\\"snapshotSerializers\\":[],\\"testEnvironment\\":\\"node\\",\\"testEnvironmentOptions\\":{},\\"testLocationInResults\\":false,\\"testMatch\\":[],\\"testPathIgnorePatterns\\":[],\\"testRegex\\":[\\"\\\\\\\\.test\\\\\\\\.js$\\"],\\"testRunner\\":\\"jest-circus/runner\\",\\"testURL\\":\\"http://localhost\\",\\"timers\\":\\"real\\",\\"transform\\":[[\\"\\\\\\\\.js$\\",\\"test_async_preprocessor\\",{}],[\\"\\\\\\\\.css$\\",\\"css-preprocessor\\",{}]],\\"transformIgnorePatterns\\":[\\"/node_modules/\\"],\\"watchPathIgnorePatterns\\":[]}","transformerConfig":{}}', }; `; @@ -665,7 +662,7 @@ exports[`ScriptTransformer uses multiple preprocessors 1`] = ` const TRANSFORMED = { filename: '/fruits/banana.js', script: 'module.exports = "banana";', - config: '{"collectCoverage":false,"collectCoverageFrom":[],"coverageProvider":"babel","supportsDynamicImport":false,"supportsExportNamespaceFrom":false,"supportsStaticESM":false,"supportsTopLevelAwait":false,"instrument":false,"cacheFS":{},"config":{"automock":false,"cache":true,"cacheDirectory":"/cache/","clearMocks":false,"coveragePathIgnorePatterns":[],"cwd":"/test_root_dir/","detectLeaks":false,"detectOpenHandles":false,"errorOnDeprecated":false,"extensionsToTreatAsEsm":[],"extraGlobals":[],"forceCoverageMatch":[],"globals":{},"haste":{},"injectGlobals":true,"moduleDirectories":[],"moduleFileExtensions":["js"],"moduleLoader":"/test_module_loader_path","moduleNameMapper":[],"modulePathIgnorePatterns":[],"modulePaths":[],"name":"test","prettierPath":"prettier","resetMocks":false,"resetModules":false,"restoreMocks":false,"rootDir":"/","roots":[],"runner":"jest-runner","setupFiles":[],"setupFilesAfterEnv":[],"skipFilter":false,"skipNodeResolution":false,"slowTestThreshold":5,"snapshotFormat":{},"snapshotSerializers":[],"testEnvironment":"node","testEnvironmentOptions":{},"testLocationInResults":false,"testMatch":[],"testPathIgnorePatterns":[],"testRegex":["\\\\.test\\\\.js$"],"testRunner":"jest-circus/runner","testURL":"http://localhost","timers":"real","transform":[["\\\\.js$","test_preprocessor",{}],["\\\\.css$","css-preprocessor",{}]],"transformIgnorePatterns":["/node_modules/"],"watchPathIgnorePatterns":[]},"configString":"{\\"automock\\":false,\\"cache\\":true,\\"cacheDirectory\\":\\"/cache/\\",\\"clearMocks\\":false,\\"coveragePathIgnorePatterns\\":[],\\"cwd\\":\\"/test_root_dir/\\",\\"detectLeaks\\":false,\\"detectOpenHandles\\":false,\\"errorOnDeprecated\\":false,\\"extensionsToTreatAsEsm\\":[],\\"extraGlobals\\":[],\\"forceCoverageMatch\\":[],\\"globals\\":{},\\"haste\\":{},\\"injectGlobals\\":true,\\"moduleDirectories\\":[],\\"moduleFileExtensions\\":[\\"js\\"],\\"moduleLoader\\":\\"/test_module_loader_path\\",\\"moduleNameMapper\\":[],\\"modulePathIgnorePatterns\\":[],\\"modulePaths\\":[],\\"name\\":\\"test\\",\\"prettierPath\\":\\"prettier\\",\\"resetMocks\\":false,\\"resetModules\\":false,\\"restoreMocks\\":false,\\"rootDir\\":\\"/\\",\\"roots\\":[],\\"runner\\":\\"jest-runner\\",\\"setupFiles\\":[],\\"setupFilesAfterEnv\\":[],\\"skipFilter\\":false,\\"skipNodeResolution\\":false,\\"slowTestThreshold\\":5,\\"snapshotFormat\\":{},\\"snapshotSerializers\\":[],\\"testEnvironment\\":\\"node\\",\\"testEnvironmentOptions\\":{},\\"testLocationInResults\\":false,\\"testMatch\\":[],\\"testPathIgnorePatterns\\":[],\\"testRegex\\":[\\"\\\\\\\\.test\\\\\\\\.js$\\"],\\"testRunner\\":\\"jest-circus/runner\\",\\"testURL\\":\\"http://localhost\\",\\"timers\\":\\"real\\",\\"transform\\":[[\\"\\\\\\\\.js$\\",\\"test_preprocessor\\",{}],[\\"\\\\\\\\.css$\\",\\"css-preprocessor\\",{}]],\\"transformIgnorePatterns\\":[\\"/node_modules/\\"],\\"watchPathIgnorePatterns\\":[]}","transformerConfig":{}}', + config: '{"collectCoverage":false,"collectCoverageFrom":[],"coverageProvider":"babel","supportsDynamicImport":false,"supportsExportNamespaceFrom":false,"supportsStaticESM":false,"supportsTopLevelAwait":false,"instrument":false,"cacheFS":{},"config":{"automock":false,"cache":true,"cacheDirectory":"/cache/","clearMocks":false,"coveragePathIgnorePatterns":[],"cwd":"/test_root_dir/","detectLeaks":false,"detectOpenHandles":false,"errorOnDeprecated":false,"extensionsToTreatAsEsm":[],"extraGlobals":[],"forceCoverageMatch":[],"globals":{},"haste":{},"injectGlobals":true,"moduleDirectories":[],"moduleFileExtensions":["js"],"moduleLoader":"/test_module_loader_path","moduleNameMapper":[],"modulePathIgnorePatterns":[],"modulePaths":[],"name":"test","prettierPath":"prettier","resetMocks":false,"resetModules":false,"restoreMocks":false,"rootDir":"/","roots":[],"runner":"jest-runner","setupFiles":[],"setupFilesAfterEnv":[],"skipFilter":false,"skipNodeResolution":false,"slowTestThreshold":5,"snapshotSerializers":[],"testEnvironment":"node","testEnvironmentOptions":{},"testLocationInResults":false,"testMatch":[],"testPathIgnorePatterns":[],"testRegex":["\\\\.test\\\\.js$"],"testRunner":"jest-circus/runner","testURL":"http://localhost","timers":"real","transform":[["\\\\.js$","test_preprocessor",{}],["\\\\.css$","css-preprocessor",{}]],"transformIgnorePatterns":["/node_modules/"],"watchPathIgnorePatterns":[]},"configString":"{\\"automock\\":false,\\"cache\\":true,\\"cacheDirectory\\":\\"/cache/\\",\\"clearMocks\\":false,\\"coveragePathIgnorePatterns\\":[],\\"cwd\\":\\"/test_root_dir/\\",\\"detectLeaks\\":false,\\"detectOpenHandles\\":false,\\"errorOnDeprecated\\":false,\\"extensionsToTreatAsEsm\\":[],\\"extraGlobals\\":[],\\"forceCoverageMatch\\":[],\\"globals\\":{},\\"haste\\":{},\\"injectGlobals\\":true,\\"moduleDirectories\\":[],\\"moduleFileExtensions\\":[\\"js\\"],\\"moduleLoader\\":\\"/test_module_loader_path\\",\\"moduleNameMapper\\":[],\\"modulePathIgnorePatterns\\":[],\\"modulePaths\\":[],\\"name\\":\\"test\\",\\"prettierPath\\":\\"prettier\\",\\"resetMocks\\":false,\\"resetModules\\":false,\\"restoreMocks\\":false,\\"rootDir\\":\\"/\\",\\"roots\\":[],\\"runner\\":\\"jest-runner\\",\\"setupFiles\\":[],\\"setupFilesAfterEnv\\":[],\\"skipFilter\\":false,\\"skipNodeResolution\\":false,\\"slowTestThreshold\\":5,\\"snapshotSerializers\\":[],\\"testEnvironment\\":\\"node\\",\\"testEnvironmentOptions\\":{},\\"testLocationInResults\\":false,\\"testMatch\\":[],\\"testPathIgnorePatterns\\":[],\\"testRegex\\":[\\"\\\\\\\\.test\\\\\\\\.js$\\"],\\"testRunner\\":\\"jest-circus/runner\\",\\"testURL\\":\\"http://localhost\\",\\"timers\\":\\"real\\",\\"transform\\":[[\\"\\\\\\\\.js$\\",\\"test_preprocessor\\",{}],[\\"\\\\\\\\.css$\\",\\"css-preprocessor\\",{}]],\\"transformIgnorePatterns\\":[\\"/node_modules/\\"],\\"watchPathIgnorePatterns\\":[]}","transformerConfig":{}}', }; `; @@ -682,7 +679,7 @@ exports[`ScriptTransformer uses the supplied preprocessor 1`] = ` const TRANSFORMED = { filename: '/fruits/banana.js', script: 'module.exports = "banana";', - config: '{"collectCoverage":false,"collectCoverageFrom":[],"coverageProvider":"babel","supportsDynamicImport":false,"supportsExportNamespaceFrom":false,"supportsStaticESM":false,"supportsTopLevelAwait":false,"instrument":false,"cacheFS":{},"config":{"automock":false,"cache":true,"cacheDirectory":"/cache/","clearMocks":false,"coveragePathIgnorePatterns":[],"cwd":"/test_root_dir/","detectLeaks":false,"detectOpenHandles":false,"errorOnDeprecated":false,"extensionsToTreatAsEsm":[],"extraGlobals":[],"forceCoverageMatch":[],"globals":{},"haste":{},"injectGlobals":true,"moduleDirectories":[],"moduleFileExtensions":["js"],"moduleLoader":"/test_module_loader_path","moduleNameMapper":[],"modulePathIgnorePatterns":[],"modulePaths":[],"name":"test","prettierPath":"prettier","resetMocks":false,"resetModules":false,"restoreMocks":false,"rootDir":"/","roots":[],"runner":"jest-runner","setupFiles":[],"setupFilesAfterEnv":[],"skipFilter":false,"skipNodeResolution":false,"slowTestThreshold":5,"snapshotFormat":{},"snapshotSerializers":[],"testEnvironment":"node","testEnvironmentOptions":{},"testLocationInResults":false,"testMatch":[],"testPathIgnorePatterns":[],"testRegex":["\\\\.test\\\\.js$"],"testRunner":"jest-circus/runner","testURL":"http://localhost","timers":"real","transform":[["\\\\.js$","test_preprocessor",{}]],"transformIgnorePatterns":["/node_modules/"],"watchPathIgnorePatterns":[]},"configString":"{\\"automock\\":false,\\"cache\\":true,\\"cacheDirectory\\":\\"/cache/\\",\\"clearMocks\\":false,\\"coveragePathIgnorePatterns\\":[],\\"cwd\\":\\"/test_root_dir/\\",\\"detectLeaks\\":false,\\"detectOpenHandles\\":false,\\"errorOnDeprecated\\":false,\\"extensionsToTreatAsEsm\\":[],\\"extraGlobals\\":[],\\"forceCoverageMatch\\":[],\\"globals\\":{},\\"haste\\":{},\\"injectGlobals\\":true,\\"moduleDirectories\\":[],\\"moduleFileExtensions\\":[\\"js\\"],\\"moduleLoader\\":\\"/test_module_loader_path\\",\\"moduleNameMapper\\":[],\\"modulePathIgnorePatterns\\":[],\\"modulePaths\\":[],\\"name\\":\\"test\\",\\"prettierPath\\":\\"prettier\\",\\"resetMocks\\":false,\\"resetModules\\":false,\\"restoreMocks\\":false,\\"rootDir\\":\\"/\\",\\"roots\\":[],\\"runner\\":\\"jest-runner\\",\\"setupFiles\\":[],\\"setupFilesAfterEnv\\":[],\\"skipFilter\\":false,\\"skipNodeResolution\\":false,\\"slowTestThreshold\\":5,\\"snapshotFormat\\":{},\\"snapshotSerializers\\":[],\\"testEnvironment\\":\\"node\\",\\"testEnvironmentOptions\\":{},\\"testLocationInResults\\":false,\\"testMatch\\":[],\\"testPathIgnorePatterns\\":[],\\"testRegex\\":[\\"\\\\\\\\.test\\\\\\\\.js$\\"],\\"testRunner\\":\\"jest-circus/runner\\",\\"testURL\\":\\"http://localhost\\",\\"timers\\":\\"real\\",\\"transform\\":[[\\"\\\\\\\\.js$\\",\\"test_preprocessor\\",{}]],\\"transformIgnorePatterns\\":[\\"/node_modules/\\"],\\"watchPathIgnorePatterns\\":[]}","transformerConfig":{}}', + config: '{"collectCoverage":false,"collectCoverageFrom":[],"coverageProvider":"babel","supportsDynamicImport":false,"supportsExportNamespaceFrom":false,"supportsStaticESM":false,"supportsTopLevelAwait":false,"instrument":false,"cacheFS":{},"config":{"automock":false,"cache":true,"cacheDirectory":"/cache/","clearMocks":false,"coveragePathIgnorePatterns":[],"cwd":"/test_root_dir/","detectLeaks":false,"detectOpenHandles":false,"errorOnDeprecated":false,"extensionsToTreatAsEsm":[],"extraGlobals":[],"forceCoverageMatch":[],"globals":{},"haste":{},"injectGlobals":true,"moduleDirectories":[],"moduleFileExtensions":["js"],"moduleLoader":"/test_module_loader_path","moduleNameMapper":[],"modulePathIgnorePatterns":[],"modulePaths":[],"name":"test","prettierPath":"prettier","resetMocks":false,"resetModules":false,"restoreMocks":false,"rootDir":"/","roots":[],"runner":"jest-runner","setupFiles":[],"setupFilesAfterEnv":[],"skipFilter":false,"skipNodeResolution":false,"slowTestThreshold":5,"snapshotSerializers":[],"testEnvironment":"node","testEnvironmentOptions":{},"testLocationInResults":false,"testMatch":[],"testPathIgnorePatterns":[],"testRegex":["\\\\.test\\\\.js$"],"testRunner":"jest-circus/runner","testURL":"http://localhost","timers":"real","transform":[["\\\\.js$","test_preprocessor",{}]],"transformIgnorePatterns":["/node_modules/"],"watchPathIgnorePatterns":[]},"configString":"{\\"automock\\":false,\\"cache\\":true,\\"cacheDirectory\\":\\"/cache/\\",\\"clearMocks\\":false,\\"coveragePathIgnorePatterns\\":[],\\"cwd\\":\\"/test_root_dir/\\",\\"detectLeaks\\":false,\\"detectOpenHandles\\":false,\\"errorOnDeprecated\\":false,\\"extensionsToTreatAsEsm\\":[],\\"extraGlobals\\":[],\\"forceCoverageMatch\\":[],\\"globals\\":{},\\"haste\\":{},\\"injectGlobals\\":true,\\"moduleDirectories\\":[],\\"moduleFileExtensions\\":[\\"js\\"],\\"moduleLoader\\":\\"/test_module_loader_path\\",\\"moduleNameMapper\\":[],\\"modulePathIgnorePatterns\\":[],\\"modulePaths\\":[],\\"name\\":\\"test\\",\\"prettierPath\\":\\"prettier\\",\\"resetMocks\\":false,\\"resetModules\\":false,\\"restoreMocks\\":false,\\"rootDir\\":\\"/\\",\\"roots\\":[],\\"runner\\":\\"jest-runner\\",\\"setupFiles\\":[],\\"setupFilesAfterEnv\\":[],\\"skipFilter\\":false,\\"skipNodeResolution\\":false,\\"slowTestThreshold\\":5,\\"snapshotSerializers\\":[],\\"testEnvironment\\":\\"node\\",\\"testEnvironmentOptions\\":{},\\"testLocationInResults\\":false,\\"testMatch\\":[],\\"testPathIgnorePatterns\\":[],\\"testRegex\\":[\\"\\\\\\\\.test\\\\\\\\.js$\\"],\\"testRunner\\":\\"jest-circus/runner\\",\\"testURL\\":\\"http://localhost\\",\\"timers\\":\\"real\\",\\"transform\\":[[\\"\\\\\\\\.js$\\",\\"test_preprocessor\\",{}]],\\"transformIgnorePatterns\\":[\\"/node_modules/\\"],\\"watchPathIgnorePatterns\\":[]}","transformerConfig":{}}', }; `; From adaa85fff83a5af341c85b0b729008b61592b3a3 Mon Sep 17 00:00:00 2001 From: Ian VanSchooten Date: Mon, 30 Aug 2021 22:57:02 -0400 Subject: [PATCH 07/39] Recombine into a single class with sync and async methods --- .../src/__mocks__/userResolverAsync.d.ts | 10 +- .../src/__mocks__/userResolverAsync.js | 6 +- .../src/__tests__/resolve.test.ts | 200 ++++- .../src/__tests__/resolveAsync.test.ts | 333 -------- packages/jest-resolve/src/defaultResolver.ts | 8 +- packages/jest-resolve/src/resolver.ts | 743 +++++++++--------- 6 files changed, 578 insertions(+), 722 deletions(-) delete mode 100644 packages/jest-resolve/src/__tests__/resolveAsync.test.ts diff --git a/packages/jest-resolve/src/__mocks__/userResolverAsync.d.ts b/packages/jest-resolve/src/__mocks__/userResolverAsync.d.ts index b292cb17a62e..b46adc0493e0 100644 --- a/packages/jest-resolve/src/__mocks__/userResolverAsync.d.ts +++ b/packages/jest-resolve/src/__mocks__/userResolverAsync.d.ts @@ -8,9 +8,11 @@ import {defaultResolverAsync} from '../defaultResolver'; // todo: can be replaced with jest.MockedFunction -declare const userResolver: jest.MockInstance< - ReturnType, - Parameters ->; +declare const userResolver: { + async: jest.MockInstance< + ReturnType, + Parameters + >; +}; export default userResolver; diff --git a/packages/jest-resolve/src/__mocks__/userResolverAsync.js b/packages/jest-resolve/src/__mocks__/userResolverAsync.js index 61dff6635904..d538df61a643 100644 --- a/packages/jest-resolve/src/__mocks__/userResolverAsync.js +++ b/packages/jest-resolve/src/__mocks__/userResolverAsync.js @@ -7,6 +7,8 @@ 'use strict'; -module.exports = function userResolver(path, options) { - return Promise.resolve('module'); +module.exports = { + async: function userResolver(path, options) { + return Promise.resolve('module'); + }, }; diff --git a/packages/jest-resolve/src/__tests__/resolve.test.ts b/packages/jest-resolve/src/__tests__/resolve.test.ts index cfbe99bc841d..29c3a87f12a9 100644 --- a/packages/jest-resolve/src/__tests__/resolve.test.ts +++ b/packages/jest-resolve/src/__tests__/resolve.test.ts @@ -8,18 +8,20 @@ import * as path from 'path'; import * as fs from 'graceful-fs'; -import {sync as resolveSync} from 'resolve'; +import resolveAsync = require('resolve'); import {ModuleMap} from 'jest-haste-map'; import userResolver from '../__mocks__/userResolver'; -import defaultResolver from '../defaultResolver'; +import userResolverAsync from '../__mocks__/userResolverAsync'; +import defaultResolver, {defaultResolverAsync} from '../defaultResolver'; import nodeModulesPaths from '../nodeModulesPaths'; import Resolver from '../resolver'; import type {ResolverConfig} from '../types'; jest.mock('../__mocks__/userResolver'); +jest.mock('../__mocks__/userResolverAsync'); // Do not fully mock `resolve` because it is used by Jest. Doing it will crash -// in very strange ways. Instead just spy on the method `sync`. +// in very strange ways. Instead just spy on it and its `sync` method. jest.mock('resolve', () => { const originalModule = jest.requireActual('resolve'); return { @@ -29,11 +31,15 @@ jest.mock('resolve', () => { }); const mockResolveSync = < - jest.Mock, Parameters> ->resolveSync; + jest.Mock< + ReturnType, + Parameters + > +>resolveAsync.sync; beforeEach(() => { userResolver.mockClear(); + userResolverAsync.async.mockClear(); mockResolveSync.mockClear(); }); @@ -148,6 +154,65 @@ describe('findNodeModule', () => { }); }); +describe('findNodeModuleAsync', () => { + it('is possible to override the default resolver', async () => { + const cwd = process.cwd(); + const resolvedCwd = fs.realpathSync(cwd) || cwd; + const nodePaths = process.env.NODE_PATH + ? process.env.NODE_PATH.split(path.delimiter) + .filter(Boolean) + .map(p => path.resolve(resolvedCwd, p)) + : null; + + userResolverAsync.async.mockImplementation(() => Promise.resolve('module')); + + const newPath = await Resolver.findNodeModuleAsync('test', { + basedir: '/', + browser: true, + extensions: ['js'], + moduleDirectory: ['node_modules'], + paths: ['/something'], + resolver: require.resolve('../__mocks__/userResolverAsync'), + }); + + expect(newPath).toBe('module'); + expect(userResolverAsync.async.mock.calls[0][0]).toBe('test'); + expect(userResolverAsync.async.mock.calls[0][1]).toStrictEqual({ + basedir: '/', + browser: true, + defaultResolver, + defaultResolverAsync, + extensions: ['js'], + moduleDirectory: ['node_modules'], + paths: (nodePaths || []).concat(['/something']), + rootDir: undefined, + }); + }); + + it('passes packageFilter to the resolve module when using the default resolver', async () => { + const packageFilter = jest.fn(); + + // A resolver that delegates to defaultResolver with a packageFilter implementation + userResolverAsync.async.mockImplementation((request, opts) => + Promise.resolve(opts.defaultResolver(request, {...opts, packageFilter})), + ); + + await Resolver.findNodeModuleAsync('test', { + basedir: '/', + resolver: require.resolve('../__mocks__/userResolverAsync'), + }); + + // Question, if we can't mock resolve (async), how do we test this? + + // expect(mockResolveSync).toHaveBeenCalledWith( + // 'test', + // expect.objectContaining({ + // packageFilter, + // }), + // ); + }); +}); + describe('resolveModule', () => { let moduleMap: ModuleMap; beforeEach(() => { @@ -238,6 +303,103 @@ describe('resolveModule', () => { }); }); +describe('resolveModuleAsync', () => { + let moduleMap: ModuleMap; + beforeEach(() => { + moduleMap = ModuleMap.create('/'); + }); + + it('is possible to resolve node modules', async () => { + const resolver = new Resolver(moduleMap, { + extensions: ['.js'], + } as ResolverConfig); + const src = require.resolve('../'); + const resolved = await resolver.resolveModuleAsync( + src, + './__mocks__/mockJsDependency', + ); + expect(resolved).toBe(require.resolve('../__mocks__/mockJsDependency.js')); + }); + + it('is possible to resolve node modules with custom extensions', async () => { + const resolver = new Resolver(moduleMap, { + extensions: ['.js', '.jsx'], + } as ResolverConfig); + const src = require.resolve('../'); + const resolvedJsx = await resolver.resolveModuleAsync( + src, + './__mocks__/mockJsxDependency', + ); + expect(resolvedJsx).toBe( + require.resolve('../__mocks__/mockJsxDependency.jsx'), + ); + }); + + it('is possible to resolve node modules with custom extensions and platforms', async () => { + const resolver = new Resolver(moduleMap, { + extensions: ['.js', '.jsx'], + platforms: ['native'], + } as ResolverConfig); + const src = require.resolve('../'); + const resolvedJsx = await resolver.resolveModuleAsync( + src, + './__mocks__/mockJsxDependency', + ); + expect(resolvedJsx).toBe( + require.resolve('../__mocks__/mockJsxDependency.native.jsx'), + ); + }); + + it('is possible to resolve node modules by resolving their realpath', async () => { + const resolver = new Resolver(moduleMap, { + extensions: ['.js'], + } as ResolverConfig); + const src = path.join( + path.resolve(__dirname, '../../src/__mocks__/bar/node_modules/'), + 'foo/index.js', + ); + const resolved = await resolver.resolveModuleAsync(src, 'dep'); + expect(resolved).toBe( + require.resolve('../../src/__mocks__/foo/node_modules/dep/index.js'), + ); + }); + + it('is possible to specify custom resolve paths', async () => { + const resolver = new Resolver(moduleMap, { + extensions: ['.js'], + } as ResolverConfig); + const src = require.resolve('../'); + const resolved = await resolver.resolveModuleAsync( + src, + 'mockJsDependency', + { + paths: [ + path.resolve(__dirname, '../../src/__tests__'), + path.resolve(__dirname, '../../src/__mocks__'), + ], + }, + ); + expect(resolved).toBe(require.resolve('../__mocks__/mockJsDependency.js')); + }); + + it('does not confuse directories with files', async () => { + const resolver = new Resolver(moduleMap, { + extensions: ['.js'], + } as ResolverConfig); + const mocksDirectory = path.resolve(__dirname, '../__mocks__'); + const fooSlashFoo = path.join(mocksDirectory, 'foo/foo.js'); + const fooSlashIndex = path.join(mocksDirectory, 'foo/index.js'); + + const resolvedWithSlash = await resolver.resolveModuleAsync( + fooSlashFoo, + './', + ); + const resolvedWithDot = await resolver.resolveModuleAsync(fooSlashFoo, '.'); + expect(resolvedWithSlash).toBe(fooSlashIndex); + expect(resolvedWithSlash).toBe(resolvedWithDot); + }); +}); + describe('getMockModule', () => { it('is possible to use custom resolver to resolve deps inside mock modules with moduleNameMapper', () => { userResolver.mockImplementation(() => 'module'); @@ -265,6 +427,34 @@ describe('getMockModule', () => { }); }); +describe('getMockModuleAsync', () => { + it('is possible to use custom resolver to resolve deps inside mock modules with moduleNameMapper', async () => { + userResolverAsync.async.mockImplementation(() => Promise.resolve('module')); + + const moduleMap = ModuleMap.create('/'); + const resolver = new Resolver(moduleMap, { + extensions: ['.js'], + moduleNameMapper: [ + { + moduleName: '$1', + regex: /(.*)/, + }, + ], + resolver: require.resolve('../__mocks__/userResolverAsync'), + } as ResolverConfig); + const src = require.resolve('../'); + + await resolver.resolveModuleAsync(src, 'dependentModule'); + + expect(userResolverAsync.async).toHaveBeenCalled(); + expect(userResolverAsync.async.mock.calls[0][0]).toBe('dependentModule'); + expect(userResolverAsync.async.mock.calls[0][1]).toHaveProperty( + 'basedir', + path.dirname(src), + ); + }); +}); + describe('nodeModulesPaths', () => { it('provides custom module paths after node_modules', () => { const src = require.resolve('../'); diff --git a/packages/jest-resolve/src/__tests__/resolveAsync.test.ts b/packages/jest-resolve/src/__tests__/resolveAsync.test.ts deleted file mode 100644 index 40e2097ff01d..000000000000 --- a/packages/jest-resolve/src/__tests__/resolveAsync.test.ts +++ /dev/null @@ -1,333 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - */ - -import * as path from 'path'; -import * as fs from 'graceful-fs'; -import {sync as resolveSync} from 'resolve'; -import {ModuleMap} from 'jest-haste-map'; -import userResolverAsync from '../__mocks__/userResolverAsync'; -import defaultResolver from '../defaultResolver'; -import nodeModulesPaths from '../nodeModulesPaths'; -import {ResolverAsync} from '../resolver'; -import type {ResolverConfig} from '../types'; - -jest.mock('../__mocks__/userResolverAsync'); - -// Do not fully mock `resolve` because it is used by Jest. Doing it will crash -// in very strange ways. Instead just spy on the method `sync`. -jest.mock('resolve', () => { - const originalModule = jest.requireActual('resolve'); - return { - ...originalModule, - sync: jest.spyOn(originalModule, 'sync'), - }; -}); - -const mockResolveSync = < - jest.Mock, Parameters> ->resolveSync; - -beforeEach(() => { - userResolverAsync.mockClear(); - mockResolveSync.mockClear(); -}); - -describe('isCoreModule', () => { - it('returns false if `hasCoreModules` is false.', () => { - const moduleMap = ModuleMap.create('/'); - const resolver = new ResolverAsync(moduleMap, { - hasCoreModules: false, - } as ResolverConfig); - const isCore = resolver.isCoreModule('assert'); - expect(isCore).toEqual(false); - }); - - it('returns true if `hasCoreModules` is true and `moduleName` is a core module.', () => { - const moduleMap = ModuleMap.create('/'); - const resolver = new ResolverAsync(moduleMap, {} as ResolverConfig); - const isCore = resolver.isCoreModule('assert'); - expect(isCore).toEqual(true); - }); - - it('returns false if `hasCoreModules` is true and `moduleName` is not a core module.', () => { - const moduleMap = ModuleMap.create('/'); - const resolver = new ResolverAsync(moduleMap, {} as ResolverConfig); - const isCore = resolver.isCoreModule('not-a-core-module'); - expect(isCore).toEqual(false); - }); - - it('returns false if `hasCoreModules` is true and `moduleNameMapper` alias a module same name with core module', () => { - const moduleMap = ModuleMap.create('/'); - const resolver = new ResolverAsync(moduleMap, { - moduleNameMapper: [ - { - moduleName: '$1', - regex: /^constants$/, - }, - ], - } as ResolverConfig); - const isCore = resolver.isCoreModule('constants'); - expect(isCore).toEqual(false); - }); -}); - -describe('findNodeModuleAsync', () => { - it('is possible to override the default resolver', async () => { - const cwd = process.cwd(); - const resolvedCwd = fs.realpathSync(cwd) || cwd; - const nodePaths = process.env.NODE_PATH - ? process.env.NODE_PATH.split(path.delimiter) - .filter(Boolean) - .map(p => path.resolve(resolvedCwd, p)) - : null; - - userResolverAsync.mockImplementation(() => Promise.resolve('module')); - - const newPath = await ResolverAsync.findNodeModuleAsync('test', { - asyncResolver: require.resolve('../__mocks__/userResolverAsync'), - basedir: '/', - browser: true, - extensions: ['js'], - moduleDirectory: ['node_modules'], - paths: ['/something'], - }); - - expect(newPath).toBe('module'); - expect(userResolverAsync.mock.calls[0][0]).toBe('test'); - expect(userResolverAsync.mock.calls[0][1]).toStrictEqual({ - basedir: '/', - browser: true, - defaultResolver, - extensions: ['js'], - moduleDirectory: ['node_modules'], - paths: (nodePaths || []).concat(['/something']), - rootDir: undefined, - }); - }); - - it('passes packageFilter to the resolve module when using the default resolver', async () => { - const packageFilter = jest.fn(); - - // A resolver that delegates to defaultResolver with a packageFilter implementation - userResolverAsync.mockImplementation((request, opts) => - Promise.resolve(opts.defaultResolver(request, {...opts, packageFilter})), - ); - - await ResolverAsync.findNodeModuleAsync('test', { - asyncResolver: require.resolve('../__mocks__/userResolverAsync'), - basedir: '/', - }); - - expect(mockResolveSync).toHaveBeenCalledWith( - 'test', - expect.objectContaining({ - packageFilter, - }), - ); - }); -}); - -describe('resolveModuleAsync', () => { - let moduleMap: ModuleMap; - beforeEach(() => { - moduleMap = ModuleMap.create('/'); - }); - - it('is possible to resolve node modules', async () => { - const resolver = new ResolverAsync(moduleMap, { - extensions: ['.js'], - } as ResolverConfig); - const src = require.resolve('../'); - const resolved = await resolver.resolveModuleAsync( - src, - './__mocks__/mockJsDependency', - ); - expect(resolved).toBe(require.resolve('../__mocks__/mockJsDependency.js')); - }); - - it('is possible to resolve node modules with custom extensions', async () => { - const resolver = new ResolverAsync(moduleMap, { - extensions: ['.js', '.jsx'], - } as ResolverConfig); - const src = require.resolve('../'); - const resolvedJsx = await resolver.resolveModuleAsync( - src, - './__mocks__/mockJsxDependency', - ); - expect(resolvedJsx).toBe( - require.resolve('../__mocks__/mockJsxDependency.jsx'), - ); - }); - - it('is possible to resolve node modules with custom extensions and platforms', async () => { - const resolver = new ResolverAsync(moduleMap, { - extensions: ['.js', '.jsx'], - platforms: ['native'], - } as ResolverConfig); - const src = require.resolve('../'); - const resolvedJsx = await resolver.resolveModuleAsync( - src, - './__mocks__/mockJsxDependency', - ); - expect(resolvedJsx).toBe( - require.resolve('../__mocks__/mockJsxDependency.native.jsx'), - ); - }); - - it('is possible to resolve node modules by resolving their realpath', async () => { - const resolver = new ResolverAsync(moduleMap, { - extensions: ['.js'], - } as ResolverConfig); - const src = path.join( - path.resolve(__dirname, '../../src/__mocks__/bar/node_modules/'), - 'foo/index.js', - ); - const resolved = await resolver.resolveModuleAsync(src, 'dep'); - expect(resolved).toBe( - require.resolve('../../src/__mocks__/foo/node_modules/dep/index.js'), - ); - }); - - it('is possible to specify custom resolve paths', async () => { - const resolver = new ResolverAsync(moduleMap, { - extensions: ['.js'], - } as ResolverConfig); - const src = require.resolve('../'); - const resolved = await resolver.resolveModuleAsync( - src, - 'mockJsDependency', - { - paths: [ - path.resolve(__dirname, '../../src/__tests__'), - path.resolve(__dirname, '../../src/__mocks__'), - ], - }, - ); - expect(resolved).toBe(require.resolve('../__mocks__/mockJsDependency.js')); - }); - - it('does not confuse directories with files', async () => { - const resolver = new ResolverAsync(moduleMap, { - extensions: ['.js'], - } as ResolverConfig); - const mocksDirectory = path.resolve(__dirname, '../__mocks__'); - const fooSlashFoo = path.join(mocksDirectory, 'foo/foo.js'); - const fooSlashIndex = path.join(mocksDirectory, 'foo/index.js'); - - const resolvedWithSlash = await resolver.resolveModuleAsync( - fooSlashFoo, - './', - ); - const resolvedWithDot = await resolver.resolveModuleAsync(fooSlashFoo, '.'); - expect(resolvedWithSlash).toBe(fooSlashIndex); - expect(resolvedWithSlash).toBe(resolvedWithDot); - }); -}); - -describe('getMockModuleAsync', () => { - it('is possible to use custom resolver to resolve deps inside mock modules with moduleNameMapper', async () => { - userResolverAsync.mockImplementation(() => Promise.resolve('module')); - - const moduleMap = ModuleMap.create('/'); - const resolver = new ResolverAsync(moduleMap, { - asyncResolver: require.resolve('../__mocks__/userResolverAsync'), - extensions: ['.js'], - moduleNameMapper: [ - { - moduleName: '$1', - regex: /(.*)/, - }, - ], - } as ResolverConfig); - const src = require.resolve('../'); - - await resolver.resolveModuleAsync(src, 'dependentModule'); - - expect(userResolverAsync).toHaveBeenCalled(); - expect(userResolverAsync.mock.calls[0][0]).toBe('dependentModule'); - expect(userResolverAsync.mock.calls[0][1]).toHaveProperty( - 'basedir', - path.dirname(src), - ); - }); -}); - -describe('nodeModulesPaths', () => { - it('provides custom module paths after node_modules', () => { - const src = require.resolve('../'); - const result = nodeModulesPaths(src, {paths: ['./customFolder']}); - expect(result[result.length - 1]).toBe('./customFolder'); - }); -}); - -describe('Resolver.getModulePaths() -> nodeModulesPaths()', () => { - const _path = path; - let moduleMap: ModuleMap; - - beforeEach(() => { - jest.resetModules(); - - moduleMap = ModuleMap.create('/'); - - // Mocking realpath to function the old way, where it just looks at - // pathstrings instead of actually trying to access the physical directory. - // This test suite won't work otherwise, since we cannot make assumptions - // about the test environment when it comes to absolute paths. - jest.doMock('graceful-fs', () => ({ - ...jest.requireActual('graceful-fs'), - realPathSync: { - native: (dirInput: string) => dirInput, - }, - })); - }); - - afterAll(() => { - jest.resetModules(); - jest.dontMock('path'); - }); - - it('can resolve node modules relative to absolute paths in "moduleDirectories" on Windows platforms', () => { - jest.doMock('path', () => _path.win32); - const path = require('path'); - const Resolver = require('../').default; - - const cwd = 'D:\\temp\\project'; - const src = 'C:\\path\\to\\node_modules'; - const resolver = new Resolver(moduleMap, { - moduleDirectories: [src, 'node_modules'], - }); - const dirs_expected = [ - src, - cwd + '\\node_modules', - path.dirname(cwd) + '\\node_modules', - 'D:\\node_modules', - ]; - const dirs_actual = resolver.getModulePaths(cwd); - expect(dirs_actual).toEqual(expect.arrayContaining(dirs_expected)); - }); - - it('can resolve node modules relative to absolute paths in "moduleDirectories" on Posix platforms', () => { - jest.doMock('path', () => _path.posix); - const path = require('path'); - const Resolver = require('../').default; - - const cwd = '/temp/project'; - const src = '/path/to/node_modules'; - const resolver = new Resolver(moduleMap, { - moduleDirectories: [src, 'node_modules'], - }); - const dirs_expected = [ - src, - cwd + '/node_modules', - path.dirname(cwd) + '/node_modules', - '/node_modules', - ]; - const dirs_actual = resolver.getModulePaths(cwd); - expect(dirs_actual).toEqual(expect.arrayContaining(dirs_expected)); - }); -}); diff --git a/packages/jest-resolve/src/defaultResolver.ts b/packages/jest-resolve/src/defaultResolver.ts index a231b6e5c91e..5316e7aca751 100644 --- a/packages/jest-resolve/src/defaultResolver.ts +++ b/packages/jest-resolve/src/defaultResolver.ts @@ -26,6 +26,10 @@ interface ResolverOptions extends ResolveOpts { rootDir?: Config.Path; } +type ResolverOptionsAsync = ResolverOptions & { + defaultResolverAsync: typeof defaultResolverAsync; +}; + // https://github.com/facebook/jest/pull/10617 declare global { namespace NodeJS { @@ -54,12 +58,14 @@ export default function defaultResolverSync( export function defaultResolverAsync( path: Config.Path, - options: ResolverOptions, + options: ResolverOptionsAsync, ): Promise { // Yarn 2 adds support to `resolve` automatically so the pnpResolver is only // needed for Yarn 1 which implements version 1 of the pnp spec if (process.versions.pnp === '1') { // QUESTION: do we need an async version of pnpResolver? + // It seems ugly to require a default sync resolver in the async method, + // just to deal with this. return Promise.resolve(pnpResolver(path, options)); } diff --git a/packages/jest-resolve/src/resolver.ts b/packages/jest-resolve/src/resolver.ts index 814f413aa61f..4a5c8e007afa 100644 --- a/packages/jest-resolve/src/resolver.ts +++ b/packages/jest-resolve/src/resolver.ts @@ -31,7 +31,6 @@ type FindNodeModuleConfig = { moduleDirectory?: Array; paths?: Array; resolver?: Config.Path | null; - asyncResolver?: Config.Path | null; rootDir?: Config.Path; throwIfNotFound?: boolean; }; @@ -54,11 +53,11 @@ const nodePaths = NODE_PATH .map(p => path.resolve(resolvedCwd, p)) : undefined; -class BaseResolver { - protected readonly _options: ResolverConfig; - protected readonly _moduleMap: IModuleMap; - protected readonly _moduleIDCache: Map; - protected readonly _moduleNameCache: Map; +export default class Resolver { + private readonly _options: ResolverConfig; + private readonly _moduleMap: IModuleMap; + private readonly _moduleIDCache: Map; + private readonly _moduleNameCache: Map; private readonly _modulePathCache: Map>; private readonly _supportsNativePlatform: boolean; @@ -81,6 +80,7 @@ class BaseResolver { this._moduleIDCache = new Map(); this._moduleNameCache = new Map(); this._modulePathCache = new Map(); + this._setResolver(options.resolver); } static ModuleNotFoundError = ModuleNotFoundError; @@ -108,20 +108,83 @@ class BaseResolver { // 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; - private _isAliasModule(moduleName: string): boolean { - const moduleNameMapper = this._options.moduleNameMapper; - if (!moduleNameMapper) { - return false; + static findNodeModule( + path: Config.Path, + options: FindNodeModuleConfig, + ): Config.Path | null { + // The resolver module could be a synchronous function, or an object with sync and/or async keys. + const resolverModule = options.resolver ? require(options.resolver) : null; + let resolver = defaultResolver; + if (typeof resolverModule === 'function') { + resolver = resolverModule; + } else if ( + resolverModule && + typeof resolverModule === 'object' && + typeof resolverModule.sync === 'function' + ) { + resolver = resolverModule.sync; } - return moduleNameMapper.some(({regex}) => regex.test(moduleName)); + const paths = options.paths; + + try { + return resolver(path, { + basedir: options.basedir, + browser: options.browser, + defaultResolver, + extensions: options.extensions, + moduleDirectory: options.moduleDirectory, + paths: paths ? (nodePaths || []).concat(paths) : nodePaths, + rootDir: options.rootDir, + }); + } catch (e) { + if (options.throwIfNotFound) { + throw e; + } + } + return null; + } + + static async findNodeModuleAsync( + path: Config.Path, + options: FindNodeModuleConfig, + ): Promise { + // The resolver module could be a synchronous function, or an object with sync and/or async keys. + const resolverModule = options.resolver ? require(options.resolver) : null; + const resolver: typeof defaultResolverAsync = + resolverModule && + typeof resolverModule === 'object' && + typeof resolverModule.async === 'function' + ? resolverModule.async + : defaultResolverAsync; + + const paths = options.paths; + + try { + const result = await resolver(path, { + basedir: options.basedir, + browser: options.browser, + defaultResolver, + defaultResolverAsync, + extensions: options.extensions, + moduleDirectory: options.moduleDirectory, + paths: paths ? (nodePaths || []).concat(paths) : nodePaths, + rootDir: options.rootDir, + }); + return result; + } catch (e) { + if (options.throwIfNotFound) { + throw e; + } + } + return null; } /** * _prepareForResolution is shared between the sync and async module resolution * methods, to try to keep them as DRY as possible. */ - protected _prepareForResolution( + private _prepareForResolution( dirname: Config.Path, moduleName: string, options?: ResolveModuleConfig, @@ -153,7 +216,7 @@ class BaseResolver { /** * _getHasteModulePath attempts to return the path to a haste module. */ - protected _getHasteModulePath(moduleName: string) { + private _getHasteModulePath(moduleName: string) { const parts = moduleName.split('/'); const hastePackage = this.getPackage(parts.shift()!); if (hastePackage) { @@ -162,10 +225,7 @@ class BaseResolver { return null; } - protected _throwModNotFoundError( - from: Config.Path, - moduleName: string, - ): never { + private _throwModNotFoundError(from: Config.Path, moduleName: string): never { const relativePath = slash(path.relative(this._options.rootDir, from)) || '.'; @@ -175,11 +235,11 @@ class BaseResolver { ); } - protected _getModuleType(moduleName: string): 'node' | 'user' { + private _getModuleType(moduleName: string): 'node' | 'user' { return this.isCoreModule(moduleName) ? 'node' : 'user'; } - protected _getMapModuleName(matches: RegExpMatchArray | null) { + private _getMapModuleName(matches: RegExpMatchArray | null) { return matches ? (moduleName: string) => moduleName.replace( @@ -189,68 +249,27 @@ class BaseResolver { : (moduleName: string) => moduleName; } - setResolver(resolver?: Config.Path | null) { - this._options.resolver = resolver; - } - - setAsyncResolver(asyncResolver?: Config.Path | null) { - this._options.asyncResolver = asyncResolver; - } - - isCoreModule(moduleName: string): boolean { - return ( - this._options.hasCoreModules && - (isBuiltinModule(moduleName) || - (moduleName.startsWith('node:') && - isBuiltinModule(moduleName.slice('node:'.length)))) && - !this._isAliasModule(moduleName) - ); - } - - getModule(name: string): Config.Path | null { - return this._moduleMap.getModule( - name, - this._options.defaultPlatform, - this._supportsNativePlatform, - ); - } - - getModulePath(from: Config.Path, moduleName: string): Config.Path { - if (moduleName[0] !== '.' || path.isAbsolute(moduleName)) { - return moduleName; + private _isAliasModule(moduleName: string): boolean { + const moduleNameMapper = this._options.moduleNameMapper; + if (!moduleNameMapper) { + return false; } - return path.normalize(path.dirname(from) + '/' + moduleName); - } - getPackage(name: string): Config.Path | null { - return this._moduleMap.getPackage( - name, - this._options.defaultPlatform, - this._supportsNativePlatform, - ); + return moduleNameMapper.some(({regex}) => regex.test(moduleName)); } - getModulePaths(from: Config.Path): Array { - const cachedModule = this._modulePathCache.get(from); - if (cachedModule) { - return cachedModule; - } - - const moduleDirectory = this._options.moduleDirectories; - const paths = nodeModulesPaths(from, {moduleDirectory}); - if (paths[paths.length - 1] === undefined) { - // circumvent node-resolve bug that adds `undefined` as last item. - paths.pop(); + private _getAbsolutePath( + virtualMocks: Map, + from: Config.Path, + moduleName: string, + options?: ResolveModuleConfig, + ): Config.Path | null { + if (this.isCoreModule(moduleName)) { + return moduleName; } - this._modulePathCache.set(from, paths); - return paths; - } -} - -export class ResolverAsync extends BaseResolver { - constructor(moduleMap: IModuleMap, options: ResolverConfig) { - super(moduleMap, options); - this.setAsyncResolver(options.asyncResolver); + return this._isModuleResolved(from, moduleName) + ? this.getModule(moduleName) + : this._getVirtualMockPath(virtualMocks, from, moduleName, options); } private async _getAbsolutePathAsync( @@ -276,6 +295,15 @@ export class ResolverAsync extends BaseResolver { ); } + private _getMockPath( + from: Config.Path, + moduleName: string, + ): Config.Path | null { + return !this.isCoreModule(moduleName) + ? this.getMockModule(from, moduleName) + : null; + } + private async _getMockPathAsync( from: Config.Path, moduleName: string, @@ -285,6 +313,20 @@ export class ResolverAsync extends BaseResolver { : null; } + private _getVirtualMockPath( + virtualMocks: Map, + from: Config.Path, + moduleName: string, + options?: ResolveModuleConfig, + ): Config.Path { + const virtualMockPath = this.getModulePath(from, moduleName); + return virtualMocks.get(virtualMockPath) + ? virtualMockPath + : moduleName + ? this.resolveModule(from, moduleName, options) + : from; + } + private async _getVirtualMockPathAsync( virtualMocks: Map, from: Config.Path, @@ -299,6 +341,12 @@ export class ResolverAsync extends BaseResolver { : from; } + private _isModuleResolved(from: Config.Path, moduleName: string): boolean { + return !!( + this.getModule(moduleName) || this.getMockModule(from, moduleName) + ); + } + private async _isModuleResolvedAsync( from: Config.Path, moduleName: string, @@ -309,295 +357,57 @@ export class ResolverAsync extends BaseResolver { ); } - async resolveStubModuleNameAsync( - from: Config.Path, - moduleName: string, - ): Promise { - const dirname = path.dirname(from); - - const {extensions, moduleDirectory, paths} = this._prepareForResolution( - dirname, - moduleName, - ); - const moduleNameMapper = this._options.moduleNameMapper; - const asyncResolver = this._options.asyncResolver; - - 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); - const mapModuleName = this._getMapModuleName(matches); - const possibleModuleNames = Array.isArray(mappedModuleName) - ? mappedModuleName - : [mappedModuleName]; - let module: string | null = null; - for (const possibleModuleName of possibleModuleNames) { - const updatedName = mapModuleName(possibleModuleName); - - module = - this.getModule(updatedName) || - (await ResolverAsync.findNodeModuleAsync(updatedName, { - asyncResolver, - basedir: dirname, - extensions, - moduleDirectory, - paths, - rootDir: this._options.rootDir, - })); - - if (module) { - break; - } - } - - if (!module) { - throw createNoMappedModuleFoundError( - moduleName, - mapModuleName, - mappedModuleName, - regex, - asyncResolver, - ); - } - return module; - } - } - } - return null; - } - - async getMockModuleAsync( - from: Config.Path, - name: string, - ): Promise { - 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; + private _setResolver(resolver?: Config.Path | null): void { + this._options.resolver = resolver; } - async getModuleIDAsync( - virtualMocks: Map, - from: Config.Path, - moduleName = '', - options?: ResolveModuleConfig, - ): Promise { - const stringifiedOptions = options ? JSON.stringify(options) : ''; - const key = from + path.delimiter + moduleName + stringifiedOptions; - const cachedModuleID = this._moduleIDCache.get(key); - if (cachedModuleID) { - return cachedModuleID; - } - - const moduleType = this._getModuleType(moduleName); - const absolutePath = await this._getAbsolutePathAsync( - virtualMocks, - from, - moduleName, - options, + isCoreModule(moduleName: string): boolean { + return ( + this._options.hasCoreModules && + (isBuiltinModule(moduleName) || + (moduleName.startsWith('node:') && + isBuiltinModule(moduleName.slice('node:'.length)))) && + !this._isAliasModule(moduleName) ); - const mockPath = await this._getMockPathAsync(from, moduleName); - - const sep = path.delimiter; - const id = - moduleType + - sep + - (absolutePath ? absolutePath + sep : '') + - (mockPath ? mockPath + sep : '') + - (stringifiedOptions ? stringifiedOptions + sep : ''); - - this._moduleIDCache.set(key, id); - return id; - } - - static async findNodeModuleAsync( - path: Config.Path, - options: FindNodeModuleConfig, - ): Promise { - const resolver: typeof defaultResolverAsync = options.asyncResolver - ? require(options.asyncResolver) - : defaultResolverAsync; - const paths = options.paths; - - try { - const result = await resolver(path, { - basedir: options.basedir, - browser: options.browser, - defaultResolver, - extensions: options.extensions, - moduleDirectory: options.moduleDirectory, - paths: paths ? (nodePaths || []).concat(paths) : nodePaths, - rootDir: options.rootDir, - }); - return result; - } catch (e) { - if (options.throwIfNotFound) { - throw e; - } - } - return null; } - async resolveModuleFromDirIfExistsAsync( - dirname: Config.Path, - moduleName: string, - options?: ResolveModuleConfig, - ): Promise { - 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: Config.Path, - throwIfNotFound = false, - ) => { - if (this.isCoreModule(name)) { - return name; - } - - return await ResolverAsync.findNodeModuleAsync(name, { - asyncResolver: this._options.asyncResolver, - basedir: dirname, - conditions: options?.conditions, - extensions, - moduleDirectory, - paths, - 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 = - (await resolveNodeModule(hasteModulePath)) || - // QUESTION: should this be async? - require.resolve(hasteModulePath); - this._moduleNameCache.set(key, resolvedModule); - return resolvedModule; - } - } catch {} - - return null; - } - - /* eslint-disable-next-line consistent-return */ - async resolveModuleAsync( - from: Config.Path, - moduleName: string, - options?: ResolveModuleConfig, - ): Promise { - 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.sync` only - // produces an error based on the dirname but we have the actual current - // module name available. - this._throwModNotFoundError(from, moduleName); - } -} - -export default class Resolver extends BaseResolver { - constructor(moduleMap: IModuleMap, options: ResolverConfig) { - super(moduleMap, options); - this.setResolver(options.resolver); + getModule(name: string): Config.Path | null { + return this._moduleMap.getModule( + name, + this._options.defaultPlatform, + this._supportsNativePlatform, + ); } - private _getAbsolutePath( - virtualMocks: Map, - from: Config.Path, - moduleName: string, - options?: ResolveModuleConfig, - ): Config.Path | null { - if (this.isCoreModule(moduleName)) { + getModulePath(from: Config.Path, moduleName: string): Config.Path { + if (moduleName[0] !== '.' || path.isAbsolute(moduleName)) { return moduleName; } - return this._isModuleResolved(from, moduleName) - ? this.getModule(moduleName) - : this._getVirtualMockPath(virtualMocks, from, moduleName, options); - } - - private _getMockPath( - from: Config.Path, - moduleName: string, - ): Config.Path | null { - return !this.isCoreModule(moduleName) - ? this.getMockModule(from, moduleName) - : null; + return path.normalize(path.dirname(from) + '/' + moduleName); } - private _getVirtualMockPath( - virtualMocks: Map, - from: Config.Path, - moduleName: string, - options?: ResolveModuleConfig, - ): Config.Path { - const virtualMockPath = this.getModulePath(from, moduleName); - return virtualMocks.get(virtualMockPath) - ? virtualMockPath - : moduleName - ? this.resolveModule(from, moduleName, options) - : from; + getPackage(name: string): Config.Path | null { + return this._moduleMap.getPackage( + name, + this._options.defaultPlatform, + this._supportsNativePlatform, + ); } - private _isModuleResolved(from: Config.Path, moduleName: string): boolean { - return !!( - this.getModule(moduleName) || this.getMockModule(from, moduleName) - ); + getModulePaths(from: Config.Path): Array { + const cachedModule = this._modulePathCache.get(from); + if (cachedModule) { + return cachedModule; + } + + const moduleDirectory = this._options.moduleDirectories; + const paths = nodeModulesPaths(from, {moduleDirectory}); + if (paths[paths.length - 1] === undefined) { + // circumvent node-resolve bug that adds `undefined` as last item. + paths.pop(); + } + this._modulePathCache.set(from, paths); + return paths; } resolveStubModuleName( @@ -659,6 +469,65 @@ export default class Resolver extends BaseResolver { return null; } + async resolveStubModuleNameAsync( + from: Config.Path, + moduleName: string, + ): Promise { + 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) { + 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); + const mapModuleName = this._getMapModuleName(matches); + const possibleModuleNames = Array.isArray(mappedModuleName) + ? mappedModuleName + : [mappedModuleName]; + let module: string | null = null; + for (const possibleModuleName of possibleModuleNames) { + const updatedName = mapModuleName(possibleModuleName); + + module = + this.getModule(updatedName) || + (await Resolver.findNodeModuleAsync(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; + } + getMockModule(from: Config.Path, name: string): Config.Path | null { const mock = this._moduleMap.getMockModule(name); if (mock) { @@ -672,6 +541,22 @@ export default class Resolver extends BaseResolver { return null; } + async getMockModuleAsync( + from: Config.Path, + name: string, + ): Promise { + 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; + } + getModuleID( virtualMocks: Map, from: Config.Path, @@ -708,32 +593,37 @@ export default class Resolver extends BaseResolver { return id; } - static findNodeModule( - path: Config.Path, - options: FindNodeModuleConfig, - ): Config.Path | null { - const resolver: typeof defaultResolver = options.resolver - ? require(options.resolver) - : defaultResolver; - const paths = options.paths; - - try { - return resolver(path, { - basedir: options.basedir, - browser: options.browser, - conditions: options.conditions, - defaultResolver, - extensions: options.extensions, - moduleDirectory: options.moduleDirectory, - paths: paths ? (nodePaths || []).concat(paths) : nodePaths, - rootDir: options.rootDir, - }); - } catch (e) { - if (options.throwIfNotFound) { - throw e; - } + async getModuleIDAsync( + virtualMocks: Map, + from: Config.Path, + moduleName = '', + options?: ResolveModuleConfig, + ): Promise { + const stringifiedOptions = options ? JSON.stringify(options) : ''; + const key = from + path.delimiter + moduleName + stringifiedOptions; + const cachedModuleID = this._moduleIDCache.get(key); + if (cachedModuleID) { + return cachedModuleID; } - return null; + + 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; } resolveModuleFromDirIfExists( @@ -810,6 +700,82 @@ export default class Resolver extends BaseResolver { return null; } + async resolveModuleFromDirIfExistsAsync( + dirname: Config.Path, + moduleName: string, + options?: ResolveModuleConfig, + ): Promise { + 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: Config.Path, + throwIfNotFound = false, + ) => + await Resolver.findNodeModuleAsync(name, { + basedir: dirname, + conditions: 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 = + (await resolveNodeModule(hasteModulePath)) || + // QUESTION: should this be async? + require.resolve(hasteModulePath); + this._moduleNameCache.set(key, resolvedModule); + return resolvedModule; + } + } catch {} + + return null; + } + /* eslint-disable-next-line consistent-return */ resolveModule( from: Config.Path, @@ -827,6 +793,29 @@ export default class Resolver extends BaseResolver { // module name available. this._throwModNotFoundError(from, moduleName); } + + /* eslint-disable-next-line consistent-return */ + async resolveModuleAsync( + from: Config.Path, + moduleName: string, + options?: ResolveModuleConfig, + ): Promise { + 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); + } } const createNoMappedModuleFoundError = ( From e9605d3151afe069a76a7f4d0f0227b2d24048d2 Mon Sep 17 00:00:00 2001 From: Ian VanSchooten Date: Mon, 30 Aug 2021 23:14:48 -0400 Subject: [PATCH 08/39] Use named export for sync defaultResolver --- packages/jest-resolve/src/__mocks__/userResolver.d.ts | 2 +- packages/jest-resolve/src/__tests__/resolve.test.ts | 2 +- packages/jest-resolve/src/defaultResolver.ts | 6 +++--- packages/jest-resolve/src/resolver.ts | 3 ++- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/jest-resolve/src/__mocks__/userResolver.d.ts b/packages/jest-resolve/src/__mocks__/userResolver.d.ts index 155346eca132..1e10b90437a3 100644 --- a/packages/jest-resolve/src/__mocks__/userResolver.d.ts +++ b/packages/jest-resolve/src/__mocks__/userResolver.d.ts @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -import defaultResolver from '../defaultResolver'; +import {defaultResolver} from '../defaultResolver'; // todo: can be replaced with jest.MockedFunction declare const userResolver: jest.MockInstance< diff --git a/packages/jest-resolve/src/__tests__/resolve.test.ts b/packages/jest-resolve/src/__tests__/resolve.test.ts index 29c3a87f12a9..a510f1ee3862 100644 --- a/packages/jest-resolve/src/__tests__/resolve.test.ts +++ b/packages/jest-resolve/src/__tests__/resolve.test.ts @@ -12,7 +12,7 @@ import resolveAsync = require('resolve'); import {ModuleMap} from 'jest-haste-map'; import userResolver from '../__mocks__/userResolver'; import userResolverAsync from '../__mocks__/userResolverAsync'; -import defaultResolver, {defaultResolverAsync} from '../defaultResolver'; +import {defaultResolver, defaultResolverAsync} from '../defaultResolver'; import nodeModulesPaths from '../nodeModulesPaths'; import Resolver from '../resolver'; import type {ResolverConfig} from '../types'; diff --git a/packages/jest-resolve/src/defaultResolver.ts b/packages/jest-resolve/src/defaultResolver.ts index 5316e7aca751..218b92f43094 100644 --- a/packages/jest-resolve/src/defaultResolver.ts +++ b/packages/jest-resolve/src/defaultResolver.ts @@ -17,11 +17,11 @@ import resolveAsync = require('resolve'); import type {Config} from '@jest/types'; import {tryRealpath} from 'jest-util'; -interface ResolverOptions extends ResolveOpts { +export interface ResolverOptions extends ResolveOpts { basedir: Config.Path; browser?: boolean; conditions?: Array; - defaultResolver: typeof defaultResolverSync; + defaultResolver: typeof defaultResolver; extensions?: Array; rootDir?: Config.Path; } @@ -39,7 +39,7 @@ declare global { } } -export default function defaultResolverSync( +export function defaultResolver( path: Config.Path, options: ResolverOptions, ): Config.Path { diff --git a/packages/jest-resolve/src/resolver.ts b/packages/jest-resolve/src/resolver.ts index 4a5c8e007afa..c277c8a3db16 100644 --- a/packages/jest-resolve/src/resolver.ts +++ b/packages/jest-resolve/src/resolver.ts @@ -14,8 +14,9 @@ import type {Config} from '@jest/types'; import type {IModuleMap} from 'jest-haste-map'; import {tryRealpath} from 'jest-util'; import ModuleNotFoundError from './ModuleNotFoundError'; -import defaultResolver, { +import { clearDefaultResolverCache, + defaultResolver, defaultResolverAsync, } from './defaultResolver'; import isBuiltinModule from './isBuiltinModule'; From aabc2afc7f84d8801a959eab42cdd8a081209dd3 Mon Sep 17 00:00:00 2001 From: Ian VanSchooten Date: Mon, 30 Aug 2021 23:14:59 -0400 Subject: [PATCH 09/39] Attempt to explain new resolver option --- docs/Configuration.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/docs/Configuration.md b/docs/Configuration.md index 384e60723092..41922eb4a103 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -783,13 +783,18 @@ By default, each test file gets its own independent module registry. Enabling `r Default: `undefined` -This option allows the use of a custom resolver. This resolver must be a node module that exports a function expecting a string as the first argument for the path to resolve and an object with the following structure as the second argument: +This option allows the use of a custom resolver. This resolver must be a node module that exports _either_: + +1. a function expecting a string as the first argument for the path to resolve and an options object as the second argument. The function should either return a path to the module that should be resolved or throw an error if the module can't be found. _or_ +2. an object containing `async` and/or `sync` properties. The `sync` property should be a function with the shape explained above, and the `async` property should also be a function that accepts the same arguments, but returns a promise which resolves with the path to the module or rejects with an error. + +The options object provided to resolvers has the shape: ```json { "basedir": string, "conditions": [string], - "defaultResolver": "function(request, options)", + "defaultResolver": "function(request, options) -> string" or "function(request, options) -> Promise", "extensions": [string], "moduleDirectory": [string], "paths": [string], @@ -799,8 +804,6 @@ This option allows the use of a custom resolver. This resolver must be a node mo } ``` -The function should either return a path to the module that should be resolved or throw an error if the module can't be found. - Note: the defaultResolver passed as an option is the Jest default resolver which might be useful when you write your custom one. It takes the same arguments as your custom one, e.g. `(request, options)`. For example, if you want to respect Browserify's [`"browser"` field](https://github.com/browserify/browserify-handbook/blob/master/readme.markdown#browser-field), you can use the following configuration: From 04cb71775ff36bcca8be85bafe8d36cf3e7740ea Mon Sep 17 00:00:00 2001 From: Ian VanSchooten Date: Tue, 31 Aug 2021 00:01:39 -0400 Subject: [PATCH 10/39] Consolidate to just defaultResolver in resolver options --- docs/Configuration.md | 4 ++-- packages/jest-resolve/src/defaultResolver.ts | 13 +++++-------- packages/jest-resolve/src/resolver.ts | 3 +-- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/docs/Configuration.md b/docs/Configuration.md index 41922eb4a103..5424dbd71397 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -794,7 +794,7 @@ The options object provided to resolvers has the shape: { "basedir": string, "conditions": [string], - "defaultResolver": "function(request, options) -> string" or "function(request, options) -> Promise", + "defaultResolver": "function(request, options) -> string | Promise", "extensions": [string], "moduleDirectory": [string], "paths": [string], @@ -804,7 +804,7 @@ The options object provided to resolvers has the shape: } ``` -Note: the defaultResolver passed as an option is the Jest default resolver which might be useful when you write your custom one. It takes the same arguments as your custom one, e.g. `(request, options)`. +Note: the defaultResolver passed as an option is the Jest default resolver which might be useful when you write your custom one. It takes the same arguments as your custom one, e.g. `(request, options)` and returns a string for sync resolvers and a promise for async resolvers. For example, if you want to respect Browserify's [`"browser"` field](https://github.com/browserify/browserify-handbook/blob/master/readme.markdown#browser-field), you can use the following configuration: diff --git a/packages/jest-resolve/src/defaultResolver.ts b/packages/jest-resolve/src/defaultResolver.ts index 218b92f43094..2209b02d83f2 100644 --- a/packages/jest-resolve/src/defaultResolver.ts +++ b/packages/jest-resolve/src/defaultResolver.ts @@ -26,8 +26,8 @@ export interface ResolverOptions extends ResolveOpts { rootDir?: Config.Path; } -type ResolverOptionsAsync = ResolverOptions & { - defaultResolverAsync: typeof defaultResolverAsync; +type ResolverOptionsAsync = Omit & { + defaultResolver: typeof defaultResolverAsync; }; // https://github.com/facebook/jest/pull/10617 @@ -56,17 +56,14 @@ export function defaultResolver( return realpathSync(result); } -export function defaultResolverAsync( +export async function defaultResolverAsync( path: Config.Path, options: ResolverOptionsAsync, ): Promise { // Yarn 2 adds support to `resolve` automatically so the pnpResolver is only // needed for Yarn 1 which implements version 1 of the pnp spec if (process.versions.pnp === '1') { - // QUESTION: do we need an async version of pnpResolver? - // It seems ugly to require a default sync resolver in the async method, - // just to deal with this. - return Promise.resolve(pnpResolver(path, options)); + return Promise.resolve(await pnpResolver(path, options)); } return new Promise((resolve, reject) => { @@ -100,7 +97,7 @@ function getSyncResolveOptions(options: ResolverOptions): SyncOpts { /** * getAsyncResolveOptions returns resolution options that are used asynchronously. */ -function getAsyncResolveOptions(options: ResolverOptions): AsyncOpts { +function getAsyncResolveOptions(options: ResolverOptionsAsync): AsyncOpts { return { ...options, isDirectory: isDirectoryAsync, diff --git a/packages/jest-resolve/src/resolver.ts b/packages/jest-resolve/src/resolver.ts index c277c8a3db16..00f31d7088c9 100644 --- a/packages/jest-resolve/src/resolver.ts +++ b/packages/jest-resolve/src/resolver.ts @@ -165,8 +165,7 @@ export default class Resolver { const result = await resolver(path, { basedir: options.basedir, browser: options.browser, - defaultResolver, - defaultResolverAsync, + defaultResolver: defaultResolverAsync, extensions: options.extensions, moduleDirectory: options.moduleDirectory, paths: paths ? (nodePaths || []).concat(paths) : nodePaths, From 211e9cb38bb867566073dd56812a4c21bad2aabf Mon Sep 17 00:00:00 2001 From: Ian VanSchooten Date: Fri, 10 Sep 2021 16:19:29 -0400 Subject: [PATCH 11/39] Fix bad rebase --- .../lib/__tests__/__snapshots__/logDebugMessages.test.ts.snap | 1 + .../src/__tests__/__snapshots__/ScriptTransformer.test.ts.snap | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/jest-core/src/lib/__tests__/__snapshots__/logDebugMessages.test.ts.snap b/packages/jest-core/src/lib/__tests__/__snapshots__/logDebugMessages.test.ts.snap index 7b74de72bd44..92b467a89d8b 100644 --- a/packages/jest-core/src/lib/__tests__/__snapshots__/logDebugMessages.test.ts.snap +++ b/packages/jest-core/src/lib/__tests__/__snapshots__/logDebugMessages.test.ts.snap @@ -41,6 +41,7 @@ exports[`prints the config object 1`] = ` "skipFilter": false, "skipNodeResolution": false, "slowTestThreshold": 5, + "snapshotFormat": {}, "snapshotSerializers": [], "testEnvironment": "node", "testEnvironmentOptions": {}, diff --git a/packages/jest-transform/src/__tests__/__snapshots__/ScriptTransformer.test.ts.snap b/packages/jest-transform/src/__tests__/__snapshots__/ScriptTransformer.test.ts.snap index 30afd6905c65..35aa66662ac4 100644 --- a/packages/jest-transform/src/__tests__/__snapshots__/ScriptTransformer.test.ts.snap +++ b/packages/jest-transform/src/__tests__/__snapshots__/ScriptTransformer.test.ts.snap @@ -55,6 +55,7 @@ exports[`ScriptTransformer in async mode, passes expected transform options to g "skipFilter": false, "skipNodeResolution": false, "slowTestThreshold": 5, + "snapshotFormat": {}, "snapshotResolver": undefined, "snapshotSerializers": Array [], "testEnvironment": "node", From 217b8cdad7d9246572e012e1be7eb763a7225770 Mon Sep 17 00:00:00 2001 From: Ian VanSchooten Date: Fri, 10 Sep 2021 16:19:49 -0400 Subject: [PATCH 12/39] Improve import format of resolve --- packages/jest-resolve/src/defaultResolver.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/packages/jest-resolve/src/defaultResolver.ts b/packages/jest-resolve/src/defaultResolver.ts index 2209b02d83f2..46b145c177f1 100644 --- a/packages/jest-resolve/src/defaultResolver.ts +++ b/packages/jest-resolve/src/defaultResolver.ts @@ -7,15 +7,11 @@ import * as fs from 'graceful-fs'; import pnpResolver from 'jest-pnp-resolver'; -import { - AsyncOpts, - Opts as ResolveOpts, - SyncOpts, - sync as resolveSync, -} from 'resolve'; import resolveAsync = require('resolve'); +import type {AsyncOpts, Opts as ResolveOpts, SyncOpts} from 'resolve'; import type {Config} from '@jest/types'; import {tryRealpath} from 'jest-util'; +const resolveSync = resolveAsync.sync; export interface ResolverOptions extends ResolveOpts { basedir: Config.Path; From d12bac28f147e90210c486fe8d30c6ef2fc31dee Mon Sep 17 00:00:00 2001 From: Ian VanSchooten Date: Fri, 10 Sep 2021 16:20:03 -0400 Subject: [PATCH 13/39] Change questions to TODOs --- packages/jest-resolve/src/defaultResolver.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/jest-resolve/src/defaultResolver.ts b/packages/jest-resolve/src/defaultResolver.ts index 46b145c177f1..47eebe01649c 100644 --- a/packages/jest-resolve/src/defaultResolver.ts +++ b/packages/jest-resolve/src/defaultResolver.ts @@ -194,7 +194,7 @@ function isFileAsync( cb: (err: Error | null, isFile?: boolean) => void, ): void { try { - // QUESTION: do we need an async version of statSyncCached? + // TODO: create an async version of statSyncCached const isFile = statSyncCached(file) === IPathType.FILE; cb(null, isFile); } catch (err) { @@ -211,7 +211,7 @@ function isDirectoryAsync( cb: (err: Error | null, isDir?: boolean) => void, ): void { try { - // QUESTION: do we need an async version of statSyncCached? + // TODO: create an async version of statSyncCached const isDir = statSyncCached(dir) === IPathType.DIRECTORY; cb(null, isDir); } catch (err) { @@ -228,7 +228,7 @@ function realpathAsync( cb: (err: Error | null, resolved?: string) => void, ): void { try { - // QUESTION: do we need an async version of realpathCached? + // TODO: create an async version of realpathCached const resolved = realpathCached(file); cb(null, resolved); } catch (err) { @@ -246,7 +246,7 @@ function readPackageAsync( cb: (err: Error | null, pkgJson?: Record) => void, ): void { try { - // QUESTION: do we need an async version of readPackageCached? + // TODO: create an async version of readPackageCached const pkgJson = readPackageCached(pkgfile); cb(null, pkgJson); } catch (err) { From 3a544bfb15813f4e1e924493c56ae7aa1df37722 Mon Sep 17 00:00:00 2001 From: Ian VanSchooten Date: Fri, 10 Sep 2021 16:20:19 -0400 Subject: [PATCH 14/39] Return pnpResolver result directly --- packages/jest-resolve/src/defaultResolver.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/jest-resolve/src/defaultResolver.ts b/packages/jest-resolve/src/defaultResolver.ts index 47eebe01649c..3b6598ccbb5f 100644 --- a/packages/jest-resolve/src/defaultResolver.ts +++ b/packages/jest-resolve/src/defaultResolver.ts @@ -59,7 +59,7 @@ export async function defaultResolverAsync( // Yarn 2 adds support to `resolve` automatically so the pnpResolver is only // needed for Yarn 1 which implements version 1 of the pnp spec if (process.versions.pnp === '1') { - return Promise.resolve(await pnpResolver(path, options)); + return pnpResolver(path, options); } return new Promise((resolve, reject) => { From 2f22e63f9d8ef2f8e0d3601422135af5b23074cb Mon Sep 17 00:00:00 2001 From: Ian VanSchooten Date: Fri, 10 Sep 2021 16:52:05 -0400 Subject: [PATCH 15/39] Update snapshots --- .../circusDeclarationErrors.test.ts.snap | 8 ++++---- .../moduleNameMapper.test.ts.snap | 4 ++-- .../resolveNoFileExtensions.test.ts.snap | 2 +- .../logDebugMessages.test.ts.snap | 1 + .../ScriptTransformer.test.ts.snap | 20 ++++++++++--------- 5 files changed, 19 insertions(+), 16 deletions(-) diff --git a/e2e/__tests__/__snapshots__/circusDeclarationErrors.test.ts.snap b/e2e/__tests__/__snapshots__/circusDeclarationErrors.test.ts.snap index e073a8300d49..36c675798378 100644 --- a/e2e/__tests__/__snapshots__/circusDeclarationErrors.test.ts.snap +++ b/e2e/__tests__/__snapshots__/circusDeclarationErrors.test.ts.snap @@ -16,7 +16,7 @@ FAIL __tests__/asyncDefinition.test.js 14 | }); 15 | }); - at eventHandler (../../packages/jest-circus/build/eventHandler.js:146:11) + at eventHandler (../../packages/jest-circus/build/eventHandler.js:190:11) at test (__tests__/asyncDefinition.test.js:12:5) ● Test suite failed to run @@ -31,7 +31,7 @@ FAIL __tests__/asyncDefinition.test.js 15 | }); 16 | - at eventHandler (../../packages/jest-circus/build/eventHandler.js:114:11) + at eventHandler (../../packages/jest-circus/build/eventHandler.js:158:11) at afterAll (__tests__/asyncDefinition.test.js:13:5) ● Test suite failed to run @@ -46,7 +46,7 @@ FAIL __tests__/asyncDefinition.test.js 20 | }); 21 | - at eventHandler (../../packages/jest-circus/build/eventHandler.js:146:11) + at eventHandler (../../packages/jest-circus/build/eventHandler.js:190:11) at test (__tests__/asyncDefinition.test.js:18:3) ● Test suite failed to run @@ -60,6 +60,6 @@ FAIL __tests__/asyncDefinition.test.js 20 | }); 21 | - at eventHandler (../../packages/jest-circus/build/eventHandler.js:114:11) + at eventHandler (../../packages/jest-circus/build/eventHandler.js:158:11) at afterAll (__tests__/asyncDefinition.test.js:19:3) `; diff --git a/e2e/__tests__/__snapshots__/moduleNameMapper.test.ts.snap b/e2e/__tests__/__snapshots__/moduleNameMapper.test.ts.snap index b1d74dc76bde..45a08c2ddae2 100644 --- a/e2e/__tests__/__snapshots__/moduleNameMapper.test.ts.snap +++ b/e2e/__tests__/__snapshots__/moduleNameMapper.test.ts.snap @@ -41,7 +41,7 @@ FAIL __tests__/index.js 12 | module.exports = () => 'test'; 13 | - at createNoMappedModuleFoundError (../../packages/jest-resolve/build/resolver.js:873:17) + at createNoMappedModuleFoundError (../../packages/jest-resolve/build/resolver.js:871:17) at Object.require (index.js:10:1) `; @@ -70,6 +70,6 @@ FAIL __tests__/index.js 12 | module.exports = () => 'test'; 13 | - at createNoMappedModuleFoundError (../../packages/jest-resolve/build/resolver.js:873:17) + at createNoMappedModuleFoundError (../../packages/jest-resolve/build/resolver.js:871:17) at Object.require (index.js:10:1) `; diff --git a/e2e/__tests__/__snapshots__/resolveNoFileExtensions.test.ts.snap b/e2e/__tests__/__snapshots__/resolveNoFileExtensions.test.ts.snap index 33975c445b5e..d72e96a41f8b 100644 --- a/e2e/__tests__/__snapshots__/resolveNoFileExtensions.test.ts.snap +++ b/e2e/__tests__/__snapshots__/resolveNoFileExtensions.test.ts.snap @@ -37,6 +37,6 @@ FAIL __tests__/test.js | ^ 9 | - at Resolver._throwModNotFoundError (../../packages/jest-resolve/build/resolver.js:249:11) + at Resolver._throwModNotFoundError (../../packages/jest-resolve/build/resolver.js:310:11) at Object.require (index.js:8:18) `; diff --git a/packages/jest-core/src/lib/__tests__/__snapshots__/logDebugMessages.test.ts.snap b/packages/jest-core/src/lib/__tests__/__snapshots__/logDebugMessages.test.ts.snap index 92b467a89d8b..bf5d5881d7f1 100644 --- a/packages/jest-core/src/lib/__tests__/__snapshots__/logDebugMessages.test.ts.snap +++ b/packages/jest-core/src/lib/__tests__/__snapshots__/logDebugMessages.test.ts.snap @@ -95,6 +95,7 @@ exports[`prints the config object 1`] = ` "runTestsByPath": false, "silent": false, "skipFilter": false, + "snapshotFormat": {}, "testFailureExitCode": 1, "testNamePattern": "", "testPathPattern": "", diff --git a/packages/jest-transform/src/__tests__/__snapshots__/ScriptTransformer.test.ts.snap b/packages/jest-transform/src/__tests__/__snapshots__/ScriptTransformer.test.ts.snap index 35aa66662ac4..ffa97cef1c0b 100644 --- a/packages/jest-transform/src/__tests__/__snapshots__/ScriptTransformer.test.ts.snap +++ b/packages/jest-transform/src/__tests__/__snapshots__/ScriptTransformer.test.ts.snap @@ -55,7 +55,7 @@ exports[`ScriptTransformer in async mode, passes expected transform options to g "skipFilter": false, "skipNodeResolution": false, "slowTestThreshold": 5, - "snapshotFormat": {}, + "snapshotFormat": Object {}, "snapshotResolver": undefined, "snapshotSerializers": Array [], "testEnvironment": "node", @@ -84,7 +84,7 @@ exports[`ScriptTransformer in async mode, passes expected transform options to g "unmockedModulePathPatterns": undefined, "watchPathIgnorePatterns": Array [], }, - "configString": "{\\"automock\\":false,\\"cache\\":true,\\"cacheDirectory\\":\\"/cache/\\",\\"clearMocks\\":false,\\"coveragePathIgnorePatterns\\":[],\\"cwd\\":\\"/test_root_dir/\\",\\"detectLeaks\\":false,\\"detectOpenHandles\\":false,\\"errorOnDeprecated\\":false,\\"extensionsToTreatAsEsm\\":[],\\"extraGlobals\\":[],\\"forceCoverageMatch\\":[],\\"globals\\":{},\\"haste\\":{},\\"injectGlobals\\":true,\\"moduleDirectories\\":[],\\"moduleFileExtensions\\":[\\"js\\"],\\"moduleLoader\\":\\"/test_module_loader_path\\",\\"moduleNameMapper\\":[],\\"modulePathIgnorePatterns\\":[],\\"modulePaths\\":[],\\"name\\":\\"test\\",\\"prettierPath\\":\\"prettier\\",\\"resetMocks\\":false,\\"resetModules\\":false,\\"restoreMocks\\":false,\\"rootDir\\":\\"/\\",\\"roots\\":[],\\"runner\\":\\"jest-runner\\",\\"setupFiles\\":[],\\"setupFilesAfterEnv\\":[],\\"skipFilter\\":false,\\"skipNodeResolution\\":false,\\"slowTestThreshold\\":5,\\"snapshotSerializers\\":[],\\"testEnvironment\\":\\"node\\",\\"testEnvironmentOptions\\":{},\\"testLocationInResults\\":false,\\"testMatch\\":[],\\"testPathIgnorePatterns\\":[],\\"testRegex\\":[\\"\\\\\\\\.test\\\\\\\\.js$\\"],\\"testRunner\\":\\"jest-circus/runner\\",\\"testURL\\":\\"http://localhost\\",\\"timers\\":\\"real\\",\\"transform\\":[[\\"\\\\\\\\.js$\\",\\"test_preprocessor\\",{\\"configKey\\":\\"configValue\\"}]],\\"transformIgnorePatterns\\":[\\"/node_modules/\\"],\\"watchPathIgnorePatterns\\":[]}", + "configString": "{\\"automock\\":false,\\"cache\\":true,\\"cacheDirectory\\":\\"/cache/\\",\\"clearMocks\\":false,\\"coveragePathIgnorePatterns\\":[],\\"cwd\\":\\"/test_root_dir/\\",\\"detectLeaks\\":false,\\"detectOpenHandles\\":false,\\"errorOnDeprecated\\":false,\\"extensionsToTreatAsEsm\\":[],\\"extraGlobals\\":[],\\"forceCoverageMatch\\":[],\\"globals\\":{},\\"haste\\":{},\\"injectGlobals\\":true,\\"moduleDirectories\\":[],\\"moduleFileExtensions\\":[\\"js\\"],\\"moduleLoader\\":\\"/test_module_loader_path\\",\\"moduleNameMapper\\":[],\\"modulePathIgnorePatterns\\":[],\\"modulePaths\\":[],\\"name\\":\\"test\\",\\"prettierPath\\":\\"prettier\\",\\"resetMocks\\":false,\\"resetModules\\":false,\\"restoreMocks\\":false,\\"rootDir\\":\\"/\\",\\"roots\\":[],\\"runner\\":\\"jest-runner\\",\\"setupFiles\\":[],\\"setupFilesAfterEnv\\":[],\\"skipFilter\\":false,\\"skipNodeResolution\\":false,\\"slowTestThreshold\\":5,\\"snapshotFormat\\":{},\\"snapshotSerializers\\":[],\\"testEnvironment\\":\\"node\\",\\"testEnvironmentOptions\\":{},\\"testLocationInResults\\":false,\\"testMatch\\":[],\\"testPathIgnorePatterns\\":[],\\"testRegex\\":[\\"\\\\\\\\.test\\\\\\\\.js$\\"],\\"testRunner\\":\\"jest-circus/runner\\",\\"testURL\\":\\"http://localhost\\",\\"timers\\":\\"real\\",\\"transform\\":[[\\"\\\\\\\\.js$\\",\\"test_preprocessor\\",{\\"configKey\\":\\"configValue\\"}]],\\"transformIgnorePatterns\\":[\\"/node_modules/\\"],\\"watchPathIgnorePatterns\\":[]}", "coverageProvider": "babel", "instrument": true, "supportsDynamicImport": false, @@ -110,7 +110,7 @@ exports[`ScriptTransformer in async mode, uses the supplied async preprocessor 1 const TRANSFORMED = { filename: '/fruits/banana.js', script: 'module.exports = "banana";', - config: '{"collectCoverage":false,"collectCoverageFrom":[],"coverageProvider":"babel","supportsDynamicImport":false,"supportsExportNamespaceFrom":false,"supportsStaticESM":false,"supportsTopLevelAwait":false,"instrument":false,"cacheFS":{},"config":{"automock":false,"cache":true,"cacheDirectory":"/cache/","clearMocks":false,"coveragePathIgnorePatterns":[],"cwd":"/test_root_dir/","detectLeaks":false,"detectOpenHandles":false,"errorOnDeprecated":false,"extensionsToTreatAsEsm":[],"extraGlobals":[],"forceCoverageMatch":[],"globals":{},"haste":{},"injectGlobals":true,"moduleDirectories":[],"moduleFileExtensions":["js"],"moduleLoader":"/test_module_loader_path","moduleNameMapper":[],"modulePathIgnorePatterns":[],"modulePaths":[],"name":"test","prettierPath":"prettier","resetMocks":false,"resetModules":false,"restoreMocks":false,"rootDir":"/","roots":[],"runner":"jest-runner","setupFiles":[],"setupFilesAfterEnv":[],"skipFilter":false,"skipNodeResolution":false,"slowTestThreshold":5,"snapshotSerializers":[],"testEnvironment":"node","testEnvironmentOptions":{},"testLocationInResults":false,"testMatch":[],"testPathIgnorePatterns":[],"testRegex":["\\\\.test\\\\.js$"],"testRunner":"jest-circus/runner","testURL":"http://localhost","timers":"real","transform":[["\\\\.js$","test_async_preprocessor",{}]],"transformIgnorePatterns":["/node_modules/"],"watchPathIgnorePatterns":[]},"configString":"{\\"automock\\":false,\\"cache\\":true,\\"cacheDirectory\\":\\"/cache/\\",\\"clearMocks\\":false,\\"coveragePathIgnorePatterns\\":[],\\"cwd\\":\\"/test_root_dir/\\",\\"detectLeaks\\":false,\\"detectOpenHandles\\":false,\\"errorOnDeprecated\\":false,\\"extensionsToTreatAsEsm\\":[],\\"extraGlobals\\":[],\\"forceCoverageMatch\\":[],\\"globals\\":{},\\"haste\\":{},\\"injectGlobals\\":true,\\"moduleDirectories\\":[],\\"moduleFileExtensions\\":[\\"js\\"],\\"moduleLoader\\":\\"/test_module_loader_path\\",\\"moduleNameMapper\\":[],\\"modulePathIgnorePatterns\\":[],\\"modulePaths\\":[],\\"name\\":\\"test\\",\\"prettierPath\\":\\"prettier\\",\\"resetMocks\\":false,\\"resetModules\\":false,\\"restoreMocks\\":false,\\"rootDir\\":\\"/\\",\\"roots\\":[],\\"runner\\":\\"jest-runner\\",\\"setupFiles\\":[],\\"setupFilesAfterEnv\\":[],\\"skipFilter\\":false,\\"skipNodeResolution\\":false,\\"slowTestThreshold\\":5,\\"snapshotSerializers\\":[],\\"testEnvironment\\":\\"node\\",\\"testEnvironmentOptions\\":{},\\"testLocationInResults\\":false,\\"testMatch\\":[],\\"testPathIgnorePatterns\\":[],\\"testRegex\\":[\\"\\\\\\\\.test\\\\\\\\.js$\\"],\\"testRunner\\":\\"jest-circus/runner\\",\\"testURL\\":\\"http://localhost\\",\\"timers\\":\\"real\\",\\"transform\\":[[\\"\\\\\\\\.js$\\",\\"test_async_preprocessor\\",{}]],\\"transformIgnorePatterns\\":[\\"/node_modules/\\"],\\"watchPathIgnorePatterns\\":[]}","transformerConfig":{}}', + config: '{"collectCoverage":false,"collectCoverageFrom":[],"coverageProvider":"babel","supportsDynamicImport":false,"supportsExportNamespaceFrom":false,"supportsStaticESM":false,"supportsTopLevelAwait":false,"instrument":false,"cacheFS":{},"config":{"automock":false,"cache":true,"cacheDirectory":"/cache/","clearMocks":false,"coveragePathIgnorePatterns":[],"cwd":"/test_root_dir/","detectLeaks":false,"detectOpenHandles":false,"errorOnDeprecated":false,"extensionsToTreatAsEsm":[],"extraGlobals":[],"forceCoverageMatch":[],"globals":{},"haste":{},"injectGlobals":true,"moduleDirectories":[],"moduleFileExtensions":["js"],"moduleLoader":"/test_module_loader_path","moduleNameMapper":[],"modulePathIgnorePatterns":[],"modulePaths":[],"name":"test","prettierPath":"prettier","resetMocks":false,"resetModules":false,"restoreMocks":false,"rootDir":"/","roots":[],"runner":"jest-runner","setupFiles":[],"setupFilesAfterEnv":[],"skipFilter":false,"skipNodeResolution":false,"slowTestThreshold":5,"snapshotFormat":{},"snapshotSerializers":[],"testEnvironment":"node","testEnvironmentOptions":{},"testLocationInResults":false,"testMatch":[],"testPathIgnorePatterns":[],"testRegex":["\\\\.test\\\\.js$"],"testRunner":"jest-circus/runner","testURL":"http://localhost","timers":"real","transform":[["\\\\.js$","test_async_preprocessor",{}]],"transformIgnorePatterns":["/node_modules/"],"watchPathIgnorePatterns":[]},"configString":"{\\"automock\\":false,\\"cache\\":true,\\"cacheDirectory\\":\\"/cache/\\",\\"clearMocks\\":false,\\"coveragePathIgnorePatterns\\":[],\\"cwd\\":\\"/test_root_dir/\\",\\"detectLeaks\\":false,\\"detectOpenHandles\\":false,\\"errorOnDeprecated\\":false,\\"extensionsToTreatAsEsm\\":[],\\"extraGlobals\\":[],\\"forceCoverageMatch\\":[],\\"globals\\":{},\\"haste\\":{},\\"injectGlobals\\":true,\\"moduleDirectories\\":[],\\"moduleFileExtensions\\":[\\"js\\"],\\"moduleLoader\\":\\"/test_module_loader_path\\",\\"moduleNameMapper\\":[],\\"modulePathIgnorePatterns\\":[],\\"modulePaths\\":[],\\"name\\":\\"test\\",\\"prettierPath\\":\\"prettier\\",\\"resetMocks\\":false,\\"resetModules\\":false,\\"restoreMocks\\":false,\\"rootDir\\":\\"/\\",\\"roots\\":[],\\"runner\\":\\"jest-runner\\",\\"setupFiles\\":[],\\"setupFilesAfterEnv\\":[],\\"skipFilter\\":false,\\"skipNodeResolution\\":false,\\"slowTestThreshold\\":5,\\"snapshotFormat\\":{},\\"snapshotSerializers\\":[],\\"testEnvironment\\":\\"node\\",\\"testEnvironmentOptions\\":{},\\"testLocationInResults\\":false,\\"testMatch\\":[],\\"testPathIgnorePatterns\\":[],\\"testRegex\\":[\\"\\\\\\\\.test\\\\\\\\.js$\\"],\\"testRunner\\":\\"jest-circus/runner\\",\\"testURL\\":\\"http://localhost\\",\\"timers\\":\\"real\\",\\"transform\\":[[\\"\\\\\\\\.js$\\",\\"test_async_preprocessor\\",{}]],\\"transformIgnorePatterns\\":[\\"/node_modules/\\"],\\"watchPathIgnorePatterns\\":[]}","transformerConfig":{}}', }; `; @@ -120,7 +120,7 @@ exports[`ScriptTransformer in async mode, uses the supplied preprocessor 1`] = ` const TRANSFORMED = { filename: '/fruits/banana.js', script: 'module.exports = "banana";', - config: '{"collectCoverage":false,"collectCoverageFrom":[],"coverageProvider":"babel","supportsDynamicImport":false,"supportsExportNamespaceFrom":false,"supportsStaticESM":false,"supportsTopLevelAwait":false,"instrument":false,"cacheFS":{},"config":{"automock":false,"cache":true,"cacheDirectory":"/cache/","clearMocks":false,"coveragePathIgnorePatterns":[],"cwd":"/test_root_dir/","detectLeaks":false,"detectOpenHandles":false,"errorOnDeprecated":false,"extensionsToTreatAsEsm":[],"extraGlobals":[],"forceCoverageMatch":[],"globals":{},"haste":{},"injectGlobals":true,"moduleDirectories":[],"moduleFileExtensions":["js"],"moduleLoader":"/test_module_loader_path","moduleNameMapper":[],"modulePathIgnorePatterns":[],"modulePaths":[],"name":"test","prettierPath":"prettier","resetMocks":false,"resetModules":false,"restoreMocks":false,"rootDir":"/","roots":[],"runner":"jest-runner","setupFiles":[],"setupFilesAfterEnv":[],"skipFilter":false,"skipNodeResolution":false,"slowTestThreshold":5,"snapshotSerializers":[],"testEnvironment":"node","testEnvironmentOptions":{},"testLocationInResults":false,"testMatch":[],"testPathIgnorePatterns":[],"testRegex":["\\\\.test\\\\.js$"],"testRunner":"jest-circus/runner","testURL":"http://localhost","timers":"real","transform":[["\\\\.js$","test_preprocessor",{}]],"transformIgnorePatterns":["/node_modules/"],"watchPathIgnorePatterns":[]},"configString":"{\\"automock\\":false,\\"cache\\":true,\\"cacheDirectory\\":\\"/cache/\\",\\"clearMocks\\":false,\\"coveragePathIgnorePatterns\\":[],\\"cwd\\":\\"/test_root_dir/\\",\\"detectLeaks\\":false,\\"detectOpenHandles\\":false,\\"errorOnDeprecated\\":false,\\"extensionsToTreatAsEsm\\":[],\\"extraGlobals\\":[],\\"forceCoverageMatch\\":[],\\"globals\\":{},\\"haste\\":{},\\"injectGlobals\\":true,\\"moduleDirectories\\":[],\\"moduleFileExtensions\\":[\\"js\\"],\\"moduleLoader\\":\\"/test_module_loader_path\\",\\"moduleNameMapper\\":[],\\"modulePathIgnorePatterns\\":[],\\"modulePaths\\":[],\\"name\\":\\"test\\",\\"prettierPath\\":\\"prettier\\",\\"resetMocks\\":false,\\"resetModules\\":false,\\"restoreMocks\\":false,\\"rootDir\\":\\"/\\",\\"roots\\":[],\\"runner\\":\\"jest-runner\\",\\"setupFiles\\":[],\\"setupFilesAfterEnv\\":[],\\"skipFilter\\":false,\\"skipNodeResolution\\":false,\\"slowTestThreshold\\":5,\\"snapshotSerializers\\":[],\\"testEnvironment\\":\\"node\\",\\"testEnvironmentOptions\\":{},\\"testLocationInResults\\":false,\\"testMatch\\":[],\\"testPathIgnorePatterns\\":[],\\"testRegex\\":[\\"\\\\\\\\.test\\\\\\\\.js$\\"],\\"testRunner\\":\\"jest-circus/runner\\",\\"testURL\\":\\"http://localhost\\",\\"timers\\":\\"real\\",\\"transform\\":[[\\"\\\\\\\\.js$\\",\\"test_preprocessor\\",{}]],\\"transformIgnorePatterns\\":[\\"/node_modules/\\"],\\"watchPathIgnorePatterns\\":[]}","transformerConfig":{}}', + config: '{"collectCoverage":false,"collectCoverageFrom":[],"coverageProvider":"babel","supportsDynamicImport":false,"supportsExportNamespaceFrom":false,"supportsStaticESM":false,"supportsTopLevelAwait":false,"instrument":false,"cacheFS":{},"config":{"automock":false,"cache":true,"cacheDirectory":"/cache/","clearMocks":false,"coveragePathIgnorePatterns":[],"cwd":"/test_root_dir/","detectLeaks":false,"detectOpenHandles":false,"errorOnDeprecated":false,"extensionsToTreatAsEsm":[],"extraGlobals":[],"forceCoverageMatch":[],"globals":{},"haste":{},"injectGlobals":true,"moduleDirectories":[],"moduleFileExtensions":["js"],"moduleLoader":"/test_module_loader_path","moduleNameMapper":[],"modulePathIgnorePatterns":[],"modulePaths":[],"name":"test","prettierPath":"prettier","resetMocks":false,"resetModules":false,"restoreMocks":false,"rootDir":"/","roots":[],"runner":"jest-runner","setupFiles":[],"setupFilesAfterEnv":[],"skipFilter":false,"skipNodeResolution":false,"slowTestThreshold":5,"snapshotFormat":{},"snapshotSerializers":[],"testEnvironment":"node","testEnvironmentOptions":{},"testLocationInResults":false,"testMatch":[],"testPathIgnorePatterns":[],"testRegex":["\\\\.test\\\\.js$"],"testRunner":"jest-circus/runner","testURL":"http://localhost","timers":"real","transform":[["\\\\.js$","test_preprocessor",{}]],"transformIgnorePatterns":["/node_modules/"],"watchPathIgnorePatterns":[]},"configString":"{\\"automock\\":false,\\"cache\\":true,\\"cacheDirectory\\":\\"/cache/\\",\\"clearMocks\\":false,\\"coveragePathIgnorePatterns\\":[],\\"cwd\\":\\"/test_root_dir/\\",\\"detectLeaks\\":false,\\"detectOpenHandles\\":false,\\"errorOnDeprecated\\":false,\\"extensionsToTreatAsEsm\\":[],\\"extraGlobals\\":[],\\"forceCoverageMatch\\":[],\\"globals\\":{},\\"haste\\":{},\\"injectGlobals\\":true,\\"moduleDirectories\\":[],\\"moduleFileExtensions\\":[\\"js\\"],\\"moduleLoader\\":\\"/test_module_loader_path\\",\\"moduleNameMapper\\":[],\\"modulePathIgnorePatterns\\":[],\\"modulePaths\\":[],\\"name\\":\\"test\\",\\"prettierPath\\":\\"prettier\\",\\"resetMocks\\":false,\\"resetModules\\":false,\\"restoreMocks\\":false,\\"rootDir\\":\\"/\\",\\"roots\\":[],\\"runner\\":\\"jest-runner\\",\\"setupFiles\\":[],\\"setupFilesAfterEnv\\":[],\\"skipFilter\\":false,\\"skipNodeResolution\\":false,\\"slowTestThreshold\\":5,\\"snapshotFormat\\":{},\\"snapshotSerializers\\":[],\\"testEnvironment\\":\\"node\\",\\"testEnvironmentOptions\\":{},\\"testLocationInResults\\":false,\\"testMatch\\":[],\\"testPathIgnorePatterns\\":[],\\"testRegex\\":[\\"\\\\\\\\.test\\\\\\\\.js$\\"],\\"testRunner\\":\\"jest-circus/runner\\",\\"testURL\\":\\"http://localhost\\",\\"timers\\":\\"real\\",\\"transform\\":[[\\"\\\\\\\\.js$\\",\\"test_preprocessor\\",{}]],\\"transformIgnorePatterns\\":[\\"/node_modules/\\"],\\"watchPathIgnorePatterns\\":[]}","transformerConfig":{}}', }; `; @@ -183,6 +183,7 @@ exports[`ScriptTransformer passes expected transform options to getCacheKey 1`] "skipFilter": false, "skipNodeResolution": false, "slowTestThreshold": 5, + "snapshotFormat": Object {}, "snapshotResolver": undefined, "snapshotSerializers": Array [], "testEnvironment": "node", @@ -211,7 +212,7 @@ exports[`ScriptTransformer passes expected transform options to getCacheKey 1`] "unmockedModulePathPatterns": undefined, "watchPathIgnorePatterns": Array [], }, - "configString": "{\\"automock\\":false,\\"cache\\":true,\\"cacheDirectory\\":\\"/cache/\\",\\"clearMocks\\":false,\\"coveragePathIgnorePatterns\\":[],\\"cwd\\":\\"/test_root_dir/\\",\\"detectLeaks\\":false,\\"detectOpenHandles\\":false,\\"errorOnDeprecated\\":false,\\"extensionsToTreatAsEsm\\":[],\\"extraGlobals\\":[],\\"forceCoverageMatch\\":[],\\"globals\\":{},\\"haste\\":{},\\"injectGlobals\\":true,\\"moduleDirectories\\":[],\\"moduleFileExtensions\\":[\\"js\\"],\\"moduleLoader\\":\\"/test_module_loader_path\\",\\"moduleNameMapper\\":[],\\"modulePathIgnorePatterns\\":[],\\"modulePaths\\":[],\\"name\\":\\"test\\",\\"prettierPath\\":\\"prettier\\",\\"resetMocks\\":false,\\"resetModules\\":false,\\"restoreMocks\\":false,\\"rootDir\\":\\"/\\",\\"roots\\":[],\\"runner\\":\\"jest-runner\\",\\"setupFiles\\":[],\\"setupFilesAfterEnv\\":[],\\"skipFilter\\":false,\\"skipNodeResolution\\":false,\\"slowTestThreshold\\":5,\\"snapshotSerializers\\":[],\\"testEnvironment\\":\\"node\\",\\"testEnvironmentOptions\\":{},\\"testLocationInResults\\":false,\\"testMatch\\":[],\\"testPathIgnorePatterns\\":[],\\"testRegex\\":[\\"\\\\\\\\.test\\\\\\\\.js$\\"],\\"testRunner\\":\\"jest-circus/runner\\",\\"testURL\\":\\"http://localhost\\",\\"timers\\":\\"real\\",\\"transform\\":[[\\"\\\\\\\\.js$\\",\\"test_preprocessor\\",{\\"configKey\\":\\"configValue\\"}]],\\"transformIgnorePatterns\\":[\\"/node_modules/\\"],\\"watchPathIgnorePatterns\\":[]}", + "configString": "{\\"automock\\":false,\\"cache\\":true,\\"cacheDirectory\\":\\"/cache/\\",\\"clearMocks\\":false,\\"coveragePathIgnorePatterns\\":[],\\"cwd\\":\\"/test_root_dir/\\",\\"detectLeaks\\":false,\\"detectOpenHandles\\":false,\\"errorOnDeprecated\\":false,\\"extensionsToTreatAsEsm\\":[],\\"extraGlobals\\":[],\\"forceCoverageMatch\\":[],\\"globals\\":{},\\"haste\\":{},\\"injectGlobals\\":true,\\"moduleDirectories\\":[],\\"moduleFileExtensions\\":[\\"js\\"],\\"moduleLoader\\":\\"/test_module_loader_path\\",\\"moduleNameMapper\\":[],\\"modulePathIgnorePatterns\\":[],\\"modulePaths\\":[],\\"name\\":\\"test\\",\\"prettierPath\\":\\"prettier\\",\\"resetMocks\\":false,\\"resetModules\\":false,\\"restoreMocks\\":false,\\"rootDir\\":\\"/\\",\\"roots\\":[],\\"runner\\":\\"jest-runner\\",\\"setupFiles\\":[],\\"setupFilesAfterEnv\\":[],\\"skipFilter\\":false,\\"skipNodeResolution\\":false,\\"slowTestThreshold\\":5,\\"snapshotFormat\\":{},\\"snapshotSerializers\\":[],\\"testEnvironment\\":\\"node\\",\\"testEnvironmentOptions\\":{},\\"testLocationInResults\\":false,\\"testMatch\\":[],\\"testPathIgnorePatterns\\":[],\\"testRegex\\":[\\"\\\\\\\\.test\\\\\\\\.js$\\"],\\"testRunner\\":\\"jest-circus/runner\\",\\"testURL\\":\\"http://localhost\\",\\"timers\\":\\"real\\",\\"transform\\":[[\\"\\\\\\\\.js$\\",\\"test_preprocessor\\",{\\"configKey\\":\\"configValue\\"}]],\\"transformIgnorePatterns\\":[\\"/node_modules/\\"],\\"watchPathIgnorePatterns\\":[]}", "coverageProvider": "babel", "instrument": true, "supportsDynamicImport": false, @@ -288,6 +289,7 @@ exports[`ScriptTransformer passes expected transform options to getCacheKeyAsync "skipFilter": false, "skipNodeResolution": false, "slowTestThreshold": 5, + "snapshotFormat": Object {}, "snapshotResolver": undefined, "snapshotSerializers": Array [], "testEnvironment": "node", @@ -316,7 +318,7 @@ exports[`ScriptTransformer passes expected transform options to getCacheKeyAsync "unmockedModulePathPatterns": undefined, "watchPathIgnorePatterns": Array [], }, - "configString": "{\\"automock\\":false,\\"cache\\":true,\\"cacheDirectory\\":\\"/cache/\\",\\"clearMocks\\":false,\\"coveragePathIgnorePatterns\\":[],\\"cwd\\":\\"/test_root_dir/\\",\\"detectLeaks\\":false,\\"detectOpenHandles\\":false,\\"errorOnDeprecated\\":false,\\"extensionsToTreatAsEsm\\":[],\\"extraGlobals\\":[],\\"forceCoverageMatch\\":[],\\"globals\\":{},\\"haste\\":{},\\"injectGlobals\\":true,\\"moduleDirectories\\":[],\\"moduleFileExtensions\\":[\\"js\\"],\\"moduleLoader\\":\\"/test_module_loader_path\\",\\"moduleNameMapper\\":[],\\"modulePathIgnorePatterns\\":[],\\"modulePaths\\":[],\\"name\\":\\"test\\",\\"prettierPath\\":\\"prettier\\",\\"resetMocks\\":false,\\"resetModules\\":false,\\"restoreMocks\\":false,\\"rootDir\\":\\"/\\",\\"roots\\":[],\\"runner\\":\\"jest-runner\\",\\"setupFiles\\":[],\\"setupFilesAfterEnv\\":[],\\"skipFilter\\":false,\\"skipNodeResolution\\":false,\\"slowTestThreshold\\":5,\\"snapshotSerializers\\":[],\\"testEnvironment\\":\\"node\\",\\"testEnvironmentOptions\\":{},\\"testLocationInResults\\":false,\\"testMatch\\":[],\\"testPathIgnorePatterns\\":[],\\"testRegex\\":[\\"\\\\\\\\.test\\\\\\\\.js$\\"],\\"testRunner\\":\\"jest-circus/runner\\",\\"testURL\\":\\"http://localhost\\",\\"timers\\":\\"real\\",\\"transform\\":[[\\"\\\\\\\\.js$\\",\\"test_async_preprocessor\\",{\\"configKey\\":\\"configValue\\"}]],\\"transformIgnorePatterns\\":[\\"/node_modules/\\"],\\"watchPathIgnorePatterns\\":[]}", + "configString": "{\\"automock\\":false,\\"cache\\":true,\\"cacheDirectory\\":\\"/cache/\\",\\"clearMocks\\":false,\\"coveragePathIgnorePatterns\\":[],\\"cwd\\":\\"/test_root_dir/\\",\\"detectLeaks\\":false,\\"detectOpenHandles\\":false,\\"errorOnDeprecated\\":false,\\"extensionsToTreatAsEsm\\":[],\\"extraGlobals\\":[],\\"forceCoverageMatch\\":[],\\"globals\\":{},\\"haste\\":{},\\"injectGlobals\\":true,\\"moduleDirectories\\":[],\\"moduleFileExtensions\\":[\\"js\\"],\\"moduleLoader\\":\\"/test_module_loader_path\\",\\"moduleNameMapper\\":[],\\"modulePathIgnorePatterns\\":[],\\"modulePaths\\":[],\\"name\\":\\"test\\",\\"prettierPath\\":\\"prettier\\",\\"resetMocks\\":false,\\"resetModules\\":false,\\"restoreMocks\\":false,\\"rootDir\\":\\"/\\",\\"roots\\":[],\\"runner\\":\\"jest-runner\\",\\"setupFiles\\":[],\\"setupFilesAfterEnv\\":[],\\"skipFilter\\":false,\\"skipNodeResolution\\":false,\\"slowTestThreshold\\":5,\\"snapshotFormat\\":{},\\"snapshotSerializers\\":[],\\"testEnvironment\\":\\"node\\",\\"testEnvironmentOptions\\":{},\\"testLocationInResults\\":false,\\"testMatch\\":[],\\"testPathIgnorePatterns\\":[],\\"testRegex\\":[\\"\\\\\\\\.test\\\\\\\\.js$\\"],\\"testRunner\\":\\"jest-circus/runner\\",\\"testURL\\":\\"http://localhost\\",\\"timers\\":\\"real\\",\\"transform\\":[[\\"\\\\\\\\.js$\\",\\"test_async_preprocessor\\",{\\"configKey\\":\\"configValue\\"}]],\\"transformIgnorePatterns\\":[\\"/node_modules/\\"],\\"watchPathIgnorePatterns\\":[]}", "coverageProvider": "babel", "instrument": true, "supportsDynamicImport": false, @@ -646,7 +648,7 @@ exports[`ScriptTransformer uses mixture of sync/async preprocessors 1`] = ` const TRANSFORMED = { filename: '/fruits/banana.js', script: 'module.exports = "banana";', - config: '{"collectCoverage":false,"collectCoverageFrom":[],"coverageProvider":"babel","supportsDynamicImport":false,"supportsExportNamespaceFrom":false,"supportsStaticESM":false,"supportsTopLevelAwait":false,"instrument":false,"cacheFS":{},"config":{"automock":false,"cache":true,"cacheDirectory":"/cache/","clearMocks":false,"coveragePathIgnorePatterns":[],"cwd":"/test_root_dir/","detectLeaks":false,"detectOpenHandles":false,"errorOnDeprecated":false,"extensionsToTreatAsEsm":[],"extraGlobals":[],"forceCoverageMatch":[],"globals":{},"haste":{},"injectGlobals":true,"moduleDirectories":[],"moduleFileExtensions":["js"],"moduleLoader":"/test_module_loader_path","moduleNameMapper":[],"modulePathIgnorePatterns":[],"modulePaths":[],"name":"test","prettierPath":"prettier","resetMocks":false,"resetModules":false,"restoreMocks":false,"rootDir":"/","roots":[],"runner":"jest-runner","setupFiles":[],"setupFilesAfterEnv":[],"skipFilter":false,"skipNodeResolution":false,"slowTestThreshold":5,"snapshotSerializers":[],"testEnvironment":"node","testEnvironmentOptions":{},"testLocationInResults":false,"testMatch":[],"testPathIgnorePatterns":[],"testRegex":["\\\\.test\\\\.js$"],"testRunner":"jest-circus/runner","testURL":"http://localhost","timers":"real","transform":[["\\\\.js$","test_async_preprocessor",{}],["\\\\.css$","css-preprocessor",{}]],"transformIgnorePatterns":["/node_modules/"],"watchPathIgnorePatterns":[]},"configString":"{\\"automock\\":false,\\"cache\\":true,\\"cacheDirectory\\":\\"/cache/\\",\\"clearMocks\\":false,\\"coveragePathIgnorePatterns\\":[],\\"cwd\\":\\"/test_root_dir/\\",\\"detectLeaks\\":false,\\"detectOpenHandles\\":false,\\"errorOnDeprecated\\":false,\\"extensionsToTreatAsEsm\\":[],\\"extraGlobals\\":[],\\"forceCoverageMatch\\":[],\\"globals\\":{},\\"haste\\":{},\\"injectGlobals\\":true,\\"moduleDirectories\\":[],\\"moduleFileExtensions\\":[\\"js\\"],\\"moduleLoader\\":\\"/test_module_loader_path\\",\\"moduleNameMapper\\":[],\\"modulePathIgnorePatterns\\":[],\\"modulePaths\\":[],\\"name\\":\\"test\\",\\"prettierPath\\":\\"prettier\\",\\"resetMocks\\":false,\\"resetModules\\":false,\\"restoreMocks\\":false,\\"rootDir\\":\\"/\\",\\"roots\\":[],\\"runner\\":\\"jest-runner\\",\\"setupFiles\\":[],\\"setupFilesAfterEnv\\":[],\\"skipFilter\\":false,\\"skipNodeResolution\\":false,\\"slowTestThreshold\\":5,\\"snapshotSerializers\\":[],\\"testEnvironment\\":\\"node\\",\\"testEnvironmentOptions\\":{},\\"testLocationInResults\\":false,\\"testMatch\\":[],\\"testPathIgnorePatterns\\":[],\\"testRegex\\":[\\"\\\\\\\\.test\\\\\\\\.js$\\"],\\"testRunner\\":\\"jest-circus/runner\\",\\"testURL\\":\\"http://localhost\\",\\"timers\\":\\"real\\",\\"transform\\":[[\\"\\\\\\\\.js$\\",\\"test_async_preprocessor\\",{}],[\\"\\\\\\\\.css$\\",\\"css-preprocessor\\",{}]],\\"transformIgnorePatterns\\":[\\"/node_modules/\\"],\\"watchPathIgnorePatterns\\":[]}","transformerConfig":{}}', + config: '{"collectCoverage":false,"collectCoverageFrom":[],"coverageProvider":"babel","supportsDynamicImport":false,"supportsExportNamespaceFrom":false,"supportsStaticESM":false,"supportsTopLevelAwait":false,"instrument":false,"cacheFS":{},"config":{"automock":false,"cache":true,"cacheDirectory":"/cache/","clearMocks":false,"coveragePathIgnorePatterns":[],"cwd":"/test_root_dir/","detectLeaks":false,"detectOpenHandles":false,"errorOnDeprecated":false,"extensionsToTreatAsEsm":[],"extraGlobals":[],"forceCoverageMatch":[],"globals":{},"haste":{},"injectGlobals":true,"moduleDirectories":[],"moduleFileExtensions":["js"],"moduleLoader":"/test_module_loader_path","moduleNameMapper":[],"modulePathIgnorePatterns":[],"modulePaths":[],"name":"test","prettierPath":"prettier","resetMocks":false,"resetModules":false,"restoreMocks":false,"rootDir":"/","roots":[],"runner":"jest-runner","setupFiles":[],"setupFilesAfterEnv":[],"skipFilter":false,"skipNodeResolution":false,"slowTestThreshold":5,"snapshotFormat":{},"snapshotSerializers":[],"testEnvironment":"node","testEnvironmentOptions":{},"testLocationInResults":false,"testMatch":[],"testPathIgnorePatterns":[],"testRegex":["\\\\.test\\\\.js$"],"testRunner":"jest-circus/runner","testURL":"http://localhost","timers":"real","transform":[["\\\\.js$","test_async_preprocessor",{}],["\\\\.css$","css-preprocessor",{}]],"transformIgnorePatterns":["/node_modules/"],"watchPathIgnorePatterns":[]},"configString":"{\\"automock\\":false,\\"cache\\":true,\\"cacheDirectory\\":\\"/cache/\\",\\"clearMocks\\":false,\\"coveragePathIgnorePatterns\\":[],\\"cwd\\":\\"/test_root_dir/\\",\\"detectLeaks\\":false,\\"detectOpenHandles\\":false,\\"errorOnDeprecated\\":false,\\"extensionsToTreatAsEsm\\":[],\\"extraGlobals\\":[],\\"forceCoverageMatch\\":[],\\"globals\\":{},\\"haste\\":{},\\"injectGlobals\\":true,\\"moduleDirectories\\":[],\\"moduleFileExtensions\\":[\\"js\\"],\\"moduleLoader\\":\\"/test_module_loader_path\\",\\"moduleNameMapper\\":[],\\"modulePathIgnorePatterns\\":[],\\"modulePaths\\":[],\\"name\\":\\"test\\",\\"prettierPath\\":\\"prettier\\",\\"resetMocks\\":false,\\"resetModules\\":false,\\"restoreMocks\\":false,\\"rootDir\\":\\"/\\",\\"roots\\":[],\\"runner\\":\\"jest-runner\\",\\"setupFiles\\":[],\\"setupFilesAfterEnv\\":[],\\"skipFilter\\":false,\\"skipNodeResolution\\":false,\\"slowTestThreshold\\":5,\\"snapshotFormat\\":{},\\"snapshotSerializers\\":[],\\"testEnvironment\\":\\"node\\",\\"testEnvironmentOptions\\":{},\\"testLocationInResults\\":false,\\"testMatch\\":[],\\"testPathIgnorePatterns\\":[],\\"testRegex\\":[\\"\\\\\\\\.test\\\\\\\\.js$\\"],\\"testRunner\\":\\"jest-circus/runner\\",\\"testURL\\":\\"http://localhost\\",\\"timers\\":\\"real\\",\\"transform\\":[[\\"\\\\\\\\.js$\\",\\"test_async_preprocessor\\",{}],[\\"\\\\\\\\.css$\\",\\"css-preprocessor\\",{}]],\\"transformIgnorePatterns\\":[\\"/node_modules/\\"],\\"watchPathIgnorePatterns\\":[]}","transformerConfig":{}}', }; `; @@ -663,7 +665,7 @@ exports[`ScriptTransformer uses multiple preprocessors 1`] = ` const TRANSFORMED = { filename: '/fruits/banana.js', script: 'module.exports = "banana";', - config: '{"collectCoverage":false,"collectCoverageFrom":[],"coverageProvider":"babel","supportsDynamicImport":false,"supportsExportNamespaceFrom":false,"supportsStaticESM":false,"supportsTopLevelAwait":false,"instrument":false,"cacheFS":{},"config":{"automock":false,"cache":true,"cacheDirectory":"/cache/","clearMocks":false,"coveragePathIgnorePatterns":[],"cwd":"/test_root_dir/","detectLeaks":false,"detectOpenHandles":false,"errorOnDeprecated":false,"extensionsToTreatAsEsm":[],"extraGlobals":[],"forceCoverageMatch":[],"globals":{},"haste":{},"injectGlobals":true,"moduleDirectories":[],"moduleFileExtensions":["js"],"moduleLoader":"/test_module_loader_path","moduleNameMapper":[],"modulePathIgnorePatterns":[],"modulePaths":[],"name":"test","prettierPath":"prettier","resetMocks":false,"resetModules":false,"restoreMocks":false,"rootDir":"/","roots":[],"runner":"jest-runner","setupFiles":[],"setupFilesAfterEnv":[],"skipFilter":false,"skipNodeResolution":false,"slowTestThreshold":5,"snapshotSerializers":[],"testEnvironment":"node","testEnvironmentOptions":{},"testLocationInResults":false,"testMatch":[],"testPathIgnorePatterns":[],"testRegex":["\\\\.test\\\\.js$"],"testRunner":"jest-circus/runner","testURL":"http://localhost","timers":"real","transform":[["\\\\.js$","test_preprocessor",{}],["\\\\.css$","css-preprocessor",{}]],"transformIgnorePatterns":["/node_modules/"],"watchPathIgnorePatterns":[]},"configString":"{\\"automock\\":false,\\"cache\\":true,\\"cacheDirectory\\":\\"/cache/\\",\\"clearMocks\\":false,\\"coveragePathIgnorePatterns\\":[],\\"cwd\\":\\"/test_root_dir/\\",\\"detectLeaks\\":false,\\"detectOpenHandles\\":false,\\"errorOnDeprecated\\":false,\\"extensionsToTreatAsEsm\\":[],\\"extraGlobals\\":[],\\"forceCoverageMatch\\":[],\\"globals\\":{},\\"haste\\":{},\\"injectGlobals\\":true,\\"moduleDirectories\\":[],\\"moduleFileExtensions\\":[\\"js\\"],\\"moduleLoader\\":\\"/test_module_loader_path\\",\\"moduleNameMapper\\":[],\\"modulePathIgnorePatterns\\":[],\\"modulePaths\\":[],\\"name\\":\\"test\\",\\"prettierPath\\":\\"prettier\\",\\"resetMocks\\":false,\\"resetModules\\":false,\\"restoreMocks\\":false,\\"rootDir\\":\\"/\\",\\"roots\\":[],\\"runner\\":\\"jest-runner\\",\\"setupFiles\\":[],\\"setupFilesAfterEnv\\":[],\\"skipFilter\\":false,\\"skipNodeResolution\\":false,\\"slowTestThreshold\\":5,\\"snapshotSerializers\\":[],\\"testEnvironment\\":\\"node\\",\\"testEnvironmentOptions\\":{},\\"testLocationInResults\\":false,\\"testMatch\\":[],\\"testPathIgnorePatterns\\":[],\\"testRegex\\":[\\"\\\\\\\\.test\\\\\\\\.js$\\"],\\"testRunner\\":\\"jest-circus/runner\\",\\"testURL\\":\\"http://localhost\\",\\"timers\\":\\"real\\",\\"transform\\":[[\\"\\\\\\\\.js$\\",\\"test_preprocessor\\",{}],[\\"\\\\\\\\.css$\\",\\"css-preprocessor\\",{}]],\\"transformIgnorePatterns\\":[\\"/node_modules/\\"],\\"watchPathIgnorePatterns\\":[]}","transformerConfig":{}}', + config: '{"collectCoverage":false,"collectCoverageFrom":[],"coverageProvider":"babel","supportsDynamicImport":false,"supportsExportNamespaceFrom":false,"supportsStaticESM":false,"supportsTopLevelAwait":false,"instrument":false,"cacheFS":{},"config":{"automock":false,"cache":true,"cacheDirectory":"/cache/","clearMocks":false,"coveragePathIgnorePatterns":[],"cwd":"/test_root_dir/","detectLeaks":false,"detectOpenHandles":false,"errorOnDeprecated":false,"extensionsToTreatAsEsm":[],"extraGlobals":[],"forceCoverageMatch":[],"globals":{},"haste":{},"injectGlobals":true,"moduleDirectories":[],"moduleFileExtensions":["js"],"moduleLoader":"/test_module_loader_path","moduleNameMapper":[],"modulePathIgnorePatterns":[],"modulePaths":[],"name":"test","prettierPath":"prettier","resetMocks":false,"resetModules":false,"restoreMocks":false,"rootDir":"/","roots":[],"runner":"jest-runner","setupFiles":[],"setupFilesAfterEnv":[],"skipFilter":false,"skipNodeResolution":false,"slowTestThreshold":5,"snapshotFormat":{},"snapshotSerializers":[],"testEnvironment":"node","testEnvironmentOptions":{},"testLocationInResults":false,"testMatch":[],"testPathIgnorePatterns":[],"testRegex":["\\\\.test\\\\.js$"],"testRunner":"jest-circus/runner","testURL":"http://localhost","timers":"real","transform":[["\\\\.js$","test_preprocessor",{}],["\\\\.css$","css-preprocessor",{}]],"transformIgnorePatterns":["/node_modules/"],"watchPathIgnorePatterns":[]},"configString":"{\\"automock\\":false,\\"cache\\":true,\\"cacheDirectory\\":\\"/cache/\\",\\"clearMocks\\":false,\\"coveragePathIgnorePatterns\\":[],\\"cwd\\":\\"/test_root_dir/\\",\\"detectLeaks\\":false,\\"detectOpenHandles\\":false,\\"errorOnDeprecated\\":false,\\"extensionsToTreatAsEsm\\":[],\\"extraGlobals\\":[],\\"forceCoverageMatch\\":[],\\"globals\\":{},\\"haste\\":{},\\"injectGlobals\\":true,\\"moduleDirectories\\":[],\\"moduleFileExtensions\\":[\\"js\\"],\\"moduleLoader\\":\\"/test_module_loader_path\\",\\"moduleNameMapper\\":[],\\"modulePathIgnorePatterns\\":[],\\"modulePaths\\":[],\\"name\\":\\"test\\",\\"prettierPath\\":\\"prettier\\",\\"resetMocks\\":false,\\"resetModules\\":false,\\"restoreMocks\\":false,\\"rootDir\\":\\"/\\",\\"roots\\":[],\\"runner\\":\\"jest-runner\\",\\"setupFiles\\":[],\\"setupFilesAfterEnv\\":[],\\"skipFilter\\":false,\\"skipNodeResolution\\":false,\\"slowTestThreshold\\":5,\\"snapshotFormat\\":{},\\"snapshotSerializers\\":[],\\"testEnvironment\\":\\"node\\",\\"testEnvironmentOptions\\":{},\\"testLocationInResults\\":false,\\"testMatch\\":[],\\"testPathIgnorePatterns\\":[],\\"testRegex\\":[\\"\\\\\\\\.test\\\\\\\\.js$\\"],\\"testRunner\\":\\"jest-circus/runner\\",\\"testURL\\":\\"http://localhost\\",\\"timers\\":\\"real\\",\\"transform\\":[[\\"\\\\\\\\.js$\\",\\"test_preprocessor\\",{}],[\\"\\\\\\\\.css$\\",\\"css-preprocessor\\",{}]],\\"transformIgnorePatterns\\":[\\"/node_modules/\\"],\\"watchPathIgnorePatterns\\":[]}","transformerConfig":{}}', }; `; @@ -680,7 +682,7 @@ exports[`ScriptTransformer uses the supplied preprocessor 1`] = ` const TRANSFORMED = { filename: '/fruits/banana.js', script: 'module.exports = "banana";', - config: '{"collectCoverage":false,"collectCoverageFrom":[],"coverageProvider":"babel","supportsDynamicImport":false,"supportsExportNamespaceFrom":false,"supportsStaticESM":false,"supportsTopLevelAwait":false,"instrument":false,"cacheFS":{},"config":{"automock":false,"cache":true,"cacheDirectory":"/cache/","clearMocks":false,"coveragePathIgnorePatterns":[],"cwd":"/test_root_dir/","detectLeaks":false,"detectOpenHandles":false,"errorOnDeprecated":false,"extensionsToTreatAsEsm":[],"extraGlobals":[],"forceCoverageMatch":[],"globals":{},"haste":{},"injectGlobals":true,"moduleDirectories":[],"moduleFileExtensions":["js"],"moduleLoader":"/test_module_loader_path","moduleNameMapper":[],"modulePathIgnorePatterns":[],"modulePaths":[],"name":"test","prettierPath":"prettier","resetMocks":false,"resetModules":false,"restoreMocks":false,"rootDir":"/","roots":[],"runner":"jest-runner","setupFiles":[],"setupFilesAfterEnv":[],"skipFilter":false,"skipNodeResolution":false,"slowTestThreshold":5,"snapshotSerializers":[],"testEnvironment":"node","testEnvironmentOptions":{},"testLocationInResults":false,"testMatch":[],"testPathIgnorePatterns":[],"testRegex":["\\\\.test\\\\.js$"],"testRunner":"jest-circus/runner","testURL":"http://localhost","timers":"real","transform":[["\\\\.js$","test_preprocessor",{}]],"transformIgnorePatterns":["/node_modules/"],"watchPathIgnorePatterns":[]},"configString":"{\\"automock\\":false,\\"cache\\":true,\\"cacheDirectory\\":\\"/cache/\\",\\"clearMocks\\":false,\\"coveragePathIgnorePatterns\\":[],\\"cwd\\":\\"/test_root_dir/\\",\\"detectLeaks\\":false,\\"detectOpenHandles\\":false,\\"errorOnDeprecated\\":false,\\"extensionsToTreatAsEsm\\":[],\\"extraGlobals\\":[],\\"forceCoverageMatch\\":[],\\"globals\\":{},\\"haste\\":{},\\"injectGlobals\\":true,\\"moduleDirectories\\":[],\\"moduleFileExtensions\\":[\\"js\\"],\\"moduleLoader\\":\\"/test_module_loader_path\\",\\"moduleNameMapper\\":[],\\"modulePathIgnorePatterns\\":[],\\"modulePaths\\":[],\\"name\\":\\"test\\",\\"prettierPath\\":\\"prettier\\",\\"resetMocks\\":false,\\"resetModules\\":false,\\"restoreMocks\\":false,\\"rootDir\\":\\"/\\",\\"roots\\":[],\\"runner\\":\\"jest-runner\\",\\"setupFiles\\":[],\\"setupFilesAfterEnv\\":[],\\"skipFilter\\":false,\\"skipNodeResolution\\":false,\\"slowTestThreshold\\":5,\\"snapshotSerializers\\":[],\\"testEnvironment\\":\\"node\\",\\"testEnvironmentOptions\\":{},\\"testLocationInResults\\":false,\\"testMatch\\":[],\\"testPathIgnorePatterns\\":[],\\"testRegex\\":[\\"\\\\\\\\.test\\\\\\\\.js$\\"],\\"testRunner\\":\\"jest-circus/runner\\",\\"testURL\\":\\"http://localhost\\",\\"timers\\":\\"real\\",\\"transform\\":[[\\"\\\\\\\\.js$\\",\\"test_preprocessor\\",{}]],\\"transformIgnorePatterns\\":[\\"/node_modules/\\"],\\"watchPathIgnorePatterns\\":[]}","transformerConfig":{}}', + config: '{"collectCoverage":false,"collectCoverageFrom":[],"coverageProvider":"babel","supportsDynamicImport":false,"supportsExportNamespaceFrom":false,"supportsStaticESM":false,"supportsTopLevelAwait":false,"instrument":false,"cacheFS":{},"config":{"automock":false,"cache":true,"cacheDirectory":"/cache/","clearMocks":false,"coveragePathIgnorePatterns":[],"cwd":"/test_root_dir/","detectLeaks":false,"detectOpenHandles":false,"errorOnDeprecated":false,"extensionsToTreatAsEsm":[],"extraGlobals":[],"forceCoverageMatch":[],"globals":{},"haste":{},"injectGlobals":true,"moduleDirectories":[],"moduleFileExtensions":["js"],"moduleLoader":"/test_module_loader_path","moduleNameMapper":[],"modulePathIgnorePatterns":[],"modulePaths":[],"name":"test","prettierPath":"prettier","resetMocks":false,"resetModules":false,"restoreMocks":false,"rootDir":"/","roots":[],"runner":"jest-runner","setupFiles":[],"setupFilesAfterEnv":[],"skipFilter":false,"skipNodeResolution":false,"slowTestThreshold":5,"snapshotFormat":{},"snapshotSerializers":[],"testEnvironment":"node","testEnvironmentOptions":{},"testLocationInResults":false,"testMatch":[],"testPathIgnorePatterns":[],"testRegex":["\\\\.test\\\\.js$"],"testRunner":"jest-circus/runner","testURL":"http://localhost","timers":"real","transform":[["\\\\.js$","test_preprocessor",{}]],"transformIgnorePatterns":["/node_modules/"],"watchPathIgnorePatterns":[]},"configString":"{\\"automock\\":false,\\"cache\\":true,\\"cacheDirectory\\":\\"/cache/\\",\\"clearMocks\\":false,\\"coveragePathIgnorePatterns\\":[],\\"cwd\\":\\"/test_root_dir/\\",\\"detectLeaks\\":false,\\"detectOpenHandles\\":false,\\"errorOnDeprecated\\":false,\\"extensionsToTreatAsEsm\\":[],\\"extraGlobals\\":[],\\"forceCoverageMatch\\":[],\\"globals\\":{},\\"haste\\":{},\\"injectGlobals\\":true,\\"moduleDirectories\\":[],\\"moduleFileExtensions\\":[\\"js\\"],\\"moduleLoader\\":\\"/test_module_loader_path\\",\\"moduleNameMapper\\":[],\\"modulePathIgnorePatterns\\":[],\\"modulePaths\\":[],\\"name\\":\\"test\\",\\"prettierPath\\":\\"prettier\\",\\"resetMocks\\":false,\\"resetModules\\":false,\\"restoreMocks\\":false,\\"rootDir\\":\\"/\\",\\"roots\\":[],\\"runner\\":\\"jest-runner\\",\\"setupFiles\\":[],\\"setupFilesAfterEnv\\":[],\\"skipFilter\\":false,\\"skipNodeResolution\\":false,\\"slowTestThreshold\\":5,\\"snapshotFormat\\":{},\\"snapshotSerializers\\":[],\\"testEnvironment\\":\\"node\\",\\"testEnvironmentOptions\\":{},\\"testLocationInResults\\":false,\\"testMatch\\":[],\\"testPathIgnorePatterns\\":[],\\"testRegex\\":[\\"\\\\\\\\.test\\\\\\\\.js$\\"],\\"testRunner\\":\\"jest-circus/runner\\",\\"testURL\\":\\"http://localhost\\",\\"timers\\":\\"real\\",\\"transform\\":[[\\"\\\\\\\\.js$\\",\\"test_preprocessor\\",{}]],\\"transformIgnorePatterns\\":[\\"/node_modules/\\"],\\"watchPathIgnorePatterns\\":[]}","transformerConfig":{}}', }; `; From b032ef5e074eea2e5a5677719a13bc6c79ed33c5 Mon Sep 17 00:00:00 2001 From: Ian VanSchooten Date: Fri, 10 Sep 2021 17:17:10 -0400 Subject: [PATCH 16/39] Apply changes from #11817 to async method --- packages/jest-resolve/src/resolver.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/jest-resolve/src/resolver.ts b/packages/jest-resolve/src/resolver.ts index 00f31d7088c9..3319cfce7a0a 100644 --- a/packages/jest-resolve/src/resolver.ts +++ b/packages/jest-resolve/src/resolver.ts @@ -733,8 +733,12 @@ export default class Resolver { const resolveNodeModule = async ( name: Config.Path, throwIfNotFound = false, - ) => - await Resolver.findNodeModuleAsync(name, { + ) => { + if (this.isCoreModule(name)) { + return name; + } + + return await Resolver.findNodeModuleAsync(name, { basedir: dirname, conditions: options?.conditions, extensions, @@ -744,6 +748,7 @@ export default class Resolver { rootDir: this._options.rootDir, throwIfNotFound, }); + }; if (!skipResolution) { module = await resolveNodeModule( From 67fce998fca9e37b6eac29d15d2687d8bf22c984 Mon Sep 17 00:00:00 2001 From: Ian VanSchooten Date: Fri, 10 Sep 2021 17:17:19 -0400 Subject: [PATCH 17/39] Fix test --- packages/jest-resolve/src/__tests__/resolve.test.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/jest-resolve/src/__tests__/resolve.test.ts b/packages/jest-resolve/src/__tests__/resolve.test.ts index a510f1ee3862..77ac51cec9d6 100644 --- a/packages/jest-resolve/src/__tests__/resolve.test.ts +++ b/packages/jest-resolve/src/__tests__/resolve.test.ts @@ -180,8 +180,7 @@ describe('findNodeModuleAsync', () => { expect(userResolverAsync.async.mock.calls[0][1]).toStrictEqual({ basedir: '/', browser: true, - defaultResolver, - defaultResolverAsync, + defaultResolver: defaultResolverAsync, extensions: ['js'], moduleDirectory: ['node_modules'], paths: (nodePaths || []).concat(['/something']), From fcccde3f7b16470ff38f2696c4d05c23463a2fe8 Mon Sep 17 00:00:00 2001 From: Ian VanSchooten Date: Fri, 10 Sep 2021 17:34:16 -0400 Subject: [PATCH 18/39] add @ts-expect-error for now --- packages/jest-resolve/src/defaultResolver.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/jest-resolve/src/defaultResolver.ts b/packages/jest-resolve/src/defaultResolver.ts index 3b6598ccbb5f..fb77c7d7d4f3 100644 --- a/packages/jest-resolve/src/defaultResolver.ts +++ b/packages/jest-resolve/src/defaultResolver.ts @@ -59,6 +59,7 @@ export async function defaultResolverAsync( // Yarn 2 adds support to `resolve` automatically so the pnpResolver is only // needed for Yarn 1 which implements version 1 of the pnp spec if (process.versions.pnp === '1') { + // @ts-expect-error until https://github.com/arcanis/jest-pnp-resolver/pull/10 is released return pnpResolver(path, options); } From af0870d9bfb3b920a67c92d5af17c577d8457efa Mon Sep 17 00:00:00 2001 From: Ian VanSchooten Date: Fri, 10 Sep 2021 17:59:11 -0400 Subject: [PATCH 19/39] Update snapshot, again --- e2e/__tests__/__snapshots__/moduleNameMapper.test.ts.snap | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/e2e/__tests__/__snapshots__/moduleNameMapper.test.ts.snap b/e2e/__tests__/__snapshots__/moduleNameMapper.test.ts.snap index 45a08c2ddae2..5f715e75a12a 100644 --- a/e2e/__tests__/__snapshots__/moduleNameMapper.test.ts.snap +++ b/e2e/__tests__/__snapshots__/moduleNameMapper.test.ts.snap @@ -41,7 +41,7 @@ FAIL __tests__/index.js 12 | module.exports = () => 'test'; 13 | - at createNoMappedModuleFoundError (../../packages/jest-resolve/build/resolver.js:871:17) + at createNoMappedModuleFoundError (../../packages/jest-resolve/build/resolver.js:876:17) at Object.require (index.js:10:1) `; @@ -70,6 +70,6 @@ FAIL __tests__/index.js 12 | module.exports = () => 'test'; 13 | - at createNoMappedModuleFoundError (../../packages/jest-resolve/build/resolver.js:871:17) + at createNoMappedModuleFoundError (../../packages/jest-resolve/build/resolver.js:876:17) at Object.require (index.js:10:1) `; From 38588b12bd5a1af8550629f0b10479d2294e725b Mon Sep 17 00:00:00 2001 From: Ian VanSchooten Date: Mon, 13 Sep 2021 17:48:12 -0400 Subject: [PATCH 20/39] Update snapshots --- e2e/__tests__/__snapshots__/moduleNameMapper.test.ts.snap | 4 ++-- .../__snapshots__/resolveNoFileExtensions.test.ts.snap | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/e2e/__tests__/__snapshots__/moduleNameMapper.test.ts.snap b/e2e/__tests__/__snapshots__/moduleNameMapper.test.ts.snap index 5f715e75a12a..7c321793693d 100644 --- a/e2e/__tests__/__snapshots__/moduleNameMapper.test.ts.snap +++ b/e2e/__tests__/__snapshots__/moduleNameMapper.test.ts.snap @@ -41,7 +41,7 @@ FAIL __tests__/index.js 12 | module.exports = () => 'test'; 13 | - at createNoMappedModuleFoundError (../../packages/jest-resolve/build/resolver.js:876:17) + at createNoMappedModuleFoundError (../../packages/jest-resolve/build/resolver.js:895:17) at Object.require (index.js:10:1) `; @@ -70,6 +70,6 @@ FAIL __tests__/index.js 12 | module.exports = () => 'test'; 13 | - at createNoMappedModuleFoundError (../../packages/jest-resolve/build/resolver.js:876:17) + at createNoMappedModuleFoundError (../../packages/jest-resolve/build/resolver.js:895:17) at Object.require (index.js:10:1) `; diff --git a/e2e/__tests__/__snapshots__/resolveNoFileExtensions.test.ts.snap b/e2e/__tests__/__snapshots__/resolveNoFileExtensions.test.ts.snap index d72e96a41f8b..4ee9eb10b22e 100644 --- a/e2e/__tests__/__snapshots__/resolveNoFileExtensions.test.ts.snap +++ b/e2e/__tests__/__snapshots__/resolveNoFileExtensions.test.ts.snap @@ -37,6 +37,6 @@ FAIL __tests__/test.js | ^ 9 | - at Resolver._throwModNotFoundError (../../packages/jest-resolve/build/resolver.js:310:11) + at Resolver._throwModNotFoundError (../../packages/jest-resolve/build/resolver.js:311:11) at Object.require (index.js:8:18) `; From a8187096607cdef53285bd97d9ec779499bfc0aa Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Sat, 11 Sep 2021 14:58:24 +0200 Subject: [PATCH 21/39] fix mock # Conflicts: # packages/jest-resolve/src/defaultResolver.ts --- packages/jest-resolve/src/__tests__/resolve.test.ts | 10 ++++++---- packages/jest-resolve/src/defaultResolver.ts | 12 ++++++++---- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/packages/jest-resolve/src/__tests__/resolve.test.ts b/packages/jest-resolve/src/__tests__/resolve.test.ts index 77ac51cec9d6..1ba5e6730e09 100644 --- a/packages/jest-resolve/src/__tests__/resolve.test.ts +++ b/packages/jest-resolve/src/__tests__/resolve.test.ts @@ -24,10 +24,12 @@ jest.mock('../__mocks__/userResolverAsync'); // in very strange ways. Instead just spy on it and its `sync` method. jest.mock('resolve', () => { const originalModule = jest.requireActual('resolve'); - return { - ...originalModule, - sync: jest.spyOn(originalModule, 'sync'), - }; + + const m = jest.fn((...args) => originalModule(...args)); + Object.assign(m, originalModule); + m.sync = jest.spyOn(originalModule, 'sync'); + + return m; }); const mockResolveSync = < diff --git a/packages/jest-resolve/src/defaultResolver.ts b/packages/jest-resolve/src/defaultResolver.ts index fb77c7d7d4f3..a4c6bf4714ae 100644 --- a/packages/jest-resolve/src/defaultResolver.ts +++ b/packages/jest-resolve/src/defaultResolver.ts @@ -8,12 +8,12 @@ import * as fs from 'graceful-fs'; import pnpResolver from 'jest-pnp-resolver'; import resolveAsync = require('resolve'); -import type {AsyncOpts, Opts as ResolveOpts, SyncOpts} from 'resolve'; import type {Config} from '@jest/types'; import {tryRealpath} from 'jest-util'; + const resolveSync = resolveAsync.sync; -export interface ResolverOptions extends ResolveOpts { +export interface ResolverOptions extends resolveAsync.Opts { basedir: Config.Path; browser?: boolean; conditions?: Array; @@ -80,7 +80,9 @@ export async function defaultResolverAsync( /** * getSyncResolveOptions returns resolution options that are used synchronously. */ -function getSyncResolveOptions(options: ResolverOptions): SyncOpts { +function getSyncResolveOptions( + options: ResolverOptions, +): resolveAsync.SyncOpts { return { ...options, isDirectory: isDirectorySync, @@ -94,7 +96,9 @@ function getSyncResolveOptions(options: ResolverOptions): SyncOpts { /** * getAsyncResolveOptions returns resolution options that are used asynchronously. */ -function getAsyncResolveOptions(options: ResolverOptionsAsync): AsyncOpts { +function getAsyncResolveOptions( + options: ResolverOptionsAsync, +): resolveAsync.AsyncOpts { return { ...options, isDirectory: isDirectoryAsync, From 9cbf64c65a988fe1262b4193137417d3e8e7e406 Mon Sep 17 00:00:00 2001 From: Ian VanSchooten Date: Mon, 13 Sep 2021 17:52:33 -0400 Subject: [PATCH 22/39] Pass conditions from options, update async test --- packages/jest-resolve/src/__tests__/resolve.test.ts | 2 ++ packages/jest-resolve/src/resolver.ts | 2 ++ 2 files changed, 4 insertions(+) diff --git a/packages/jest-resolve/src/__tests__/resolve.test.ts b/packages/jest-resolve/src/__tests__/resolve.test.ts index 1ba5e6730e09..444b881239ad 100644 --- a/packages/jest-resolve/src/__tests__/resolve.test.ts +++ b/packages/jest-resolve/src/__tests__/resolve.test.ts @@ -171,6 +171,7 @@ describe('findNodeModuleAsync', () => { const newPath = await Resolver.findNodeModuleAsync('test', { basedir: '/', browser: true, + conditions: ['conditions, woooo'], extensions: ['js'], moduleDirectory: ['node_modules'], paths: ['/something'], @@ -182,6 +183,7 @@ describe('findNodeModuleAsync', () => { expect(userResolverAsync.async.mock.calls[0][1]).toStrictEqual({ basedir: '/', browser: true, + conditions: ['conditions, woooo'], defaultResolver: defaultResolverAsync, extensions: ['js'], moduleDirectory: ['node_modules'], diff --git a/packages/jest-resolve/src/resolver.ts b/packages/jest-resolve/src/resolver.ts index 3319cfce7a0a..baf1eb7d3780 100644 --- a/packages/jest-resolve/src/resolver.ts +++ b/packages/jest-resolve/src/resolver.ts @@ -132,6 +132,7 @@ export default class Resolver { return resolver(path, { basedir: options.basedir, browser: options.browser, + conditions: options.conditions, defaultResolver, extensions: options.extensions, moduleDirectory: options.moduleDirectory, @@ -165,6 +166,7 @@ export default class Resolver { const result = await resolver(path, { basedir: options.basedir, browser: options.browser, + conditions: options.conditions, defaultResolver: defaultResolverAsync, extensions: options.extensions, moduleDirectory: options.moduleDirectory, From e58d96b9175fefb944315c2e6d1a36afd8733774 Mon Sep 17 00:00:00 2001 From: Ian VanSchooten Date: Mon, 13 Sep 2021 20:52:35 -0400 Subject: [PATCH 23/39] Update snapshots --- e2e/__tests__/__snapshots__/moduleNameMapper.test.ts.snap | 4 ++-- .../__snapshots__/resolveNoFileExtensions.test.ts.snap | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/e2e/__tests__/__snapshots__/moduleNameMapper.test.ts.snap b/e2e/__tests__/__snapshots__/moduleNameMapper.test.ts.snap index 7c321793693d..fd00db3ca460 100644 --- a/e2e/__tests__/__snapshots__/moduleNameMapper.test.ts.snap +++ b/e2e/__tests__/__snapshots__/moduleNameMapper.test.ts.snap @@ -41,7 +41,7 @@ FAIL __tests__/index.js 12 | module.exports = () => 'test'; 13 | - at createNoMappedModuleFoundError (../../packages/jest-resolve/build/resolver.js:895:17) + at createNoMappedModuleFoundError (../../packages/jest-resolve/build/resolver.js:897:17) at Object.require (index.js:10:1) `; @@ -70,6 +70,6 @@ FAIL __tests__/index.js 12 | module.exports = () => 'test'; 13 | - at createNoMappedModuleFoundError (../../packages/jest-resolve/build/resolver.js:895:17) + at createNoMappedModuleFoundError (../../packages/jest-resolve/build/resolver.js:897:17) at Object.require (index.js:10:1) `; diff --git a/e2e/__tests__/__snapshots__/resolveNoFileExtensions.test.ts.snap b/e2e/__tests__/__snapshots__/resolveNoFileExtensions.test.ts.snap index 4ee9eb10b22e..49c38a715aef 100644 --- a/e2e/__tests__/__snapshots__/resolveNoFileExtensions.test.ts.snap +++ b/e2e/__tests__/__snapshots__/resolveNoFileExtensions.test.ts.snap @@ -37,6 +37,6 @@ FAIL __tests__/test.js | ^ 9 | - at Resolver._throwModNotFoundError (../../packages/jest-resolve/build/resolver.js:311:11) + at Resolver._throwModNotFoundError (../../packages/jest-resolve/build/resolver.js:313:11) at Object.require (index.js:8:18) `; From f55fa077af4d9eeffae017b9c537fa899f85a5a7 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Wed, 15 Dec 2021 11:17:13 +0100 Subject: [PATCH 24/39] moar package filter --- packages/jest-resolve/src/defaultResolver.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/jest-resolve/src/defaultResolver.ts b/packages/jest-resolve/src/defaultResolver.ts index c1a5c8927007..0641866e5b02 100644 --- a/packages/jest-resolve/src/defaultResolver.ts +++ b/packages/jest-resolve/src/defaultResolver.ts @@ -126,6 +126,10 @@ function getAsyncResolveOptions( ...options, isDirectory: isDirectoryAsync, isFile: isFileAsync, + packageFilter: createPackageFilter( + options.conditions, + options.packageFilter, + ), preserveSymlinks: false, readPackage: readPackageAsync, realpath: realpathAsync, From a5bd89118c0854c287bf9f9e40c2d6b6cd5f1f82 Mon Sep 17 00:00:00 2001 From: Ian VanSchooten Date: Tue, 21 Dec 2021 13:58:23 -0800 Subject: [PATCH 25/39] Update packages/jest-resolve/src/fileWalkers.ts Co-authored-by: Simen Bekkhus --- packages/jest-resolve/src/fileWalkers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/jest-resolve/src/fileWalkers.ts b/packages/jest-resolve/src/fileWalkers.ts index a4162d17a70c..8a38afaeb4dc 100644 --- a/packages/jest-resolve/src/fileWalkers.ts +++ b/packages/jest-resolve/src/fileWalkers.ts @@ -146,7 +146,7 @@ export function isDirectoryAsync( ): void { try { // TODO: create an async version of statSyncCached - const isDir = statSyncCached(dir) === IPathType.DIRECTORY; + const isDir = isDirectorySync(dir); cb(null, isDir); } catch (err: any) { cb(err); From 5944e4d4234d43f54bb0b1006c9a1b1de5b520f5 Mon Sep 17 00:00:00 2001 From: Ian VanSchooten Date: Tue, 21 Dec 2021 13:58:30 -0800 Subject: [PATCH 26/39] Update packages/jest-resolve/src/fileWalkers.ts Co-authored-by: Simen Bekkhus --- packages/jest-resolve/src/fileWalkers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/jest-resolve/src/fileWalkers.ts b/packages/jest-resolve/src/fileWalkers.ts index 8a38afaeb4dc..879e4acbe7cc 100644 --- a/packages/jest-resolve/src/fileWalkers.ts +++ b/packages/jest-resolve/src/fileWalkers.ts @@ -129,7 +129,7 @@ export function isFileAsync( ): void { try { // TODO: create an async version of statSyncCached - const isFile = statSyncCached(file) === IPathType.FILE; + const isFile = isFileSync(file); cb(null, isFile); } catch (err: any) { cb(err); From 27444337da189b68d1e1919c850b50e75bb46ffa Mon Sep 17 00:00:00 2001 From: Ian VanSchooten Date: Tue, 21 Dec 2021 13:58:39 -0800 Subject: [PATCH 27/39] Update packages/jest-resolve/src/defaultResolver.ts Co-authored-by: Simen Bekkhus --- packages/jest-resolve/src/defaultResolver.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/jest-resolve/src/defaultResolver.ts b/packages/jest-resolve/src/defaultResolver.ts index 0641866e5b02..61bf6ef6f162 100644 --- a/packages/jest-resolve/src/defaultResolver.ts +++ b/packages/jest-resolve/src/defaultResolver.ts @@ -147,7 +147,7 @@ function readPackageSync(_: unknown, file: Config.Path): PkgJson { function readPackageAsync( _: unknown, pkgfile: string, - cb: (err: Error | null, pkgJson?: Record) => void, + cb: (err: Error | null, pkgJson?: PkgJson) => void, ): void { try { // TODO: create an async version of readPackageCached From 70bddfe7b60fbf829f6fc4625384628361ed1716 Mon Sep 17 00:00:00 2001 From: Ian VanSchooten Date: Tue, 21 Dec 2021 13:58:45 -0800 Subject: [PATCH 28/39] Update packages/jest-resolve/src/defaultResolver.ts Co-authored-by: Simen Bekkhus --- packages/jest-resolve/src/defaultResolver.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/jest-resolve/src/defaultResolver.ts b/packages/jest-resolve/src/defaultResolver.ts index 61bf6ef6f162..5e0ca9c15dd1 100644 --- a/packages/jest-resolve/src/defaultResolver.ts +++ b/packages/jest-resolve/src/defaultResolver.ts @@ -146,7 +146,7 @@ function readPackageSync(_: unknown, file: Config.Path): PkgJson { function readPackageAsync( _: unknown, - pkgfile: string, + pkgfile: Config.Path, cb: (err: Error | null, pkgJson?: PkgJson) => void, ): void { try { From 7344e67520d9acace0ce5330dba99acab918fa3a Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Tue, 22 Feb 2022 09:59:07 +0100 Subject: [PATCH 29/39] remove Path --- .../jest-resolve/src/ModuleNotFoundError.ts | 5 +- .../node_modules/exports/package.json | 2 +- .../jest-resolve/src/__mocks__/package.json | 3 +- packages/jest-resolve/src/defaultResolver.ts | 29 +++-- packages/jest-resolve/src/fileWalkers.ts | 23 ++-- packages/jest-resolve/src/nodeModulesPaths.ts | 11 +- packages/jest-resolve/src/resolver.ts | 109 ++++++++---------- packages/jest-resolve/src/shouldLoadAsEsm.ts | 11 +- packages/jest-resolve/src/types.ts | 10 +- packages/jest-resolve/src/utils.ts | 16 +-- 10 files changed, 96 insertions(+), 123 deletions(-) diff --git a/packages/jest-resolve/src/ModuleNotFoundError.ts b/packages/jest-resolve/src/ModuleNotFoundError.ts index a2b0935c6348..7b6685c5ba96 100644 --- a/packages/jest-resolve/src/ModuleNotFoundError.ts +++ b/packages/jest-resolve/src/ModuleNotFoundError.ts @@ -7,12 +7,11 @@ import * as path from 'path'; import slash = require('slash'); -import type {Config} from '@jest/types'; export default class ModuleNotFoundError extends Error { public code = 'MODULE_NOT_FOUND'; public hint?: string; - public requireStack?: Array; + public requireStack?: Array; public siblingWithSimilarExtensionFound?: boolean; public moduleName?: string; @@ -24,7 +23,7 @@ export default class ModuleNotFoundError extends Error { this.moduleName = moduleName; } - public buildMessage(rootDir: Config.Path): void { + public buildMessage(rootDir: string): void { if (!this._originalMessage) { this._originalMessage = this.message || ''; } diff --git a/packages/jest-resolve/src/__mocks__/conditions/node_modules/exports/package.json b/packages/jest-resolve/src/__mocks__/conditions/node_modules/exports/package.json index 7fd4bab2a661..89b8152b8398 100644 --- a/packages/jest-resolve/src/__mocks__/conditions/node_modules/exports/package.json +++ b/packages/jest-resolve/src/__mocks__/conditions/node_modules/exports/package.json @@ -8,7 +8,7 @@ "default": "./default.js" }, "./nested": "./nestedDefault.js", - "./deeplyNested" : { + "./deeplyNested": { "require": "./nestedRequire.js", "default": "./nestedDefault.js" } diff --git a/packages/jest-resolve/src/__mocks__/package.json b/packages/jest-resolve/src/__mocks__/package.json index 2807645e6dca..16d6367d52d4 100644 --- a/packages/jest-resolve/src/__mocks__/package.json +++ b/packages/jest-resolve/src/__mocks__/package.json @@ -1,6 +1,5 @@ { "name": "__mocks__", "version": "1.0.0", - "dependencies": { - } + "dependencies": {} } diff --git a/packages/jest-resolve/src/defaultResolver.ts b/packages/jest-resolve/src/defaultResolver.ts index 411d52e9dd86..19598ec43e81 100644 --- a/packages/jest-resolve/src/defaultResolver.ts +++ b/packages/jest-resolve/src/defaultResolver.ts @@ -13,7 +13,6 @@ import { resolve as resolveExports, } from 'resolve.exports'; import slash = require('slash'); -import type {Config} from '@jest/types'; import { PkgJson, isDirectoryAsync, @@ -30,14 +29,14 @@ const resolveSync = resolveAsync.sync; // copy from `resolve`'s types so we don't have their types in our definition // files export interface ResolverOptions { - basedir: Config.Path; + basedir: string; browser?: boolean; conditions?: Array; defaultResolver: typeof defaultResolver; extensions?: Array; moduleDirectory?: Array; - paths?: Array; - rootDir?: Config.Path; + paths?: Array; + rootDir?: string; packageFilter?: (pkg: PkgJson, dir: string) => PkgJson; pathFilter?: (pkg: PkgJson, path: string, relativePath: string) => string; } @@ -56,9 +55,9 @@ declare global { } export function defaultResolver( - path: Config.Path, + path: string, options: ResolverOptions, -): Config.Path { +): string { // Yarn 2 adds support to `resolve` automatically so the pnpResolver is only // needed for Yarn 1 which implements version 1 of the pnp spec if (process.versions.pnp === '1') { @@ -73,9 +72,9 @@ export function defaultResolver( } export async function defaultResolverAsync( - path: Config.Path, + path: string, options: ResolverOptionsAsync, -): Promise { +): Promise { // Yarn 2 adds support to `resolve` automatically so the pnpResolver is only // needed for Yarn 1 which implements version 1 of the pnp spec if (process.versions.pnp === '1') { @@ -101,7 +100,7 @@ export async function defaultResolverAsync( * getSyncResolveOptions returns resolution options that are used synchronously. */ function getSyncResolveOptions( - path: Config.Path, + path: string, options: ResolverOptions, ): resolveAsync.SyncOpts { return { @@ -120,7 +119,7 @@ function getSyncResolveOptions( * getAsyncResolveOptions returns resolution options that are used asynchronously. */ function getAsyncResolveOptions( - path: Config.Path, + path: string, options: ResolverOptionsAsync, ): resolveAsync.AsyncOpts { return { @@ -139,13 +138,13 @@ function getAsyncResolveOptions( * helper functions */ -function readPackageSync(_: unknown, file: Config.Path): PkgJson { +function readPackageSync(_: unknown, file: string): PkgJson { return readPackageCached(file); } function readPackageAsync( _: unknown, - pkgfile: Config.Path, + pkgfile: string, cb: (err: Error | null, pkgJson?: PkgJson) => void, ): void { try { @@ -158,7 +157,7 @@ function readPackageAsync( } function createPackageFilter( - originalPath: Config.Path, + originalPath: string, userFilter?: ResolverOptions['packageFilter'], ): ResolverOptions['packageFilter'] { if (shouldIgnoreRequestForExports(originalPath)) { @@ -186,7 +185,7 @@ function createPackageFilter( } function createPathFilter( - originalPath: Config.Path, + originalPath: string, conditions?: Array, userFilter?: ResolverOptions['pathFilter'], ): ResolverOptions['pathFilter'] { @@ -221,5 +220,5 @@ function createPathFilter( } // if it's a relative import or an absolute path, exports are ignored -const shouldIgnoreRequestForExports = (path: Config.Path) => +const shouldIgnoreRequestForExports = (path: string) => path.startsWith('.') || isAbsolute(path); diff --git a/packages/jest-resolve/src/fileWalkers.ts b/packages/jest-resolve/src/fileWalkers.ts index ea2cffa78fdd..1112e213fc6c 100644 --- a/packages/jest-resolve/src/fileWalkers.ts +++ b/packages/jest-resolve/src/fileWalkers.ts @@ -7,7 +7,6 @@ import {dirname, resolve} from 'path'; import * as fs from 'graceful-fs'; -import type {Config} from '@jest/types'; import {tryRealpath} from 'jest-util'; export function clearFsCache(): void { @@ -53,7 +52,7 @@ function statSyncCached(path: string): IPathType { } const checkedRealpathPaths = new Map(); -function realpathCached(path: Config.Path): Config.Path { +function realpathCached(path: string): string { let result = checkedRealpathPaths.get(path); if (result != null) { @@ -75,7 +74,7 @@ function realpathCached(path: Config.Path): Config.Path { export type PkgJson = Record; const packageContents = new Map(); -export function readPackageCached(path: Config.Path): PkgJson { +export function readPackageCached(path: string): PkgJson { let result = packageContents.get(path); if (result != null) { @@ -92,9 +91,7 @@ export function readPackageCached(path: Config.Path): PkgJson { // adapted from // https://github.com/lukeed/escalade/blob/2477005062cdbd8407afc90d3f48f4930354252b/src/sync.js // to use cached `fs` calls -export function findClosestPackageJson( - start: Config.Path, -): Config.Path | undefined { +export function findClosestPackageJson(start: string): string | undefined { let dir = resolve('.', start); if (!isDirectorySync(dir)) { dir = dirname(dir); @@ -120,12 +117,12 @@ export function findClosestPackageJson( /* * helper functions */ -export function isFileSync(file: Config.Path): boolean { +export function isFileSync(file: string): boolean { return statSyncCached(file) === IPathType.FILE; } export function isFileAsync( - file: Config.Path, + file: string, cb: (err: Error | null, isFile?: boolean) => void, ): void { try { @@ -137,12 +134,12 @@ export function isFileAsync( } } -export function isDirectorySync(dir: Config.Path): boolean { +export function isDirectorySync(dir: string): boolean { return statSyncCached(dir) === IPathType.DIRECTORY; } export function isDirectoryAsync( - dir: Config.Path, + dir: string, cb: (err: Error | null, isDir?: boolean) => void, ): void { try { @@ -154,13 +151,13 @@ export function isDirectoryAsync( } } -export function realpathSync(file: Config.Path): Config.Path { +export function realpathSync(file: string): string { return realpathCached(file); } export function realpathAsync( - file: Config.Path, - cb: (err: Error | null, resolved?: Config.Path) => void, + file: string, + cb: (err: Error | null, resolved?: string) => void, ): void { try { // TODO: create an async version of realpathCached diff --git a/packages/jest-resolve/src/nodeModulesPaths.ts b/packages/jest-resolve/src/nodeModulesPaths.ts index 604e071c01fe..a2e91914de66 100644 --- a/packages/jest-resolve/src/nodeModulesPaths.ts +++ b/packages/jest-resolve/src/nodeModulesPaths.ts @@ -8,18 +8,17 @@ */ import * as path from 'path'; -import type {Config} from '@jest/types'; import {tryRealpath} from 'jest-util'; type NodeModulesPathsOptions = { moduleDirectory?: Array; - paths?: Array; + paths?: Array; }; export default function nodeModulesPaths( - basedir: Config.Path, + basedir: string, options: NodeModulesPathsOptions, -): Array { +): Array { const modules = options && options.moduleDirectory ? Array.from(options.moduleDirectory) @@ -46,7 +45,7 @@ export default function nodeModulesPaths( physicalBasedir = basedirAbs; } - const paths: Array = [physicalBasedir]; + const paths: Array = [physicalBasedir]; let parsed = path.parse(physicalBasedir); while (parsed.dir !== paths[paths.length - 1]) { paths.push(parsed.dir); @@ -54,7 +53,7 @@ export default function nodeModulesPaths( } const dirs = paths - .reduce>( + .reduce>( (dirs, aPath) => dirs.concat( modules.map(moduleDir => diff --git a/packages/jest-resolve/src/resolver.ts b/packages/jest-resolve/src/resolver.ts index 3fb04003e2f6..b70f319ea37f 100644 --- a/packages/jest-resolve/src/resolver.ts +++ b/packages/jest-resolve/src/resolver.ts @@ -10,7 +10,6 @@ import * as path from 'path'; import chalk = require('chalk'); import slash = require('slash'); -import type {Config} from '@jest/types'; import type {IModuleMap} from 'jest-haste-map'; import {tryRealpath} from 'jest-util'; import ModuleNotFoundError from './ModuleNotFoundError'; @@ -22,21 +21,21 @@ import shouldLoadAsEsm, {clearCachedLookups} from './shouldLoadAsEsm'; import type {ResolverConfig} from './types'; type FindNodeModuleConfig = { - basedir: Config.Path; + basedir: string; browser?: boolean; conditions?: Array; extensions?: Array; moduleDirectory?: Array; - paths?: Array; - resolver?: Config.Path | null; - rootDir?: Config.Path; + paths?: Array; + resolver?: string | null; + rootDir?: string; throwIfNotFound?: boolean; }; export type ResolveModuleConfig = { conditions?: Array; skipNodeResolution?: boolean; - paths?: Array; + paths?: Array; }; const NATIVE_PLATFORM = 'native'; @@ -55,8 +54,8 @@ export default class Resolver { private readonly _options: ResolverConfig; private readonly _moduleMap: IModuleMap; private readonly _moduleIDCache: Map; - private readonly _moduleNameCache: Map; - private readonly _modulePathCache: Map>; + private readonly _moduleNameCache: Map; + private readonly _modulePathCache: Map>; private readonly _supportsNativePlatform: boolean; constructor(moduleMap: IModuleMap, options: ResolverConfig) { @@ -107,9 +106,9 @@ export default class Resolver { static unstable_shouldLoadAsEsm = shouldLoadAsEsm; static findNodeModule( - path: Config.Path, + path: string, options: FindNodeModuleConfig, - ): Config.Path | null { + ): string | null { // The resolver module could be a synchronous function, or an object with sync and/or async keys. const resolverModule = options.resolver ? require(options.resolver) : null; let resolver = defaultResolver; @@ -145,9 +144,9 @@ export default class Resolver { } static async findNodeModuleAsync( - path: Config.Path, + path: string, options: FindNodeModuleConfig, - ): Promise { + ): Promise { // The resolver module could be a synchronous function, or an object with sync and/or async keys. const resolverModule = options.resolver ? require(options.resolver) : null; const resolver: typeof defaultResolverAsync = @@ -184,7 +183,7 @@ export default class Resolver { * methods, to try to keep them as DRY as possible. */ private _prepareForResolution( - dirname: Config.Path, + dirname: string, moduleName: string, options?: ResolveModuleConfig, ) { @@ -224,7 +223,7 @@ export default class Resolver { return null; } - private _throwModNotFoundError(from: Config.Path, moduleName: string): never { + private _throwModNotFoundError(from: string, moduleName: string): never { const relativePath = slash(path.relative(this._options.rootDir, from)) || '.'; @@ -259,10 +258,10 @@ export default class Resolver { private _getAbsolutePath( virtualMocks: Map, - from: Config.Path, + from: string, moduleName: string, options?: ResolveModuleConfig, - ): Config.Path | null { + ): string | null { if (this.isCoreModule(moduleName)) { return moduleName; } @@ -273,10 +272,10 @@ export default class Resolver { private async _getAbsolutePathAsync( virtualMocks: Map, - from: Config.Path, + from: string, moduleName: string, options?: ResolveModuleConfig, - ): Promise { + ): Promise { if (this.isCoreModule(moduleName)) { return moduleName; } @@ -294,19 +293,16 @@ export default class Resolver { ); } - private _getMockPath( - from: Config.Path, - moduleName: string, - ): Config.Path | null { + private _getMockPath(from: string, moduleName: string): string | null { return !this.isCoreModule(moduleName) ? this.getMockModule(from, moduleName) : null; } private async _getMockPathAsync( - from: Config.Path, + from: string, moduleName: string, - ): Promise { + ): Promise { return !this.isCoreModule(moduleName) ? await this.getMockModuleAsync(from, moduleName) : null; @@ -314,10 +310,10 @@ export default class Resolver { private _getVirtualMockPath( virtualMocks: Map, - from: Config.Path, + from: string, moduleName: string, options?: ResolveModuleConfig, - ): Config.Path { + ): string { const virtualMockPath = this.getModulePath(from, moduleName); return virtualMocks.get(virtualMockPath) ? virtualMockPath @@ -328,10 +324,10 @@ export default class Resolver { private async _getVirtualMockPathAsync( virtualMocks: Map, - from: Config.Path, + from: string, moduleName: string, options?: ResolveModuleConfig, - ): Promise { + ): Promise { const virtualMockPath = this.getModulePath(from, moduleName); return virtualMocks.get(virtualMockPath) ? virtualMockPath @@ -340,14 +336,14 @@ export default class Resolver { : from; } - private _isModuleResolved(from: Config.Path, moduleName: string): boolean { + private _isModuleResolved(from: string, moduleName: string): boolean { return !!( this.getModule(moduleName) || this.getMockModule(from, moduleName) ); } private async _isModuleResolvedAsync( - from: Config.Path, + from: string, moduleName: string, ): Promise { return !!( @@ -356,7 +352,7 @@ export default class Resolver { ); } - private _setResolver(resolver?: Config.Path | null): void { + private _setResolver(resolver?: string | null): void { this._options.resolver = resolver; } @@ -370,7 +366,7 @@ export default class Resolver { ); } - getModule(name: string): Config.Path | null { + getModule(name: string): string | null { return this._moduleMap.getModule( name, this._options.defaultPlatform, @@ -378,14 +374,14 @@ export default class Resolver { ); } - getModulePath(from: Config.Path, moduleName: string): Config.Path { + getModulePath(from: string, moduleName: string): string { if (moduleName[0] !== '.' || path.isAbsolute(moduleName)) { return moduleName; } return path.normalize(path.dirname(from) + '/' + moduleName); } - getPackage(name: string): Config.Path | null { + getPackage(name: string): string | null { return this._moduleMap.getPackage( name, this._options.defaultPlatform, @@ -393,7 +389,7 @@ export default class Resolver { ); } - getModulePaths(from: Config.Path): Array { + getModulePaths(from: string): Array { const cachedModule = this._modulePathCache.get(from); if (cachedModule) { return cachedModule; @@ -409,10 +405,7 @@ export default class Resolver { return paths; } - resolveStubModuleName( - from: Config.Path, - moduleName: string, - ): Config.Path | null { + resolveStubModuleName(from: string, moduleName: string): string | null { const dirname = path.dirname(from); const {extensions, moduleDirectory, paths} = this._prepareForResolution( @@ -469,9 +462,9 @@ export default class Resolver { } async resolveStubModuleNameAsync( - from: Config.Path, + from: string, moduleName: string, - ): Promise { + ): Promise { const dirname = path.dirname(from); const {extensions, moduleDirectory, paths} = this._prepareForResolution( @@ -527,7 +520,7 @@ export default class Resolver { return null; } - getMockModule(from: Config.Path, name: string): Config.Path | null { + getMockModule(from: string, name: string): string | null { const mock = this._moduleMap.getMockModule(name); if (mock) { return mock; @@ -540,10 +533,7 @@ export default class Resolver { return null; } - async getMockModuleAsync( - from: Config.Path, - name: string, - ): Promise { + async getMockModuleAsync(from: string, name: string): Promise { const mock = this._moduleMap.getMockModule(name); if (mock) { return mock; @@ -558,7 +548,7 @@ export default class Resolver { getModuleID( virtualMocks: Map, - from: Config.Path, + from: string, _moduleName?: string, options?: ResolveModuleConfig, ): string { @@ -594,7 +584,7 @@ export default class Resolver { async getModuleIDAsync( virtualMocks: Map, - from: Config.Path, + from: string, moduleName = '', options?: ResolveModuleConfig, ): Promise { @@ -629,10 +619,10 @@ export default class Resolver { } resolveModuleFromDirIfExists( - dirname: Config.Path, + dirname: string, moduleName: string, options?: ResolveModuleConfig, - ): Config.Path | null { + ): string | null { const {extensions, key, moduleDirectory, paths, skipResolution} = this._prepareForResolution(dirname, moduleName, options); @@ -658,7 +648,7 @@ export default class Resolver { // 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 = (name: Config.Path, throwIfNotFound = false) => { + const resolveNodeModule = (name: string, throwIfNotFound = false) => { if (this.isCoreModule(name)) { return name; } @@ -703,10 +693,10 @@ export default class Resolver { } async resolveModuleFromDirIfExistsAsync( - dirname: Config.Path, + dirname: string, moduleName: string, options?: ResolveModuleConfig, - ): Promise { + ): Promise { const {extensions, key, moduleDirectory, paths, skipResolution} = this._prepareForResolution(dirname, moduleName, options); @@ -732,10 +722,7 @@ export default class Resolver { // 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: Config.Path, - throwIfNotFound = false, - ) => { + const resolveNodeModule = async (name: string, throwIfNotFound = false) => { if (this.isCoreModule(name)) { return name; } @@ -784,10 +771,10 @@ export default class Resolver { } resolveModule( - from: Config.Path, + from: string, moduleName: string, options?: ResolveModuleConfig, - ): Config.Path { + ): string { const dirname = path.dirname(from); const module = this.resolveStubModuleName(from, moduleName) || @@ -801,10 +788,10 @@ export default class Resolver { } async resolveModuleAsync( - from: Config.Path, + from: string, moduleName: string, options?: ResolveModuleConfig, - ): Promise { + ): Promise { const dirname = path.dirname(from); const module = (await this.resolveStubModuleNameAsync(from, moduleName)) || diff --git a/packages/jest-resolve/src/shouldLoadAsEsm.ts b/packages/jest-resolve/src/shouldLoadAsEsm.ts index aadca23c4d01..f937913a95d5 100644 --- a/packages/jest-resolve/src/shouldLoadAsEsm.ts +++ b/packages/jest-resolve/src/shouldLoadAsEsm.ts @@ -8,7 +8,6 @@ import {dirname, extname} from 'path'; // @ts-expect-error: experimental, not added to the types import {SyntheticModule} from 'vm'; -import type {Config} from '@jest/types'; import {findClosestPackageJson, readPackageCached} from './fileWalkers'; const runtimeSupportsVmModules = typeof SyntheticModule === 'function'; @@ -24,8 +23,8 @@ export function clearCachedLookups(): void { } export default function cachedShouldLoadAsEsm( - path: Config.Path, - extensionsToTreatAsEsm: Array, + path: string, + extensionsToTreatAsEsm: Array, ): boolean { if (!runtimeSupportsVmModules) { return false; @@ -43,8 +42,8 @@ export default function cachedShouldLoadAsEsm( // this is a bad version of what https://github.com/nodejs/modules/issues/393 would provide function shouldLoadAsEsm( - path: Config.Path, - extensionsToTreatAsEsm: Array, + path: string, + extensionsToTreatAsEsm: Array, ): boolean { const extension = extname(path); @@ -72,7 +71,7 @@ function shouldLoadAsEsm( return cachedLookup; } -function cachedPkgCheck(cwd: Config.Path): boolean { +function cachedPkgCheck(cwd: string): boolean { const pkgPath = findClosestPackageJson(cwd); if (!pkgPath) { return false; diff --git a/packages/jest-resolve/src/types.ts b/packages/jest-resolve/src/types.ts index e2c672925fe4..363e1668389d 100644 --- a/packages/jest-resolve/src/types.ts +++ b/packages/jest-resolve/src/types.ts @@ -5,19 +5,17 @@ * LICENSE file in the root directory of this source tree. */ -import type {Config} from '@jest/types'; - export type ResolverConfig = { defaultPlatform?: string | null; extensions: Array; hasCoreModules: boolean; moduleDirectories: Array; moduleNameMapper?: Array | null; - modulePaths?: Array; + modulePaths?: Array; platforms?: Array; - resolver?: Config.Path | null; - asyncResolver?: Config.Path | null; - rootDir: Config.Path; + resolver?: string | null; + asyncResolver?: string | null; + rootDir: string; }; type ModuleNameMapperConfig = { diff --git a/packages/jest-resolve/src/utils.ts b/packages/jest-resolve/src/utils.ts index fbbdf19598b8..6a51c4061800 100644 --- a/packages/jest-resolve/src/utils.ts +++ b/packages/jest-resolve/src/utils.ts @@ -7,7 +7,6 @@ import * as path from 'path'; import chalk = require('chalk'); -import type {Config} from '@jest/types'; import {ValidationError} from 'jest-validate'; import Resolver from './resolver'; @@ -19,10 +18,7 @@ const DOCUMENTATION_NOTE = ` ${chalk.bold('Configuration Documentation:')} const createValidationError = (message: string) => new ValidationError(`${BULLET}Validation Error`, message, DOCUMENTATION_NOTE); -const replaceRootDirInPath = ( - rootDir: Config.Path, - filePath: Config.Path, -): string => { +const replaceRootDirInPath = (rootDir: string, filePath: string): string => { if (!/^/.test(filePath)) { return filePath; } @@ -48,7 +44,7 @@ const resolveWithPrefix = ( optionName: string; prefix: string; requireResolveFunction: (moduleName: string) => string; - rootDir: Config.Path; + rootDir: string; }, ): string => { const fileName = replaceRootDirInPath(rootDir, filePath); @@ -98,7 +94,7 @@ export const resolveTestEnvironment = ({ testEnvironment: filePath, requireResolveFunction, }: { - rootDir: Config.Path; + rootDir: string; testEnvironment: string; requireResolveFunction: (moduleName: string) => string; }): string => { @@ -137,7 +133,7 @@ export const resolveWatchPlugin = ( requireResolveFunction, }: { filePath: string; - rootDir: Config.Path; + rootDir: string; requireResolveFunction: (moduleName: string) => string; }, ): string => @@ -166,7 +162,7 @@ export const resolveRunner = ( requireResolveFunction, }: { filePath: string; - rootDir: Config.Path; + rootDir: string; requireResolveFunction: (moduleName: string) => string; }, ): string => @@ -187,7 +183,7 @@ export const resolveSequencer = ( requireResolveFunction, }: { filePath: string; - rootDir: Config.Path; + rootDir: string; requireResolveFunction: (moduleName: string) => string; }, ): string => From 8c21ab5b2d85e8999a35d08c22ad74d599eca233 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Tue, 22 Feb 2022 10:38:02 +0100 Subject: [PATCH 30/39] remove async from default resolver --- .../src/__tests__/resolve.test.ts | 18 ++- packages/jest-resolve/src/defaultResolver.ts | 120 ++++-------------- packages/jest-resolve/src/fileWalkers.ts | 47 +------ packages/jest-resolve/src/resolver.ts | 10 +- 4 files changed, 43 insertions(+), 152 deletions(-) diff --git a/packages/jest-resolve/src/__tests__/resolve.test.ts b/packages/jest-resolve/src/__tests__/resolve.test.ts index 0292678243ab..ffb83062968f 100644 --- a/packages/jest-resolve/src/__tests__/resolve.test.ts +++ b/packages/jest-resolve/src/__tests__/resolve.test.ts @@ -12,7 +12,7 @@ import resolveAsync = require('resolve'); import {ModuleMap} from 'jest-haste-map'; import userResolver from '../__mocks__/userResolver'; import userResolverAsync from '../__mocks__/userResolverAsync'; -import {defaultResolver, defaultResolverAsync} from '../defaultResolver'; +import defaultResolver from '../defaultResolver'; import nodeModulesPaths from '../nodeModulesPaths'; import Resolver from '../resolver'; import type {ResolverConfig} from '../types'; @@ -287,7 +287,7 @@ describe('findNodeModuleAsync', () => { basedir: '/', browser: true, conditions: ['conditions, woooo'], - defaultResolver: defaultResolverAsync, + defaultResolver, extensions: ['js'], moduleDirectory: ['node_modules'], paths: (nodePaths || []).concat(['/something']), @@ -308,14 +308,12 @@ describe('findNodeModuleAsync', () => { resolver: require.resolve('../__mocks__/userResolverAsync'), }); - // Question, if we can't mock resolve (async), how do we test this? - - // expect(mockResolveSync).toHaveBeenCalledWith( - // 'test', - // expect.objectContaining({ - // packageFilter, - // }), - // ); + expect(mockResolveSync).toHaveBeenCalledWith( + 'test', + expect.objectContaining({ + packageFilter, + }), + ); }); }); diff --git a/packages/jest-resolve/src/defaultResolver.ts b/packages/jest-resolve/src/defaultResolver.ts index 23cf47de224a..12632faa9803 100644 --- a/packages/jest-resolve/src/defaultResolver.ts +++ b/packages/jest-resolve/src/defaultResolver.ts @@ -7,27 +7,22 @@ import {dirname, isAbsolute, resolve as pathResolve} from 'path'; import pnpResolver from 'jest-pnp-resolver'; -import resolveAsync = require('resolve'); +import {SyncOpts as UpstreamResolveOptions, sync as resolveSync} from 'resolve'; import { Options as ResolveExportsOptions, resolve as resolveExports, } from 'resolve.exports'; import { PkgJson, - isDirectoryAsync, - isDirectorySync, - isFileAsync, - isFileSync, + isDirectory, + isFile, readPackageCached, - realpathAsync, realpathSync, } from './fileWalkers'; -const resolveSync = resolveAsync.sync; - // copy from `resolve`'s types so we don't have their types in our definition // files -export interface ResolverOptions { +interface ResolverOptions { basedir: string; browser?: boolean; conditions?: Array; @@ -40,15 +35,16 @@ export interface ResolverOptions { pathFilter?: (pkg: PkgJson, path: string, relativePath: string) => string; } -type ResolverOptionsAsync = Omit & { - defaultResolver: typeof defaultResolverAsync; -}; - -type UpstreamSyncResolveOptionsWithConditions = resolveAsync.SyncOpts & +type UpstreamResolveOptionsWithConditions = UpstreamResolveOptions & Pick; -type UpstreamAsyncResolveOptionsWithConditions = resolveAsync.AsyncOpts & - Pick; +type SyncResolver = (path: string, options: ResolverOptions) => string; +type AsyncResolver = ( + path: string, + options: ResolverOptions, +) => Promise; + +export type Resolver = SyncResolver | AsyncResolver; // https://github.com/facebook/jest/pull/10617 declare global { @@ -59,17 +55,21 @@ declare global { } } -export function defaultResolver( - path: string, - options: ResolverOptions, -): string { +const defaultResolver: SyncResolver = (path, options) => { // Yarn 2 adds support to `resolve` automatically so the pnpResolver is only // needed for Yarn 1 which implements version 1 of the pnp spec if (process.versions.pnp === '1') { return pnpResolver(path, options); } - const resolveOptions = getSyncResolveOptions(options); + const resolveOptions: UpstreamResolveOptionsWithConditions = { + ...options, + isDirectory, + isFile, + preserveSymlinks: false, + readPackageSync, + realpathSync, + }; const pathToResolve = getPathInModule(path, resolveOptions); @@ -82,65 +82,9 @@ export function defaultResolver( // Dereference symlinks to ensure we don't create a separate // module instance depending on how it was referenced. return realpathSync(result); -} - -export async function defaultResolverAsync( - path: string, - options: ResolverOptionsAsync, -): Promise { - // Yarn 2 adds support to `resolve` automatically so the pnpResolver is only - // needed for Yarn 1 which implements version 1 of the pnp spec - if (process.versions.pnp === '1') { - // @ts-expect-error until https://github.com/arcanis/jest-pnp-resolver/pull/10 is released - return pnpResolver(path, options); - } - - // TODO: handle `exports` - return new Promise((resolve, reject) => { - function resolveCb(err: Error | null, result?: string) { - if (err) { - reject(err); - } - if (result) { - resolve(realpathSync(result)); - } - } - const opts = getAsyncResolveOptions(options); - resolveAsync(path, opts, resolveCb); - }); -} - -/** - * getSyncResolveOptions returns resolution options that are used synchronously. - */ -function getSyncResolveOptions( - options: ResolverOptions, -): UpstreamSyncResolveOptionsWithConditions { - return { - ...options, - isDirectory: isDirectorySync, - isFile: isFileSync, - preserveSymlinks: false, - readPackageSync, - realpathSync, - }; -} +}; -/** - * getAsyncResolveOptions returns resolution options that are used asynchronously. - */ -function getAsyncResolveOptions( - options: ResolverOptionsAsync, -): UpstreamAsyncResolveOptionsWithConditions { - return { - ...options, - isDirectory: isDirectoryAsync, - isFile: isFileAsync, - preserveSymlinks: false, - readPackage: readPackageAsync, - realpath: realpathAsync, - }; -} +export default defaultResolver; /* * helper functions @@ -150,23 +94,9 @@ function readPackageSync(_: unknown, file: string): PkgJson { return readPackageCached(file); } -function readPackageAsync( - _: unknown, - pkgfile: string, - cb: (err: Error | null, pkgJson?: PkgJson) => void, -): void { - try { - // TODO: create an async version of readPackageCached - const pkgJson = readPackageCached(pkgfile); - cb(null, pkgJson); - } catch (err: any) { - cb(err); - } -} - function getPathInModule( path: string, - options: UpstreamSyncResolveOptionsWithConditions, + options: UpstreamResolveOptionsWithConditions, ): string { if (shouldIgnoreRequestForExports(path)) { return path; @@ -190,7 +120,7 @@ function getPathInModule( // ignore if package.json cannot be found } - if (packageJsonPath && isFileSync(packageJsonPath)) { + if (packageJsonPath && isFile(packageJsonPath)) { const pkg = readPackageCached(packageJsonPath); if (pkg.exports) { diff --git a/packages/jest-resolve/src/fileWalkers.ts b/packages/jest-resolve/src/fileWalkers.ts index 1112e213fc6c..88c2110e9f72 100644 --- a/packages/jest-resolve/src/fileWalkers.ts +++ b/packages/jest-resolve/src/fileWalkers.ts @@ -93,13 +93,13 @@ export function readPackageCached(path: string): PkgJson { // to use cached `fs` calls export function findClosestPackageJson(start: string): string | undefined { let dir = resolve('.', start); - if (!isDirectorySync(dir)) { + if (!isDirectory(dir)) { dir = dirname(dir); } while (true) { const pkgJsonFile = resolve(dir, './package.json'); - const hasPackageJson = isFileSync(pkgJsonFile); + const hasPackageJson = isFile(pkgJsonFile); if (hasPackageJson) { return pkgJsonFile; @@ -117,53 +117,14 @@ export function findClosestPackageJson(start: string): string | undefined { /* * helper functions */ -export function isFileSync(file: string): boolean { +export function isFile(file: string): boolean { return statSyncCached(file) === IPathType.FILE; } -export function isFileAsync( - file: string, - cb: (err: Error | null, isFile?: boolean) => void, -): void { - try { - // TODO: create an async version of statSyncCached - const isFile = isFileSync(file); - cb(null, isFile); - } catch (err: any) { - cb(err); - } -} - -export function isDirectorySync(dir: string): boolean { +export function isDirectory(dir: string): boolean { return statSyncCached(dir) === IPathType.DIRECTORY; } -export function isDirectoryAsync( - dir: string, - cb: (err: Error | null, isDir?: boolean) => void, -): void { - try { - // TODO: create an async version of statSyncCached - const isDir = isDirectorySync(dir); - cb(null, isDir); - } catch (err: any) { - cb(err); - } -} - export function realpathSync(file: string): string { return realpathCached(file); } - -export function realpathAsync( - file: string, - cb: (err: Error | null, resolved?: string) => void, -): void { - try { - // TODO: create an async version of realpathCached - const resolved = realpathCached(file); - cb(null, resolved); - } catch (err: any) { - cb(err); - } -} diff --git a/packages/jest-resolve/src/resolver.ts b/packages/jest-resolve/src/resolver.ts index b70f319ea37f..497284f13c4e 100644 --- a/packages/jest-resolve/src/resolver.ts +++ b/packages/jest-resolve/src/resolver.ts @@ -13,7 +13,9 @@ import slash = require('slash'); import type {IModuleMap} from 'jest-haste-map'; import {tryRealpath} from 'jest-util'; import ModuleNotFoundError from './ModuleNotFoundError'; -import {defaultResolver, defaultResolverAsync} from './defaultResolver'; +import defaultResolver, { + Resolver as ResolverInterface, +} from './defaultResolver'; import {clearFsCache} from './fileWalkers'; import isBuiltinModule from './isBuiltinModule'; import nodeModulesPaths from './nodeModulesPaths'; @@ -149,12 +151,12 @@ export default class Resolver { ): Promise { // The resolver module could be a synchronous function, or an object with sync and/or async keys. const resolverModule = options.resolver ? require(options.resolver) : null; - const resolver: typeof defaultResolverAsync = + const resolver: ResolverInterface = resolverModule && typeof resolverModule === 'object' && typeof resolverModule.async === 'function' ? resolverModule.async - : defaultResolverAsync; + : defaultResolver; const paths = options.paths; @@ -163,7 +165,7 @@ export default class Resolver { basedir: options.basedir, browser: options.browser, conditions: options.conditions, - defaultResolver: defaultResolverAsync, + defaultResolver, extensions: options.extensions, moduleDirectory: options.moduleDirectory, paths: paths ? (nodePaths || []).concat(paths) : nodePaths, From 8cb9c4963f1accfe69adc7d46abdd836e6a7eedf Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Tue, 22 Feb 2022 10:55:49 +0100 Subject: [PATCH 31/39] docs cleanup --- docs/Configuration.md | 4 ++-- .../jest-resolve/src/__mocks__/userResolver.d.ts | 8 ++------ .../src/__mocks__/userResolverAsync.d.ts | 9 ++------- packages/jest-resolve/src/__tests__/resolve.test.ts | 12 +++--------- 4 files changed, 9 insertions(+), 24 deletions(-) diff --git a/docs/Configuration.md b/docs/Configuration.md index 3557ccd57c45..1c4817388a0e 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -790,7 +790,7 @@ The options object provided to resolvers has the shape: { "basedir": string, "conditions": [string], - "defaultResolver": "function(request, options) -> string | Promise", + "defaultResolver": "function(request, options) -> string", "extensions": [string], "moduleDirectory": [string], "paths": [string], @@ -800,7 +800,7 @@ The options object provided to resolvers has the shape: } ``` -Note: the defaultResolver passed as an option is the Jest default resolver which might be useful when you write your custom one. It takes the same arguments as your custom one, e.g. `(request, options)` and returns a string for sync resolvers and a promise for async resolvers. +Note: the `defaultResolver` passed as an option is the Jest default resolver which might be useful when you write your custom one. It takes the same arguments as your custom synchronous one, e.g. `(request, options)` and returns a string or throws. For example, if you want to respect Browserify's [`"browser"` field](https://github.com/browserify/browserify-handbook/blob/master/readme.markdown#browser-field), you can use the following configuration: diff --git a/packages/jest-resolve/src/__mocks__/userResolver.d.ts b/packages/jest-resolve/src/__mocks__/userResolver.d.ts index 1e10b90437a3..8d6bd2c26306 100644 --- a/packages/jest-resolve/src/__mocks__/userResolver.d.ts +++ b/packages/jest-resolve/src/__mocks__/userResolver.d.ts @@ -5,12 +5,8 @@ * LICENSE file in the root directory of this source tree. */ -import {defaultResolver} from '../defaultResolver'; +import type {Resolver} from '../defaultResolver'; -// todo: can be replaced with jest.MockedFunction -declare const userResolver: jest.MockInstance< - ReturnType, - Parameters ->; +declare const userResolver: Resolver; export default userResolver; diff --git a/packages/jest-resolve/src/__mocks__/userResolverAsync.d.ts b/packages/jest-resolve/src/__mocks__/userResolverAsync.d.ts index b46adc0493e0..8c23f0f74fda 100644 --- a/packages/jest-resolve/src/__mocks__/userResolverAsync.d.ts +++ b/packages/jest-resolve/src/__mocks__/userResolverAsync.d.ts @@ -5,14 +5,9 @@ * LICENSE file in the root directory of this source tree. */ -import {defaultResolverAsync} from '../defaultResolver'; +import type {Resolver} from '../defaultResolver'; // todo: can be replaced with jest.MockedFunction -declare const userResolver: { - async: jest.MockInstance< - ReturnType, - Parameters - >; -}; +declare const userResolver: Resolver; export default userResolver; diff --git a/packages/jest-resolve/src/__tests__/resolve.test.ts b/packages/jest-resolve/src/__tests__/resolve.test.ts index ffb83062968f..52359c2c745d 100644 --- a/packages/jest-resolve/src/__tests__/resolve.test.ts +++ b/packages/jest-resolve/src/__tests__/resolve.test.ts @@ -8,7 +8,7 @@ import * as path from 'path'; import * as fs from 'graceful-fs'; -import resolveAsync = require('resolve'); +import {sync as resolveSync} from 'resolve'; import {ModuleMap} from 'jest-haste-map'; import userResolver from '../__mocks__/userResolver'; import userResolverAsync from '../__mocks__/userResolverAsync'; @@ -17,8 +17,7 @@ import nodeModulesPaths from '../nodeModulesPaths'; import Resolver from '../resolver'; import type {ResolverConfig} from '../types'; -jest.mock('../__mocks__/userResolver'); -jest.mock('../__mocks__/userResolverAsync'); +jest.mock('../__mocks__/userResolver').mock('../__mocks__/userResolverAsync'); // Do not fully mock `resolve` because it is used by Jest. Doing it will crash // in very strange ways. Instead just spy on it and its `sync` method. @@ -32,12 +31,7 @@ jest.mock('resolve', () => { return m; }); -const mockResolveSync = < - jest.Mock< - ReturnType, - Parameters - > ->resolveAsync.sync; +const mockResolveSync = jest.mocked(resolveSync); beforeEach(() => { userResolver.mockClear(); From c009333b00392671eb76681656fa43b728dd563b Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Tue, 22 Feb 2022 10:59:08 +0100 Subject: [PATCH 32/39] remove unused type --- packages/jest-resolve/src/types.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/jest-resolve/src/types.ts b/packages/jest-resolve/src/types.ts index 363e1668389d..8469e2b9dc3d 100644 --- a/packages/jest-resolve/src/types.ts +++ b/packages/jest-resolve/src/types.ts @@ -14,7 +14,6 @@ export type ResolverConfig = { modulePaths?: Array; platforms?: Array; resolver?: string | null; - asyncResolver?: string | null; rootDir: string; }; From 82577106aea5e6f8f9861c527e093cb57a6858db Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Tue, 22 Feb 2022 11:02:38 +0100 Subject: [PATCH 33/39] move things to reduce diff --- packages/jest-resolve/src/resolver.ts | 774 +++++++++++++------------- 1 file changed, 387 insertions(+), 387 deletions(-) diff --git a/packages/jest-resolve/src/resolver.ts b/packages/jest-resolve/src/resolver.ts index 497284f13c4e..de4135ebcdd0 100644 --- a/packages/jest-resolve/src/resolver.ts +++ b/packages/jest-resolve/src/resolver.ts @@ -70,6 +70,7 @@ export default class Resolver { moduleNameMapper: options.moduleNameMapper, modulePaths: options.modulePaths, platforms: options.platforms, + resolver: options.resolver, rootDir: options.rootDir, }; this._supportsNativePlatform = options.platforms @@ -79,7 +80,6 @@ export default class Resolver { this._moduleIDCache = new Map(); this._moduleNameCache = new Map(); this._modulePathCache = new Map(); - this._setResolver(options.resolver); } static ModuleNotFoundError = ModuleNotFoundError; @@ -104,9 +104,6 @@ export default class Resolver { clearCachedLookups(); } - // 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; - static findNodeModule( path: string, options: FindNodeModuleConfig, @@ -180,6 +177,200 @@ export default class Resolver { 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; + + resolveModuleFromDirIfExists( + dirname: string, + moduleName: string, + options?: ResolveModuleConfig, + ): string | null { + 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 = (name: string, throwIfNotFound = false) => { + if (this.isCoreModule(name)) { + return name; + } + + return Resolver.findNodeModule(name, { + basedir: dirname, + conditions: options?.conditions, + extensions, + moduleDirectory, + paths, + resolver: this._options.resolver, + rootDir: this._options.rootDir, + throwIfNotFound, + }); + }; + + if (!skipResolution) { + module = 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(hasteModulePath) || + require.resolve(hasteModulePath); + this._moduleNameCache.set(key, resolvedModule); + return resolvedModule; + } + } catch {} + + return null; + } + + async resolveModuleFromDirIfExistsAsync( + dirname: string, + moduleName: string, + options?: ResolveModuleConfig, + ): Promise { + 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: string, throwIfNotFound = false) => { + if (this.isCoreModule(name)) { + return name; + } + + return await Resolver.findNodeModuleAsync(name, { + basedir: dirname, + conditions: 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 = + (await resolveNodeModule(hasteModulePath)) || + // QUESTION: should this be async? + require.resolve(hasteModulePath); + this._moduleNameCache.set(key, resolvedModule); + return resolvedModule; + } + } catch {} + + return null; + } + + resolveModule( + from: string, + moduleName: string, + options?: ResolveModuleConfig, + ): string { + const dirname = path.dirname(from); + const module = + this.resolveStubModuleName(from, moduleName) || + this.resolveModuleFromDirIfExists(dirname, moduleName, options); + if (module) return module; + + // 5. Throw an error if the module could not be found. `resolve.sync` only + // produces an error based on the dirname but we have the actual current + // module name available. + this._throwModNotFoundError(from, moduleName); + } + + async resolveModuleAsync( + from: string, + moduleName: string, + options?: ResolveModuleConfig, + ): Promise { + 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. @@ -235,10 +426,6 @@ export default class Resolver { ); } - private _getModuleType(moduleName: string): 'node' | 'user' { - return this.isCoreModule(moduleName) ? 'node' : 'user'; - } - private _getMapModuleName(matches: RegExpMatchArray | null) { return matches ? (moduleName: string) => @@ -258,50 +445,205 @@ export default class Resolver { return moduleNameMapper.some(({regex}) => regex.test(moduleName)); } - private _getAbsolutePath( - virtualMocks: Map, - from: string, - moduleName: string, - options?: ResolveModuleConfig, - ): string | null { - if (this.isCoreModule(moduleName)) { - return moduleName; - } - return this._isModuleResolved(from, moduleName) - ? this.getModule(moduleName) - : this._getVirtualMockPath(virtualMocks, from, moduleName, options); + isCoreModule(moduleName: string): boolean { + return ( + this._options.hasCoreModules && + (isBuiltinModule(moduleName) || + (moduleName.startsWith('node:') && + isBuiltinModule(moduleName.slice('node:'.length)))) && + !this._isAliasModule(moduleName) + ); } - private async _getAbsolutePathAsync( - virtualMocks: Map, - from: string, - moduleName: string, - options?: ResolveModuleConfig, - ): Promise { - if (this.isCoreModule(moduleName)) { - return moduleName; - } - const isModuleResolved = await this._isModuleResolvedAsync( - from, - moduleName, + getModule(name: string): string | null { + return this._moduleMap.getModule( + name, + this._options.defaultPlatform, + this._supportsNativePlatform, ); - return isModuleResolved - ? this.getModule(moduleName) - : await this._getVirtualMockPathAsync( - virtualMocks, - from, - moduleName, - options, - ); } - private _getMockPath(from: string, moduleName: string): string | null { - return !this.isCoreModule(moduleName) - ? this.getMockModule(from, moduleName) - : null; + getModulePath(from: string, moduleName: string): string { + if (moduleName[0] !== '.' || path.isAbsolute(moduleName)) { + return moduleName; + } + return path.normalize(path.dirname(from) + '/' + moduleName); } - private async _getMockPathAsync( + getPackage(name: string): string | null { + return this._moduleMap.getPackage( + name, + this._options.defaultPlatform, + this._supportsNativePlatform, + ); + } + + getMockModule(from: string, name: string): string | null { + const mock = this._moduleMap.getMockModule(name); + if (mock) { + return mock; + } else { + const moduleName = this.resolveStubModuleName(from, name); + if (moduleName) { + return this.getModule(moduleName) || moduleName; + } + } + return null; + } + + async getMockModuleAsync(from: string, name: string): Promise { + 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: string): Array { + const cachedModule = this._modulePathCache.get(from); + if (cachedModule) { + return cachedModule; + } + + const moduleDirectory = this._options.moduleDirectories; + const paths = nodeModulesPaths(from, {moduleDirectory}); + if (paths[paths.length - 1] === undefined) { + // circumvent node-resolve bug that adds `undefined` as last item. + paths.pop(); + } + this._modulePathCache.set(from, paths); + return paths; + } + + getModuleID( + virtualMocks: Map, + from: string, + moduleName = '', + options?: ResolveModuleConfig, + ): string { + const stringifiedOptions = options ? JSON.stringify(options) : ''; + const key = from + path.delimiter + moduleName + stringifiedOptions; + const cachedModuleID = this._moduleIDCache.get(key); + if (cachedModuleID) { + return cachedModuleID; + } + + const moduleType = this._getModuleType(moduleName); + const absolutePath = this._getAbsolutePath( + virtualMocks, + from, + moduleName, + options, + ); + const mockPath = this._getMockPath(from, moduleName); + + const sep = path.delimiter; + const id = + moduleType + + sep + + (absolutePath ? absolutePath + sep : '') + + (mockPath ? mockPath + sep : '') + + (stringifiedOptions ? stringifiedOptions + sep : ''); + + this._moduleIDCache.set(key, id); + return id; + } + + async getModuleIDAsync( + virtualMocks: Map, + from: string, + moduleName = '', + options?: ResolveModuleConfig, + ): Promise { + 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; + } + + private _getModuleType(moduleName: string): 'node' | 'user' { + return this.isCoreModule(moduleName) ? 'node' : 'user'; + } + + private _getAbsolutePath( + virtualMocks: Map, + from: string, + moduleName: string, + options?: ResolveModuleConfig, + ): string | null { + if (this.isCoreModule(moduleName)) { + return moduleName; + } + if (moduleName.startsWith('data:')) { + return moduleName; + } + return this._isModuleResolved(from, moduleName) + ? this.getModule(moduleName) + : this._getVirtualMockPath(virtualMocks, from, moduleName, options); + } + + private async _getAbsolutePathAsync( + virtualMocks: Map, + from: string, + moduleName: string, + options?: ResolveModuleConfig, + ): Promise { + 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, + ); + } + + private _getMockPath(from: string, moduleName: string): string | null { + return !this.isCoreModule(moduleName) + ? this.getMockModule(from, moduleName) + : null; + } + + private async _getMockPathAsync( from: string, moduleName: string, ): Promise { @@ -354,59 +696,6 @@ export default class Resolver { ); } - private _setResolver(resolver?: string | null): void { - this._options.resolver = resolver; - } - - isCoreModule(moduleName: string): boolean { - return ( - this._options.hasCoreModules && - (isBuiltinModule(moduleName) || - (moduleName.startsWith('node:') && - isBuiltinModule(moduleName.slice('node:'.length)))) && - !this._isAliasModule(moduleName) - ); - } - - getModule(name: string): string | null { - return this._moduleMap.getModule( - name, - this._options.defaultPlatform, - this._supportsNativePlatform, - ); - } - - getModulePath(from: string, moduleName: string): string { - if (moduleName[0] !== '.' || path.isAbsolute(moduleName)) { - return moduleName; - } - return path.normalize(path.dirname(from) + '/' + moduleName); - } - - getPackage(name: string): string | null { - return this._moduleMap.getPackage( - name, - this._options.defaultPlatform, - this._supportsNativePlatform, - ); - } - - getModulePaths(from: string): Array { - const cachedModule = this._modulePathCache.get(from); - if (cachedModule) { - return cachedModule; - } - - const moduleDirectory = this._options.moduleDirectories; - const paths = nodeModulesPaths(from, {moduleDirectory}); - if (paths[paths.length - 1] === undefined) { - // circumvent node-resolve bug that adds `undefined` as last item. - paths.pop(); - } - this._modulePathCache.set(from, paths); - return paths; - } - resolveStubModuleName(from: string, moduleName: string): string | null { const dirname = path.dirname(from); @@ -521,295 +810,6 @@ export default class Resolver { } return null; } - - getMockModule(from: string, name: string): string | null { - const mock = this._moduleMap.getMockModule(name); - if (mock) { - return mock; - } else { - const moduleName = this.resolveStubModuleName(from, name); - if (moduleName) { - return this.getModule(moduleName) || moduleName; - } - } - return null; - } - - async getMockModuleAsync(from: string, name: string): Promise { - 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; - } - - getModuleID( - virtualMocks: Map, - from: string, - _moduleName?: string, - options?: ResolveModuleConfig, - ): string { - const moduleName = _moduleName || ''; - - const stringifiedOptions = options ? JSON.stringify(options) : ''; - const key = from + path.delimiter + moduleName + stringifiedOptions; - const cachedModuleID = this._moduleIDCache.get(key); - if (cachedModuleID) { - return cachedModuleID; - } - - const moduleType = this._getModuleType(moduleName); - const absolutePath = this._getAbsolutePath( - virtualMocks, - from, - moduleName, - options, - ); - const mockPath = this._getMockPath(from, moduleName); - - const sep = path.delimiter; - const id = - moduleType + - sep + - (absolutePath ? absolutePath + sep : '') + - (mockPath ? mockPath + sep : '') + - (stringifiedOptions ? stringifiedOptions + sep : ''); - - this._moduleIDCache.set(key, id); - return id; - } - - async getModuleIDAsync( - virtualMocks: Map, - from: string, - moduleName = '', - options?: ResolveModuleConfig, - ): Promise { - 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; - } - - resolveModuleFromDirIfExists( - dirname: string, - moduleName: string, - options?: ResolveModuleConfig, - ): string | null { - 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 = (name: string, throwIfNotFound = false) => { - if (this.isCoreModule(name)) { - return name; - } - - return Resolver.findNodeModule(name, { - basedir: dirname, - conditions: options?.conditions, - extensions, - moduleDirectory, - paths, - resolver: this._options.resolver, - rootDir: this._options.rootDir, - throwIfNotFound, - }); - }; - - if (!skipResolution) { - module = 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(hasteModulePath) || - require.resolve(hasteModulePath); - this._moduleNameCache.set(key, resolvedModule); - return resolvedModule; - } - } catch {} - - return null; - } - - async resolveModuleFromDirIfExistsAsync( - dirname: string, - moduleName: string, - options?: ResolveModuleConfig, - ): Promise { - 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: string, throwIfNotFound = false) => { - if (this.isCoreModule(name)) { - return name; - } - - return await Resolver.findNodeModuleAsync(name, { - basedir: dirname, - conditions: 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 = - (await resolveNodeModule(hasteModulePath)) || - // QUESTION: should this be async? - require.resolve(hasteModulePath); - this._moduleNameCache.set(key, resolvedModule); - return resolvedModule; - } - } catch {} - - return null; - } - - resolveModule( - from: string, - moduleName: string, - options?: ResolveModuleConfig, - ): string { - const dirname = path.dirname(from); - const module = - this.resolveStubModuleName(from, moduleName) || - this.resolveModuleFromDirIfExists(dirname, moduleName, options); - if (module) return module; - - // 5. Throw an error if the module could not be found. `resolve.sync` only - // produces an error based on the dirname but we have the actual current - // module name available. - this._throwModNotFoundError(from, moduleName); - } - - async resolveModuleAsync( - from: string, - moduleName: string, - options?: ResolveModuleConfig, - ): Promise { - 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); - } } const createNoMappedModuleFoundError = ( From 9867dc2d064cbddee5d337113ec8c8a010647819 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Tue, 22 Feb 2022 11:24:48 +0100 Subject: [PATCH 34/39] changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a456eb437b5d..36f4cb581d81 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ - `[jest-mock]` Improve `isMockFunction` to infer types of passed function ([#12442](https://github.com/facebook/jest/pull/12442)) - `[jest-resolve]` [**BREAKING**] Add support for `package.json` `exports` ([#11961](https://github.com/facebook/jest/pull/11961), [#12373](https://github.com/facebook/jest/pull/12373)) - `[jest-resolve, jest-runtime]` Add support for `data:` URI import and mock ([#12392](https://github.com/facebook/jest/pull/12392)) +- `[jest-resolve, jest-runtime]` Add support for async resolver ([#11540](https://github.com/facebook/jest/pull/11540)) - `[@jest/schemas]` New module for JSON schemas for Jest's config ([#12384](https://github.com/facebook/jest/pull/12384)) - `[jest-worker]` [**BREAKING**] Allow only absolute `workerPath` ([#12343](https://github.com/facebook/jest/pull/12343)) - `[pretty-format]` New `maxWidth` parameter ([#12402](https://github.com/facebook/jest/pull/12402)) From b2b7d4b8d96a187cde17a503e3671b7378477019 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Tue, 22 Feb 2022 11:31:17 +0100 Subject: [PATCH 35/39] use in runtime --- packages/jest-runtime/src/index.ts | 41 ++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/packages/jest-runtime/src/index.ts b/packages/jest-runtime/src/index.ts index 363eef8f90cb..aee13f52934c 100644 --- a/packages/jest-runtime/src/index.ts +++ b/packages/jest-runtime/src/index.ts @@ -63,7 +63,7 @@ export type {Context} from './types'; const esmIsAvailable = typeof SourceTextModule === 'function'; -const dataURIregex = +const dataURIRegex = /^data:(?text\/javascript|application\/json|application\/wasm)(?:;(?charset=utf-8|base64))?,(?.*)$/; interface JestGlobals extends Global.TestFrameworkGlobals { @@ -531,16 +531,17 @@ export default class Runtime { return module; } - private resolveModule( + private async resolveModule( specifier: string, referencingIdentifier: string, context: VMContext, - ): Promise | T | void { + ): Promise { if (this.isTornDown) { this._logFormattedReferenceError( 'You are trying to `import` a file after the Jest environment has been torn down.', ); process.exitCode = 1; + // @ts-expect-error - exiting return; } @@ -574,7 +575,7 @@ export default class Runtime { return fromCache; } - const match = specifier.match(dataURIregex); + const match = specifier.match(dataURIRegex); if (!match || !match.groups) { throw new Error('Invalid data URI'); @@ -653,7 +654,7 @@ export default class Runtime { return this.importMock(referencingIdentifier, path, context); } - const resolved = this._resolveModule(referencingIdentifier, path, { + const resolved = await this._resolveModule(referencingIdentifier, path, { conditions: this.esmConditions, }); @@ -713,7 +714,7 @@ export default class Runtime { const [path, query] = (moduleName ?? '').split('?'); - const modulePath = this._resolveModule(from, path, { + const modulePath = await this._resolveModule(from, path, { conditions: this.esmConditions, }); @@ -757,7 +758,7 @@ export default class Runtime { moduleName: string, context: VMContext, ): Promise { - const moduleID = this._resolver.getModuleID( + const moduleID = await this._resolver.getModuleIDAsync( this._virtualModuleMocks, from, moduleName, @@ -808,7 +809,7 @@ export default class Runtime { const namedExports = new Set(exports); reexports.forEach(reexport => { - const resolved = this._resolveModule(modulePath, reexport, { + const resolved = this._resolveCjsModule(modulePath, reexport, { conditions: this.esmConditions, }); @@ -861,7 +862,7 @@ export default class Runtime { } if (!modulePath) { - modulePath = this._resolveModule(from, moduleName, { + modulePath = this._resolveCjsModule(from, moduleName, { conditions: this.cjsConditions, }); } @@ -977,7 +978,9 @@ export default class Runtime { let modulePath = this._resolver.getMockModule(from, moduleName) || - this._resolveModule(from, moduleName, {conditions: this.cjsConditions}); + this._resolveCjsModule(from, moduleName, { + conditions: this.cjsConditions, + }); let isManualMock = manualMockOrStub && @@ -1300,7 +1303,7 @@ export default class Runtime { this.isTornDown = true; } - private _resolveModule( + private _resolveCjsModule( from: string, to: string | undefined, options?: ResolveModuleConfig, @@ -1308,6 +1311,14 @@ export default class Runtime { return to ? this._resolver.resolveModule(from, to, options) : from; } + private _resolveModule( + from: string, + to: string | undefined, + options?: ResolveModuleConfig, + ) { + return to ? this._resolver.resolveModuleAsync(from, to, options) : from; + } + private _requireResolve( from: string, moduleName?: string, @@ -1353,7 +1364,7 @@ export default class Runtime { } try { - return this._resolveModule(from, moduleName, { + return this._resolveCjsModule(from, moduleName, { conditions: this.cjsConditions, }); } catch (err) { @@ -1720,7 +1731,9 @@ export default class Runtime { private _generateMock(from: string, moduleName: string) { const modulePath = this._resolver.resolveStubModuleName(from, moduleName) || - this._resolveModule(from, moduleName, {conditions: this.cjsConditions}); + this._resolveCjsModule(from, moduleName, { + conditions: this.cjsConditions, + }); if (!this._mockMetaDataCache.has(modulePath)) { // This allows us to handle circular dependencies while generating an // automock @@ -1794,7 +1807,7 @@ export default class Runtime { let modulePath; try { - modulePath = this._resolveModule(from, moduleName, options); + modulePath = this._resolveCjsModule(from, moduleName, options); } catch (e) { const manualMock = this._resolver.getMockModule(from, moduleName); if (manualMock) { From 6595c16082bdfcfb9a3ca0ab255efaf3c72a17ba Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Tue, 22 Feb 2022 11:35:39 +0100 Subject: [PATCH 36/39] snaps --- e2e/__tests__/__snapshots__/moduleNameMapper.test.ts.snap | 4 ++-- .../__snapshots__/resolveNoFileExtensions.test.ts.snap | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/e2e/__tests__/__snapshots__/moduleNameMapper.test.ts.snap b/e2e/__tests__/__snapshots__/moduleNameMapper.test.ts.snap index 825a160c4b3c..b4739ed4f2da 100644 --- a/e2e/__tests__/__snapshots__/moduleNameMapper.test.ts.snap +++ b/e2e/__tests__/__snapshots__/moduleNameMapper.test.ts.snap @@ -41,7 +41,7 @@ exports[`moduleNameMapper wrong array configuration 1`] = ` 12 | module.exports = () => 'test'; 13 | - at createNoMappedModuleFoundError (../../packages/jest-resolve/build/resolver.js:890:17) + at createNoMappedModuleFoundError (../../packages/jest-resolve/build/resolver.js:892:17) at Object.require (index.js:10:1)" `; @@ -70,6 +70,6 @@ exports[`moduleNameMapper wrong configuration 1`] = ` 12 | module.exports = () => 'test'; 13 | - at createNoMappedModuleFoundError (../../packages/jest-resolve/build/resolver.js:890:17) + at createNoMappedModuleFoundError (../../packages/jest-resolve/build/resolver.js:892:17) at Object.require (index.js:10:1)" `; diff --git a/e2e/__tests__/__snapshots__/resolveNoFileExtensions.test.ts.snap b/e2e/__tests__/__snapshots__/resolveNoFileExtensions.test.ts.snap index 12c619cac5a9..df9cf992fb34 100644 --- a/e2e/__tests__/__snapshots__/resolveNoFileExtensions.test.ts.snap +++ b/e2e/__tests__/__snapshots__/resolveNoFileExtensions.test.ts.snap @@ -37,6 +37,6 @@ exports[`show error message with matching files 1`] = ` | ^ 9 | - at Resolver._throwModNotFoundError (../../packages/jest-resolve/build/resolver.js:308:11) + at Resolver._throwModNotFoundError (../../packages/jest-resolve/build/resolver.js:486:11) at Object.require (index.js:8:18)" `; From 067bb556cd02a430242e2b372bb84ea50693702b Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Tue, 22 Feb 2022 12:20:53 +0100 Subject: [PATCH 37/39] validate custom resolver --- .../moduleNameMapper.test.ts.snap | 4 +- .../resolveNoFileExtensions.test.ts.snap | 2 +- packages/jest-resolve/src/defaultResolver.ts | 4 +- packages/jest-resolve/src/resolver.ts | 72 ++++++++++++++----- 4 files changed, 61 insertions(+), 21 deletions(-) diff --git a/e2e/__tests__/__snapshots__/moduleNameMapper.test.ts.snap b/e2e/__tests__/__snapshots__/moduleNameMapper.test.ts.snap index b4739ed4f2da..9ef544f4584a 100644 --- a/e2e/__tests__/__snapshots__/moduleNameMapper.test.ts.snap +++ b/e2e/__tests__/__snapshots__/moduleNameMapper.test.ts.snap @@ -41,7 +41,7 @@ exports[`moduleNameMapper wrong array configuration 1`] = ` 12 | module.exports = () => 'test'; 13 | - at createNoMappedModuleFoundError (../../packages/jest-resolve/build/resolver.js:892:17) + at createNoMappedModuleFoundError (../../packages/jest-resolve/build/resolver.js:897:17) at Object.require (index.js:10:1)" `; @@ -70,6 +70,6 @@ exports[`moduleNameMapper wrong configuration 1`] = ` 12 | module.exports = () => 'test'; 13 | - at createNoMappedModuleFoundError (../../packages/jest-resolve/build/resolver.js:892:17) + at createNoMappedModuleFoundError (../../packages/jest-resolve/build/resolver.js:897:17) at Object.require (index.js:10:1)" `; diff --git a/e2e/__tests__/__snapshots__/resolveNoFileExtensions.test.ts.snap b/e2e/__tests__/__snapshots__/resolveNoFileExtensions.test.ts.snap index df9cf992fb34..8ee95a4ed134 100644 --- a/e2e/__tests__/__snapshots__/resolveNoFileExtensions.test.ts.snap +++ b/e2e/__tests__/__snapshots__/resolveNoFileExtensions.test.ts.snap @@ -37,6 +37,6 @@ exports[`show error message with matching files 1`] = ` | ^ 9 | - at Resolver._throwModNotFoundError (../../packages/jest-resolve/build/resolver.js:486:11) + at Resolver._throwModNotFoundError (../../packages/jest-resolve/build/resolver.js:491:11) at Object.require (index.js:8:18)" `; diff --git a/packages/jest-resolve/src/defaultResolver.ts b/packages/jest-resolve/src/defaultResolver.ts index 12632faa9803..f20921cb6ff6 100644 --- a/packages/jest-resolve/src/defaultResolver.ts +++ b/packages/jest-resolve/src/defaultResolver.ts @@ -38,8 +38,8 @@ interface ResolverOptions { type UpstreamResolveOptionsWithConditions = UpstreamResolveOptions & Pick; -type SyncResolver = (path: string, options: ResolverOptions) => string; -type AsyncResolver = ( +export type SyncResolver = (path: string, options: ResolverOptions) => string; +export type AsyncResolver = ( path: string, options: ResolverOptions, ) => Promise; diff --git a/packages/jest-resolve/src/resolver.ts b/packages/jest-resolve/src/resolver.ts index de4135ebcdd0..b0ec60847963 100644 --- a/packages/jest-resolve/src/resolver.ts +++ b/packages/jest-resolve/src/resolver.ts @@ -14,7 +14,9 @@ import type {IModuleMap} from 'jest-haste-map'; import {tryRealpath} from 'jest-util'; import ModuleNotFoundError from './ModuleNotFoundError'; import defaultResolver, { + AsyncResolver, Resolver as ResolverInterface, + SyncResolver, } from './defaultResolver'; import {clearFsCache} from './fileWalkers'; import isBuiltinModule from './isBuiltinModule'; @@ -108,16 +110,12 @@ export default class Resolver { path: string, options: FindNodeModuleConfig, ): string | null { - // The resolver module could be a synchronous function, or an object with sync and/or async keys. - const resolverModule = options.resolver ? require(options.resolver) : null; - let resolver = defaultResolver; + const resolverModule = loadResolver(options.resolver); + let resolver: SyncResolver = defaultResolver; + if (typeof resolverModule === 'function') { resolver = resolverModule; - } else if ( - resolverModule && - typeof resolverModule === 'object' && - typeof resolverModule.sync === 'function' - ) { + } else if (typeof resolverModule.sync === 'function') { resolver = resolverModule.sync; } @@ -146,14 +144,23 @@ export default class Resolver { path: string, options: FindNodeModuleConfig, ): Promise { - // The resolver module could be a synchronous function, or an object with sync and/or async keys. - const resolverModule = options.resolver ? require(options.resolver) : null; - const resolver: ResolverInterface = - resolverModule && - typeof resolverModule === 'object' && - typeof resolverModule.async === 'function' - ? resolverModule.async - : defaultResolver; + const resolverModule = loadResolver(options.resolver); + let resolver: ResolverInterface = defaultResolver; + + if (typeof resolverModule === 'function') { + resolver = resolverModule; + } else if ( + typeof resolverModule.async === 'function' || + typeof resolverModule.sync === 'function' + ) { + const asyncOrSync = resolverModule.async || resolverModule.sync; + + if (asyncOrSync == null) { + throw new Error(`Unable to load resolver at ${options.resolver}`); + } + + resolver = asyncOrSync; + } const paths = options.paths; @@ -846,3 +853,36 @@ Please check your configuration for these entries: return error; }; + +type ResolverSyncObject = {sync: SyncResolver; async?: AsyncResolver}; +type ResolverAsyncObject = {sync?: SyncResolver; async: AsyncResolver}; +type ResolverObject = ResolverSyncObject | ResolverAsyncObject; + +function loadResolver( + resolver: string | undefined | null, +): SyncResolver | ResolverObject { + if (resolver == null) { + return defaultResolver; + } + + const loadedResolver = require(resolver); + + if (loadedResolver == null) { + throw new Error(`Resolver located at ${resolver} does not export anything`); + } + + if (typeof loadedResolver === 'function') { + return loadedResolver as SyncResolver; + } + + if ( + typeof loadedResolver === 'object' && + (loadedResolver.sync != null || loadedResolver.async != null) + ) { + return loadedResolver as ResolverObject; + } + + throw new Error( + `Resolver located at ${resolver} does not export a function or an object with "sync" and "async" props`, + ); +} From c6a27101d786a6e5f359c7e811065b56d1687f7f Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Tue, 22 Feb 2022 12:21:53 +0100 Subject: [PATCH 38/39] async should mock --- packages/jest-runtime/src/index.ts | 85 +++++++++++++++++++++++++++--- 1 file changed, 79 insertions(+), 6 deletions(-) diff --git a/packages/jest-runtime/src/index.ts b/packages/jest-runtime/src/index.ts index aee13f52934c..b36b69d55569 100644 --- a/packages/jest-runtime/src/index.ts +++ b/packages/jest-runtime/src/index.ts @@ -559,7 +559,7 @@ export default class Runtime { if (specifier.startsWith('data:')) { if ( - this._shouldMock( + await this._shouldMockModule( referencingIdentifier, specifier, this._explicitShouldMockModule, @@ -644,7 +644,7 @@ export default class Runtime { const [path, query] = specifier.split('?'); if ( - this._shouldMock( + await this._shouldMockModule( referencingIdentifier, path, this._explicitShouldMockModule, @@ -869,11 +869,10 @@ export default class Runtime { if (this.unstable_shouldLoadAsEsm(modulePath)) { // Node includes more info in the message - const error = new Error( + const error: NodeJS.ErrnoException = new Error( `Must use import to load ES Module: ${modulePath}`, ); - // @ts-expect-error: `code` is not defined error.code = 'ERR_REQUIRE_ESM'; throw error; @@ -1085,7 +1084,7 @@ export default class Runtime { try { if ( - this._shouldMock(from, moduleName, this._explicitShouldMock, { + this._shouldMockCjs(from, moduleName, this._explicitShouldMock, { conditions: this.cjsConditions, }) ) { @@ -1773,7 +1772,7 @@ export default class Runtime { ); } - private _shouldMock( + private _shouldMockCjs( from: string, moduleName: string, explicitShouldMock: Map, @@ -1844,6 +1843,80 @@ export default class Runtime { return true; } + private async _shouldMockModule( + from: string, + moduleName: string, + explicitShouldMock: Map, + options: ResolveModuleConfig, + ): Promise { + const moduleID = await this._resolver.getModuleIDAsync( + this._virtualMocks, + from, + moduleName, + options, + ); + const key = from + path.delimiter + moduleID; + + if (explicitShouldMock.has(moduleID)) { + // guaranteed by `has` above + return explicitShouldMock.get(moduleID)!; + } + + if ( + !this._shouldAutoMock || + this._resolver.isCoreModule(moduleName) || + this._shouldUnmockTransitiveDependenciesCache.get(key) + ) { + return false; + } + + if (this._shouldMockModuleCache.has(moduleID)) { + // guaranteed by `has` above + return this._shouldMockModuleCache.get(moduleID)!; + } + + let modulePath; + try { + modulePath = await this._resolveModule(from, moduleName, options); + } catch (e) { + const manualMock = await this._resolver.getMockModuleAsync( + from, + moduleName, + ); + if (manualMock) { + this._shouldMockModuleCache.set(moduleID, true); + return true; + } + throw e; + } + + if (this._unmockList && this._unmockList.test(modulePath)) { + this._shouldMockModuleCache.set(moduleID, false); + return false; + } + + // transitive unmocking for package managers that store flat packages (npm3) + const currentModuleID = await this._resolver.getModuleIDAsync( + this._virtualMocks, + from, + undefined, + options, + ); + if ( + this._transitiveShouldMock.get(currentModuleID) === false || + (from.includes(NODE_MODULES) && + modulePath.includes(NODE_MODULES) && + ((this._unmockList && this._unmockList.test(from)) || + explicitShouldMock.get(currentModuleID) === false)) + ) { + this._transitiveShouldMock.set(moduleID, false); + this._shouldUnmockTransitiveDependenciesCache.set(key, true); + return false; + } + this._shouldMockModuleCache.set(moduleID, true); + return true; + } + private _createRequireImplementation( from: InitialModule, options?: InternalModuleOptions, From 6c4db087d10e3833091b214a80075657f969b5f7 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Tue, 22 Feb 2022 12:22:10 +0100 Subject: [PATCH 39/39] e2e --- .../__snapshots__/resolveAsync.test.ts.snap | 9 +++++++ e2e/__tests__/resolveAsync.test.ts | 25 +++++++++++++++++++ .../__tests__/resolveAsync.test.js | 12 +++++++++ e2e/resolve-async/package.json | 8 ++++++ e2e/resolve-async/resolver.cjs | 24 ++++++++++++++++++ e2e/resolve-async/some-other-file.js | 8 ++++++ .../__tests__/resolveGetPaths.test.js | 1 - .../__tests__/resolveWithPaths.test.js | 1 - 8 files changed, 86 insertions(+), 2 deletions(-) create mode 100644 e2e/__tests__/__snapshots__/resolveAsync.test.ts.snap create mode 100644 e2e/__tests__/resolveAsync.test.ts create mode 100644 e2e/resolve-async/__tests__/resolveAsync.test.js create mode 100644 e2e/resolve-async/package.json create mode 100644 e2e/resolve-async/resolver.cjs create mode 100644 e2e/resolve-async/some-other-file.js diff --git a/e2e/__tests__/__snapshots__/resolveAsync.test.ts.snap b/e2e/__tests__/__snapshots__/resolveAsync.test.ts.snap new file mode 100644 index 000000000000..8b2c6acb57fd --- /dev/null +++ b/e2e/__tests__/__snapshots__/resolveAsync.test.ts.snap @@ -0,0 +1,9 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`on node >=12.16.0 runs test with native ESM 1`] = ` +"Test Suites: 1 passed, 1 total +Tests: 1 passed, 1 total +Snapshots: 0 total +Time: <> +Ran all test suites." +`; diff --git a/e2e/__tests__/resolveAsync.test.ts b/e2e/__tests__/resolveAsync.test.ts new file mode 100644 index 000000000000..a5588985f235 --- /dev/null +++ b/e2e/__tests__/resolveAsync.test.ts @@ -0,0 +1,25 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import {onNodeVersions} from '@jest/test-utils'; +import {extractSummary} from '../Utils'; +import runJest from '../runJest'; + +// The versions where vm.Module exists and commonjs with "exports" is not broken +onNodeVersions('>=12.16.0', () => { + test('runs test with native ESM', () => { + const {exitCode, stderr, stdout} = runJest('resolve-async', [], { + nodeOptions: '--experimental-vm-modules --no-warnings', + }); + + const {summary} = extractSummary(stderr); + + expect(summary).toMatchSnapshot(); + expect(stdout).toBe(''); + expect(exitCode).toBe(0); + }); +}); diff --git a/e2e/resolve-async/__tests__/resolveAsync.test.js b/e2e/resolve-async/__tests__/resolveAsync.test.js new file mode 100644 index 000000000000..4a7928b21a2b --- /dev/null +++ b/e2e/resolve-async/__tests__/resolveAsync.test.js @@ -0,0 +1,12 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import greeting from '../some-file'; + +test('async resolver resolves to correct file', () => { + expect(greeting).toEqual('Hello from mapped file!!'); +}); diff --git a/e2e/resolve-async/package.json b/e2e/resolve-async/package.json new file mode 100644 index 000000000000..50e870688cbf --- /dev/null +++ b/e2e/resolve-async/package.json @@ -0,0 +1,8 @@ +{ + "type": "module", + "jest": { + "resolver": "/resolver.cjs", + "transform": { + } + } +} diff --git a/e2e/resolve-async/resolver.cjs b/e2e/resolve-async/resolver.cjs new file mode 100644 index 000000000000..1c0af2d913fa --- /dev/null +++ b/e2e/resolve-async/resolver.cjs @@ -0,0 +1,24 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +'use strict'; + +const {promisify} = require('util'); + +const wait = promisify(setTimeout); + +module.exports = { + async: async (request, opts) => { + await wait(500); + + if (request === '../some-file') { + request = '../some-other-file'; + } + + return opts.defaultResolver(request, opts); + }, +}; diff --git a/e2e/resolve-async/some-other-file.js b/e2e/resolve-async/some-other-file.js new file mode 100644 index 000000000000..bcd5de5faab8 --- /dev/null +++ b/e2e/resolve-async/some-other-file.js @@ -0,0 +1,8 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +export default 'Hello from mapped file!!'; diff --git a/e2e/resolve-get-paths/__tests__/resolveGetPaths.test.js b/e2e/resolve-get-paths/__tests__/resolveGetPaths.test.js index ee40aa4153a2..86aa66cce0de 100644 --- a/e2e/resolve-get-paths/__tests__/resolveGetPaths.test.js +++ b/e2e/resolve-get-paths/__tests__/resolveGetPaths.test.js @@ -4,7 +4,6 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ -'use strict'; import {resolve} from 'path'; diff --git a/e2e/resolve-with-paths/__tests__/resolveWithPaths.test.js b/e2e/resolve-with-paths/__tests__/resolveWithPaths.test.js index f6891ea61742..5e7950a90503 100644 --- a/e2e/resolve-with-paths/__tests__/resolveWithPaths.test.js +++ b/e2e/resolve-with-paths/__tests__/resolveWithPaths.test.js @@ -4,7 +4,6 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ -'use strict'; import {resolve} from 'path';