diff --git a/CHANGELOG.md b/CHANGELOG.md index 24ac70583279..5502e9857119 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -62,6 +62,7 @@ - `[jest-resolve]` [**BREAKING**] Migrate to ESM ([#10688](https://github.com/facebook/jest/pull/10688)) - `[jest-resolve-dependencies]` [**BREAKING**] Migrate to ESM ([#10876](https://github.com/facebook/jest/pull/10876)) - `[jest-mock]` [**BREAKING**] Migrate to ESM ([#10887](https://github.com/facebook/jest/pull/10887)) +- `[jest-resolve, jest-runtime]` [**BREAKING**] Use `Map`s instead of objects for all cached resources ([#10968](https://github.com/facebook/jest/pull/10968)) - `[jest-runner]` [**BREAKING**] Migrate to ESM ([#10900](https://github.com/facebook/jest/pull/10900)) - `[jest-runtime]` [**BREAKING**] Remove deprecated and unnused `getSourceMapInfo` from Runtime ([#9969](https://github.com/facebook/jest/pull/9969)) - `[jest-util]` No longer checking `enumerable` when adding `process.domain` ([#10862](https://github.com/facebook/jest/pull/10862)) diff --git a/packages/jest-repl/src/cli/runtime-cli.ts b/packages/jest-repl/src/cli/runtime-cli.ts index 1d60dbd7e6e9..be0eda03c153 100644 --- a/packages/jest-repl/src/cli/runtime-cli.ts +++ b/packages/jest-repl/src/cli/runtime-cli.ts @@ -91,7 +91,7 @@ export async function run( config, environment, hasteMap.resolver, - undefined, + new Map(), undefined, filePath, ); diff --git a/packages/jest-resolve/src/index.ts b/packages/jest-resolve/src/index.ts index bc5610a21431..76a28c9e4902 100644 --- a/packages/jest-resolve/src/index.ts +++ b/packages/jest-resolve/src/index.ts @@ -31,9 +31,6 @@ type FindNodeModuleConfig = { throwIfNotFound?: boolean; }; -// TODO: replace with a Map in Jest 27 -type BooleanObject = Record; - export type ResolveModuleConfig = { skipNodeResolution?: boolean; paths?: Array; @@ -314,7 +311,7 @@ class Resolver { } getModuleID( - virtualMocks: BooleanObject, + virtualMocks: Map, from: Config.Path, _moduleName?: string, ): string { @@ -346,7 +343,7 @@ class Resolver { } private _getAbsolutePath( - virtualMocks: BooleanObject, + virtualMocks: Map, from: Config.Path, moduleName: string, ): Config.Path | null { @@ -368,12 +365,12 @@ class Resolver { } private _getVirtualMockPath( - virtualMocks: BooleanObject, + virtualMocks: Map, from: Config.Path, moduleName: string, ): Config.Path { const virtualMockPath = this.getModulePath(from, moduleName); - return virtualMocks[virtualMockPath] + return virtualMocks.get(virtualMockPath) ? virtualMockPath : moduleName ? this.resolveModule(from, moduleName) diff --git a/packages/jest-runner/src/runTest.ts b/packages/jest-runner/src/runTest.ts index ab7d90a96d35..d68c7cfb7677 100644 --- a/packages/jest-runner/src/runTest.ts +++ b/packages/jest-runner/src/runTest.ts @@ -146,7 +146,7 @@ async function runTestInternal( ? new LeakDetector(environment) : null; - const cacheFS = {[path]: testSource}; + const cacheFS = new Map([[path, testSource]]); setGlobal(environment.global, 'console', testConsole); const runtime = new Runtime( @@ -182,8 +182,7 @@ async function runTestInternal( environment: 'node', handleUncaughtExceptions: false, retrieveSourceMap: source => { - const sourceMaps = runtime.getSourceMaps(); - const sourceMapSource = sourceMaps && sourceMaps[source]; + const sourceMapSource = runtime.getSourceMaps()?.get(source); if (sourceMapSource) { try { diff --git a/packages/jest-runtime/src/__mocks__/createRuntime.js b/packages/jest-runtime/src/__mocks__/createRuntime.js index 478359de5c2c..ff87494d2212 100644 --- a/packages/jest-runtime/src/__mocks__/createRuntime.js +++ b/packages/jest-runtime/src/__mocks__/createRuntime.js @@ -35,27 +35,14 @@ const setupModuleNameMapper = (config, rootDir) => { }; const setupTransform = (config, rootDir) => { - if (config && config.transform) { + if (config?.transform) { const transform = config.transform; return Object.keys(transform).map(regex => [ regex, path.resolve(rootDir, transform[regex]), ]); } - return [ - [ - '^.+\\.[jt]sx?$', - path.resolve( - __dirname, - '..', - '..', - '..', - 'babel-jest', - 'build', - 'index.js', - ), - ], - ]; + return [['^.+\\.[jt]sx?$', require.resolve('babel-jest')]]; }; module.exports = async function createRuntime(filename, config) { @@ -67,32 +54,29 @@ module.exports = async function createRuntime(filename, config) { const moduleNameMapper = setupModuleNameMapper(config, rootDir); const transform = setupTransform(config, rootDir); - config = makeProjectConfig( - { - cacheDirectory: getCacheDirectory(), - cwd: path.resolve(__dirname, '..', '..', '..', '..'), - haste: { - hasteImplModulePath: path.resolve( - __dirname, - '..', - '..', - '..', - 'jest-haste-map', - 'src', - '__tests__', - 'haste_impl.js', - ), - }, - moduleDirectories: ['node_modules'], - moduleFileExtensions: ['js', 'json', 'jsx', 'ts', 'tsx', 'node'], - name: 'Runtime-' + filename.replace(/\W/, '-') + '.tests', - rootDir, - ...config, - moduleNameMapper, - transform, + config = makeProjectConfig({ + cacheDirectory: getCacheDirectory(), + cwd: path.resolve(__dirname, '..', '..', '..', '..'), + haste: { + hasteImplModulePath: path.resolve( + __dirname, + '..', + '..', + '..', + 'jest-haste-map', + 'src', + '__tests__', + 'haste_impl.js', + ), }, - {}, - ); + moduleDirectories: ['node_modules'], + moduleFileExtensions: ['js', 'jsx', 'ts', 'tsx', 'json', 'node'], + name: 'Runtime-' + filename.replace(/\W/, '-') + '.tests', + rootDir, + ...config, + moduleNameMapper, + transform, + }); if (!config.roots.length) { config.roots = [config.rootDir]; @@ -110,7 +94,7 @@ module.exports = async function createRuntime(filename, config) { config, environment, Runtime.createResolver(config, hasteMap.moduleMap), - undefined, + new Map(), undefined, filename, ); diff --git a/packages/jest-runtime/src/__tests__/__snapshots__/runtime_require_module_no_ext.test.js.snap b/packages/jest-runtime/src/__tests__/__snapshots__/runtime_require_module_no_ext.test.js.snap index ae7b862935a6..ef24320408a0 100644 --- a/packages/jest-runtime/src/__tests__/__snapshots__/runtime_require_module_no_ext.test.js.snap +++ b/packages/jest-runtime/src/__tests__/__snapshots__/runtime_require_module_no_ext.test.js.snap @@ -6,7 +6,7 @@ exports[`Runtime requireModule with no extension throws error pointing out file However, Jest was able to find: './RegularModuleWithWrongExt.txt' -You might want to include a file extension in your import, or update your 'moduleFileExtensions', which is currently ['js', 'json', 'jsx', 'ts', 'tsx', 'node']. +You might want to include a file extension in your import, or update your 'moduleFileExtensions', which is currently ['js', 'jsx', 'ts', 'tsx', 'json', 'node']. See https://jestjs.io/docs/en/configuration#modulefileextensions-arraystring" `; diff --git a/packages/jest-runtime/src/index.ts b/packages/jest-runtime/src/index.ts index a3f63c6f853f..ecd7fb6e3aac 100644 --- a/packages/jest-runtime/src/index.ts +++ b/packages/jest-runtime/src/index.ts @@ -102,18 +102,6 @@ type ResolveOptions = Parameters[1] & { [JEST_RESOLVE_OUTSIDE_VM_OPTION]?: true; }; -type StringMap = Map; -type BooleanMap = Map; - -const fromEntries: typeof Object.fromEntries = - Object.fromEntries ?? - function fromEntries(iterable: Iterable<[string, T]>) { - return [...iterable].reduce>((obj, [key, val]) => { - obj[key] = val; - return obj; - }, {}); - }; - const testTimeoutSymbol = Symbol.for('TEST_TIMEOUT_SYMBOL'); const retryTimesSymbol = Symbol.for('RETRY_TIMES'); @@ -154,12 +142,12 @@ const supportsTopLevelAwait = })(); export default class Runtime { - private readonly _cacheFS: StringMap; + private readonly _cacheFS: Map; private readonly _config: Config.ProjectConfig; private readonly _coverageOptions: ShouldInstrumentOptions; private _currentlyExecutingModulePath: string; private readonly _environment: JestEnvironment; - private readonly _explicitShouldMock: BooleanMap; + private readonly _explicitShouldMock: Map; private _fakeTimersImplementation: | LegacyFakeTimers | ModernFakeTimers @@ -181,16 +169,19 @@ export default class Runtime { private readonly _testPath: Config.Path | undefined; private readonly _resolver: Resolver; private _shouldAutoMock: boolean; - private readonly _shouldMockModuleCache: BooleanMap; - private readonly _shouldUnmockTransitiveDependenciesCache: BooleanMap; - private readonly _sourceMapRegistry: StringMap; + private readonly _shouldMockModuleCache: Map; + private readonly _shouldUnmockTransitiveDependenciesCache: Map< + string, + boolean + >; + private readonly _sourceMapRegistry: Map; private readonly _scriptTransformer: ScriptTransformer; private readonly _fileTransforms: Map; private _v8CoverageInstrumenter: CoverageInstrumenter | undefined; private _v8CoverageResult: V8Coverage | undefined; - private readonly _transitiveShouldMock: BooleanMap; + private readonly _transitiveShouldMock: Map; private _unmockList: RegExp | undefined; - private readonly _virtualMocks: BooleanMap; + private readonly _virtualMocks: Map; private _moduleImplementation?: typeof nativeModule.Module; private readonly jestObjectCaches: Map; private jestGlobals?: JestGlobals; @@ -199,12 +190,12 @@ export default class Runtime { config: Config.ProjectConfig, environment: JestEnvironment, resolver: Resolver, - cacheFS: Record = {}, + cacheFS: Map, coverageOptions?: ShouldInstrumentOptions, // TODO: Make mandatory in Jest 27 testPath?: Config.Path, ) { - this._cacheFS = new Map(Object.entries(cacheFS)); + this._cacheFS = cacheFS; this._config = config; this._coverageOptions = coverageOptions || { changedFiles: undefined, @@ -222,8 +213,11 @@ export default class Runtime { this._mainModule = null; this._mockFactories = new Map(); this._mockRegistry = new Map(); - // during setup, this cannot be null (and it's fine to explode if it is) - this._moduleMocker = this._environment.moduleMocker!; + invariant( + this._environment.moduleMocker, + '`moduleMocker` must be set on an environment when created', + ); + this._moduleMocker = this._environment.moduleMocker; this._isolatedModuleRegistry = null; this._isolatedMockRegistry = null; this._moduleRegistry = new Map(); @@ -256,10 +250,12 @@ export default class Runtime { } if (config.automock) { - const virtualMocks = fromEntries(this._virtualMocks); config.setupFiles.forEach(filePath => { - if (filePath && filePath.includes(NODE_MODULES)) { - const moduleID = this._resolver.getModuleID(virtualMocks, filePath); + if (filePath.includes(NODE_MODULES)) { + const moduleID = this._resolver.getModuleID( + this._virtualMocks, + filePath, + ); this._transitiveShouldMock.set(moduleID, false); } }); @@ -575,7 +571,7 @@ export default class Runtime { isRequireActual?: boolean | null, ): T { const moduleID = this._resolver.getModuleID( - fromEntries(this._virtualMocks), + this._virtualMocks, from, moduleName, ); @@ -670,7 +666,7 @@ export default class Runtime { requireMock(from: Config.Path, moduleName: string): T { const moduleID = this._resolver.getModuleID( - fromEntries(this._virtualMocks), + this._virtualMocks, from, moduleName, ); @@ -922,7 +918,7 @@ export default class Runtime { } getSourceMaps(): SourceMapRegistry { - return fromEntries(this._sourceMapRegistry); + return this._sourceMapRegistry; } setMock( @@ -937,7 +933,7 @@ export default class Runtime { this._virtualMocks.set(mockPath, true); } const moduleID = this._resolver.getModuleID( - fromEntries(this._virtualMocks), + this._virtualMocks, from, moduleName, ); @@ -1365,7 +1361,7 @@ export default class Runtime { private _shouldMock(from: Config.Path, moduleName: string): boolean { const explicitShouldMock = this._explicitShouldMock; const moduleID = this._resolver.getModuleID( - fromEntries(this._virtualMocks), + this._virtualMocks, from, moduleName, ); @@ -1408,7 +1404,7 @@ export default class Runtime { // transitive unmocking for package managers that store flat packages (npm3) const currentModuleID = this._resolver.getModuleID( - fromEntries(this._virtualMocks), + this._virtualMocks, from, ); if ( @@ -1493,7 +1489,7 @@ export default class Runtime { }; const unmock = (moduleName: string) => { const moduleID = this._resolver.getModuleID( - fromEntries(this._virtualMocks), + this._virtualMocks, from, moduleName, ); @@ -1502,7 +1498,7 @@ export default class Runtime { }; const deepUnmock = (moduleName: string) => { const moduleID = this._resolver.getModuleID( - fromEntries(this._virtualMocks), + this._virtualMocks, from, moduleName, ); @@ -1516,7 +1512,7 @@ export default class Runtime { } const moduleID = this._resolver.getModuleID( - fromEntries(this._virtualMocks), + this._virtualMocks, from, moduleName, ); diff --git a/packages/jest-source-map/src/__tests__/getCallsite.test.ts b/packages/jest-source-map/src/__tests__/getCallsite.test.ts index ddb7903e0777..1ff293058e83 100644 --- a/packages/jest-source-map/src/__tests__/getCallsite.test.ts +++ b/packages/jest-source-map/src/__tests__/getCallsite.test.ts @@ -31,7 +31,7 @@ describe('getCallsite', () => { throw new Error('Mock error'); }); - const site = getCallsite(0, {[__filename]: 'mockedSourceMapFile'}); + const site = getCallsite(0, new Map([[__filename, 'mockedSourceMapFile']])); expect(site.getFileName()).toEqual(__filename); expect(site.getColumnNumber()).toEqual(expect.any(Number)); @@ -59,7 +59,7 @@ describe('getCallsite', () => { } }; - const site = getCallsite(0, {[__filename]: 'mockedSourceMapFile'}); + const site = getCallsite(0, new Map([[__filename, 'mockedSourceMapFile']])); expect(site.getFileName()).toEqual(__filename); expect(site.getColumnNumber()).toEqual(sourceMapColumn); diff --git a/packages/jest-source-map/src/getCallsite.ts b/packages/jest-source-map/src/getCallsite.ts index 1af2fe6973fa..5a0f74a5bdfb 100644 --- a/packages/jest-source-map/src/getCallsite.ts +++ b/packages/jest-source-map/src/getCallsite.ts @@ -52,7 +52,7 @@ export default ( ): callsites.CallSite => { const levelAfterThisCall = level + 1; const stack = callsites()[levelAfterThisCall]; - const sourceMapFileName = sourceMaps && sourceMaps[stack.getFileName() || '']; + const sourceMapFileName = sourceMaps?.get(stack.getFileName() || ''); if (sourceMapFileName) { try { diff --git a/packages/jest-source-map/src/types.ts b/packages/jest-source-map/src/types.ts index 71104c44f895..8ea2f235f8f8 100644 --- a/packages/jest-source-map/src/types.ts +++ b/packages/jest-source-map/src/types.ts @@ -5,4 +5,4 @@ * LICENSE file in the root directory of this source tree. */ -export type SourceMapRegistry = Record; +export type SourceMapRegistry = Map;