split-require
Version:
Bundle splitting for CommonJS and ES modules (dynamic `import()`) in browserify
211 lines (183 loc) • 6.55 kB
JavaScript
var test = require('tape')
var path = require('path')
var fs = require('fs')
var browserify = require('browserify')
var readTree = require('read-file-tree')
var rimraf = require('rimraf')
var mkdirp = require('mkdirp')
var to = require('flush-write-stream')
var concat = require('concat-stream')
var splitRequirePlugin = require('../plugin')
function splitRequirePath (b) {
var resolve = b._bresolve
b._bresolve = function (id, opts, cb) {
if (id === 'split-require') {
id = require.resolve('../')
}
return resolve(id, opts, cb)
}
}
function testFixture (t, name, opts, message, next) {
var entry = path.join(__dirname, name, 'app.js')
var expectedDir = path.join(__dirname, name, 'expected')
var actualDir = path.join(__dirname, name, 'actual')
var plugin = opts.plugin || function (b, opts) {
return b.plugin(splitRequirePlugin, opts)
}
opts.dir = actualDir
rimraf.sync(actualDir)
mkdirp.sync(actualDir)
browserify(entry)
.plugin(splitRequirePath)
.plugin(plugin, opts)
.bundle()
.pipe(fs.createWriteStream(path.join(actualDir, 'bundle.js')))
.on('error', t.fail)
.on('finish', onbuilt)
function onbuilt () {
var expected = readTree.sync(expectedDir, { encoding: 'utf8' })
var actual = readTree.sync(actualDir, { encoding: 'utf8' })
t.deepEqual(actual, expected, message)
next()
}
}
test('basic', function (t) {
testFixture(t, 'basic', {}, 'should work, and not duplicate the ./xyz module', t.end)
})
test('chain', function (t) {
testFixture(t, 'chain', {}, 'should work with dynamic imports in dynamically imported modules', t.end)
})
test('also-required', function (t) {
testFixture(t, 'also-required', {}, 'splitRequire() should work on modules that are already included in the same bundle for some other reason', t.end)
})
test('flat', function (t) {
testFixture(t, 'flat', {
plugin: function (b, opts) {
b.plugin('browser-pack-flat/plugin')
b.on('split.pipeline', function (pipeline) {
pipeline.get('pack').splice(0, 1,
require('browser-pack-flat')({ raw: true }))
})
b.plugin(splitRequirePlugin, opts)
}
}, 'works together with browser-pack-flat', t.end)
})
test('public-path', function (t) {
testFixture(t, 'public-path', {
public: 'http://localhost:9966/build/'
}, 'chunks can be loaded from a configurable base url', t.end)
})
test('output stream', function (t) {
t.timeoutAfter(3000)
var entry = path.join(__dirname, 'output-stream/app.js')
var expectedDir = path.join(__dirname, 'output-stream/expected')
var actualTree = {}
var opts = {
output: function (bundleName, entry) {
t.equal(typeof bundleName, 'string', 'output() gets the public filename in the first parameter')
t.equal(typeof entry, 'object', 'output() gets the entry row in the second parameter')
return concat(function (contents) {
t.equal('bundle.js' in actualTree, false, 'should have written all dynamic bundles before completing the main bundle')
actualTree[bundleName] = contents.toString('utf8')
})
}
}
browserify(entry)
.plugin(splitRequirePath)
.plugin(splitRequirePlugin, opts)
.bundle()
.pipe(concat(function (contents) {
actualTree['bundle.js'] = contents.toString('utf8')
}))
.on('error', t.fail)
.on('finish', onbuilt)
function onbuilt () {
readTree(expectedDir, { encoding: 'utf8' }, function (err, expectedTree) {
t.ifErr(err)
t.deepEqual(actualTree, expectedTree, 'should have piped all bundles into output() streams')
t.end()
})
}
})
test('without splitRequire()', function (t) {
testFixture(t, 'no-imports', {}, 'works when splitRequire() is not used', t.end)
})
test('factor-bundle', function (t) {
var inputDir = path.join(__dirname, 'factor-bundle')
var actualDir = path.join(__dirname, 'factor-bundle/actual')
var expectedDir = path.join(__dirname, 'factor-bundle/expected')
rimraf.sync(actualDir)
mkdirp.sync(actualDir)
var b = browserify({
entries: [
path.join(inputDir, 'app.js'),
path.join(inputDir, 'world.js')
]
})
b.on('factor.pipeline', function (file, pipeline) {
pipeline.get('pack').unshift(
splitRequirePlugin.createStream(b, { dir: actualDir }))
})
b.plugin('factor-bundle', {
outputs: [
path.join(actualDir, 'entry1.js'),
path.join(actualDir, 'entry2.js')
]
})
b.plugin(splitRequirePath)
b.plugin(splitRequirePlugin, { dir: actualDir })
b.bundle()
.pipe(fs.createWriteStream(path.join(actualDir, 'bundle.js')))
.on('error', t.fail)
.on('finish', onbuilt)
function onbuilt () {
// wait a bit for other bundles to be written
setTimeout(function () {
var expected = readTree.sync(expectedDir, { encoding: 'utf8' })
var actual = readTree.sync(actualDir, { encoding: 'utf8' })
t.equal(Object.keys(actual).length, 4, 'should output 4 files: common, entry 1, entry 2, dynamic 1')
t.deepEqual(actual, expected, 'should have same contents as expected')
t.end()
}, 100)
}
})
test('naming bundles by emitting `name` event on a stream', function (t) {
var crypto = require('crypto')
var input = path.join(__dirname, 'name/app.js')
var actualDir = path.join(__dirname, 'name/actual')
var expectedDir = path.join(__dirname, 'name/expected')
rimraf.sync(actualDir)
mkdirp.sync(actualDir)
var b = browserify(input)
b.plugin(splitRequirePath)
b.plugin(splitRequirePlugin, {
output: function (bundleName) {
var stream = fs.createWriteStream(path.join(actualDir, bundleName))
var hash = crypto.createHash('sha1')
return to(onwrite, onend)
function onwrite (chunk, enc, cb) {
hash.update(chunk)
stream.write(chunk, cb)
}
function onend (cb) {
var self = this
stream.end(function (err) {
if (err) return cb(err)
var name = hash.digest('hex').slice(0, 10) + '.js'
self.emit('name', name)
fs.rename(path.join(actualDir, bundleName), path.join(actualDir, name), cb)
})
}
}
})
b.bundle()
.pipe(fs.createWriteStream(path.join(actualDir, 'bundle.js')))
.on('error', t.fail)
.on('finish', onbuilt)
function onbuilt () {
var expected = readTree.sync(expectedDir, { encoding: 'utf8' })
var actual = readTree.sync(actualDir, { encoding: 'utf8' })
t.deepEqual(actual, expected, 'should have same contents as expected')
t.end()
}
})