Compile and Bundle ES6 imports and exports into Enqueued JS

Continuing the discussion from Using es6 version of main.js:

Hey folks, I have JavaScript files making use of import, require, and export statements that i’d like to compile with Babel and include in the Sage 8 asset-builder.

The file that needs compiling is declared in manifest.json rather than being included in main.js.

...
 },
    "city.js": {
      "files": [
        "scripts/routes/city.js"
      ]
    }
...

I’ve gotten as far as installing gulp-babel and making the necessary changes to gulp.js though the imports look strange. i.e…

import CityPageModel from '../models/City'

compiles to:

var _City = require('../models/City');

Not sure if this an issue with the asset path or the compiler but shouldn’t this statement pull in the file contents and compile everything together into the importing file just like main.js?

Here’s a portion of my gulp file:

var filter       = require('gulp-filter');
var babel        = require("gulp-babel");

....

var jsTasks = function(filename) {
  var f = filter(project.js, {restore: true});
  return lazypipe()
    .pipe(function() {
      return gulpif(enabled.maps, sourcemaps.init());
    })
    .pipe(function() {
      return f;
    })
    .pipe(function() {
      return babel({ presets: ['es2015'] });
    })
    .pipe(function() {
      return f.restore;
    })
    .pipe(concat, filename)
    // .pipe(uglify, {
    //   compress: {
    //     'drop_debugger': enabled.stripJSDebug
    //   }
    // })
    .pipe(function() {
      return gulpif(enabled.rev, rev());
    })
    .pipe(function() {
      return gulpif(enabled.maps, sourcemaps.write('.', {
        sourceRoot: 'assets/scripts/'
      }));
    })();
};

The help is appreciated!

Babel doesn’t bundle the files, if you want to use import you have to use babel AND webpack or browserify, it will bundle import and require files.
Webpack is really better than concat for handling javascript files, but is a bit hard to configure. I will send you a config that works with gulp and webpack tomorrow inchaallah.

This config is not really compatible with sage 8 because sage 8 use manifest.json to list the js files.

In the following config, we put all dependencies in a js file bundle.js whose content is like this:

import 'js/ticker.js';
import 'app/app.js';
import 'app/config.js';

Gulp will take this file (bundle.js) and send it to webpack :

gulpfile.js :

gulp.task('js', ['lint.js'], (done) => {
  gulp.src(paths.src + 'bundle.js')
    .pipe(webpack(require('./webpack.config.js')))
    .pipe(gulp.dest(paths.dist))
    .on('end', done);
});

Webpack will take the bundle.js:

  • babel will make all files compatibles ES5 (import will become require)
  • Webpack will resolve the require and bundle all dependencies in bundle.js file (require is only for nodejs and not for javascript run in a browser)

webpack.config.js:

// source : http://stackoverflow.com/questions/36105790/how-to-use-webpack-and-gulp-with-multiple-entry-points-to-transpile-app-and-test
// and : http://stackoverflow.com/questions/25956937/how-to-build-minified-and-uncompressed-bundle-with-webpack
var path = require('path');
var webpack = require('webpack');
var paths = {
  js: ['hooks/**/*.js', 'src/*.js', 'src/app/**/*.js', 'src/js/*.js', 'gulpfile.js'],
  src: './src/',
  dist: './www/'
};

module.exports = {
  entry: {
    bundle: paths.src + 'bundle.js'
  },
  output: {
    filename: '[name].js',
    sourceMapFilename: '[name].map'
  },
  devtool: 'source-map',
  module: {
    loaders: [
      {
        loader: 'babel',
        test: /\.jsx?$/,
        exclude: /(node_modules|bower_components)/,
        query: {
          presets: ['es2015'],
          plugins: ['angularjs-annotate']
        }
      }
    ]
  },
  plugins: [
    new webpack.optimize.UglifyJsPlugin({
      // include: /\.min\.js$/,
      // minimize: true
    })
  ],
  resolve: {
    root: path.resolve(paths.src),
    extensions: ['', '.js']
  }
};

You won’t need angularjs-annotate I think

Some packages and versions that I use:

"webpack": "^1.14.0",
"webpack-stream": "^3.2.0"
"babel-core": "^6.21.0",
"babel-eslint": "^7.1.1",
"babel-loader": "^6.2.10",
"babel-plugin-angularjs-annotate": "^0.7.0",
"babel-preset-es2015": "^6.18.0"

It is not from a sage project, so you will have to adapt the configs. Be careful that if you return a lazypipe, you have to pipe a function like in your config.

The main difference between this method and using gulp-concat is that gulp-concat take all javascript files and concat it in a big file. Webpack actually resolve import and require and give you a big file at the end.

You can also use Rollup instead of Webpack to bundle the files.

Rollup for gulp: https://www.npmjs.com/package/gulp-better-rollup
Rollup plugin for resolving node modules imports: http://npmjs.com/package/rollup-plugin-node-resolve
Rollup plugin for also bundling CommonJS files: https://www.npmjs.com/package/rollup-plugin-commonjs
Rollup plugin for babel: https://www.npmjs.com/package/rollup-plugin-babel

Then, you could do something like this:

