Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

gulp-browserify-watchify-glob

Package Overview
Dependencies
Maintainers
1
Versions
6
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

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.

  • 1.0.1
  • latest
  • Source
  • npm
  • Socket score

Version published
Maintainers
1
Created
Source

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.

Keywords

FAQs

Package last updated on 23 Oct 2019

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc