npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

gulp-browserify-watchify-glob

v1.0.1

Published

Create a Browserify bundler with multiple entries based on a glob pattern. Rebuild incrementally watching for changes, including additions and deletions of files that match the glob pattern.

Downloads

2

Readme

pipeline status coverage report libraries.io dependency status latest version on npm inspect the package.json code hosted on GitLab issue tracker on GitLab author: Julian Gonggrijp license text made with CoffeeScript contributing changelog

gulp-browserify-watchify-glob

Create a Browserify bundler with multiple entries based on a glob pattern. Rebuild incrementally watching for changes, including additions and deletions of files that match the glob pattern.

Motivating example

With just browserify, you can define a bundle based on a fixed set of entries.

const testBundler = browserify({
    entries: ['test1.js', 'test2.js'],
    debug: true,
});

You can build the bundle as often as you want from this definition, but only manually.

testBundler.bundle();

If you combine browserify with gulp, your setup might look like this.

const vinylStream = require('vinyl-source-stream');

const testEntries = ['test1.js', 'test2.js'];

const testBundler = browserify({
    entries: testEntries,
    debug: true,
});

function bundleTests() {
    testBundler.bundle()
        .pipe(vinylStream('testBundle.js'))
        .pipe(gulp.dest('test-directory'));
}

You can watch for changes in the entries and their dependencies by using watchify. This gives you efficient, automatic, incremental rebuilds.

const testEntries = ['test1.js', 'test2.js'];

const testBundler = browserify({
    entries: testEntries,
    debug: true,
    cache: {},
    packageCache: {},
}).plugin('watchify');

function bundleTests() {
    return testBundler.bundle()
        .pipe(vinylStream('testBundle.js'))
        .pipe(gulp.dest('test-directory'));
}

function watch(done) {
    testBundler.on('update', bundleTests);
    // You don't often see the following lines. They ensure a clean
    // exit when you stop the watch task with a keyboard interrupt.
    const finish = () => done(null, testBundler.close());
    process.once('SIGINT', finish);
    process.once('SIGTERM', finish);
}

You might have lots of entries, for example when you have lots of test modules. If this is the case, your entries are best enumerated with a glob.

const glob = require('glob');

const testEntriesGlob = 'src/**/*-test.js';
const testEntries = glob.sync(testEntriesGlob);
// ['src/apple-test.js', 'src/banana-test.js', 'src/cherry/drop-test.js', ...]

Alas, the set of entries becomes fixed again directly after the line above. If you add a test module while the watch task is running, watchify will not include the new module in the bundle. Even worse, if you delete an entry, the build will break.

You could try to work around this by using gulp.watch instead of watchify.

function bundleTests() {
    return browserify({
        entries: glob.sync(testEntriesGlob),
        debug: true,
    }).bundle()
        .pipe(vinylStream('testBundle.js'))
        .pipe(gulp.dest('test-directory'));
}

function watch(done) {
    const watcher = gulp.watch(testEntriesGlob, bundleTests);
    const finish = () => done(null, watcher.close());
    process.once('SIGINT', finish);
    process.once('SIGTERM', finish);
}

Unfortunately, this only watches the entries; it doesn't detect changes in the dependencies. To make matters worse, the rebuild is not incremental anymore; bundleTests redefines and rebuilds the bundle entirely from scratch every time.

gulp-browserify-watchify-glob lets you have the best of both worlds.

const globbedBrowserify = require('gulp-browserify-watchify-glob');

const testBundler = globbedBrowserify({
    entries: testEntriesGlob,
    debug: true,
});

function bundleTests() {
    return testBundler.bundle()
        .pipe(vinylStream('testBundle.js'))
        .pipe(gulp.dest('test-directory'));
}

function watch(done) {
    testBundler.watch(bundleTests)();
    const finish = () => done(null, testBundler.close());
    process.once('SIGINT', finish);
    process.once('SIGTERM', finish);
}

Finally, you can have a dynamic set of entries and rebuild incrementally, whenever an entry or any of its dependencies is modified and whenever an entry is added or deleted.

Usage

Install:

yarn add gulp-browserify-watchify-glob -D
# or
npm i gulp-browserify-watchify-glob -D

Import:

const globbedBrowserify = require('gulp-browserify-watchify-glob');
// or
import globbedBrowserify from 'gulp-browserify-watchify-glob';

globbedBrowserify is a drop-in replacement for the browserify constructor. It accepts the same options and it returns a browserify instance that you can invoke the same methods on.

const bundler = globbedBrowserify({
    entries: 'src/**/*-test.js',
    debug: true,
    // cache and packageCache default to {}
}).transform(/* whatever */);

There are two main differences:

  • You must set the entries option, which may be a glob pattern or an array of glob patterns.
  • The returned bundler has a watch method that you should use instead of other watching mechanisms, such as watchify and gulp.watch.

The watch method causes the bundler to start watching all files that match the glob pattern(s) in the entries and all of their dependencies. It accepts two arguments, an update task and a kickoff task.

const wrappedKickoff = bundler.watch(updateTask, kickoffTask);

The kickoff task is optional. If omitted, it is assumed to be the same as the update task. Both tasks should be functions that meet the requirements of a gulp task and both should call bundler.bundle() internally. The kickoff task should contain the necessary steps for the first build while the update task will be invoked on every update.

function updateTask() {
    // main option one: return a stream that starts with the build output
    return bundler.bundle()
        .pipe(...);
}

function kickoffTask(done) {
    // main option two: handle the complete build output with a callback
    bundler.bundle((error, buffer) => {
        if (error) return done(error);
        doSomethingAsyncWith(buffer, done);
    });
}

The return value wrappedKickoff is a wrapper of the kickoff task that still needs to be invoked in order to trigger the first build. This is not done automatically to facilitate async workflows. If you want to trigger the first build immediately, simply invoke the return value:

bundler.watch(updateTask, kickoffTask)();

Once the watch method has been invoked, the bundler also has a close method which you can invoke to stop watching the entries glob(s) and the dependencies.

bundler.close();

gulp-browserify-watchify-glob wraps the tasks to prevent race conditions. If you have another (third, fourth...) task that also invokes bundler.bundle(), wrap it so that builds will not be triggered while a previous build is still in progress.

const wrappedOtherTask = bundler.wrap(otherTask);

Caveat

Browserify was not designed with this use case in mind. I rely on browserify implementation details in order to make the magic work.