Gulp and Browserify/Webpack in Sage?

Hi!

First of all, thank you for an excellent started theme and a great introduction video. I’m a long time Roots user, and the upgrade from Roots to Sage went seamless even for older projects. The introduction of Browsersync especially is great, literally shaves hours of work off the old workflow.

Anyway, I’m interested in somehow including either Browserify or Webpack into the Gulpfile for my JS, as I want to start using React and handle my dependencies a bit better. I’ve managed to get Browserify working in the gulpfile through a separate task, but I just can’t seem to integrate it into the main JS task. I was wondering if someone a bit more experienced and knowledgable could help me shoehorn my code into the main JS task. Here’s the separate gulp task I have working now:

gulp.task('react', function(){
browserify({
    'entries' : ['./assets/scripts/react/app.jsx'],
    'bundleName': 'react.js',
    'sourcemap' : true,
    'transform' : [ 'reactify' ],
    'global'    : true
  })
    .bundle()
    .pipe(source('react.js'))
    .pipe(buffer())
    .pipe(gulp.dest('./assets/scripts/'));
});

As you can see it’s a bit limited, but at least it’s working. Anyway, integrating this into the main JS task somehow would be awesome. Anyone got any ideas about how I’d go about doing that?

Thanks a ton in advance!

Hey @meeko!

Glad you are having a good time with Sage! I’ve got some stuff due soon so I can’t go super in depth with actual code, however I can point you in the right direction:

constraints of main js task

So the current main JS task is definitely built with a couple constraints in mind:

  • Needs to be compatible with WordPress use-cases: this means it needs to be compatible with all the random jQuery plugins and stuff WordPress users might come across. Also needs to be able to work with existing plugin JS.
  • Bower compatibility: as an extension of the first constraint, it needs to work with bower. npm (a proper registry) just doesn’t have the amount of modules WP people would need. Having a module on the npm registry also requires active maintenance on the dev side which isn’t the case with random jQuery plugins.

Differences between WordPress and regular webapp

The main js task is going to handle everything in WordPress land. That means concat and optimizing plugin js, extracting jquery into it’s own file, extracting modernizr, etc. All that stuff.

When you use something like browserify your best option is to keep those two worlds separate. Truth be told: your react app doesn’t even know WordPress exists. If done properly you could probably use the same bundle.js on a static site on AWS as you could your wordpress site.

Recommendations

The way you are doing it is fine

Having a separate task for browserify is a good idea. Treat your browserify build as a separate app from WordPress. You are going to have to have separate tasks for running your tests and stuff anyway.

Gulp task

Here is approximately what I do if I need to integrate gulp and browserify:

var gulp = require('gulp');
var browserify = require('browserify');
var transform = require('vinyl-transform');
var uglify = require('gulp-uglify');
gulp.task('browserify', function () {
  var browserified = transform(function(filename) {
    var b = browserify(filename);
    return b.bundle();
  });

  return gulp.src(['./path/to/file.js']) 
    .pipe(browserified)
    .pipe(uglify())
    .pipe(gulp.dest('./dist'));
});

An explantation behind what is going on is available at this great article: gulp + browserify, the gulp-y way | by Hafiz Ismail | wehavefaces

In my experience: doing things the “gulp way” is kinda slower than regular ol’ browserify transforms but I recommend tackling that problem only when it becomes a problem. When that becomes a problem I recommend you move to browserify transforms+watchify.

3 Likes

When attempting to use vinyl-transform as above, I get the following error:

Starting 'browserify'...
_stream_readable.js:540
    var ret = dest.write(chunk);
                   ^
TypeError: undefined is not a function
    at Producer.ondata (_stream_readable.js:540:20)
    at Producer.emit (events.js:107:17)
    at Producer.Readable.read (_stream_readable.js:373:10)
    at flow (_stream_readable.js:750:26)
    at resume_ (_stream_readable.js:730:3)
    at _stream_readable.js:717:7
    at process._tickCallback (node.js:355:11)

This is documented at the browserify repo. It looks like the solution is to use vinyl-source-stream instead, but that seems a little clunky to me.

Does that make sense to you? I’m not actually sure exactly how to put all these modules together in the gulpfile. (I’m a SPA novice, and I don’t know gulp all that well, so forgive me if I’m just missing something dumb.)

PS: If it makes any difference, I’m using Riot.

After much poking around, I cobbled together a Gulpfile that works for me using vinyl-source-stream and vinyl-buffer, instead of vinyl-transform:

var browserify   = require('browserify');
var riotify      = require('riotify');
var vSource      = require('vinyl-source-stream');
var buffer       = require('vinyl-buffer');
var globify      = require('require-globify');

...

// ### Riot Compilation
gulp.task('browserify', function(){
  var s = path.source + 'scripts/app.js';
  browserify({
    entries:    s,
    transform:  ['require-globify', 'riotify']
  })
  .bundle()
  .pipe(vSource('app.js'))
  .pipe(buffer())
  .pipe(sourcemaps.init({loadMaps: true}))
  .pipe(uglify({ mangle: false }))
  .pipe(sourcemaps.write('./'))
  .pipe(gulp.dest(path.dist + 'scripts'));
});

Hopefully that helps somebody.

(And obviously, if anybody has tips as to how I can improve this task, please chime in–this is the first time I’ve really been able to dive into Gulp and SPAs.)

I created a browserify task based on the examples here. It looks very similar to the others but uses the jsTasks function to do all of the same common tasks like linting and minification that it does.

// ### Browserify
gulp.task('browserify', function(){
  var s = path.source + 'scripts/app.js';
  browserify({
    entries:    s,
    transform:  ['vueify']
  })
    .bundle()
    .pipe(source('app.js'))
    .pipe(buffer())
    .pipe(jsTasks('app.js'))
    .pipe(writeToManifest('scripts'))
    .pipe(gulp.dest(path.dist + 'scripts'));
});

writeToManifest is also important so that you can leverage asset revisioning :rocket:

1 Like