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.
That's not true. For example, if I write my code in ES2015 and use Babel in my build, I don't assume that someone who is consuming my module needs to know about it. It's all part of my build step. How is brfs different? Isn't that just another step in my build?