Skip to content

Commit b9edb7f

Browse files
VictorystickAndarist
authored andcommitted
Merge pull request #19 from rollup/gh-17
Better handling of helpers
1 parent 0000062 commit b9edb7f

File tree

8 files changed

+110
-31
lines changed

8 files changed

+110
-31
lines changed

packages/rollup-plugin-babel/README.md

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -44,35 +44,42 @@ All options are as per the [Babel documentation](https://babeljs.io/), except `o
4444
Babel will respect `.babelrc` files – this is generally the best place to put your configuration.
4545

4646

47-
## Babel 5
47+
## Configuring Babel
4848

49-
The latest version of rollup-plugin-babel is designed to work with Babel 6. To use rollup-plugin-babel with Babel 5, install a 1.x version:
49+
**The following applies to Babel 6 only. If you're using Babel 5, do `npm i -D rollup-plugin-babel@1`, as version 2 and above no longer supports Babel 5**
50+
51+
tl;dr: use the `es2015-rollup` preset instead of `es2015`.
5052

5153
```bash
52-
npm install --save-dev rollup-plugin-babel@1
54+
npm install --save-dev babel-preset-es2015-rollup
5355
```
5456

55-
56-
## Babel 6
57-
58-
With Babel 5, rollup-plugin-babel overrides the configuration to ensure that module syntax is left alone and that external helpers are collected for inclusion at the top of the bundle.
59-
60-
Babel 6 works differently – there's no `blacklist` or `externalHelpers` options. Instead of using the `es2015` preset, install and use [babel-preset-es2015-rollup](https://github.com/rollup/babel-preset-es2015-rollup):
61-
6257
```js
6358
// .babelrc
6459
{
6560
"presets": [ "es2015-rollup" ]
6661
}
6762
```
6863

69-
If you're not using the preset, be sure to include the external helpers plugin:
64+
### Modules
65+
66+
The `es2015` preset includes the [transform-es2015-modules-commonjs](http://babeljs.io/docs/plugins/transform-es2015-modules-commonjs/) plugin, which converts ES6 modules to CommonJS – preventing Rollup from working. Instead, you should either use the `es2015-rollup` preset, which excludes that plugin, or otherwise ensure that the `modules-commonjs` plugin is excluded. Rollup will throw an error if this is incorrectly configured.
67+
68+
### Helpers
69+
70+
In some cases Babel uses *helpers* to avoid repeating chunks of code – for example, if you use the `class` keyword, it will use a `classCallCheck` function to ensure that the class is instantiated correctly.
71+
72+
By default, those helpers will be inserted at the top of the file being transformed, which can lead to duplication. It's therefore recommended that you use the `external-helpers-2` plugin, which is automatically included in the `es2015-rollup` preset. Rollup will combine the helpers in a single block at the top of your bundle.
73+
74+
Alternatively, if you know what you're doing, you can use the `transform-runtime` plugin. If you do this, use `runtimeHelpers: true`:
7075

7176
```js
72-
// .babelrc
73-
{
74-
"plugins": [ "external-helpers-2" ]
75-
}
77+
rollup.rollup({
78+
...,
79+
plugins: [
80+
babel({ runtimeHelpers: true })
81+
]
82+
}).then(...)
7683
```
7784

7885

packages/rollup-plugin-babel/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
"rollup-pluginutils": "^1.1.0"
2929
},
3030
"devDependencies": {
31+
"babel-plugin-transform-runtime": "^6.3.13",
32+
"babel-plugin-transform-decorators-legacy": "^1.3.4",
3133
"babel-preset-es2015": "^6.1.18",
3234
"babel-preset-es2015-rollup": "^1.0.0",
3335
"babel-register": "^6.3.13",
Lines changed: 53 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,20 @@
11
import { buildExternalHelpers, transform } from 'babel-core';
22
import { createFilter } from 'rollup-pluginutils';
33

4+
const INLINE = {};
5+
const RUNTIME = {};
6+
const BUNDLED = {};
7+
48
function preflightCheck ( localOpts ) {
59
var check = transform( 'export default class Foo {}', localOpts ).code;
6-
if ( ~check.indexOf( 'function _classCallCheck' ) ) throw new Error( 'External helpers are not enabled. Please add the "external-helpers-2" plugin or use the "es2015-rollup" preset. See https://github.com/rollup/rollup-plugin-babel#TK for more information' );
7-
if ( !~check.indexOf( 'export default' ) ) throw new Error( 'It looks like your Babel configuration specifies a module transformer. Please disable it. If you\'re using the "es2015" preset, consider using "es2015-rollup" instead. See https://github.com/rollup/rollup-plugin-babel#TK for more information' );
10+
11+
if ( !~check.indexOf( 'export default' ) && !~check.indexOf( 'export { Foo as default }' ) ) throw new Error( 'It looks like your Babel configuration specifies a module transformer. Please disable it. If you\'re using the "es2015" preset, consider using "es2015-rollup" instead. See https://github.com/rollup/rollup-plugin-babel#TK for more information' );
12+
13+
if ( ~check.indexOf( 'import _classCallCheck from "babel-runtime' ) ) return RUNTIME;
14+
if ( ~check.indexOf( 'function _classCallCheck' ) ) return INLINE;
15+
if ( ~check.indexOf( 'babelHelpers.classCallCheck' ) ) return BUNDLED;
16+
17+
throw new Error( 'An unexpected situation arose. Please raise an issue at https://github.com/rollup/rollup-plugin-babel/issues. Thanks!' );
818
}
919

1020
function assign ( target, source ) {
@@ -14,9 +24,17 @@ function assign ( target, source ) {
1424
return target;
1525
}
1626

27+
let warned = {};
28+
function warnOnce ( msg ) {
29+
if ( warned[ msg ] ) return;
30+
warned[ msg ] = true;
31+
console.warn( msg );
32+
}
33+
1734
export default function babel ( options ) {
1835
options = assign( {}, options || {} );
19-
var usedHelpers = [];
36+
var bundledHelpers = {};
37+
var inlineHelpers = {};
2038

2139
var filter = createFilter( options.include, options.exclude );
2240
delete options.include;
@@ -26,30 +44,52 @@ export default function babel ( options ) {
2644
if ( options.sourceMaps !== false ) options.sourceMaps = true;
2745
delete options.sourceMap;
2846

47+
const runtimeHelpers = options.runtimeHelpers;
48+
delete options.runtimeHelpers;
49+
2950
return {
3051
transform ( code, id ) {
3152
if ( !filter( id ) ) return null;
3253

3354
var localOpts = assign({ filename: id }, options );
34-
preflightCheck( localOpts );
55+
const helpers = preflightCheck( localOpts );
3556

3657
var transformed = transform( code, localOpts );
58+
const { usedHelpers } = transformed.metadata;
3759

38-
transformed.metadata.usedHelpers.forEach( helper => {
39-
if ( !~usedHelpers.indexOf( helper ) ) usedHelpers.push( helper );
40-
});
60+
if ( usedHelpers.length ) {
61+
if ( helpers === BUNDLED ) {
62+
usedHelpers.forEach( helper => {
63+
bundledHelpers[ helper ] = true;
64+
});
65+
} else if ( helpers === RUNTIME && !runtimeHelpers ) {
66+
throw new Error( 'Runtime helpers are not enabled. Either exclude the transform-runtime Babel plugin or pass the `runtimeHelpers: true` option. See https://github.com/rollup/rollup-plugin-babel#configuring-babel for more information' );
67+
} else {
68+
usedHelpers.forEach( helper => {
69+
if ( inlineHelpers[ helper ] ) {
70+
warnOnce( `The '${helper} Babel helper is used more than once in your code. It's strongly recommended that you use the "external-helpers-2" plugin or the "es2015-rollup" preset. See https://github.com/rollup/rollup-plugin-babel#configuring-babel for more information` );
71+
}
72+
73+
inlineHelpers[ helper ] = true;
74+
});
75+
}
76+
}
4177

4278
return {
43-
code: transformed.code,
79+
code: transformed.code.replace( /babelHelpers\./g, 'babelHelpers_' ),
4480
map: transformed.map
4581
};
4682
},
4783
intro () {
48-
// TODO replace babelHelpers.foo with babelHelpers_foo – though first
49-
// we need the ability to find and replace within the generated bundle
50-
return usedHelpers.length ?
51-
buildExternalHelpers( usedHelpers, 'var' ).trim() :
52-
'';
84+
const helpers = Object.keys( bundledHelpers );
85+
if ( !helpers.length ) return '';
86+
87+
return buildExternalHelpers( helpers, 'var' )
88+
.replace( /var babelHelpers.+\n/, '' )
89+
.replace( /babelHelpers\.(.+) = function/g, 'function babelHelpers_$1' )
90+
.replace( /babelHelpers\.(.+) = /g, 'var babelHelpers_$1 = ' )
91+
.replace( 'babelHelpers;', '' ) // not sure where this comes from...
92+
.trim() + '\n';
5393
}
5494
};
5595
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"presets": [ "es2015-rollup" ],
3+
"plugins": "transform-runtime"
4+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default class Foo {
2+
3+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"presets": [ "es2015-rollup" ],
3+
"plugins": [ "transform-decorators-legacy" ]
4+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export default 42;

packages/rollup-plugin-babel/test/test.js

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,10 @@ describe( 'rollup-plugin-babel', function () {
3232
this.timeout( 15000 );
3333

3434
it( 'runs code through babel', function () {
35-
var start = Date.now();
3635
return rollup.rollup({
3736
entry: 'samples/basic/main.js',
3837
plugins: [ babelPlugin() ]
3938
}).then( function ( bundle ) {
40-
start = Date.now();
4139
var generated = bundle.generate();
4240

4341
var code = generated.code;
@@ -54,7 +52,7 @@ describe( 'rollup-plugin-babel', function () {
5452
var generated = bundle.generate();
5553
var code = generated.code;
5654

57-
assert.ok( code.indexOf( 'babelHelpers.classCallCheck =' ) !== -1, generated.code );
55+
assert.ok( code.indexOf( 'function babelHelpers_classCallCheck' ) !== -1, generated.code );
5856
assert.ok( code.indexOf( 'var _createClass =' ) === -1, generated.code );
5957
});
6058
});
@@ -106,6 +104,13 @@ describe( 'rollup-plugin-babel', function () {
106104
});
107105
});
108106

107+
it( 'works with transform-decorators-legacy (#18)', function () {
108+
return rollup.rollup({
109+
entry: 'samples/transform-decorators-legacy/main.js',
110+
plugins: [ babelPlugin() ]
111+
});
112+
});
113+
109114
it( 'checks config per-file', function () {
110115
return rollup.rollup({
111116
entry: 'samples/checks/main.js',
@@ -118,4 +123,17 @@ describe( 'rollup-plugin-babel', function () {
118123
assert.ok( /es2015-rollup/.test( err.message ), 'Expected an error about external helpers or module transform, got "' + err.message + '"' );
119124
});
120125
});
126+
127+
it( 'allows transform-runtime to be used instead of bundled helpers', function () {
128+
return rollup.rollup({
129+
entry: 'samples/runtime-helpers/main.js',
130+
plugins: [ babelPlugin({ runtimeHelpers: true }) ],
131+
onwarn: function ( msg ) {
132+
assert.equal( msg, `Treating 'babel-runtime/helpers/classCallCheck' as external dependency` );
133+
}
134+
}).then( function ( bundle ) {
135+
var cjs = bundle.generate({ format: 'cjs' }).code;
136+
assert.ok( !~cjs.indexOf( 'babelHelpers' ) );
137+
});
138+
});
121139
});

0 commit comments

Comments
 (0)