diff --git a/packages/jest-runtime/src/__tests__/runtime_internal_module.test.js b/packages/jest-runtime/src/__tests__/runtime_internal_module.test.js index aeca406d3ea2..71293e89c33b 100644 --- a/packages/jest-runtime/src/__tests__/runtime_internal_module.test.js +++ b/packages/jest-runtime/src/__tests__/runtime_internal_module.test.js @@ -32,7 +32,6 @@ describe('Runtime', () => { runtime.requireModule(modulePath); }).toThrow(new Error('preprocessor must not run.')); }); - it('loads internal modules without applying transforms', async () => { const runtime = await createRuntime(__filename, { transform: {'\\.js$': './test_preprocessor'}, @@ -56,7 +55,6 @@ describe('Runtime', () => { const exports = runtime.requireModule(modulePath); expect(exports).toEqual({foo: 'foo'}); }); - it('loads internal JSON modules without applying transforms', async () => { const runtime = await createRuntime(__filename, { transform: {'\\.json$': './test_json_preprocessor'}, @@ -68,5 +66,29 @@ describe('Runtime', () => { const exports = runtime.requireInternalModule(modulePath); expect(exports).toEqual({foo: 'bar'}); }); + + const OPTIMIZED_MODULE_EXAMPLE = 'chalk'; + it('loads modules normally even if on the optimization list', () => + createRuntime(__filename).then(runtime => { + const modulePath = path.resolve( + path.dirname(runtime.__mockRootPath), + 'require-by-name.js', + ); + const requireByName = runtime.requireModule(modulePath); + expect(requireByName(OPTIMIZED_MODULE_EXAMPLE)).not.toBe( + require(OPTIMIZED_MODULE_EXAMPLE), + ); + })); + it('loads internal modules from outside if on the optimization list', () => + createRuntime(__filename).then(runtime => { + const modulePath = path.resolve( + path.dirname(runtime.__mockRootPath), + 'require-by-name.js', + ); + const requireByName = runtime.requireInternalModule(modulePath); + expect(requireByName(OPTIMIZED_MODULE_EXAMPLE)).toBe( + require(OPTIMIZED_MODULE_EXAMPLE), + ); + })); }); }); diff --git a/packages/jest-runtime/src/__tests__/test_root/require-by-name.js b/packages/jest-runtime/src/__tests__/test_root/require-by-name.js new file mode 100644 index 000000000000..793879bcfa07 --- /dev/null +++ b/packages/jest-runtime/src/__tests__/test_root/require-by-name.js @@ -0,0 +1,10 @@ +/** + * 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 = moduleName => require(moduleName); diff --git a/packages/jest-runtime/src/index.ts b/packages/jest-runtime/src/index.ts index a4992f045f4f..8a71b2fd8646 100644 --- a/packages/jest-runtime/src/index.ts +++ b/packages/jest-runtime/src/index.ts @@ -95,6 +95,16 @@ const defaultTransformOptions: InternalModuleOptions = { type InitialModule = Omit; type ModuleRegistry = Map; +// These are modules that we know +// * are safe to require from the outside (not stateful, not prone to errors passing in instances from different realms), and +// * take sufficiently long to require to warrant an optimization. +// When required from the outside, they use the worker's require cache and are thus +// only loaded once per worker, not once per test file. +// Note that this only applies when they are required in an internal context; +// users who require one of these modules in their tests will still get the module from inside the VM. +// Prefer listing a module here only if it is impractical to use the jest-resolve-outside-vm-option where it is required, +// e.g. because there are many require sites spread across the dependency graph. +const INTERNAL_MODULE_REQUIRE_OUTSIDE_OPTIMIZED_MODULES = new Set(['chalk']); const JEST_RESOLVE_OUTSIDE_VM_OPTION = Symbol.for( 'jest-resolve-outside-vm-option', ); @@ -660,9 +670,12 @@ export default class Runtime { requireInternalModule(from: Config.Path, to?: string): T { if (to) { + if (INTERNAL_MODULE_REQUIRE_OUTSIDE_OPTIMIZED_MODULES.has(to)) { + return nativeModule.createRequire(from)(to); + } const outsideJestVmPath = decodePossibleOutsideJestVmPath(to); if (outsideJestVmPath) { - return require(outsideJestVmPath); + return nativeModule.createRequire(from)(outsideJestVmPath); } }