browserify / watchify

watch mode for browserify builds
Other
1.79k stars 181 forks source link

Emit `update` events without `w.bundle()` #279

Closed MadLittleMods closed 9 years ago

MadLittleMods commented 9 years ago

Currently:

Important: Watchify will not emit 'update' events until you've called w.bundle() once and completely drained the stream it returns. https://github.com/substack/watchify#watchifyb-opts


Use case:

When using with gulp, I can't find a way around the extra bundle when making standalone and watch tasks and composing them together.

Here is a overview snippet of what I am currently doing. When calling the default gulp task: bundle gets called once in the build task and once when the watch starts up(to start the update events). Although the time when watch starts up is not critical(because it is after the entire build), I would like to try to avoid it somehow.

I could create a separate build stack the doesn't include build-scripts-browserify but that feels a bit messy.


Link to the official gulp browserify recipe: https://github.com/gulpjs/gulp/blob/master/docs/recipes/fast-browserify-builds-with-watchify.md

Reading through https://github.com/substack/watchify/issues/254 I understand that watchify grabs the dependencies to watch via the w.bundle() call. Perhaps it isn't possible to avoid at the moment but an alternative glob option could solve this.

zertosh commented 9 years ago

This is kinda what I do. The idea is to realize that the browserify b instance can be applied the watchify plugin whenever. And that b.bundle() is just a stream like any other:

var _bundle;
var getBundle = function() {
  if (_bundle) return _bundle;
  var b = browserify({
    entries: [/***/],
    cache: {},
    packageCache: {}
  });
  b.transform(/****/);
  b.vinyl = function() {
    return b.bundle()
      .pipe(source(/***/))
      .pipe(sourcemaps.init()) // whatever else
      .pipe(gulp.dest(/**/));
  };
};

gulp.task('scripts', function() {
  return getBundle().vinyl();
});

gulp.task('watch', function() {
  var b = getBundle();
  b.plugin(watchify);
  // "suck" the data out w/o any extra work - don't even write to disk
  b.bundle().once('end', function(){});

  b.on('update', function() {
    b.vinyl();
  });
});

This allows you call getBundle from any number of tasks and not worry about creating multiple instances. And you've separated the b.bundle() from all the gulp stuff.

This is just an example to help you figure out what works for you.

zertosh commented 9 years ago

Here's another example: https://github.com/substack/watchify/pull/213#issuecomment-97636601

MadLittleMods commented 9 years ago

@zertosh I have this sort of thing set up already but I am trying to avoid the extra bundle when watch starts up, in your example b.bundle().once('end', function(){});.

@jskrzypek brought up a reasonable solution in the gulpjs/gulp room to just move the watchify instance outside, hook onto update event in the watch task and only bundle if it hasn't been called before on that instance(state variable).

zertosh commented 9 years ago

Ohhhhh... you just don't want to call bundle again, if its been called. There are lots of ways to figure this out, here is one:

var b = browserify({/**/});
b.once('bundle', function() { b.hasRunOnce = true; });

// In your watch task

if (!b.hasRunOnce) {
  b.bundle();
}
b.on('update', /***/)
MadLittleMods commented 9 years ago

@zertosh :+1: looks like the solution I described from @jskrzypek. I think this is the way to go.

I updated the gist with what I ended up with: https://gist.github.com/MadLittleMods/133ac3a8fdeebf6c642c