"Rollup! Rollup for the mystery tour!" - Lennon and McCartney
Besides a slew of bugfixes and the new
revs_limit feature, the big news about 5.2.0 is that PouchDB is now authored with ES6 modules rather than CommonJS. Using Rollup, it's transpiled into one big
index.js for Node and an
index-browser.js for Webpack/Browserify.
The most immediate benefit is that PouchDB's minified/gzipped size has dropped by 3KB (6.7% smaller!) with no loss of functionality. This is thanks to Rollup's ability to remove the typical Browserify/Webpack cruft by hoisting all submodules into a single scope.
I'll explore that topic later on in the post, but first, here's the full changelog for 5.2.0:
Features and improvements
- Migrate to ES6/Rollup, build one
- Add ability to disable timeout in
- Return conflict error when inserting an unknown rev (#4712)
- Catch XHR errors and propagate (#4595)
- Fix and test Webpack (#4700)
- Improve error when user sends invalid base64 (#4208)
- Consider changes erroring a valid result (#4677)
- Allow bulkGet requests without an explicit rev (#4530)
- Fix for xhr.upload detection failing (#4560)
- Coerce options which should be numbers to number (#4578)
- Add timeout option to replication (#4540)
- Remove binary string conversion (#4529)
- Remove CORS explanation (#4677)
- Add browser sniffing and remove nonce by default (#4543)
- Remove unneeded host.headers from ajax (#4567)
- Fix double-encoding name in http adapter (#4514)
- Fix and document heartbeat for replication (#4538)
- Fallback from
_bulk_geton 50x requests (#4542)
- Remove Cordova init checks (#4756)
To explain why we're now using Rollup to bundle one big
I've been working on PouchDB for about two years. In that short amount of time, I've seen the frontend ecosystem largely transition from prebuilt
A big impact of this is that library authors no longer have control over the "last mile." Whereas our code used to be plopped directly into an HTML page (or merely concatenated), nowadays it's often parsed, chopped up, and transformed before it reaches that HTML page. This process inevitably introduces bugs and inconsistencies.
For instance, PouchDB has been struggling with Webpack support for much of the past year. The number of times we've had Webpack-related bugs filed on us, and the number of times we've later regressed because we only test with Browserify, eventually compelled us to test Webpack separately. This means we're essentially testing both Browserify and Webpack as build targets.
Think this sounds extreme? Consider this example.
Example 1: requiring JSON
MyLibrary.version = require('./package.json').version;
This will work in Browserify and Node, but it will break in Webpack unless the end-user adds a special json-loader to their Webpack configuration.
Does your library do this? Do you have a dependency that does this? Then congratulations, you cannot fix this for your users. All you can do is document it in your README and/or endure the inevitable bug reports.
This is not something Webpack intends to fix, since it seems to constitute a philosophical difference between Browserify and Webpack. Their argument is that end-users should have control over how libraries are built. (You can read this discussion for details.)
Example 2: Browserify transforms
Another good example of Browserify/Webpack differences is Browserify transforms. These are modules that can transform your library at build time, when it's consumed by Browserify.
Transforms are very popular in the Browserify ecosystem, but unfortunately they don't work in Webpack unless you use the transform-loader. Worse, this is another thing that end-users are expected to configure for every dependency (and every dependency of a dependency…).
So if your project is built with Browserify, it's easy to accidentally add a Browserify transform, while forgetting that it will break for Webpack users. (We nearly made that mistake ourselves, before we started testing Webpack.) So the only reasonable choice for library authors is to avoid Browserify transforms entirely.
Example 3: strict mode
To stop picking on Webpack for a bit, I also recently blogged about a bug in the "buffer" project that was caused by Babel re-interpreting the code as strict, even though it was not distributed in strict mode. This is another great example of a "last mile" bug that ended up being something the library author needed to deal with.
Of course, in this case, the library author could have just said "not my bug." But then the Babel authors might have (quite reasonably) responded, "your library should be strict; we don't support non-strict code," at which point it would become a game of hot potato. And all the while, end-users would be adding messy hacks to their build scripts to work around the issue.
Lessons from the browser world
The benefits are clear: we can use Browserify transforms and
require('./package.json') and whatever else we want, as long as they're only compile steps that build a bundle to be consumed by Node (and therefore Browserify/Webpack). As a bonus, this allows us to build a smaller and sleeker bundle, which is also faster for the end-user to build, since most of the build steps have already been run in advance.
Along those same lines, we also have an open issue to add the ability to create custom builds (e.g. no WebSQL, no map/reduce, no replication, etc.). Presumably this can be made even easier with Rollup's tree-shaking capabilities, but we're still investigating this feature.