Skip to content

Commit a03f826

Browse files
Stephen Sorensennovemberborn
Stephen Sorensen
authored andcommitted
Close PR #573: Added support for babel config under package.json/ava key
Fixes #448
1 parent 3fcd492 commit a03f826

7 files changed

+201
-22
lines changed

api.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ Api.prototype.run = function (files) {
187187
uniqueTempDir();
188188

189189
self.options.cacheDir = cacheDir;
190-
self.precompiler = new CachingPrecompiler(cacheDir);
190+
self.precompiler = new CachingPrecompiler(cacheDir, self.options.babelConfig);
191191
self.fileCount = files.length;
192192
self.base = path.relative('.', commonPathPrefix(files)) + path.sep;
193193

cli.js

+7-2
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,11 @@ var Api = require('./api');
3535
// Bluebird specific
3636
Promise.longStackTraces();
3737

38-
var conf = pkgConf.sync('ava');
38+
var conf = pkgConf.sync('ava', {
39+
defaults: {
40+
babel: 'default'
41+
}
42+
});
3943

4044
var cli = meow([
4145
'Usage',
@@ -102,7 +106,8 @@ var api = new Api({
102106
require: arrify(cli.flags.require),
103107
cacheEnabled: cli.flags.cache !== false,
104108
explicitTitles: cli.flags.watch,
105-
match: arrify(cli.flags.match)
109+
match: arrify(cli.flags.match),
110+
babelConfig: conf.babel
106111
});
107112

108113
var reporter;

lib/caching-precompiler.js

+27-13
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,21 @@ var path = require('path');
33
var cachingTransform = require('caching-transform');
44
var md5Hex = require('md5-hex');
55
var stripBom = require('strip-bom');
6+
var objectAssign = require('object-assign');
67

78
module.exports = CachingPrecompiler;
89

9-
function CachingPrecompiler(cacheDir) {
10+
function CachingPrecompiler(cacheDir, babelConfig) {
1011
if (!(this instanceof CachingPrecompiler)) {
1112
throw new TypeError('Class constructor CachingPrecompiler cannot be invoked without \'new\'');
1213
}
1314

1415
this.cacheDir = cacheDir;
1516
this.filenameToHash = {};
16-
this.transform = this._createTransform();
17+
this.transform = this._createTransform(babelConfig);
1718
}
1819

19-
CachingPrecompiler.prototype._factory = function (cacheDir) {
20+
CachingPrecompiler.prototype._factory = function (babelConfig, cacheDir) {
2021
// This factory method is only called once per process, and only as needed, to defer loading expensive dependencies.
2122
var babel = require('babel-core');
2223
var convertSourceMap = require('convert-source-map');
@@ -30,15 +31,27 @@ CachingPrecompiler.prototype._factory = function (cacheDir) {
3031
// Extract existing source maps from the code.
3132
var sourceMap = convertSourceMap.fromSource(code) || convertSourceMap.fromMapFileSource(code, path.dirname(filename));
3233

33-
return {
34-
presets: [presetStage2, presetES2015],
35-
plugins: [powerAssert, transformRuntime],
34+
var options = {babelrc: false};
35+
36+
if (!babelConfig || babelConfig === 'default') {
37+
objectAssign(options, {presets: [presetStage2, presetES2015]});
38+
} else if (babelConfig === 'inherit') {
39+
objectAssign(options, {babelrc: true});
40+
} else {
41+
objectAssign(options, babelConfig);
42+
}
43+
44+
objectAssign(options, {
45+
inputSourceMap: sourceMap && sourceMap.toObject(),
3646
filename: filename,
3747
sourceMaps: true,
38-
ast: false,
39-
babelrc: false,
40-
inputSourceMap: sourceMap && sourceMap.toObject()
41-
};
48+
ast: false
49+
});
50+
51+
options.plugins = options.plugins || [];
52+
options.plugins.push(powerAssert, transformRuntime);
53+
54+
return options;
4255
}
4356

4457
return function (code, filename, hash) {
@@ -61,14 +74,15 @@ CachingPrecompiler.prototype._createEspowerPlugin = function (babel) {
6174
});
6275
};
6376

64-
CachingPrecompiler.prototype._createTransform = function () {
77+
CachingPrecompiler.prototype._createTransform = function (babelConfig) {
6578
return cachingTransform({
66-
factory: this._factory.bind(this),
79+
factory: this._factory.bind(this, babelConfig),
6780
cacheDir: this.cacheDir,
6881
salt: new Buffer(JSON.stringify({
6982
'babel-plugin-espower': require('babel-plugin-espower/package.json').version,
7083
'ava': require('../package.json').version,
71-
'babel-core': require('babel-core/package.json').version
84+
'babel-core': require('babel-core/package.json').version,
85+
'babelConfig': babelConfig
7286
})),
7387
ext: '.js',
7488
hash: this._hash.bind(this)

profile.js

+6-2
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,11 @@ globals.setTimeout = setTimeout.bind(null);
2020
globals.clearTimeout = clearTimeout.bind(null);
2121

2222
Promise.longStackTraces();
23-
var conf = pkgConf.sync('ava');
23+
var conf = pkgConf.sync('ava', {
24+
defaults: {
25+
babel: 'default'
26+
}
27+
});
2428

2529
// Define a minimal set of options from the main CLI.
2630
var cli = meow([
@@ -63,7 +67,7 @@ var opts = {
6367
require: arrify(cli.flags.require),
6468
tty: false,
6569
cacheDir: cacheDir,
66-
precompiled: new CachingPrecompiler(cacheDir).generateHashForFile(file)
70+
precompiled: new CachingPrecompiler(cacheDir, conf.babel).generateHashForFile(file)
6771
};
6872

6973
var events = new EventEmitter();

readme.md

+57-1
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,8 @@ All of the CLI options can be configured in the `ava` section of your `package.j
154154
"tap": true,
155155
"require": [
156156
"babel-register"
157-
]
157+
],
158+
"babel": "inherit"
158159
}
159160
}
160161
```
@@ -423,6 +424,61 @@ AVA comes with builtin support for ES2015 through [Babel 6](https://babeljs.io).
423424

424425
AVA includes typings for TypeScript. You have to setup transpilation yourself. When you set `module` to `commonjs` in your `tsconfig.json` file, TypeScript will automatically find the type definitions for AVA. You should set `target` to `es2015` to use Promises and async functions.
425426

427+
### Babel Configuration for Test Scripts
428+
429+
If you want to customize the babel transpiler for test files, you can do so by adding a `"babel"` key to the `ava` section in your `package.json` file.
430+
431+
```json
432+
{
433+
"ava": {
434+
"babel": {
435+
"presets": [
436+
"es2015",
437+
"stage-0",
438+
"react"
439+
]
440+
}
441+
},
442+
}
443+
```
444+
445+
In addition to specifying a custom Babel config, you can also use the special `"inherit"` keyword. When you do this, AVA will allow tests to be transpiled using the configuration defined in your `.babelrc` file or in package.json/babel. This way, your test files will be transpiled using the same options as your source files, but you won't have to define the options twice.
446+
447+
```json
448+
{
449+
"babel": {
450+
"presets": [
451+
"es2015",
452+
"stage-0",
453+
"react"
454+
]
455+
},
456+
"ava": {
457+
"babel": "inherit",
458+
},
459+
}
460+
```
461+
462+
Note: When configuring Babel for tests manually, the espower and transform-runtime plugins will be
463+
added for you.
464+
465+
## Default Babel Configuration for Test Scripts
466+
467+
If you don't explicitly configure Babel for your tests using the `"babel"` key in package.json, your tests will be transpiled using AVA's default Babel configuration, which is as follows:
468+
469+
```json
470+
{
471+
"presets": [
472+
"es2015",
473+
"stage-0",
474+
],
475+
"plugins": [
476+
"espower",
477+
"transform-runtime"
478+
]
479+
}
480+
```
481+
426482
#### Transpiling Imported Modules
427483

428484
AVA currently only transpiles the tests you ask it to run. *It will not transpile modules you ```import``` from outside of the test.* While there are valid reasons for taking this approach, it may not be what you expect!

test/api.js

+31
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ var rimraf = require('rimraf');
55
var fs = require('fs');
66
var test = require('tap').test;
77
var Api = require('../api');
8+
var testDoublerPlugin = require('./fixture/babel-plugin-test-doubler');
89

910
test('must be called with new', function (t) {
1011
t.throws(function () {
@@ -684,3 +685,33 @@ test('verify test count', function (t) {
684685
t.is(api.todoCount, 1);
685686
});
686687
});
688+
689+
test('Custom Babel Plugin Support', function (t) {
690+
t.plan(1);
691+
692+
var api = new Api({
693+
babelConfig: {
694+
presets: ['es2015', 'stage-2'],
695+
plugins: [testDoublerPlugin]
696+
}
697+
});
698+
699+
api.run([path.join(__dirname, 'fixture/es2015.js')])
700+
.then(
701+
function () {
702+
t.is(api.passCount, 2);
703+
},
704+
t.threw
705+
);
706+
});
707+
708+
test('Default babel config doesn\'t use .babelrc', function (t) {
709+
t.plan(1);
710+
711+
var api = new Api();
712+
713+
return api.run([path.join(__dirname, 'fixture/babelrc/test.js')])
714+
.then(function () {
715+
t.is(api.passCount, 1);
716+
});
717+
});

test/caching-precompiler.js

+72-3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ var fs = require('fs');
33
var path = require('path');
44
var test = require('tap').test;
55
var uniqueTempDir = require('unique-temp-dir');
6+
var sinon = require('sinon');
7+
var babel = require('babel-core');
8+
var transformRuntime = require('babel-plugin-transform-runtime');
69

710
var CachingPrecompiler = require('../lib/caching-precompiler');
811

@@ -18,24 +21,26 @@ function endsWithMap(filename) {
1821
return /\.js$/.test(filename);
1922
}
2023

24+
sinon.spy(babel, 'transform');
25+
2126
test('creation with new', function (t) {
2227
var tempDir = uniqueTempDir();
23-
var precompiler = new CachingPrecompiler(tempDir);
28+
var precompiler = new CachingPrecompiler(tempDir, null);
2429
t.is(precompiler.cacheDir, tempDir);
2530
t.end();
2631
});
2732

2833
test('must be called with new', function (t) {
2934
t.throws(function () {
3035
var cachingPrecompiler = CachingPrecompiler;
31-
cachingPrecompiler(uniqueTempDir());
36+
cachingPrecompiler(uniqueTempDir(), null);
3237
}, {message: 'Class constructor CachingPrecompiler cannot be invoked without \'new\''});
3338
t.end();
3439
});
3540

3641
test('adds files and source maps to the cache directory as needed', function (t) {
3742
var tempDir = uniqueTempDir();
38-
var precompiler = new CachingPrecompiler(tempDir);
43+
var precompiler = new CachingPrecompiler(tempDir, null);
3944

4045
t.false(fs.existsSync(tempDir), 'cache directory is not created before it is needed');
4146

@@ -48,3 +53,67 @@ test('adds files and source maps to the cache directory as needed', function (t)
4853
t.is(files.filter(endsWithMap).length, 1, 'one .map file is saved to the cache');
4954
t.end();
5055
});
56+
57+
test('uses default babel options when babelConfig === "default"', function (t) {
58+
var tempDir = uniqueTempDir();
59+
var precompiler = new CachingPrecompiler(tempDir, 'default');
60+
babel.transform.reset();
61+
62+
precompiler.precompileFile(fixture('es2015.js'));
63+
64+
t.true(babel.transform.calledOnce);
65+
var options = babel.transform.firstCall.args[1];
66+
67+
t.true('filename' in options);
68+
t.true(options.sourceMaps);
69+
t.false(options.ast);
70+
t.true('inputSourceMap' in options);
71+
t.false(options.babelrc);
72+
t.true(Array.isArray(options.presets));
73+
t.true(Array.isArray(options.plugins));
74+
t.end();
75+
});
76+
77+
test('allows babel config from package.json/babel when babelConfig === "inherit"', function (t) {
78+
var tempDir = uniqueTempDir();
79+
var precompiler = new CachingPrecompiler(tempDir, 'inherit');
80+
babel.transform.reset();
81+
82+
precompiler.precompileFile(fixture('es2015.js'));
83+
84+
t.true(babel.transform.calledOnce);
85+
var options = babel.transform.firstCall.args[1];
86+
87+
t.true('filename' in options);
88+
t.true(options.sourceMaps);
89+
t.false(options.ast);
90+
t.true('inputSourceMap' in options);
91+
t.true(options.babelrc);
92+
t.end();
93+
});
94+
95+
test('uses babelConfig for babel options when babelConfig is an object', function (t) {
96+
var tempDir = uniqueTempDir();
97+
var customPlugin = sinon.stub().returns({visitor: {}});
98+
var powerAssert = sinon.stub().returns({visitor: {}});
99+
var precompiler = new CachingPrecompiler(tempDir, {
100+
presets: ['stage-2', 'es2015'],
101+
plugins: [customPlugin]
102+
});
103+
sinon.stub(precompiler, '_createEspowerPlugin').returns(powerAssert);
104+
babel.transform.reset();
105+
106+
precompiler.precompileFile(fixture('es2015.js'));
107+
108+
t.true(babel.transform.calledOnce);
109+
var options = babel.transform.firstCall.args[1];
110+
111+
t.true('filename' in options);
112+
t.true(options.sourceMaps);
113+
t.false(options.ast);
114+
t.true('inputSourceMap' in options);
115+
t.false(options.babelrc);
116+
t.same(options.presets, ['stage-2', 'es2015']);
117+
t.same(options.plugins, [customPlugin, powerAssert, transformRuntime]);
118+
t.end();
119+
});

0 commit comments

Comments
 (0)