diff --git a/packages/babel-core/test/fixtures/plugins/multiple-definition-evaluation/exec.js b/packages/babel-core/test/fixtures/plugins/multiple-definition-evaluation/exec.js
index 522d97637c..2d8f08b6c6 100644
--- a/packages/babel-core/test/fixtures/plugins/multiple-definition-evaluation/exec.js
+++ b/packages/babel-core/test/fixtures/plugins/multiple-definition-evaluation/exec.js
@@ -1,8 +1,8 @@
-var code = multiline([
- "function foo() {",
- " var a = a ? a : a;",
- "}",
-]);
+var code = `
+ function foo() {
+ var a = a ? a : a;
+ }
+`;
transform(code, {
plugins: [
diff --git a/packages/babel-core/test/fixtures/plugins/regression-2772/exec.js b/packages/babel-core/test/fixtures/plugins/regression-2772/exec.js
index b38b6197d9..b88b1876f2 100644
--- a/packages/babel-core/test/fixtures/plugins/regression-2772/exec.js
+++ b/packages/babel-core/test/fixtures/plugins/regression-2772/exec.js
@@ -1,12 +1,12 @@
-var code = multiline([
- "(function() {",
- " var bar = 'lol';",
- " function foo(b){",
- " b === bar;",
- " foo(b);",
- " }",
- "})();",
-]);
+var code = `
+ (function() {
+ var bar = 'lol';
+ function foo(b){
+ b === bar
+ foo(b);
+ }
+ })();
+`;
transform(code, {
plugins: [
diff --git a/packages/babel-helper-transform-fixture-test-runner/package.json b/packages/babel-helper-transform-fixture-test-runner/package.json
index 5387c003e0..54440589ca 100644
--- a/packages/babel-helper-transform-fixture-test-runner/package.json
+++ b/packages/babel-helper-transform-fixture-test-runner/package.json
@@ -23,6 +23,7 @@
"jest": "^24.8.0",
"jest-diff": "^24.8.0",
"lodash": "^4.17.19",
+ "quick-lru": "5.1.0",
"resolve": "^1.3.2",
"source-map": "^0.5.0"
}
diff --git a/packages/babel-helper-transform-fixture-test-runner/src/index.js b/packages/babel-helper-transform-fixture-test-runner/src/index.js
index ac00464f16..2917364da3 100644
--- a/packages/babel-helper-transform-fixture-test-runner/src/index.js
+++ b/packages/babel-helper-transform-fixture-test-runner/src/index.js
@@ -14,34 +14,100 @@ import fs from "fs";
import path from "path";
import vm from "vm";
import checkDuplicatedNodes from "babel-check-duplicated-nodes";
+import QuickLRU from "quick-lru";
import diff from "jest-diff";
-const moduleCache = {};
-const testContext = vm.createContext({
- ...helpers,
- process: process,
- transform: babel.transform,
- setTimeout: setTimeout,
- setImmediate: setImmediate,
- expect,
-});
-testContext.global = testContext;
+const cachedScripts = new QuickLRU({ maxSize: 10 });
+const contextModuleCache = new WeakMap();
+const sharedTestContext = createContext();
-// Initialize the test context with the polyfill, and then freeze the global to prevent implicit
-// global creation in tests, which could cause things to bleed between tests.
-runModuleInTestContext("@babel/polyfill", __filename);
+function createContext() {
+ const context = vm.createContext({
+ ...helpers,
+ process: process,
+ transform: babel.transform,
+ setTimeout: setTimeout,
+ setImmediate: setImmediate,
+ expect,
+ });
+ context.global = context;
-// Populate the "babelHelpers" global with Babel's helper utilities.
-runCodeInTestContext(buildExternalHelpers(), {
- filename: path.join(__dirname, "babel-helpers-in-memory.js"),
-});
+ const moduleCache = Object.create(null);
+ contextModuleCache.set(context, moduleCache);
+
+ // Initialize the test context with the polyfill, and then freeze the global to prevent implicit
+ // global creation in tests, which could cause things to bleed between tests.
+ runModuleInTestContext(
+ "@babel/polyfill/dist/polyfill.min",
+ __filename,
+ context,
+ moduleCache,
+ );
+
+ // Populate the "babelHelpers" global with Babel's helper utilities.
+ runCacheableScriptInTestContext(
+ path.join(__dirname, "babel-helpers-in-memory.js"),
+ buildExternalHelpers,
+ context,
+ moduleCache,
+ );
+
+ return context;
+}
+
+function runCacheableScriptInTestContext(
+ filename: string,
+ srcFn: () => string,
+ context: Context,
+ moduleCache: Object,
+) {
+ let cached = cachedScripts.get(filename);
+ if (!cached) {
+ const code = `(function (exports, require, module, __filename, __dirname) {\n${srcFn()}\n});`;
+ cached = {
+ code,
+ cachedData: undefined,
+ };
+ cachedScripts.set(filename, cached);
+ }
+
+ const script = new vm.Script(cached.code, {
+ filename,
+ displayErrors: true,
+ lineOffset: -1,
+ cachedData: cached.cachedData,
+ produceCachedData: true,
+ });
+
+ if (script.cachedDataProduced) {
+ cached.cachedData = script.cachedData;
+ }
+
+ const module = {
+ id: filename,
+ exports: {},
+ };
+ const req = id => runModuleInTestContext(id, filename, context, moduleCache);
+ const dirname = path.dirname(filename);
+
+ script
+ .runInContext(context)
+ .call(module.exports, module.exports, req, module, filename, dirname);
+
+ return module;
+}
/**
* A basic implementation of CommonJS so we can execute `@babel/polyfill` inside our test context.
* This allows us to run our unittests
*/
-function runModuleInTestContext(id: string, relativeFilename: string) {
+function runModuleInTestContext(
+ id: string,
+ relativeFilename: string,
+ context: Context,
+ moduleCache: Object,
+) {
const filename = resolve.sync(id, {
basedir: path.dirname(relativeFilename),
});
@@ -50,23 +116,17 @@ function runModuleInTestContext(id: string, relativeFilename: string) {
// the context's global scope.
if (filename === id) return require(id);
+ // Modules can only evaluate once per context, so the moduleCache is a
+ // stronger cache guarantee than the LRU's Script cache.
if (moduleCache[filename]) return moduleCache[filename].exports;
- const module = (moduleCache[filename] = {
- id: filename,
- exports: {},
- });
- const dirname = path.dirname(filename);
- const req = id => runModuleInTestContext(id, filename);
-
- const src = fs.readFileSync(filename, "utf8");
- const code = `(function (exports, require, module, __filename, __dirname) {\n${src}\n});`;
-
- vm.runInContext(code, testContext, {
+ const module = runCacheableScriptInTestContext(
filename,
- displayErrors: true,
- lineOffset: -1,
- }).call(module.exports, module.exports, req, module, filename, dirname);
+ () => fs.readFileSync(filename, "utf8"),
+ context,
+ moduleCache,
+ );
+ moduleCache[filename] = module;
return module.exports;
}
@@ -76,10 +136,15 @@ function runModuleInTestContext(id: string, relativeFilename: string) {
*
* Exposed for unit tests, not for use as an API.
*/
-export function runCodeInTestContext(code: string, opts: { filename: string }) {
+export function runCodeInTestContext(
+ code: string,
+ opts: { filename: string },
+ context = sharedTestContext,
+) {
const filename = opts.filename;
const dirname = path.dirname(filename);
- const req = id => runModuleInTestContext(id, filename);
+ const moduleCache = contextModuleCache.get(context);
+ const req = id => runModuleInTestContext(id, filename, context, moduleCache);
const module = {
id: filename,
@@ -94,7 +159,7 @@ export function runCodeInTestContext(code: string, opts: { filename: string }) {
// Note: This isn't doing .call(module.exports, ...) because some of our tests currently
// rely on 'this === global'.
const src = `(function(exports, require, module, __filename, __dirname, opts) {\n${code}\n});`;
- return vm.runInContext(src, testContext, {
+ return vm.runInContext(src, context, {
filename,
displayErrors: true,
lineOffset: -1,
@@ -183,13 +248,14 @@ function run(task) {
let resultExec;
if (execCode) {
+ const context = createContext();
const execOpts = getOpts(exec);
result = babel.transform(execCode, execOpts);
checkDuplicatedNodes(babel, result.ast);
execCode = result.code;
try {
- resultExec = runCodeInTestContext(execCode, execOpts);
+ resultExec = runCodeInTestContext(execCode, execOpts, context);
} catch (err) {
// Pass empty location to include the whole file in the output.
err.message =
diff --git a/packages/babel-plugin-proposal-object-rest-spread/test/fixtures/object-spread-loose-builtins/no-object-assign-exec/exec.js b/packages/babel-plugin-proposal-object-rest-spread/test/fixtures/object-spread-loose-builtins/no-object-assign-exec/exec.js
index b8efc6d372..40fdaabb0e 100644
--- a/packages/babel-plugin-proposal-object-rest-spread/test/fixtures/object-spread-loose-builtins/no-object-assign-exec/exec.js
+++ b/packages/babel-plugin-proposal-object-rest-spread/test/fixtures/object-spread-loose-builtins/no-object-assign-exec/exec.js
@@ -1,27 +1,25 @@
"use strict";
-const NOSET = `NOSET${__filename}`;
-const NOWRITE = `NOWRITE${__filename}`;
-Object.defineProperty(Object.prototype, NOSET, {
+Object.defineProperty(Object.prototype, 'NOSET', {
get(value) {
// noop
},
});
-Object.defineProperty(Object.prototype, NOWRITE, {
+Object.defineProperty(Object.prototype, 'NOWRITE', {
writable: false,
value: 'abc',
});
-const obj = { [NOSET]: 123 };
+const obj = { 'NOSET': 123 };
// this won't work as expected if transformed as Object.assign (or equivalent)
// because those trigger object setters (spread don't)
expect(() => {
const objSpread = { ...obj };
}).toThrow();
-const obj2 = { [NOWRITE]: 456 };
-// this throws `TypeError: Cannot assign to read only property 'NOWRITE'`
+const obj2 = { 'NOWRITE': 456 };
+// this throws `TypeError: Cannot assign to read only property 'NOWRITE'`
// if transformed as Object.assign (or equivalent) because those use *assignment* for creating properties
// (spread defines them)
expect(() => {
diff --git a/packages/babel-plugin-proposal-object-rest-spread/test/fixtures/object-spread-loose/no-object-assign-exec/exec.js b/packages/babel-plugin-proposal-object-rest-spread/test/fixtures/object-spread-loose/no-object-assign-exec/exec.js
index b8efc6d372..4d937710e3 100644
--- a/packages/babel-plugin-proposal-object-rest-spread/test/fixtures/object-spread-loose/no-object-assign-exec/exec.js
+++ b/packages/babel-plugin-proposal-object-rest-spread/test/fixtures/object-spread-loose/no-object-assign-exec/exec.js
@@ -1,27 +1,24 @@
"use strict";
-const NOSET = `NOSET${__filename}`;
-const NOWRITE = `NOWRITE${__filename}`;
-
-Object.defineProperty(Object.prototype, NOSET, {
+Object.defineProperty(Object.prototype, 'NOSET', {
get(value) {
// noop
},
});
-Object.defineProperty(Object.prototype, NOWRITE, {
+Object.defineProperty(Object.prototype, 'NOWRITE', {
writable: false,
value: 'abc',
});
-const obj = { [NOSET]: 123 };
+const obj = { 'NOSET': 123 };
// this won't work as expected if transformed as Object.assign (or equivalent)
// because those trigger object setters (spread don't)
expect(() => {
const objSpread = { ...obj };
}).toThrow();
-const obj2 = { [NOWRITE]: 456 };
-// this throws `TypeError: Cannot assign to read only property 'NOWRITE'`
+const obj2 = { 'NOWRITE': 456 };
+// this throws `TypeError: Cannot assign to read only property 'NOWRITE'`
// if transformed as Object.assign (or equivalent) because those use *assignment* for creating properties
// (spread defines them)
expect(() => {
diff --git a/packages/babel-plugin-proposal-object-rest-spread/test/fixtures/object-spread/no-object-assign-exec/exec.js b/packages/babel-plugin-proposal-object-rest-spread/test/fixtures/object-spread/no-object-assign-exec/exec.js
index e358e69cfe..2fbdb3cb1d 100644
--- a/packages/babel-plugin-proposal-object-rest-spread/test/fixtures/object-spread/no-object-assign-exec/exec.js
+++ b/packages/babel-plugin-proposal-object-rest-spread/test/fixtures/object-spread/no-object-assign-exec/exec.js
@@ -1,31 +1,27 @@
"use strict";
-const NOSET = `NOSET${__filename}`;
-const NOWRITE = `NOWRITE${__filename}`;
-
-Object.defineProperty(Object.prototype, NOSET, {
- set(value) {
+Object.defineProperty(Object.prototype, 'NOSET', {
+ get(value) {
// noop
},
});
-Object.defineProperty(Object.prototype, NOWRITE, {
+Object.defineProperty(Object.prototype, 'NOWRITE', {
writable: false,
value: 'abc',
});
-const obj = { [NOSET]: 123 };
+const obj = { NOSET: 123 };
// this wouldn't work as expected if transformed as Object.assign (or equivalent)
// because those trigger object setters (spread don't)
const objSpread = { ...obj };
+expect(objSpread).toHaveProperty('NOSET', 123);
-const obj2 = { NOSET: 123, [NOWRITE]: 456 };
+const obj2 = { NOWRITE: 456 };
// this line would throw `TypeError: Cannot assign to read only property 'NOWRITE'`
// if transformed as Object.assign (or equivalent) because those use *assignment* for creating properties
// (spread defines them)
const obj2Spread = { ...obj2 };
-
-expect(objSpread).toEqual(obj);
-expect(obj2Spread).toEqual(obj2);
+expect(obj2Spread).toHaveProperty('NOWRITE', 456);
const KEY = Symbol('key');
const obj3Spread = { ...{ get foo () { return 'bar' } }, [KEY]: 'symbol' };
diff --git a/packages/babel-plugin-transform-block-scoping/test/fixtures/general/issue-10339/exec.js b/packages/babel-plugin-transform-block-scoping/test/fixtures/general/issue-10339/exec.js
index 9fea95129b..9073078104 100644
--- a/packages/babel-plugin-transform-block-scoping/test/fixtures/general/issue-10339/exec.js
+++ b/packages/babel-plugin-transform-block-scoping/test/fixtures/general/issue-10339/exec.js
@@ -1,12 +1,12 @@
-const code = multiline([
- "for (const {foo, ...bar} of { bar: [] }) {",
- "() => foo;",
- "const [qux] = bar;",
- "try {} catch (e) {",
- "let quux = qux;",
- "}",
- "}"
-]);
+const code = `
+ for (const {foo, ...bar} of { bar: [] }) {
+ () => foo;
+ const [qux] = bar;
+ try {} catch (e) {
+ let quux = qux;
+ }
+ }
+`;
let programPath;
let forOfPath;
diff --git a/packages/babel-plugin-transform-classes/test/fixtures/regression/8499/exec.js b/packages/babel-plugin-transform-classes/test/fixtures/regression/8499/exec.js
index 1a5d146d5e..143ef440d8 100644
--- a/packages/babel-plugin-transform-classes/test/fixtures/regression/8499/exec.js
+++ b/packages/babel-plugin-transform-classes/test/fixtures/regression/8499/exec.js
@@ -1,26 +1,15 @@
-const oldReflect = this.Reflect;
-const oldHTMLElement = this.HTMLElement;
+// Pretend that `Reflect.construct` isn't supported.
+global.Reflect = undefined;
-try {
- // Pretend that `Reflect.construct` isn't supported.
- this.Reflect = undefined;
+global.HTMLElement = function() {
+ // Here, `this.HTMLElement` is this function, not the original HTMLElement
+ // constructor. `this.constructor` should be this function too, but isn't.
+ constructor = this.constructor;
+};
- this.HTMLElement = function() {
- // Here, `this.HTMLElement` is this function, not the original HTMLElement
- // constructor. `this.constructor` should be this function too, but isn't.
- constructor = this.constructor;
- };
-
- var constructor;
-
- class CustomElement extends HTMLElement {};
- new CustomElement();
-
- expect(constructor).toBe(CustomElement);
-} finally {
- // Restore original env
- this.Reflect = oldReflect;
- this.HTMLElement = oldHTMLElement;
-}
+var constructor;
+class CustomElement extends HTMLElement {}
+new CustomElement();
+expect(constructor).toBe(CustomElement);
diff --git a/packages/babel-plugin-transform-destructuring/test/fixtures/destructuring/array-symbol-unsupported/exec.js b/packages/babel-plugin-transform-destructuring/test/fixtures/destructuring/array-symbol-unsupported/exec.js
index ea73919662..4ee3b0499b 100644
--- a/packages/babel-plugin-transform-destructuring/test/fixtures/destructuring/array-symbol-unsupported/exec.js
+++ b/packages/babel-plugin-transform-destructuring/test/fixtures/destructuring/array-symbol-unsupported/exec.js
@@ -1,13 +1,9 @@
var a = (() => [1, 2, 3])();
// Simulate old environment
-let _Symbol = Symbol;
-Symbol = void 0;
-try {
- var [first, ...rest] = a;
+global.Symbol = void 0;
- expect(first).toBe(1);
- expect(rest).toEqual([2, 3]);
-} finally {
- Symbol = _Symbol;
-}
+var [first, ...rest] = a;
+
+expect(first).toBe(1);
+expect(rest).toEqual([2, 3]);
diff --git a/packages/babel-plugin-transform-destructuring/test/fixtures/destructuring/non-iterable/exec.js b/packages/babel-plugin-transform-destructuring/test/fixtures/destructuring/non-iterable/exec.js
index e0bac050d6..d110d30779 100644
--- a/packages/babel-plugin-transform-destructuring/test/fixtures/destructuring/non-iterable/exec.js
+++ b/packages/babel-plugin-transform-destructuring/test/fixtures/destructuring/non-iterable/exec.js
@@ -8,15 +8,8 @@ expect(
() => [foo, bar] = {}
).toThrow(/destructure non-iterable/);
-// Simulate old browser
-let _Symbol = Symbol;
-Symbol = void 0;
-try {
+global.Symbol = void 0;
- expect(
- () => [foo, bar] = {}
- ).toThrow(/destructure non-iterable/);
-
-} finally {
- Symbol = _Symbol;
-}
+expect(
+ () => [foo, bar] = {}
+).toThrow(/destructure non-iterable/);
diff --git a/packages/babel-plugin-transform-react-jsx-source/test/fixtures/react-source/basic-sample/exec.js b/packages/babel-plugin-transform-react-jsx-source/test/fixtures/react-source/basic-sample/exec.js
index c3bb778238..21bf1293f1 100644
--- a/packages/babel-plugin-transform-react-jsx-source/test/fixtures/react-source/basic-sample/exec.js
+++ b/packages/babel-plugin-transform-react-jsx-source/test/fixtures/react-source/basic-sample/exec.js
@@ -3,13 +3,13 @@ var actual = transform(
Object.assign({}, opts, { filename: '/fake/path/mock.js' })
).code;
-var expected = multiline([
- 'var _jsxFileName = "/fake/path/mock.js";',
- 'var x = ;',
-]);
+var expected = `
+var _jsxFileName = "/fake/path/mock.js";
+var x = ;
+`.trim();
expect(actual).toBe(expected);
diff --git a/packages/babel-plugin-transform-spread/test/fixtures/spread/array-symbol-unsupported/exec.js b/packages/babel-plugin-transform-spread/test/fixtures/spread/array-symbol-unsupported/exec.js
index 93ad0b7a1e..0d320afa2e 100644
--- a/packages/babel-plugin-transform-spread/test/fixtures/spread/array-symbol-unsupported/exec.js
+++ b/packages/babel-plugin-transform-spread/test/fixtures/spread/array-symbol-unsupported/exec.js
@@ -1,10 +1,6 @@
var a = (() => [2, 3])();
// Simulate old environment
-let _Symbol = Symbol;
-Symbol = void 0;
-try {
- expect([1, ...a]).toEqual([1, 2, 3]);
-} finally {
- Symbol = _Symbol;
-}
+global.Symbol = void 0;
+
+expect([1, ...a]).toEqual([1, 2, 3]);
diff --git a/packages/babel-plugin-transform-spread/test/fixtures/spread/non-iterable/exec.js b/packages/babel-plugin-transform-spread/test/fixtures/spread/non-iterable/exec.js
index 0ff7a48766..f00f0ae0cf 100644
--- a/packages/babel-plugin-transform-spread/test/fixtures/spread/non-iterable/exec.js
+++ b/packages/babel-plugin-transform-spread/test/fixtures/spread/non-iterable/exec.js
@@ -5,6 +5,6 @@ expect(() => [...undefined]).toThrow(/spread non-iterable/);
expect(() => [...o]).toThrow(/spread non-iterable/);
// Simulate old browser
-Symbol = void 0;
+global.Symbol = void 0;
expect(() => [...o]).toThrow(/spread non-iterable/);