Search code examples
gulpecmascript-6browserifybabeljs

Gulp - using ES6 modules in a combined js file?


I am trying to use ES6 modules in my current GULP setup. I've read that this is yet to be supported by browsers or Babel, so there is a need some elaborate setup to make this work, using things such Browserify, babelify, vinyl-source-stream. (Seems extremely complex setup).

What I want is different from examples I had found online. All the examples are with external files being imported, and I really don't want that. I want all the files to be bundled into a single file, with all the modules there already. Here's what I have:

My current GULP setup is like this:

gulp.task('buildJS', function() {
    var src = [
        './js/dist/app.js',
        './js/dist/templates.js',
        './js/dist/connect.js',
        './js/dist/config.js',
        './js/dist/utilities.js',
        './js/dist/components/*.js',
        './js/dist/pages/**/*.js',
        './js/dist/modals/*.js',
        './js/dist/init.js' // must be last
    ];

    gulp.src(src)
        .pipe(concat('app.js'))
        .pipe(babel({modules:"common"})) // I have no idea what "modules" actually does
        .pipe(gulp.dest('../js/'))

});

And this is an example of a component file in /js/dist/components/.
There are many files like this, and they are all combined to a single file.

module "components/foo" {
    export function render(settings) {
        return ...
    }
}

So later in some page controller I would use it:

import { render } from "components/foo";

Question:

Now that I have a single file, (been transformed using Babel), how can I use the modules via Import?


Solution

  • Since the end of 2015 I have been using rollupjs in order to create a bundle of ES2015 (ES6) modules, so I could use import/export freely in my code.


    I've found Rollupjs to be very good and easy to use. The people behind it are great people which devote themselves to the project. I've had many questions which I had posted on the project's Github issues page and I always got answered pretty quickly.


    Setup includes these rollupjs plugins:

    1. rollup (basic rollupjs bundler)
    2. rollup-plugin-babel (converts ES2015 code to ES5 or earlier, for legacy browsers support)
    3. rollup-plugin-eslint (verify the javascript code is valid)
    4. rollup-plugin-uglify (minify the code, to make it smaller)
    5. rollup-plugin-progress (shows bundle progress in terminal. shows which file being "worked on")
    6. beepbeep (Make a console beep sound. I use this to inform me of compilaction errors)

    Simplified GULP setup I'm using:

    var gulp               = require('gulp'),
        gutil              = require('gulp-util'),
        rollup             = require('rollup').rollup,
        babelRollup        = require('rollup-plugin-babel'),
        eslintRollup       = require('rollup-plugin-eslint'),
        uglifyRollup       = require('rollup-plugin-uglify'),
        rollupProgress     = require('rollup-plugin-progress'),
        beep               = require('beepbeep');
    
    // ESlint 
    var eslint_settings = {
        rulePaths: [],
        rules: {
            "no-mixed-spaces-and-tabs" : [2, "smart-tabs"],
            "block-spacing"            : [2, "always"],
            "comma-style"              : [2, "last"],
            "no-debugger"              : [1],
            "no-alert"                 : [2],
            "indent-legacy"            : [1, 4, {"SwitchCase":1}],
            'strict'                   : 0,
            'no-undef'                 : 1
        },
        ecmaFeatures : {
            modules: true,
            sourceType: "module"
        },
        "parserOptions": {
            "ecmaVersion" : 6,
            "sourceType": "module",
            "ecmaFeatures": {
                "jsx": false,
                "experimentalObjectRestSpread": true
            }
        },
        globals : ['$', '_', 'afterEach', 'assert', 'beforeEach', 'Cookies', 'd3', 'dataLayer', 'describe', 'done', 'expect', 'ga', 'it', 'jQuery', 'sinon'], baseConfig: {
            //parser: 'babel-eslint',
        },
        envs: [
            'browser', 'es6'
        ]
    };
    
    
    // Rollup plugins configuration
    function getRollupPlugins( settings = {} ){
        var rollupPlugins = [];
    
        rollupPlugins.push({
            presets        : [['es2015', {"modules": false}]], //['es2015-rollup'],
            runtimeHelpers : true,
            exclude        : 'node_modules/**',
            plugins        : ["external-helpers"]
        });
    
        rollupPlugins.push(eslintRollup( Object.assign({throwOnError:true}, eslint_settings) ))
    
        rollupPlugins.push(rollupProgress({
             clearLine:true // default: true
        }))
    
        // I would advise Babel to only be used for production output since it greatly slower bundle creation
        if( settings.ENV == 'production' ){
            rollupPlugins.push(uglifyRollup())
            rollupPlugins.push(babelRollup(rollupPlugins__babel));
        }
    
        return rollupPlugins;
    }
    
    var rollupPlugins = getRollupPlugins();
    
    /**
     * a generic Rollup bundle creator
     * @param  {String} outputPath     [where to save the bundle to (must end with /)]
     * @param  {String} outputFileName [bundle file name]
     * @param  {String} entryFile      [rollup entry file to start scanning from]
     * @return {Object}                [Promise]
     */
    function rollupBundle(outputPath, outputFileName, entryFile, bundleOptions){
        bundleOptions = bundleOptions || {};
        bundleOptions.plugins = bundleOptions.plugins || rollupPlugins;
    
        return new Promise(function(resolve, reject) {
            outputFileName += '.js';
            var cache;
    
            // fs.truncate(outputPath + outputFileName, 0, function() {
            //     gutil.log( gutil.colors.dim.gray('Emptied: '+ outputPath + outputFileName) );
            // });
    
            rollup({
                entry   : entryFile,
                plugins : bundleOptions.plugins,
                cache   : cache
            })
            .then(function (bundle) {
                var bundleSettings = {
                        format    : bundleOptions.format || 'umd',
                        sourceMap : false,
                        banner    : config.banner
                    },
                    result = bundle.generate(bundleSettings),
                    mapFileName = outputFileName + '.map',
                    sourceMappingURL = '\n//# sourceMappingURL='+ mapFileName;
    
                cache = bundle;
    
                // if folder does not exists, create it
                if( !fs.existsSync(outputPath) ){
                    gutil.log( gutil.colors.black.bgWhite('Creating directory ' + outputPath) );
                    fs.mkdirSync(outputPath);
                }
    
                // save bundle file to disk
                fs.writeFile( outputPath + outputFileName, result.code + (bundleSettings.sourceMap ? sourceMappingURL : ''), function(){
                    resolve();
                });
    
                // save map file to disk
                if( bundleSettings.sourceMap )
                    fs.writeFile( outputPath + mapFileName, result.map.toString());
            })
            .catch(function(err){
                beep(1);
                gutil.log( gutil.colors.white.bgRed('Rollup [catch]: ', err.stack) );
                resolve();
            })
        });
    }
    
    // This task bundles the main application, using an entry file which itself has many imports, 
    // and those imports also has imports.. like a tree branching
    gulp.task('bundle-app', ()=>{
        return rollupBundle('../dist/js/', 'app', 'js/dist/app.js', {format:'cjs'});
    });