Most of the modules I write are "agnostic" in that they should work in Node, browserify, webpack, Rollup, jspm... hell, even Unreal.js. It's just ES5, CommonJS and a few quirks like process.nextTick
or require('path')
(which browserify and webpack will shim).
Other modules are a bit trickier, and need to include a static asset like HTML, GLSL or another file.
In Node you might see this:
var fs = require('fs')
var path = require('path')
var html = fs.readFileSync(path.join(__dirname, 'index.html'), 'utf8')
If I specify brfs in my package.json config, the above code will work fine when the end-user installs and uses my module with browserify
(even if it's deep in their dependency tree). The asset gets statically inlined during the bundle step.
This is great since the module has a single entry point for both node and the browser, and I can test/develop with node
, nodemon
and tape
.This approach can even handle large base64/binary files as a Buffer
; while it may not be great for a web application, it can be great for quick prototyping in the browser.
However, this approach is incompatible with webpack and other bundlers. Examples of problematic modules which depend on a browserify transform:
- soundcloud-badge
- pixi.js
- aframe
- three-vignette-background (which uses glslify)
Webpack has a thing called transform-loader. This is more of a hack, and it forces configuration on the end-user of your module. It's not automatically resolved, see #378 β whereas browserify transforms are resolved per module. This is especially annoying with dependencies-of-dependencies.
Further, it assumes that your end-user is either using browserify or webpack. Other bundlers like jspm and Rollup will not work.
Update Jan 2016
ify-loader can now be used in Webpack configs to resolve browserify transforms automatically across your node_modules, as browserify intends it to be. This fixes the automatic transform resolution, but does not solve the end-user configuration problem.
You can run your module through webpack or browserify, but this will lead to duplication in the end-user's final bundle, since some dependencies may be getting bundled twice and won't be de-duplicated by browserify/webpack.
It also makes your published code pretty ugly to look at, and you'll want to add source maps.
Update Dec 2015
With webpack, you can use
external
to avoid bundling a module and preserve de-duplication. See the discussion thread below.
I think it would be great if somebody developed a Babel plugin/transform that supports browserify transform streams listed in package.json
"browserify" field. This would allow you to transpile the source rather than bundling it, fixing the dedupe issue.
I'm not sure how complex this is, and whether it carries other problems.
Update Jan 2016
babel-plugin-webpack-loaders can now be used which may help build "agnostic modules" by transpiling webpack loaders on pre-publish (like CSS or HTML inlining). However, it is a bit klunky since Babel is not designed to run asynchronous source transforms.
Another solution is if somebody build a source transpiler that doesn't try to bundle code (like browserify/webpack) but instead just tries to support browserify transforms.
This way you can publish an npm dist that looks exactly like the source entry point, but with static files inlined so that it works in Node, browserify, webpack, etc.
Update Jan 2016
With transpilify (work in progress) you can transpile a source file with simple browserify transforms, but without adding the overhead of the bundler itself.
Sadly all of these solutions require more configuration and setup in one way or another, adding some overhead for module authors. :)
This is closely related with some of the things webpack tries to solve, like handling image and SVG assets. I think it would be novel if there was a standardized solution that supported Node and all modern bundlers... But I think a more realistic goal (for now) is just to get ASCII assets (HTML/GLSL/text/etc) working.
Try using any module with a transform step (like
soundcloud-badge
), and you will get an error like this:This isn't just limited to browserify transforms. Say your dependency
foo
suddenly chooses to use Webpack'sraw-loader
. If you haven't set that up in yourwebpack.config.js
, your app will not work.On the other hand, browserify users don't need to worry about this problem. During bundling, the transforms are resolved per-module, so even if
soundcloud-badge
is deep in the dependency tree, the static asset will be inlined without any configuration for the end-user.Off-topic, but this has bit me in the ass before. You start depending on ES2015 modules, and eventually authors will commit some code that relies on
Array.prototype.find
or another feature that Babel doesn't transpile. Suddenly, the root app is breaking on legacy browsers because a module deep in the dependency tree was authored in ES2015 and has a peer dependency onbabel-polyfill
. This is especially frustrating when the app you are writing isn't even being authored in ES2015.