Skip to content

Commit

Permalink
wrap code in runtime rather than transformer
Browse files Browse the repository at this point in the history
  • Loading branch information
SimenB committed Dec 1, 2019
1 parent 367e507 commit 4965e77
Show file tree
Hide file tree
Showing 9 changed files with 72 additions and 136 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Expand Up @@ -30,7 +30,7 @@
- `[jest-snapshot]` Improve colors when snapshots are updatable ([#9132](https://github.com/facebook/jest/pull/9132))
- `[jest-snapshot]` Ignore indentation for most serialized objects ([#9203](https://github.com/facebook/jest/pull/9203))
- `[jest-transform]` Create `createTranspilingRequire` function for easy transpiling modules ([#9194](https://github.com/facebook/jest/pull/9194))
- `[jest-transform]` [**BREAKING**] Do not automatically pass arguments to the script wrapper ([#9253](https://github.com/facebook/jest/pull/9253))
- `[jest-transform]` [**BREAKING**] Return transformed code as a string, do not wrap in `vm.Script` ([#9253](https://github.com/facebook/jest/pull/9253))
- `[@jest/test-result]` Create method to create empty `TestResult` ([#8867](https://github.com/facebook/jest/pull/8867))
- `[jest-worker]` [**BREAKING**] Return a promise from `end()`, resolving with the information whether workers exited gracefully ([#8206](https://github.com/facebook/jest/pull/8206))
- `[jest-reporters]` Transform file paths into hyperlinks ([#8980](https://github.com/facebook/jest/pull/8980))
Expand Down
1 change: 0 additions & 1 deletion packages/jest-environment/package.json
Expand Up @@ -11,7 +11,6 @@
"types": "build/index.d.ts",
"dependencies": {
"@jest/fake-timers": "^24.9.0",
"@jest/transform": "^24.9.0",
"@jest/types": "^24.9.0",
"jest-mock": "^24.9.0"
},
Expand Down
5 changes: 1 addition & 4 deletions packages/jest-environment/src/index.ts
Expand Up @@ -8,7 +8,6 @@
import {Script} from 'vm';
import {Circus, Config, Global} from '@jest/types';
import jestMock = require('jest-mock');
import {ScriptTransformer} from '@jest/transform';
import {
JestFakeTimers as LegacyFakeTimers,
LolexFakeTimers,
Expand Down Expand Up @@ -44,9 +43,7 @@ export declare class JestEnvironment {
fakeTimers: LegacyFakeTimers<unknown> | null;
fakeTimersLolex: LolexFakeTimers | null;
moduleMocker: jestMock.ModuleMocker | null;
runScript(
script: Script,
): {[ScriptTransformer.EVAL_RESULT_VARIABLE]: ModuleWrapper} | null;
runScript<T = unknown>(script: Script): T | null;
setup(): Promise<void>;
teardown(): Promise<void>;
handleTestEvent?(event: Circus.Event, state: Circus.State): void;
Expand Down
1 change: 0 additions & 1 deletion packages/jest-environment/tsconfig.json
Expand Up @@ -6,7 +6,6 @@
},
"references": [
{"path": "../jest-fake-timers"},
{"path": "../jest-transform"},
{"path": "../jest-types"},
{"path": "../jest-util"}
]
Expand Down
28 changes: 25 additions & 3 deletions packages/jest-runtime/src/index.ts
Expand Up @@ -6,12 +6,14 @@
*/

import * as path from 'path';
import {Script} from 'vm';
import {Config} from '@jest/types';
import {
Jest,
JestEnvironment,
LocalModuleRequire,
Module,
ModuleWrapper,
} from '@jest/environment';
import {SourceMapRegistry} from '@jest/source-map';
import jestMock = require('jest-mock');
Expand Down Expand Up @@ -78,6 +80,10 @@ const getModuleNameMapper = (config: Config.ProjectConfig) => {

const unmockRegExpCache = new WeakMap();

const EVAL_RESULT_VARIABLE = 'Object.<anonymous>';

type RunScriptEvalResult = {[EVAL_RESULT_VARIABLE]: ModuleWrapper};

/* eslint-disable-next-line no-redeclare */
class Runtime {
static ScriptTransformer: typeof ScriptTransformer;
Expand Down Expand Up @@ -489,7 +495,6 @@ class Runtime {
collectCoverage: this._coverageOptions.collectCoverage,
collectCoverageFrom: this._coverageOptions.collectCoverageFrom,
collectCoverageOnlyFrom: this._coverageOptions.collectCoverageOnlyFrom,
moduleArguments: this.constructInjectedModuleArguments(),
};
}

Expand Down Expand Up @@ -726,7 +731,14 @@ class Runtime {
}
}

const runScript = this._environment.runScript(transformedFile.script);
const script = new Script(this.wrap(transformedFile.code), {
displayErrors: true,
filename: this._resolver.isCoreModule(filename)
? `jest-nodejs-core-${filename}`
: filename,
});

const runScript = this._environment.runScript<RunScriptEvalResult>(script);

if (runScript === null) {
this._logFormattedReferenceError(
Expand All @@ -737,7 +749,7 @@ class Runtime {
}

//Wrapper
runScript[ScriptTransformer.EVAL_RESULT_VARIABLE].call(
runScript[EVAL_RESULT_VARIABLE].call(
localModule.exports,
localModule as NodeModule, // module object
localModule.exports, // module exports
Expand Down Expand Up @@ -1087,6 +1099,16 @@ class Runtime {
);
}

private wrap(content: string) {
return (
'({"' +
EVAL_RESULT_VARIABLE +
`":function(${this.constructInjectedModuleArguments().join(',')}){` +
content +
'\n}});'
);
}

private constructInjectedModuleArguments() {
return [
'module',
Expand Down
41 changes: 6 additions & 35 deletions packages/jest-transform/src/ScriptTransformer.ts
Expand Up @@ -7,7 +7,6 @@

import {createHash} from 'crypto';
import * as path from 'path';
import {Script} from 'vm';
import {Config} from '@jest/types';
import {createDirectory, interopRequireDefault, isPromise} from 'jest-util';
import * as fs from 'graceful-fs';
Expand Down Expand Up @@ -60,7 +59,6 @@ async function waitForPromiseWithCleanup(
}

export default class ScriptTransformer {
static EVAL_RESULT_VARIABLE: 'Object.<anonymous>';
private _cache: ProjectCache;
private _config: Config.ProjectConfig;
private _transformCache: Map<Config.Path, Transformer>;
Expand Down Expand Up @@ -347,12 +345,11 @@ export default class ScriptTransformer {
): TransformResult {
const isInternalModule = !!(options && options.isInternalModule);
const isCoreModule = !!(options && options.isCoreModule);
const moduleArguments = (options && options.moduleArguments) || [];
const content = stripShebang(
fileSource || fs.readFileSync(filename, 'utf8'),
);

let scriptContent: string;
let code = content;
let sourceMapPath: string | null = null;
let mapCoverage = false;

Expand All @@ -369,20 +366,14 @@ export default class ScriptTransformer {
instrument,
);

scriptContent = wrap(transformedSource.code, moduleArguments);
code = transformedSource.code;
sourceMapPath = transformedSource.sourceMapPath;
mapCoverage = transformedSource.mapCoverage;
} else {
scriptContent = wrap(content, moduleArguments);
}

return {
code,
mapCoverage,
script: new Script(scriptContent, {
displayErrors: true,
filename: isCoreModule ? 'jest-nodejs-core-' + filename : filename,
}),
scriptContent,
sourceMapPath,
};
} catch (e) {
Expand Down Expand Up @@ -413,7 +404,7 @@ export default class ScriptTransformer {

if (!options.isCoreModule) {
instrument = shouldInstrument(filename, options, this._config);
scriptCacheKey = getScriptCacheKey(filename, instrument, options);
scriptCacheKey = getScriptCacheKey(filename, instrument);
const result = this._cache.transformedFiles.get(scriptCacheKey);
if (result) {
return result;
Expand Down Expand Up @@ -667,19 +658,9 @@ const readCacheFile = (cachePath: Config.Path): string | null => {
return fileData;
};

const getScriptCacheKey = (
filename: Config.Path,
instrument: boolean,
options: Options,
) => {
const getScriptCacheKey = (filename: Config.Path, instrument: boolean) => {
const mtime = fs.statSync(filename).mtime;
return (
filename +
'_' +
mtime.getTime() +
(instrument ? '_instrumented' : '') +
(options.moduleArguments ? options.moduleArguments.join('') : '')
);
return filename + '_' + mtime.getTime() + (instrument ? '_instrumented' : '');
};

const calcIgnorePatternRegExp = (config: Config.ProjectConfig) => {
Expand Down Expand Up @@ -709,13 +690,3 @@ const calcTransformRegExp = (config: Config.ProjectConfig) => {

return transformRegexp;
};

const wrap = (content: string, moduleArguments: Array<string>) =>
'({"' +
ScriptTransformer.EVAL_RESULT_VARIABLE +
`":function(${moduleArguments.join(',')}){` +
content +
'\n}});';

// TODO: Can this be added to the static property?
ScriptTransformer.EVAL_RESULT_VARIABLE = 'Object.<anonymous>';
Expand Up @@ -75,7 +75,7 @@ Object {
`;

exports[`ScriptTransformer transforms a file properly 1`] = `
({"Object.<anonymous>":function(){/* istanbul ignore next */
/* istanbul ignore next */
function cov_25u22311x4() {
var path = "/fruits/banana.js";
var hash = "4be0f6184160be573fc43f7c2a5877c28b7ce249";
Expand Down Expand Up @@ -122,11 +122,10 @@ function cov_25u22311x4() {
cov_25u22311x4().s[0]++;
module.exports = "banana";
}});
`;

exports[`ScriptTransformer transforms a file properly 2`] = `
({"Object.<anonymous>":function(){/* istanbul ignore next */
/* istanbul ignore next */
function cov_23yvu8etmu() {
var path = "/fruits/kiwi.js";
var hash = "7705dd5fcfbc884dcea7062944cfb8cc5d141d1a";
Expand Down Expand Up @@ -217,48 +216,33 @@ module.exports = () => {
cov_23yvu8etmu().s[1]++;
return "kiwi";
};
}});
`;

exports[`ScriptTransformer transforms a file properly 3`] = `
({"Object.<anonymous>":function(){module.exports = "banana";
}});
`;

exports[`ScriptTransformer uses multiple preprocessors 1`] = `
({"Object.<anonymous>":function(){const TRANSFORMED = {
const TRANSFORMED = {
filename: '/fruits/banana.js',
script: 'module.exports = "banana";',
config: '{"automock":false,"browser":false,"cache":true,"cacheDirectory":"/cache/","clearMocks":false,"coveragePathIgnorePatterns":[],"cwd":"/test_root_dir/","detectLeaks":false,"detectOpenHandles":false,"errorOnDeprecated":false,"extraGlobals":[],"filter":null,"forceCoverageMatch":[],"globalSetup":null,"globalTeardown":null,"globals":{},"haste":{"providesModuleNodeModules":[]},"moduleDirectories":[],"moduleFileExtensions":["js"],"moduleLoader":"/test_module_loader_path","moduleNameMapper":[],"modulePathIgnorePatterns":[],"modulePaths":[],"name":"test","prettierPath":"prettier","resetMocks":false,"resetModules":false,"resolver":null,"restoreMocks":false,"rootDir":"/","roots":[],"runner":"jest-runner","setupFiles":[],"setupFilesAfterEnv":[],"skipFilter":false,"skipNodeResolution":false,"snapshotResolver":null,"snapshotSerializers":[],"testEnvironment":"node","testEnvironmentOptions":{},"testLocationInResults":false,"testMatch":[],"testPathIgnorePatterns":[],"testRegex":["\\\\.test\\\\.js$"],"testRunner":"jest-jasmine2","testURL":"http://localhost","timers":"real","transform":[["^.+\\\\.js$","test_preprocessor"],["^.+\\\\.css$","css-preprocessor"]],"transformIgnorePatterns":["/node_modules/"],"unmockedModulePathPatterns":null,"watchPathIgnorePatterns":[]}',
};
}});
`;

exports[`ScriptTransformer uses multiple preprocessors 2`] = `
({"Object.<anonymous>":function(){module.exports = {
module.exports = {
filename: /styles/App.css,
rawFirstLine: root {,
};
}});
`;
exports[`ScriptTransformer uses multiple preprocessors 3`] = `
({"Object.<anonymous>":function(){module.exports = "react";
}});
`;
exports[`ScriptTransformer uses multiple preprocessors 3`] = `module.exports = "react";`;
exports[`ScriptTransformer uses the supplied preprocessor 1`] = `
({"Object.<anonymous>":function(){const TRANSFORMED = {
const TRANSFORMED = {
filename: '/fruits/banana.js',
script: 'module.exports = "banana";',
config: '{"automock":false,"browser":false,"cache":true,"cacheDirectory":"/cache/","clearMocks":false,"coveragePathIgnorePatterns":[],"cwd":"/test_root_dir/","detectLeaks":false,"detectOpenHandles":false,"errorOnDeprecated":false,"extraGlobals":[],"filter":null,"forceCoverageMatch":[],"globalSetup":null,"globalTeardown":null,"globals":{},"haste":{"providesModuleNodeModules":[]},"moduleDirectories":[],"moduleFileExtensions":["js"],"moduleLoader":"/test_module_loader_path","moduleNameMapper":[],"modulePathIgnorePatterns":[],"modulePaths":[],"name":"test","prettierPath":"prettier","resetMocks":false,"resetModules":false,"resolver":null,"restoreMocks":false,"rootDir":"/","roots":[],"runner":"jest-runner","setupFiles":[],"setupFilesAfterEnv":[],"skipFilter":false,"skipNodeResolution":false,"snapshotResolver":null,"snapshotSerializers":[],"testEnvironment":"node","testEnvironmentOptions":{},"testLocationInResults":false,"testMatch":[],"testPathIgnorePatterns":[],"testRegex":["\\\\.test\\\\.js$"],"testRunner":"jest-jasmine2","testURL":"http://localhost","timers":"real","transform":[["^.+\\\\.js$","test_preprocessor"]],"transformIgnorePatterns":["/node_modules/"],"unmockedModulePathPatterns":null,"watchPathIgnorePatterns":[]}',
};
}});
`;
exports[`ScriptTransformer uses the supplied preprocessor 2`] = `
({"Object.<anonymous>":function(){module.exports = "react";
}});
`;
exports[`ScriptTransformer uses the supplied preprocessor 2`] = `module.exports = "react";`;
exports[`ScriptTransformer warns of unparseable inlined source maps from the preprocessor 1`] = `jest-transform: The source map produced for the file /fruits/banana.js by preprocessor-with-sourcemaps was invalid. Proceeding without source mapping for that file.`;

0 comments on commit 4965e77

Please sign in to comment.