const gulp = require('gulp')
...
...
...
const betterRollup = require('gulp-better-rollup')
const rollUpBabel = require('rollup-plugin-babel')
const rollUpCommonjs = require('rollup-plugin-commonjs')
const rollUpNodeResolve = require('rollup-plugin-node-resolve')

var jsTasks = function(filename) {
  var f = filter(project.js, {restore: true});
  return lazypipe()
    .pipe(function() {
      return gulpif(enabled.maps, sourcemaps.init());
    })
    .pipe(function() {
      return f;
    })
    .pipe(betterRollup({
      plugins: [
        rollUpNodeResolve({
          module: true,
          jsnext: true,
          main: true,
          browser: true,
          extensions: ['.js'],
          preferBuiltins: true,
        }),
        rollUpCommonjs(),
        rollUpBabel(),
      ],
    }, {
      format: 'iife',
    }))
    .pipe(function() {
      return f.restore;
    })
    .pipe(concat, filename)
    .pipe(function() {
      return gulpif(enabled.rev, rev());
    })
    .pipe(function() {
      return gulpif(enabled.maps, sourcemaps.write('.', {
        sourceRoot: 'assets/scripts/'
      }));
    })();

@kaisermann is this working with sage8.5.1 ? I have some es6 js im trying to include into my build.

Thanks for the heads up

[09:18:43] ‘build’ errored after 6.4 s
[09:18:43] ReferenceError in plugin 'run-sequence(scripts)'
Message:
filter is not defined
Stack:
ReferenceError: filter is not defined

The sample I wrote should work with sage, however, the “filter(project.js…” part was based on the OP’s code, not sage’s (if my mind’s not failling me). You can remove the var f = filter... and the pipe using the f.

Hmm, yeah no dice. I have a collectoin of es6 js files from another developer and I am not super knowlagable with es6. I just need to get them. wrapped into my projects main.js. I might be going at this in the wrong way given my lack of knowledge at this point. Anyways here is my dump

[09:44:08] ‘scripts’ errored after 5.9 ms
[09:44:08] Error: Invalid call to lazypipe().pipe(): argument is not a function.
Remember not to call stream creation functions directly! e.g.: pipe(foo), not pipe(foo())
at validateStep (/Sites/resortshare/htdocs/wp-content/themes/resortshare/node_modules/lazypipe/index.js:11:10)
at Function.build.pipe (/Sites/resortshare/htdocs/wp-content/themes/resortshare/node_modules/lazypipe/index.js:36:4)
at jsTasks (/Sites/resortshare/htdocs/wp-content/themes/resortshare/gulpfile.js:144:6)
at /Sites/resortshare/htdocs/wp-content/themes/resortshare/gulpfile.js:223:15
at arrayEach (/Sites/resortshare/htdocs/wp-content/themes/resortshare/node_modules/lodash/index.js:1289:13)
at Function. (/Sites/resortshare/htdocs/wp-content/themes/resortshare/node_modules/lodash/index.js:3345:13)
at module.exports.Manifest.forEachDependency (/Sites/resortshare/htdocs/wp-content/themes/resortshare/node_modules/asset-builder/lib/Manifest.js:47:5)
at Gulp. (/Sites/resortshare/htdocs/wp-content/themes/resortshare/gulpfile.js:219:12)
at module.exports (/Sites/resortshare/htdocs/wp-content/themes/resortshare/node_modules/orchestrator/lib/runTask.js:34:7)
at Gulp.Orchestrator._runTask (/Sites/resortshare/htdocs/wp-content/themes/resortshare/node_modules/orchestrator/index.js:273:3)
[09:44:08] ‘build’ errored after 6.42 s
[09:44:08] Error in plugin 'run-sequence(scripts)'
Message:
Invalid call to lazypipe().pipe(): argument is not a function.
Remember not to call stream creation functions directly! e.g.: pipe(foo), not pipe(foo())
Stack:
Error: Invalid call to lazypipe().pipe(): argument is not a function.
Remember not to call stream creation functions directly! e.g.: pipe(foo), not pipe(foo())
at validateStep (/Sites/resortshare/htdocs/wp-content/themes/resortshare/node_modules/lazypipe/index.js:11:10)
at Function.build.pipe (/Sites/resortshare/htdocs/wp-content/themes/resortshare/node_modules/lazypipe/index.js:36:4)
at jsTasks (/Sites/resortshare/htdocs/wp-content/themes/resortshare/gulpfile.js:144:6)
at /Sites/resortshare/htdocs/wp-content/themes/resortshare/gulpfile.js:223:15
at arrayEach (/Sites/resortshare/htdocs/wp-content/themes/resortshare/node_modules/lodash/index.js:1289:13)
at Function. (/Sites/resortshare/htdocs/wp-content/themes/resortshare/node_modules/lodash/index.js:3345:13)
at module.exports.Manifest.forEachDependency (/Sites/resortshare/htdocs/wp-content/themes/resortshare/node_modules/asset-builder/lib/Manifest.js:47:5)
at Gulp. (/Sites/resortshare/htdocs/wp-content/themes/resortshare/gulpfile.js:219:12)
at module.exports (/Sites/resortshare/htdocs/wp-content/themes/resortshare/node_modules/orchestrator/lib/runTask.js:34:7)
at Gulp.Orchestrator._runTask (/Sites/resortshare/htdocs/wp-content/themes/resortshare/node_modules/orchestrator/index.js:273:3)