From 917f7ac6453d95de95ace8875c98dd91c70cb4d2 Mon Sep 17 00:00:00 2001 From: Tim Seckinger Date: Sat, 14 Nov 2020 15:57:49 +0000 Subject: [PATCH 1/2] make requiring from outside the Jest VM easier w/ a Babel plugin and rename the Symbol to align with other Symbols that we have and use in Babel plugins --- babel.config.js | 1 + .../__tests__/runtime_require_resolve.test.ts | 2 +- .../test_root/resolve_and_require_outside.js | 2 +- packages/jest-runtime/src/index.ts | 8 +-- packages/jest-types/src/Global.ts | 4 ++ .../babel-plugin-jest-require-outside-vm.js | 53 +++++++++++++++++++ 6 files changed, 64 insertions(+), 6 deletions(-) create mode 100644 scripts/babel-plugin-jest-require-outside-vm.js diff --git a/babel.config.js b/babel.config.js index b0dc52a720ba..d0ead878ed16 100644 --- a/babel.config.js +++ b/babel.config.js @@ -56,6 +56,7 @@ module.exports = { ['@babel/plugin-transform-modules-commonjs', {allowTopLevelThis: true}], '@babel/plugin-transform-strict-mode', '@babel/plugin-proposal-class-properties', + require.resolve('./scripts/babel-plugin-jest-require-outside-vm'), ], presets: [ [ diff --git a/packages/jest-runtime/src/__tests__/runtime_require_resolve.test.ts b/packages/jest-runtime/src/__tests__/runtime_require_resolve.test.ts index 77e35813eceb..493fc115501e 100644 --- a/packages/jest-runtime/src/__tests__/runtime_require_resolve.test.ts +++ b/packages/jest-runtime/src/__tests__/runtime_require_resolve.test.ts @@ -44,7 +44,7 @@ describe('Runtime require.resolve', () => { ); }); - describe('with the OUTSIDE_JEST_VM_RESOLVE_OPTION', () => { + describe('with the jest-resolve-outside-vm-option', () => { it('forwards to the real Node require in an internal context', async () => { const runtime = await createRuntime(__filename); const module = runtime.requireInternalModule( diff --git a/packages/jest-runtime/src/__tests__/test_root/resolve_and_require_outside.js b/packages/jest-runtime/src/__tests__/test_root/resolve_and_require_outside.js index e39ba2527380..05c7c2cd6187 100644 --- a/packages/jest-runtime/src/__tests__/test_root/resolve_and_require_outside.js +++ b/packages/jest-runtime/src/__tests__/test_root/resolve_and_require_outside.js @@ -8,7 +8,7 @@ 'use strict'; const resolved = require.resolve('./create_require_module', { - [Symbol.for('OUTSIDE_JEST_VM_RESOLVE_OPTION')]: true, + [Symbol.for('jest-resolve-outside-vm-option')]: true, }); if (typeof resolved !== 'string') { throw new Error('require.resolve not spec-compliant: must return a string'); diff --git a/packages/jest-runtime/src/index.ts b/packages/jest-runtime/src/index.ts index 65376a7c7403..9252124b4e6f 100644 --- a/packages/jest-runtime/src/index.ts +++ b/packages/jest-runtime/src/index.ts @@ -92,11 +92,11 @@ const defaultTransformOptions: InternalModuleOptions = { type InitialModule = Omit; type ModuleRegistry = Map; -const OUTSIDE_JEST_VM_RESOLVE_OPTION = Symbol.for( - 'OUTSIDE_JEST_VM_RESOLVE_OPTION', +const JEST_RESOLVE_OUTSIDE_VM_OPTION = Symbol.for( + 'jest-resolve-outside-vm-option', ); type ResolveOptions = Parameters[1] & { - [OUTSIDE_JEST_VM_RESOLVE_OPTION]?: true; + [JEST_RESOLVE_OUTSIDE_VM_OPTION]?: true; }; type StringMap = Map; @@ -1402,7 +1402,7 @@ export default class Runtime { resolveOptions, ); if ( - resolveOptions?.[OUTSIDE_JEST_VM_RESOLVE_OPTION] && + resolveOptions?.[JEST_RESOLVE_OUTSIDE_VM_OPTION] && options?.isInternalModule ) { return createOutsideJestVmPath(resolved); diff --git a/packages/jest-types/src/Global.ts b/packages/jest-types/src/Global.ts index 411e61191d23..1918122ca665 100644 --- a/packages/jest-types/src/Global.ts +++ b/packages/jest-types/src/Global.ts @@ -119,3 +119,7 @@ export interface Global Omit { [extras: string]: unknown; } + +declare global { + const requireOutside: typeof require; +} diff --git a/scripts/babel-plugin-jest-require-outside-vm.js b/scripts/babel-plugin-jest-require-outside-vm.js new file mode 100644 index 000000000000..c2fe79cf26b9 --- /dev/null +++ b/scripts/babel-plugin-jest-require-outside-vm.js @@ -0,0 +1,53 @@ +/** + * 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 assert = require('assert'); + +/* +Replace + +requireOutside('package') + +with + +require(require.resolve('package', { + [Symbol.for('jest-resolve-outside-vm-option')]: true, +})); +*/ + +const REQUIRE_OUTSIDE_FUNCTION_NAME = 'requireOutside'; + +module.exports = ({template, types: t}) => { + const replacement = template(` + require(require.resolve(IMPORT_PATH, { + [Symbol.for('jest-resolve-outside-vm-option')]: true, + })); + `); + return { + name: 'jest-require-outside-vm', + visitor: { + CallExpression(path) { + const {callee, arguments: args} = path.node; + if ( + t.isIdentifier(callee) && + callee.name === REQUIRE_OUTSIDE_FUNCTION_NAME && + !path.scope.hasBinding(REQUIRE_OUTSIDE_FUNCTION_NAME) + ) { + assert.strictEqual( + args.length, + 1, + 'requireOutside must be called with exactly one argument', + ); + const importPath = args[0]; + path.replaceWith(replacement({IMPORT_PATH: importPath})); + } + }, + }, + }; +}; From c4149835bbe688cdd56365f4f94e317a4430552a Mon Sep 17 00:00:00 2001 From: Tim Seckinger Date: Sat, 14 Nov 2020 17:15:34 +0000 Subject: [PATCH 2/2] PR comments --- CHANGELOG.md | 1 + packages/jest-types/src/Global.ts | 4 ---- scripts/babel-plugin-jest-require-outside-vm.js | 2 +- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 25f7b3a3b2e5..b513ec69015e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ - `[*]` [**BREAKING**] Only support Node LTS releases and Node 15 ([#10685](https://github.com/facebook/jest/pull/10685)) - `[*]` [**BREAKING**] Add `exports` field to all `package.json`s ([#9921](https://github.com/facebook/jest/pull/9921)) +- `[*]` Make it easier for Jest's packages to use the VM escape hatch ([#10824](https://github.com/facebook/jest/pull/10824)) - `[jest-config]` [**BREAKING**] Remove `enabledTestsMap` config, use `filter` instead ([#10787](https://github.com/facebook/jest/pull/10787)) - `[jest-resolve]` [**BREAKING**] Migrate to ESM ([#10688](https://github.com/facebook/jest/pull/10688)) diff --git a/packages/jest-types/src/Global.ts b/packages/jest-types/src/Global.ts index 1918122ca665..411e61191d23 100644 --- a/packages/jest-types/src/Global.ts +++ b/packages/jest-types/src/Global.ts @@ -119,7 +119,3 @@ export interface Global Omit { [extras: string]: unknown; } - -declare global { - const requireOutside: typeof require; -} diff --git a/scripts/babel-plugin-jest-require-outside-vm.js b/scripts/babel-plugin-jest-require-outside-vm.js index c2fe79cf26b9..07bd0eab714c 100644 --- a/scripts/babel-plugin-jest-require-outside-vm.js +++ b/scripts/babel-plugin-jest-require-outside-vm.js @@ -26,7 +26,7 @@ const REQUIRE_OUTSIDE_FUNCTION_NAME = 'requireOutside'; module.exports = ({template, types: t}) => { const replacement = template(` require(require.resolve(IMPORT_PATH, { - [Symbol.for('jest-resolve-outside-vm-option')]: true, + [(global['jest-symbol-do-not-touch'] || global.Symbol).for('jest-resolve-outside-vm-option')]: true, })); `); return {