Skip to content
This repository has been archived by the owner on Aug 4, 2021. It is now read-only.

Commit

Permalink
Merge pull request #19 from rollup/gh-17
Browse files Browse the repository at this point in the history
Better handling of helpers
  • Loading branch information
Victorystick committed Dec 29, 2015
2 parents a1ece89 + 708f86c commit 2fc82d6
Show file tree
Hide file tree
Showing 8 changed files with 110 additions and 31 deletions.
37 changes: 22 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,35 +44,42 @@ All options are as per the [Babel documentation](https://babeljs.io/), except `o
Babel will respect `.babelrc` files – this is generally the best place to put your configuration.


## Babel 5
## Configuring Babel

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:
**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**

tl;dr: use the `es2015-rollup` preset instead of `es2015`.

```bash
npm install --save-dev rollup-plugin-babel@1
npm install --save-dev babel-preset-es2015-rollup
```


## Babel 6

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.

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):

```js
// .babelrc
{
"presets": [ "es2015-rollup" ]
}
```

If you're not using the preset, be sure to include the external helpers plugin:
### Modules

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.

### Helpers

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.

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.

Alternatively, if you know what you're doing, you can use the `transform-runtime` plugin. If you do this, use `runtimeHelpers: true`:

```js
// .babelrc
{
"plugins": [ "external-helpers-2" ]
}
rollup.rollup({
...,
plugins: [
babel({ runtimeHelpers: true })
]
}).then(...)
```


Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
"rollup-pluginutils": "^1.1.0"
},
"devDependencies": {
"babel-plugin-transform-runtime": "^6.3.13",
"babel-plugin-transform-decorators-legacy": "^1.3.4",
"babel-preset-es2015": "^6.1.18",
"babel-preset-es2015-rollup": "^1.0.0",
"babel-register": "^6.3.13",
Expand Down
66 changes: 53 additions & 13 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
import { buildExternalHelpers, transform } from 'babel-core';
import { createFilter } from 'rollup-pluginutils';

const INLINE = {};
const RUNTIME = {};
const BUNDLED = {};

function preflightCheck ( localOpts ) {
var check = transform( 'export default class Foo {}', localOpts ).code;
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' );
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' );

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' );

if ( ~check.indexOf( 'import _classCallCheck from "babel-runtime' ) ) return RUNTIME;
if ( ~check.indexOf( 'function _classCallCheck' ) ) return INLINE;
if ( ~check.indexOf( 'babelHelpers.classCallCheck' ) ) return BUNDLED;

throw new Error( 'An unexpected situation arose. Please raise an issue at https://github.com/rollup/rollup-plugin-babel/issues. Thanks!' );
}

function assign ( target, source ) {
Expand All @@ -14,9 +24,17 @@ function assign ( target, source ) {
return target;
}

let warned = {};
function warnOnce ( msg ) {
if ( warned[ msg ] ) return;
warned[ msg ] = true;
console.warn( msg );
}

export default function babel ( options ) {
options = assign( {}, options || {} );
var usedHelpers = [];
var bundledHelpers = {};
var inlineHelpers = {};

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

const runtimeHelpers = options.runtimeHelpers;
delete options.runtimeHelpers;

return {
transform ( code, id ) {
if ( !filter( id ) ) return null;

var localOpts = assign({ filename: id }, options );
preflightCheck( localOpts );
const helpers = preflightCheck( localOpts );

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

transformed.metadata.usedHelpers.forEach( helper => {
if ( !~usedHelpers.indexOf( helper ) ) usedHelpers.push( helper );
});
if ( usedHelpers.length ) {
if ( helpers === BUNDLED ) {
usedHelpers.forEach( helper => {
bundledHelpers[ helper ] = true;
});
} else if ( helpers === RUNTIME && !runtimeHelpers ) {
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' );
} else {
usedHelpers.forEach( helper => {
if ( inlineHelpers[ helper ] ) {
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` );
}

inlineHelpers[ helper ] = true;
});
}
}

return {
code: transformed.code,
code: transformed.code.replace( /babelHelpers\./g, 'babelHelpers_' ),
map: transformed.map
};
},
intro () {
// TODO replace babelHelpers.foo with babelHelpers_foo – though first
// we need the ability to find and replace within the generated bundle
return usedHelpers.length ?
buildExternalHelpers( usedHelpers, 'var' ).trim() :
'';
const helpers = Object.keys( bundledHelpers );
if ( !helpers.length ) return '';

return buildExternalHelpers( helpers, 'var' )
.replace( /var babelHelpers.+\n/, '' )
.replace( /babelHelpers\.(.+) = function/g, 'function babelHelpers_$1' )
.replace( /babelHelpers\.(.+) = /g, 'var babelHelpers_$1 = ' )
.replace( 'babelHelpers;', '' ) // not sure where this comes from...
.trim() + '\n';
}
};
}
4 changes: 4 additions & 0 deletions test/samples/runtime-helpers/.babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"presets": [ "es2015-rollup" ],
"plugins": "transform-runtime"
}
3 changes: 3 additions & 0 deletions test/samples/runtime-helpers/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default class Foo {

}
4 changes: 4 additions & 0 deletions test/samples/transform-decorators-legacy/.babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"presets": [ "es2015-rollup" ],
"plugins": [ "transform-decorators-legacy" ]
}
1 change: 1 addition & 0 deletions test/samples/transform-decorators-legacy/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default 42;
24 changes: 21 additions & 3 deletions test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,10 @@ describe( 'rollup-plugin-babel', function () {
this.timeout( 15000 );

it( 'runs code through babel', function () {
var start = Date.now();
return rollup.rollup({
entry: 'samples/basic/main.js',
plugins: [ babelPlugin() ]
}).then( function ( bundle ) {
start = Date.now();
var generated = bundle.generate();

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

assert.ok( code.indexOf( 'babelHelpers.classCallCheck =' ) !== -1, generated.code );
assert.ok( code.indexOf( 'function babelHelpers_classCallCheck' ) !== -1, generated.code );
assert.ok( code.indexOf( 'var _createClass =' ) === -1, generated.code );
});
});
Expand Down Expand Up @@ -106,6 +104,13 @@ describe( 'rollup-plugin-babel', function () {
});
});

it( 'works with transform-decorators-legacy (#18)', function () {
return rollup.rollup({
entry: 'samples/transform-decorators-legacy/main.js',
plugins: [ babelPlugin() ]
});
});

it( 'checks config per-file', function () {
return rollup.rollup({
entry: 'samples/checks/main.js',
Expand All @@ -118,4 +123,17 @@ describe( 'rollup-plugin-babel', function () {
assert.ok( /es2015-rollup/.test( err.message ), 'Expected an error about external helpers or module transform, got "' + err.message + '"' );
});
});

it( 'allows transform-runtime to be used instead of bundled helpers', function () {
return rollup.rollup({
entry: 'samples/runtime-helpers/main.js',
plugins: [ babelPlugin({ runtimeHelpers: true }) ],
onwarn: function ( msg ) {
assert.equal( msg, `Treating 'babel-runtime/helpers/classCallCheck' as external dependency` );
}
}).then( function ( bundle ) {
var cjs = bundle.generate({ format: 'cjs' }).code;
assert.ok( !~cjs.indexOf( 'babelHelpers' ) );
});
});
});

0 comments on commit 2fc82d6

Please sign in to comment.