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 flowing vinyl
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', {
groups: 'page/**/index.js',
})
var vfs = require('vinyl-fs')
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: {
output: 'common.js',
filter: '**/*.js',
},
})
var vfs = require('vinyl-fs')
b.bundle().pipe(vfs.dest('/path/to/build'))
output:
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'
Specify the path to the new bundle
Type: String
output
should be a path relative to the final build directory.
{
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.
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'
Specify the file path to the new common bundle.
Type: String
b.plugin('common-bundle', {
common: {
output: 'common.js',
filter: '**/*.js',
},
})
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', {
groups: 'page/**/index.js',
common: [
{
output: 'ab.js',
filter: ['page/A/index.js', 'page/B/index.js'],
},
{
output: 'cd.js',
filter: ['page/C/index.js', 'page/D/index.js'],
},
{
output: 'common.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'],
},
{
output: '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
{
"page/hi/index.js": {
"modules": [
"/Users/zoubin/usr/src/reducejs/common-bundle/example/map/src/page/hi/index.js"
],
"deps": [
"common.js"
]
},
"page/hello/index.js": {
"modules": [
"/Users/zoubin/usr/src/reducejs/common-bundle/example/map/src/page/hello/index.js"
],
"deps": [
"common.js"
]
},
"common.js": {
"modules": [
"/Users/zoubin/usr/src/reducejs/common-bundle/example/map/src/node_modules/lodash/index.js",
"/Users/zoubin/usr/src/reducejs/common-bundle/example/map/src/web_modules/say/index.js"
]
}
}
inputMap
{
"/Users/zoubin/usr/src/reducejs/common-bundle/example/map/src/page/hello/index.js": [
"common.js",
"page/hello/index.js"
],
"/Users/zoubin/usr/src/reducejs/common-bundle/example/map/src/page/hi/index.js": [
"common.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:
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
.
var through = require('through2')
var browserify = require('browserify')
gulp.task('build', function() {
var b = browserify({ basedir: '/path/to/src' })
b.plugin('common-bundle', {
groups: 'page/**/index.js',
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', {
groups: 'page/**/index.js',
common: 'common.js',
})
b.plugin('watchify2', {
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)))
}
})