Skip to content

Commit 7de208a

Browse files
committed
change: replace browserify-incremental w/rebundler
browserify-incremental doesn’t work with multiple bundles compiled at the same time. This makes it very unreliable and can produce very hard to trace errors in client-side code. This uses rebundler instead, which, as long as it’s given the `persistKey` option, doesn’t have the same problem.
1 parent 613c0df commit 7de208a

4 files changed

Lines changed: 104 additions & 90 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ If `true`, minifies source code and sets debug to true. If object, passed as opt
117117
If `true`, [watchify](https://github.com/substack/watchify) will be used to create a file watcher and speed up subsequent builds.
118118

119119
#### opts.cache
120-
If truthy, will use [browserify-incremental](https://github.com/jsdf/browserify-incremental) to cache the result of a build. This can give you dramatically faster build times if you're not using `opts.watch`.
120+
If truthy, will use [rebundler](https://github.com/bjoerge/rebundler) to cache the result of a build. This can give you dramatically faster build times if you're not using `opts.watch`.
121121

122122
If `opts.cache` is a string, it will be used as the file path to save the cache file to.
123123

index.js

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
'use strict'
22

33
var browserify = require('browserify')
4-
, browserifyInc = require('browserify-incremental')
4+
, rebundler = require('rebundler')
55
, path = require('path')
66
, fs = require('fs')
7+
, md5 = require('MD5')
78
, events = require('events')
89
, mkdirp = require('mkdirp')
910
, watchify = require('watchify')
@@ -30,6 +31,8 @@ ctor = module.exports = function atomifyJs (opts, cb) {
3031
, _outputcb
3132
, _buffercb
3233
, browserifyOptions
34+
, persistKey
35+
, cacheChecker
3336
, transforms
3437
, assets
3538
, outputs
@@ -90,25 +93,33 @@ ctor = module.exports = function atomifyJs (opts, cb) {
9093
// remove the default 600ms delay because speed is tood
9194
// ignoreWatch to true to ignore node_modules
9295
if (opts.watch) _.extend({delay: 0, ignoreWatch: true}, browserifyOptions, watchify.args)
93-
// mixin the required browserifyInc options if we need to cache
94-
if (opts.cache) _.extend(browserifyOptions, browserifyInc.args)
95-
96-
b = browserify(browserifyOptions)
9796

9897
if (opts.cache) {
99-
b = browserifyInc(b
100-
, _.isString(opts.cache) ? {cacheFile: opts.cache} : {}
101-
)
98+
persistKey = md5(opts.entry || opts.entries.toString() || opts.require.toString())
99+
100+
b = browserify(_.extend(browserifyOptions, {
101+
fullPaths: true
102+
}))
103+
emitter.emit('browserify', b)
104+
105+
cacheChecker = rebundler({persist: true, persistKey: persistKey, cacheDir: typeof opts.cache === 'string' ? opts.cache : null}, function rebundle (cache, packageCache) {
106+
b._mdeps.cache = {
107+
deps: cache
108+
, pkgs: packageCache
109+
, mtimes: b._mdeps.cache.mtimes
110+
}
111+
return b
112+
})
113+
}
114+
else {
115+
b = browserify(browserifyOptions)
116+
emitter.emit('browserify', b)
102117
}
103-
104-
emitter.emit('browserify', b)
105118

106119
if (opts.watch) {
107120
w = watchify(b)
108121
emitter.emit('watchify', w)
109-
}
110122

111-
if (opts.watch) {
112123
w.on('update', function onUpdate (ids) {
113124
ids.forEach(function eachId (id) {
114125
emitter.emit('changed', id)
@@ -225,7 +236,7 @@ ctor = module.exports = function atomifyJs (opts, cb) {
225236
})
226237

227238
// we need to wrap the callback to output an object with all the bundles
228-
return b.bundle(function bundledWithCommon (err, common) {
239+
return (opts.cache ? cacheChecker() : b).bundle(function bundledWithCommon (err, common) {
229240
var hasCallback = _.isFunction(cb)
230241
, out = {}
231242

@@ -249,7 +260,7 @@ ctor = module.exports = function atomifyJs (opts, cb) {
249260
})
250261
}
251262
// if we don't need to use factor bundle, just browserify!
252-
else return b.bundle(cb)
263+
else return (opts.cache ? cacheChecker() : b).bundle(cb)
253264
}
254265

255266
ctor.emitter = emitter

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@
3939
"babelify": "^6.1.2",
4040
"brfs": "^1.4.0",
4141
"browserify": "^7.1.0",
42-
"browserify-incremental": "^1.5.0",
4342
"ejsify": "^1.0.0",
4443
"envify": "^3.4.0",
4544
"factor-bundle": "^2.4.1",

test/cache.js

Lines changed: 78 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,20 @@ var test = require('tape')
88
, outputPath = path.join(entryPath, 'output')
99
, changerPath = path.join(outputPath, 'changer.js')
1010
, mkdirp = require('mkdirp')
11-
, cachePath = path.join(outputPath, 'cache.json')
12-
, setup = function setup(value){
11+
, cacheDir = path.join(outputPath, 'cache')
12+
, rimraf = require('rimraf')
13+
, setup = function setup (value) {
1314
var file = 'module.exports = ' + value || Date.now()
1415

1516
mkdirp.sync(outputPath)
17+
mkdirp.sync(cacheDir)
1618
fs.writeFileSync(changerPath, file)
1719
}
1820

19-
test('opts.cache', function(t){
20-
var parseTime = function parseTime(logMsg){
21-
return parseFloat(logMsg.replace(/.*?\(([0-9\.]{1,}) seconds\)/, '$1'))
22-
}
23-
21+
test('opts.cache', function (t) {
22+
var startTime = Date.now()
23+
, runTests
24+
, b
2425
setup()
2526

2627
t.plan(5)
@@ -30,117 +31,120 @@ test('opts.cache', function(t){
3031
, 'should throw if opts.watch is also set'
3132
)
3233

33-
lib.emitter.once('browserify', function (b){
34-
b.once('log', function(msg){
35-
var initialTime = parseTime(msg)
34+
lib.emitter.once('browserify', function (br) {
35+
b = br
36+
})
3637

37-
t.ok(
38-
msg
39-
, 'compiles once'
40-
)
38+
runTests = function () {
39+
var firstBundleTime = Date.now()
40+
, firstBundleDuration = firstBundleTime - startTime
41+
42+
t.ok(
43+
firstBundleDuration
44+
, 'compiles once'
45+
)
4146

42-
b.bundle()
43-
b.once('log', function(msg2){
44-
var secondTime = parseTime(msg2)
47+
b.bundle(function () {
48+
var secondBundleTime = Date.now()
49+
, secondBundleDuration = secondBundleTime - firstBundleTime
4550

51+
// wait for 1000ms, b/c rebundler does
52+
setTimeout(function () {
4653
// wait for the second callback because some fs are slow. (linux)
4754
t.ok(
48-
fs.existsSync(cachePath)
55+
fs.existsSync(cacheDir)
4956
, 'writes the cache file'
5057
)
5158

5259
// cleanup
53-
fs.unlinkSync(cachePath)
60+
rimraf.sync(cacheDir)
61+
}, 1050)
5462

55-
t.ok(
56-
msg2
57-
, 'compiles a second time'
58-
)
63+
t.ok(
64+
secondBundleDuration
65+
, 'compiles a second time'
66+
)
5967

60-
t.ok(
61-
secondTime <= initialTime
62-
, 'the second of ' + secondTime + ' compiles faster than ' + initialTime
63-
)
64-
})
68+
t.ok(
69+
secondBundleDuration <= firstBundleDuration
70+
, 'the second bundle time of ' + secondBundleDuration + ' compiles faster than the initial ' + firstBundleDuration
71+
)
6572
})
66-
})
73+
}
6774

68-
lib({cache: cachePath, entry: path.join(entryPath, 'index.js')})
75+
lib({cache: cacheDir, entry: path.join(entryPath, 'index.js')}, runTests)
6976
})
7077

71-
test('opts.cache works with opts.common', function(t){
72-
var parseTime = function parseTime(logMsg){
73-
return parseFloat(logMsg.replace(/.*?\(([0-9\.]{1,}) seconds\)/, '$1'))
74-
}
78+
test('opts.cache works with opts.common', function (t) {
79+
var startTime = Date.now()
7580
, bundleNames = ['common', 'dep-1', 'dep-2', 'index']
81+
, runTests
7682
, b
7783

7884
setup()
7985

80-
t.plan(6 + bundleNames.length)
86+
t.plan(5 + bundleNames.length)
8187

8288
t.throws(
8389
lib.bind(null, {watch: true, cache: true})
8490
, 'should throw if opts.watch is also set'
8591
)
8692

87-
lib.emitter.once('browserify', function (browserifyInstance){
93+
lib.emitter.once('browserify', function (browserifyInstance) {
8894
b = browserifyInstance
89-
b.once('log', function(msg){
90-
var initialTime = parseTime(msg)
95+
})
96+
97+
runTests = function (err, bundles) {
98+
var bundleKeys = Object.keys(bundles)
99+
, firstBundleTime = Date.now()
100+
, firstBundleDuration = firstBundleTime - startTime
91101

102+
t.error(err, 'should not error')
103+
104+
bundleNames.forEach(function ensureEachBundleExists (bundleName) {
92105
t.ok(
93-
msg
94-
, 'compiles once'
106+
bundleKeys.indexOf(bundleName) > -1
107+
, 'creates the ' + bundleName + ' bundle'
95108
)
109+
})
96110

97-
b.once('log', function(msg2){
98-
var secondTime = parseTime(msg2)
111+
t.ok(
112+
firstBundleTime
113+
, 'compiles once'
114+
)
99115

100-
// wait for the second callback because some fs are slow. (linux)
101-
t.ok(
102-
fs.existsSync(cachePath)
103-
, 'writes the cache file'
104-
)
116+
// trigger another bundle so that we can time the difference
117+
b.bundle(function () {
118+
var secondBundleTime = Date.now()
119+
, secondBundleDuration = secondBundleTime - firstBundleTime
105120

121+
// wait for 1000ms, b/c rebundler does
122+
setTimeout(function () {
106123
// cleanup
107-
fs.unlinkSync(cachePath)
124+
if (fs.existsSync(cacheDir)) rimraf.sync(cacheDir)
125+
}, 1020)
108126

109-
t.ok(
110-
msg2
111-
, 'compiles a second time'
112-
)
127+
t.ok(
128+
secondBundleDuration
129+
, 'compiles a second time'
130+
)
113131

114-
t.ok(
115-
secondTime <= initialTime
116-
, 'the second of ' + secondTime + ' compiles faster than ' + initialTime
117-
)
118-
})
132+
t.ok(
133+
secondBundleDuration <= firstBundleDuration
134+
, 'the second bundle time of ' + secondBundleDuration + ' compiles faster than the initial ' + firstBundleDuration
135+
)
119136
})
120-
})
137+
}
121138

122139
lib({
123-
cache: cachePath
140+
cache: cacheDir
124141
, entries: [
125142
path.join(entryPath, 'index.js')
126143
, path.join(entryPath, '..', 'entry', 'dep-2.js')
127144
, path.join(entryPath, '..', 'entry', 'dep-1.js')
128145
]
129146
, common: true
130147
, debug: true
131-
}
132-
, function(err, bundles){
133-
var bundleKeys = Object.keys(bundles)
134-
t.error(err, 'should not error')
135-
136-
bundleNames.forEach(function ensureEachBundleExists(bundleName){
137-
t.ok(
138-
bundleKeys.indexOf(bundleName) > -1
139-
, 'creates the ' + bundleName + ' bundle'
140-
)
141-
})
142-
143-
// trigger another bundle so that we can time the difference
144-
b.bundle()
145-
})
148+
}
149+
, runTests)
146150
})

0 commit comments

Comments
 (0)