Skip to content

Commit eb2eaee

Browse files
committed
Use script elements to evaluate scripts instead of the vm module
1 parent 4506693 commit eb2eaee

File tree

7 files changed

+60
-20
lines changed

7 files changed

+60
-20
lines changed

src/HasteModuleLoader/HasteModuleLoader.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ Loader.prototype._execModule = function(moduleObj) {
234234
this._isCurrentlyExecutingManualMock = modulePath;
235235

236236
utils.runContentWithLocalBindings(
237-
this._environment.runSourceText.bind(this._environment),
237+
this._environment,
238238
moduleContent,
239239
modulePath,
240240
moduleLocalBindings

src/HasteModuleLoader/__tests__/HasteModuleLoader-currentTestPath-test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,4 @@ describe('nodeHasteModuleLoader', function() {
1515
expect(jest.currentTestPath()).toMatch(/currentTestPath-test/);
1616
});
1717
});
18-
});
18+
});

src/JSDomEnvironment.js

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99

1010
var FakeTimers = require('./lib/FakeTimers');
1111
var utils = require('./lib/utils');
12-
var vm = require('vm');
12+
13+
var USE_JSDOM_EVAL = false;
1314

1415
function JSDomEnvironment(config) {
1516
// We lazily require jsdom because it takes a good ~.5s to load.
@@ -19,7 +20,13 @@ function JSDomEnvironment(config) {
1920
// a workerpool parent), this is the best way to ensure we only spend time
2021
// require()ing this when necessary.
2122
var jsdom = require('./lib/jsdom-compat');
22-
this.document = jsdom.jsdom();
23+
this.document = jsdom.jsdom(/* markup */undefined, {
24+
resourceLoader: this._fetchExternalResource.bind(this),
25+
features: {
26+
FetchExternalResources: ['script'],
27+
ProcessExternalResources: ['script'],
28+
},
29+
});
2330
this.global = this.document.defaultView;
2431

2532
// Node's error-message stack size is limited at 10, but it's pretty useful to
@@ -94,17 +101,40 @@ JSDomEnvironment.prototype.dispose = function() {
94101
};
95102

96103
/**
97-
* Evaluates the given source text as if it were in a file with the given name
98-
* and returns the result.
104+
* Evaluates the given source text as if it were in a file with the given name.
105+
* This method returns nothing.
99106
*/
100107
JSDomEnvironment.prototype.runSourceText = function(sourceText, fileName) {
101-
// TODO: Stop using the private API and instead configure jsdom with a
102-
// resource loader and insert <script src="${filename}"> in the document. The
103-
// reason we use vm for now is because the script element technique is slow.
104-
return vm.runInContext(sourceText, this.document._ownerDocument._global, {
105-
filename: fileName,
106-
displayErrors: false,
107-
});
108+
if (!USE_JSDOM_EVAL) {
109+
var vm = require('vm');
110+
vm.runInContext(sourceText, this.document._ownerDocument._global, {
111+
filename: fileName,
112+
displayErrors: false,
113+
});
114+
return;
115+
}
116+
117+
// We evaluate code by inserting <script src="${filename}"> into the document
118+
// and using jsdom's resource loader to simulate serving the source code.
119+
this._scriptToServe = sourceText;
120+
121+
var scriptElement = this.document.createElement('script');
122+
scriptElement.src = fileName;
123+
124+
this.document.head.appendChild(scriptElement);
125+
this.document.head.removeChild(scriptElement);
126+
};
127+
128+
JSDomEnvironment.prototype._fetchExternalResource = function(
129+
resource,
130+
callback
131+
) {
132+
var content = this._scriptToServe;
133+
delete this._scriptToServe;
134+
if (content === null || content === undefined) {
135+
var error = new Error('Unable to find source for ' + resource.url.href);
136+
}
137+
callback(error, content);
108138
};
109139

110140
JSDomEnvironment.prototype.runWithRealTimers = function(cb) {

src/TestRunner.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -361,7 +361,7 @@ TestRunner.prototype.runTest = function(testFilePath) {
361361

362362
if (config.setupEnvScriptFile) {
363363
utils.runContentWithLocalBindings(
364-
env.runSourceText.bind(env),
364+
env,
365365
utils.readAndPreprocessFileContent(config.setupEnvScriptFile, config),
366366
config.setupEnvScriptFile,
367367
{

src/__mocks__/JSDomEnvironment.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ JSDomEnvironmentMock.mockImplementation(function(config) {
2020

2121
JSDomEnvironmentMock.prototype.runSourceText.mockImplementation(
2222
function(codeStr, fileName) {
23-
return vm.runInNewContext(codeStr, this.global, {
23+
vm.runInNewContext(codeStr, this.global, {
2424
filename: fileName,
2525
displayErrors: false,
2626
});

src/jasmineTestRunner/jasmineTestRunner.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ function jasmineTestRunner(config, environment, moduleLoader, testPath) {
204204
);
205205

206206
utils.runContentWithLocalBindings(
207-
environment.runSourceText.bind(environment),
207+
environment,
208208
setupScriptContent,
209209
config.setupTestFrameworkScriptFile,
210210
{

src/lib/utils.js

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ var DEFAULT_CONFIG_VALUES = {
3232
noHighlight: false,
3333
};
3434

35+
// This shows up in the stack trace when a test file throws an unhandled error
36+
// when evaluated. Node's require prints Object.<anonymous> when initializing
37+
// modules, so do the same here solely for visual consistency.
38+
var EVAL_RESULT_VARIABLE = 'Object.<anonymous>';
39+
3540
function _replaceRootDirTags(rootDir, config) {
3641
switch (typeof config) {
3742
case 'object':
@@ -361,21 +366,26 @@ function readAndPreprocessFileContent(filePath, config) {
361366
return cacheRec.content;
362367
}
363368

364-
function runContentWithLocalBindings(contextRunner, scriptContent, scriptPath,
369+
function runContentWithLocalBindings(environment, scriptContent, scriptPath,
365370
bindings) {
366371
var boundIdents = Object.keys(bindings);
367372
try {
368-
var wrapperFunc = contextRunner(
369-
'(function(' + boundIdents.join(',') + '){' +
373+
var wrapperScript = 'this["' + EVAL_RESULT_VARIABLE + '"] = ' +
374+
'function (' + boundIdents.join(',') + ') {' +
370375
scriptContent +
371-
'\n})',
376+
'\n};';
377+
environment.runSourceText(
378+
wrapperScript,
372379
scriptPath
373380
);
374381
} catch (e) {
375382
e.message = scriptPath + ': ' + e.message;
376383
throw e;
377384
}
378385

386+
var wrapperFunc = environment.global[EVAL_RESULT_VARIABLE];
387+
delete environment.global[EVAL_RESULT_VARIABLE];
388+
379389
var bindingValues = boundIdents.map(function(ident) {
380390
return bindings[ident];
381391
});

0 commit comments

Comments
 (0)