common-bundle
A browserify
plugin for packing modules into common shared bundles.
Features:
- Group one or more entries (modules) together to create a bundle.
- Extract common modules from bundles to create additional shared bundles.
-
b.bundle()
generates a stream flowingvinyl
file objects.
Example
input:
One bundle for each page
var browserify = require('browserify')
var glob = require('glob')
var basedir = '/path/to/src'
var entries = glob.sync('page/**/index.js', { cwd: basedir })
var b = browserify(entries, { basedir: basedir })
b.plugin('common-bundle', {
// Pack each index.js and their dependencies into one bundle
// (with a path like page/**/index.js)
groups: 'page/**/index.js',
})
var vfs = require('vinyl-fs')
// Write all bundles to the build directory
b.bundle().pipe(vfs.dest('/path/to/build'))
output:
One additional bundle shared by all page-specific bundles
var browserify = require('browserify')
var glob = require('glob')
var basedir = '/path/to/src'
var entries = glob.sync('page/**/index.js', { cwd: basedir })
var b = browserify(entries, { basedir: basedir })
b.plugin('common-bundle', {
groups: 'page/**/index.js',
common: {
// create a common bundle for all bundles
// whose path matches **/*.js
output: 'common.js',
filter: '**/*.js',
},
})
var vfs = require('vinyl-fs')
b.bundle().pipe(vfs.dest('/path/to/build'))
output:
factor-bundle
The default output of factor-bundle
would be:
NOTE:
factor-bundle
may pack modules, that are not needed by some pages, into the common bundle. common-bundle
will try to make sure every page load only necessary modules.
Shared bundle for each group of page-specific bundles
var browserify = require('browserify')
var glob = require('glob')
var basedir = '/path/to/src'
var entries = glob.sync('page/**/index.js', { cwd: basedir })
var b = browserify(entries, { basedir: basedir })
b.plugin('common-bundle', {
groups: 'page/**/index.js',
common: [
{
output: 'common-hello-and-hi.js',
filter: ['page/hello/index.js', 'page/hi/index.js']
},
{
output: 'common-red-and-green.js',
filter: ['page/red/index.js', 'page/green/index.js']
},
],
})
var vfs = require('vinyl-fs')
b.bundle().pipe(vfs.dest('/path/to/build'))
output:
Usage
var browserify = require('browserify')
var b = browserify(entries, bopts)
b.plugin('common-bundle', options)
options
groups
Specify some entries and create a bundle for containing them and their dependencies.
typeof options.groups === 'object'
options.groups.output
Specify the path to the new bundle
Type: String
output
should be a path relative to the final build directory.
{
// entries with a pattern like page/**/index.js will be packed into bundle.js
output: 'bundle.js',
filter: 'page/**/index.js',
}
Type: Function
filter
is ignored.
output
will be called with each module file path,
and the returned value (if there is any) is used as the file path to the new bundle.
{
output: function (file) {
if (file.endsWith('/page/A/index.js')) {
return 'page/A/index.js'
}
if (file.endsWith('/page/B/index.js')) {
return 'page/B/index.js'
}
},
}
Type: Falsy
If options.groups.filter
says that the given module should go to a new bundle,
path.relative(basedir, moduleFile)
is used as the file path to the new bundle.
options.groups.filter
Specify the the entries that should go to the new bundle
Type: String
, Array
Passed to multimatch
to test module files.
Relative patterns will be resolved to absolute paths from basedir.
Type: Function
Called with each module file path.
If true
returned, that module will be packed into the new bundle.
typeof options.groups === 'string'
b.plugin('common-bundle', { groups: pattern })
is equivalent to
b.plugin('common-bundle', { groups: { filter: pattern } })
.
typeof options.groups === 'function'
b.plugin('common-bundle', { groups: fn })
is equivalent to
b.plugin('common-bundle', { groups: { output: fn } })
.
Array.isArray(options.groups) === true
Each element is processed as a groups
option.
[
{
output: 'page/A/index.js',
filter: 'page/A/index.js',
},
{
output: 'page/B/index.js',
filter: 'page/B/index.js',
},
]
common
After processing options.groups
, some bundles have been created,
which are called original bundles.
The original bundles (or some of them)
may share a lot of common modules.
We can use options.common
to create common bundles
containing the shared modules,
and remove them from the original bundles.
NOTE If there is only one single original bundle, this option is ignored.
typeof options.common === 'object'
options.common.output
Specify the file path to the new common bundle.
Type: String
// Modules shared by all original bundles go to `common.js`
b.plugin('common-bundle', {
common: {
// the file path to the new common bundle
output: 'common.js',
// the filter glob will be used to match original bundles
filter: '**/*.js',
},
})
options.common.filter
Specify which group of bundles should share the new common bundle.
Type: String
, Array
Passed to multimatch
to test bundle files.
Type: Function
Receives all the bundles created yet, and should return an array of some of them.
Type: otherwise
The new common bundle is shared by all bundles created yet.
b.plugin('common-bundle', { common: 'common.js' })
Array.isArray(options.common) === true
Each element is treated as an options.common
to create bundles.
b.plugin('common-bundle', {
// create original page-specific bundles
// with path `page/**/index.js` for each page A, B, C, D
groups: 'page/**/index.js',
common: [
{
// new common bundle
output: 'ab.js',
// match against the four page-specific bundles
filter: ['page/A/index.js', 'page/B/index.js'],
},
{
// new common bundle
output: 'cd.js',
// match against the four page-specific bundles and `ab.js`
filter: ['page/C/index.js', 'page/D/index.js'],
},
{
// new common bundle
output: 'common.js',
// match against the four page-specific bundles,
// `ab.js`, and `cd.js`
filter: ['ab.js', 'cd.js'],
},
],
})
The first configuration is processed the way above to create a new common bundle.
However, when processing the second,
its filter
will match against all the known bundles,
including the original bundles
and the common bundle created from the first configuration.
The same thing happens for the third configuration, and so on and on.
Thus, common of common bundles could be possible,
like common.js
in the example above.
What if two configurations share the same output
?
b.plugin('common-bundle', {
groups: 'page/**/index.js',
common: [
{
output: 'common.js',
filter: ['page/A/index.js', 'page/B/index.js'],
},
{
output: 'minor.js',
filter: ['page/**/index.js', '!page/A/index.js', '!page/B/index.js'],
},
{
// common.js has already been created
output: 'common.js',
// nothing happens to common.js
// but modules in minor.js are removed if they are also in common.js
filter: 'minor.js',
},
],
})
basedir
Specify the base for relative paths to bundles and module files.
Type: String
Default: b._options.basedir
Events
b.on('common.map', (bundleMap, inputMap) => {})
Suppose there are two pages, hi
and hello
,
and both depend upon lodash
and say
.
We can use the following options to create a common.js
,
and check bundleMap
and inputMap
.
b.plugin('common-bundle', {
groups: 'page/**/index.js',
common: 'common.js',
})
b.on('common.map', function (bundleMap, inputMap) {
console.log(JSON.stringify(bundleMap, null, 2))
console.log(JSON.stringify(inputMap, null, 2))
})
bundleMap
{
// bundle => {}
"page/hi/index.js": {
"modules": [
// modules in this bundle
"page/hi/index.js"
],
"deps": [
// bundles should come before this bundle
"common.js"
]
},
"page/hello/index.js": {
"modules": [
"page/hello/index.js"
],
"deps": [
"common.js"
]
},
"common.js": {
"modules": [
"node_modules/lodash/index.js",
"web_modules/say/index.js"
]
}
}
inputMap
{
// input file => [bundles]
"page/hello/index.js": [
"page/hello/index.js"
],
"page/hi/index.js": [
"page/hi/index.js"
]
}
b.on('common.pipeline', (id, pipeline) => {})
Every time a bundle created, a common.pipeline
event is emitted with its id
and the packing pipeline
.
A pipeline
is a labeled-stream-splicer:
-
'pack'
- browser-pack -
'wrap'
- apply final wrapping
You can call pipeline.get
with a label name to get a handle on a stream pipeline that you can push()
, unshift()
, or splice()
to insert your own transform streams.
Event handlers must be attached before calling b.plugin
.
watchify2
and gulp
Work with var through = require('through2')
var browserify = require('browserify')
gulp.task('build', function() {
var b = browserify({ basedir: '/path/to/src' })
b.plugin('common-bundle', {
// page-specific bundles
groups: 'page/**/index.js',
// common bundle shared by all pages
common: 'common.js',
})
return gulp.src('page/**/index.js', {
cwd: b._options.basedir,
read: false,
})
.pipe(through.obj(function (file, _, next) {
b.add(file.path)
next()
}, function (next) {
b.bundle()
.on('data', file => this.push(file))
.on('end', () => this.push(null))
}))
.pipe(gulp.dest('build'))
})
gulp.task('watch', function (cb) {
var b = browserify({ basedir: '/path/to/src' })
b.plugin('common-bundle', {
// page-specific bundles
groups: 'page/**/index.js',
// common bundle shared by all pages
common: 'common.js',
})
b.plugin('watchify2', {
// now we can add or remove page entries
// and that would cause `b.bundle()` to be executed.
entryGlob: 'page/**/index.js',
})
gulp.src('page/**/index.js', {
cwd: b._options.basedir,
read: false,
})
.pipe(through.obj(function (file, _, next) {
b.add(file.path)
next()
}, function (next) {
b.on('update', bundle)
bundle()
}))
function bundle() {
b.bundle().pipe(gulp.dest('build'))
.on('data', file => console.log('bundle:', file.relative))
.on('end', () => console.log('-'.repeat(40)))
}
})