diff --git a/CHANGELOG.md b/CHANGELOG.md index 839115831aea..de22b98fd823 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ### Features +- `[jest-runtime]` Populate `require.cache` ([#9841](https://github.com/facebook/jest/pull/9841)) + ### Fixes ### Chore & Maintenance diff --git a/packages/jest-runtime/src/__tests__/runtime_require_cache.test.js b/packages/jest-runtime/src/__tests__/runtime_require_cache.test.js new file mode 100644 index 000000000000..ec06833c9d04 --- /dev/null +++ b/packages/jest-runtime/src/__tests__/runtime_require_cache.test.js @@ -0,0 +1,41 @@ +/** + * 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'; + +let createRuntime; + +describe('Runtime require.cache', () => { + beforeEach(() => { + createRuntime = require('createRuntime'); + }); + + it('require.cache returns loaded module list as native Nodejs require does', () => + createRuntime(__filename).then(runtime => { + const regularModule = runtime.requireModule( + runtime.__mockRootPath, + 'RegularModule', + ).module; + + expect(regularModule.require.cache[regularModule.id]).toBe(regularModule); + })); + + it('require.cache is tolerant readonly', () => + createRuntime(__filename).then(runtime => { + const regularModule = runtime.requireModule( + runtime.__mockRootPath, + 'RegularModule', + ).module; + + delete regularModule.require.cache[regularModule.id]; + expect(regularModule.require.cache[regularModule.id]).toBe(regularModule); + + regularModule.require.cache[regularModule.id] = 'something'; + expect(regularModule.require.cache[regularModule.id]).toBe(regularModule); + })); +}); diff --git a/packages/jest-runtime/src/index.ts b/packages/jest-runtime/src/index.ts index 4a7883aa260b..d516e903d1e6 100644 --- a/packages/jest-runtime/src/index.ts +++ b/packages/jest-runtime/src/index.ts @@ -80,6 +80,8 @@ type ResolveOptions = Parameters[1]; type BooleanObject = Record; type CacheFS = {[path: string]: string}; +type RequireCache = {[key: string]: Module}; + namespace Runtime { export type Context = JestContext; // ditch this export when moving to esm - for now we need it for to avoid faulty type elision @@ -1270,11 +1272,25 @@ class Runtime { this, from.filename, )) as LocalModuleRequire; - moduleRequire.cache = Object.create(null); moduleRequire.extensions = Object.create(null); moduleRequire.requireActual = this.requireActual.bind(this, from.filename); moduleRequire.requireMock = this.requireMock.bind(this, from.filename); moduleRequire.resolve = resolve; + moduleRequire.cache = (() => { + const notPermittedMethod = () => { + console.warn('`require.cache` modification is not permitted'); + return true; + }; + return new Proxy(Object.create(null), { + defineProperty: notPermittedMethod, + deleteProperty: notPermittedMethod, + get: (_target, key) => + typeof key === 'string' ? this._moduleRegistry.get(key) : undefined, + has: (_target, key) => + typeof key === 'string' && this._moduleRegistry.has(key), + set: notPermittedMethod, + }); + })(); Object.defineProperty(moduleRequire, 'main', { enumerable: true,