Notes

React, Browserify & Gulp

21 Jun 2014

Update: I looks like gulp-browserify has been blacklisted see Gulp & Browserify Revisited for details of the changes.

var buildView = function (view) {
  return function () {
    return browserify("./views/" + view + ".jsx")
      .bundle()
      .pipe(source(view + ".js"))
      .pipe(gulp.dest("dist"));
  };
};

I’ve been experimenting with browserify and gulp for building a react project. I’ve now got it into a position where it’s more or less doing what I want so I thought I’d write about it.

Previously I’d been looking at require.js, what I liked about it was that it could be run purely in the browser with no compile step. What I was finding was that it wasn’t working out too well with browser caching and so I thought I’d spend some time looking at browserify as it also has the advantage slightly less boilerplate code in each file.

I had actually started with grunt rather than gulp but I couldn’t work out how to do what I wanted so switched to gulp with which I’m more familiar and who’s code over configuration outlook seemed more likely to work out for what I wanted to do.

The problem I was trying to solve is that I want a fast build where only what’s needed gets compiled but I also want to be able to generate intermediate components as well as the full app so they can be tested / developed in isolation.

With gulp (since it’s scripted) I can generate build tasks for each of my components so I can watch or compile individual components when needed.

So npm’s the beast for getting this stuff going, everything’s installed to --save-dev locally but gulp is also installed globally ( -g ) so you can use if from the command line.

So this leaves us with imports (for the gulpfile.js) that look like this:

var gulp = require("gulp");
var rename = require("gulp-rename");
var browserify = require("gulp-browserify");

What’s interesting here is that we don’t require reactify, we let browserify require it when it needs it.

I then have a list of all my components - this could probably be generated by listing all jsx files but for now it’s quicker to just list them:

var components = ["ComponentA", "ComponentB", "ComponentC"];

The secret sauce is having a task generator function, so tasks can be generated for each of the components:

var createJsxTask = function (componentFile) {
  return function () {
    gulp
      .src(componentFile)
      .pipe(
        browserify({
          transform: ["reactify"], // here's how we use reactify
        })
      )
      .pipe(rename({ extname: ".js" }))
      .pipe(gulp.dest("dist"));
  };
};

So browserify will follow all of the component’s dependencies and use reactify to transform the jsx into JavaScript, we then rename the file and dump it into the dist directory. So after this each ComponentX.js file under dist will be the component with all it’s dependencies burned in.

Similary for I have a second generator that generates a watch task for each component:

var createWatchTask = function (componentName) {
  return function () {
    gulp.watch(["components/*.jsx", "scripts/*.js"], [componentName]);
  };
};

I had a little look at gulp-watchify which I believe would be more efficient (since I’m watching all files and edits to unrelated files will still cause a re-build) but couldn’t get it to work with react.

So all that’s left is a simple loop that generates the two tasks per component:

components.forEach(function (componentName) {
  gulp.task(
    componentName,
    createJsxTask("components/" + componentName + ".jsx")
  );
  gulp.task("watch" + componentName, createWatchTask(componentName));
});

And that’s it, in my version I did the same for stylesheets by having one less file per component (which @imports its dependencies) and similiary generated tasks for them.