diff --git a/lib/babel.js b/lib/babel.js index bdff5e78db..390b1599f8 100644 --- a/lib/babel.js +++ b/lib/babel.js @@ -1,10 +1,13 @@ 'use strict'; +var path = require('path'); var resolveFrom = require('resolve-from'); var createEspowerPlugin = require('babel-plugin-espower/create'); var requireFromString = require('require-from-string'); var hasGenerators = parseInt(process.version.slice(1), 10) > 0; var testPath = process.argv[2]; +var testDir = path.dirname(testPath); +var Module = module.constructor; var babel; try { @@ -14,17 +17,59 @@ try { babel = require('babel-core'); } -var options = { - blacklist: hasGenerators ? ['regenerator'] : [], - optional: hasGenerators ? ['asyncToGenerator', 'runtime'] : ['runtime'], - plugins: [ - createEspowerPlugin(babel, { - patterns: require('./enhance-assert').PATTERNS - }) - ] -}; - -var transpiled = babel.transformFileSync(testPath, options); -requireFromString(transpiled.code, testPath, { - appendPaths: module.paths -}); +function options() { + return { + blacklist: hasGenerators ? ['regenerator'] : [], + optional: hasGenerators ? ['asyncToGenerator', 'runtime'] : ['runtime'], + plugins: [ + createEspowerPlugin(babel, { + patterns: require('./enhance-assert').PATTERNS + }) + ] + }; +} + +// TODO: This needs to be smarter. +function shouldTranspile(filePath) { + var fileName = path.basename(filePath); + return testDir === path.dirname(filePath) && fileName[0] === '_' || filePath === testPath; +} + +function requireHook(file) { + // Most of this is Module._load with a few modifications. + + file = Module._resolveFilename(file, this); + + var cachedModule = Module._cache[file]; + if (cachedModule) { + return cachedModule.exports; + } + + if (shouldTranspile(file)) { + var code = babel.transformFileSync(file, options()).code; + + var hadException = true; + + var m = requireFromString.load(file, { + appendPaths: module.paths, + require: requireHook + }); + + try { + Module._cache[file] = m; + + m._compile(code, file); + + hadException = false; + } finally { + if (hadException) { + delete Module._cache[file]; + } + } + + return m.exports; + } + return Module._load(file, this); +} + +requireHook.call(module, testPath); diff --git a/lib/fork.js b/lib/fork.js index 8fadb88054..9160731e48 100644 --- a/lib/fork.js +++ b/lib/fork.js @@ -33,7 +33,7 @@ module.exports = function (args) { ps.on('exit', function (code) { if (code > 0 && code !== 143) { - reject(); + reject(new Error(file + ' exited with error code ' + code)); } else { resolve(testResults); } diff --git a/package.json b/package.json index 36f967c38f..22e98820ea 100644 --- a/package.json +++ b/package.json @@ -81,7 +81,7 @@ "power-assert-formatter": "^1.3.0", "power-assert-renderers": "^0.1.0", "pretty-ms": "^2.0.0", - "require-from-string": "^1.1.0", + "require-from-string": "jamestalmage/require-from-string#allow-transforms", "resolve-from": "^1.0.0", "serialize-error": "^1.0.0", "set-immediate-shim": "^1.0.1", diff --git a/test/fixture/_es6-helper.js b/test/fixture/_es6-helper.js new file mode 100644 index 0000000000..50951f5ec7 --- /dev/null +++ b/test/fixture/_es6-helper.js @@ -0,0 +1,5 @@ +'use strict'; + +export default function () { + return Promise.resolve('es6').then(prefix => prefix + ' helper'); +} diff --git a/test/fixture/babel-hook-imported.js b/test/fixture/babel-hook-imported.js index 121e27f6a8..81aff321c0 100644 --- a/test/fixture/babel-hook-imported.js +++ b/test/fixture/babel-hook-imported.js @@ -1 +1 @@ -module.export = async () => {}; +module.exports = async () => {}; diff --git a/test/fixture/uses-es6-helper.js b/test/fixture/uses-es6-helper.js new file mode 100644 index 0000000000..a00dcb828a --- /dev/null +++ b/test/fixture/uses-es6-helper.js @@ -0,0 +1,9 @@ +'use strict'; +import test from '../../'; + +import helper from './_es6-helper'; + +test(async t => { + t.is(await helper(), 'es6 helper'); + t.end(); +}); diff --git a/test/fork.js b/test/fork.js index a8922735c5..56988cfee9 100644 --- a/test/fork.js +++ b/test/fork.js @@ -35,7 +35,7 @@ test('rejects on error and streams output', function (t) { buffer += data; }) .catch(function () { - t.ok(/no such file or directory/.test(buffer)); + t.ok(/Cannot find module/.test(buffer)); t.end(); }); }); diff --git a/test/test.js b/test/test.js index a25f30c98b..7370703d50 100644 --- a/test/test.js +++ b/test/test.js @@ -963,10 +963,27 @@ test('change process.cwd() to a test\'s directory', function (t) { }); }); -test('Babel require hook only applies to the test file', function (t) { - execCli('fixture/babel-hook.js', function (err) { +test('Babel require hook does apply to helpers ("_" prefix)', function (t) { + t.plan(1); + + execCli('fixture/uses-es6-helper.js', function (err) { + t.ifError(err); + t.end(); + }); +}); + +test('Babel require does not apply to non-helper (no "_" prefix)', function (t) { + execCli('fixture/babel-hook.js', function (err, stdout) { + // TODO: This test was passing for the wrong reasons. Make sure this gets fixed in master regardless. t.ok(err); t.is(err.code, 1); + + // module.exports = async () => {}; + // ^ + // SyntaxError: Unexpected reserved word + + // TODO: Why standard error? + t.true(/module.exports = async \(\) => \{\};\s*\n.*?\n.*?Unexpected token >/.test(stdout)); t.end(); }); });