diff --git a/package.json b/package.json index b55a35d..5c40f84 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "rollup-pluginutils": "^1.1.0" }, "devDependencies": { + "babel-plugin-transform-runtime": "^6.3.13", "babel-preset-es2015": "^6.1.18", "babel-preset-es2015-rollup": "^1.0.0", "babel-register": "^6.3.13", diff --git a/src/index.js b/src/index.js index 73f0900..8148fa1 100644 --- a/src/index.js +++ b/src/index.js @@ -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( '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 ) { @@ -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; @@ -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'; } }; } diff --git a/test/samples/runtime-helpers/.babelrc b/test/samples/runtime-helpers/.babelrc new file mode 100644 index 0000000..30d8df0 --- /dev/null +++ b/test/samples/runtime-helpers/.babelrc @@ -0,0 +1,4 @@ +{ + "presets": [ "es2015-rollup" ], + "plugins": "transform-runtime" +} diff --git a/test/samples/runtime-helpers/main.js b/test/samples/runtime-helpers/main.js new file mode 100644 index 0000000..b0cee91 --- /dev/null +++ b/test/samples/runtime-helpers/main.js @@ -0,0 +1,3 @@ +export default class Foo { + +} diff --git a/test/test.js b/test/test.js index bee8043..fce284c 100644 --- a/test/test.js +++ b/test/test.js @@ -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; @@ -46,7 +44,7 @@ describe( 'rollup-plugin-babel', function () { }); }); - it( 'adds helpers', function () { + it.only( 'adds helpers', function () { return rollup.rollup({ entry: 'samples/class/main.js', plugins: [ babelPlugin() ] @@ -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 ); }); }); @@ -118,4 +116,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' ) ); + }); + }); });