diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f22279851bf..8856d5fa0e1f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ - `[jest-diff]` Add `changeColor` and `patchColor` options ([#8911](https://github.com/facebook/jest/pull/8911)) - `[jest-diff]` Add `trailingSpaceFormatter` option and replace cyan with `commonColor` ([#8927](https://github.com/facebook/jest/pull/8927)) - `[jest-diff]` Add `firstOrLastEmptyLineReplacement` option and export 3 `diffLines` functions ([#8955](https://github.com/facebook/jest/pull/8955)) +- `[jest-environment]` Support compiling a function, rather than using a module wrapper ([#9252](https://github.com/facebook/jest/pull/9252)) - `[jest-environment-jsdom]` Add `fakeTimersLolex` ([#8925](https://github.com/facebook/jest/pull/8925)) - `[jest-environment-node]` Add `fakeTimersLolex` ([#8925](https://github.com/facebook/jest/pull/8925)) - `[jest-environment-node]` Add `queueMicrotask` ([#9140](https://github.com/facebook/jest/pull/9140)) @@ -26,6 +27,7 @@ - `[jest-reporters]` Export utils for path formatting ([#9162](https://github.com/facebook/jest/pull/9162)) - `[jest-runner]` Warn if a worker had to be force exited ([#8206](https://github.com/facebook/jest/pull/8206)) - `[jest-runtime]` [**BREAKING**] Do not export `ScriptTransformer` - it can be imported from `@jest/transform` instead ([#9256](https://github.com/facebook/jest/pull/9256)) +- `[jest-runtime]` Support compiling a function, rather than using a module wrapper ([#9252](https://github.com/facebook/jest/pull/9252)) - `[jest-snapshot]` Display change counts in annotation lines ([#8982](https://github.com/facebook/jest/pull/8982)) - `[jest-snapshot]` [**BREAKING**] Improve report when the matcher has properties ([#9104](https://github.com/facebook/jest/pull/9104)) - `[jest-snapshot]` Improve colors when snapshots are updatable ([#9132](https://github.com/facebook/jest/pull/9132)) diff --git a/packages/jest-environment-node/src/index.ts b/packages/jest-environment-node/src/index.ts index 897c3734c784..d7887908fd8d 100644 --- a/packages/jest-environment-node/src/index.ts +++ b/packages/jest-environment-node/src/index.ts @@ -5,7 +5,13 @@ * LICENSE file in the root directory of this source tree. */ -import {Context, Script, createContext, runInContext} from 'vm'; +import { + Context, + Script, + compileFunction, + createContext, + runInContext, +} from 'vm'; import {Config, Global} from '@jest/types'; import {ModuleMocker} from 'jest-mock'; import {installCommonGlobals} from 'jest-util'; @@ -110,6 +116,20 @@ class NodeEnvironment implements JestEnvironment { } return null; } + + compileFunction(code: string, params: Array, filename: string) { + if (this.context) { + return compileFunction(code, params, { + filename, + parsingContext: this.context, + }) as any; + } + return null; + } +} + +if (typeof compileFunction !== 'function') { + delete NodeEnvironment.prototype.compileFunction; } export = NodeEnvironment; diff --git a/packages/jest-environment/src/index.ts b/packages/jest-environment/src/index.ts index d28dba5dda70..5f265aba0b65 100644 --- a/packages/jest-environment/src/index.ts +++ b/packages/jest-environment/src/index.ts @@ -44,6 +44,11 @@ export declare class JestEnvironment { fakeTimersLolex: LolexFakeTimers | null; moduleMocker: jestMock.ModuleMocker | null; runScript(script: Script): T | null; + compileFunction?( + code: string, + params: Array, + filename: string, + ): T | null; setup(): Promise; teardown(): Promise; handleTestEvent?(event: Circus.Event, state: Circus.State): void; diff --git a/packages/jest-runtime/src/index.ts b/packages/jest-runtime/src/index.ts index d33f5570e9fe..a082739ba7fb 100644 --- a/packages/jest-runtime/src/index.ts +++ b/packages/jest-runtime/src/index.ts @@ -730,11 +730,33 @@ class Runtime { } } - const script = this.createScriptFromCode(transformedFile.code, filename); + let compiledFunction: ModuleWrapper | null; + + if (typeof this._environment.compileFunction === 'function') { + try { + compiledFunction = this._environment.compileFunction( + transformedFile.code, + this.constructInjectedModuleParameters(), + filename, + ); + } catch (e) { + throw handlePotentialSyntaxError(e); + } + } else { + const script = this.createScriptFromCode(transformedFile.code, filename); - const runScript = this._environment.runScript(script); + const runScript = this._environment.runScript( + script, + ); - if (runScript === null) { + if (runScript === null) { + compiledFunction = null; + } else { + compiledFunction = runScript[EVAL_RESULT_VARIABLE]; + } + } + + if (compiledFunction === null) { this._logFormattedReferenceError( 'You are trying to `import` a file after the Jest environment has been torn down.', ); @@ -742,8 +764,7 @@ class Runtime { return; } - //Wrapper - runScript[EVAL_RESULT_VARIABLE].call( + compiledFunction.call( localModule.exports, localModule as NodeModule, // module object localModule.exports, // module exports @@ -1107,7 +1128,19 @@ class Runtime { } private wrapCodeInModuleWrapper(content: string) { - const args = [ + const args = this.constructInjectedModuleParameters(); + + return ( + '({"' + + EVAL_RESULT_VARIABLE + + `":function(${args.join(',')}){` + + content + + '\n}});' + ); + } + + private constructInjectedModuleParameters() { + return [ 'module', 'exports', 'require', @@ -1117,14 +1150,6 @@ class Runtime { 'jest', ...this._config.extraGlobals, ]; - - return ( - '({"' + - EVAL_RESULT_VARIABLE + - `":function(${args.join(',')}){` + - content + - '\n}});' - ); } } diff --git a/packages/jest-transform/src/enhanceUnexpectedTokenMessage.ts b/packages/jest-transform/src/enhanceUnexpectedTokenMessage.ts index 69a0ac2a2660..d5e3b60111bb 100644 --- a/packages/jest-transform/src/enhanceUnexpectedTokenMessage.ts +++ b/packages/jest-transform/src/enhanceUnexpectedTokenMessage.ts @@ -17,7 +17,8 @@ export default function handlePotentialSyntaxError( } if ( - e instanceof SyntaxError && + // `instanceof` might come from the wrong context + e.name === 'SyntaxError' && (e.message.includes('Unexpected token') || e.message.includes('Cannot use import')) && !e.message.includes(' expected')