Skip to main content

Writing Cross Environment NPM Package for Both Browser and Node


Cross Browser NPM Packages
Many node modules have to support Cross Environment Javascript namely in Node.js and browser. UMD was a good wrapper around checking the environment and then writing our module’s logic, but it has its caveats. Namely, we’d have to write too much if the else
logic in one single function or file node-browser-resolve npm package helps us write a module for multiple environments right from the package.json file.
If you are thinking of authoring an npm module, the chances are that you might stumble across ways to make your module work cross environment, for example with Node, browser. And if you are a maintainer of legacy projects, require js’s AMD pattern modules. The very common way to author a module to ensure cross environment compatibility is UMD or Universal Module Definition.
This beautiful pattern was inspired by @kriskowal when used for the Q promise library.
It passes this context and a function having a dependency (which might be optional) in an IIFE, which are accessed inside the function as root and factory function respectively.
The if and else conditional statements check for the environment in which the module is being used. If define is defined and is a function, that means something like RequireJS, is being used to manage dependencies asynchronously. If module is an object and module.exports is not null or undefined, then that means our module is being used in a node environment. If it’s none of the above two (for example the module is being used in the browser, where the this context is the window object), then we attach the factory function of our module (where the module’s logic resides) directly to the root (in this case window global browser object).
But wait, I do not use RequireJS anymore because I’ve ES2015 imports or Node’s CommonJS module like require very handy with me, along with the power of build tools now that I can use with ease. There’s an easy way, where you can leverage npm and Node together to seamlessly tell your module in which environment it is being used, and how to behave accordingly. Cross Environment behavior made easy.
Let’s take an example of a simple javascript utility package, which takes a string as an input and outputs base-64-encoded version.
For the browser, this is easy, since we can just use the btao inbuilt function:
In Node though, there is btao function. So, we’ll have to create a Buffer instead, and then call buffer.toString() on it:
Now, we need some way (like the UMD pattern but less verbose, since we can) to detect whether we’re running in the browser or in Node, so that we could be sure to use the right version in the right environment. Both Browserify and Webpack bundling tools define a process.browser field which returns true, whereas in Node it’s false. So we can simply do
Now, we just name our file index.js then type npm publish through the root of our module through our terminal, and we should be done.
BUT THERE’S A BIG PERFORMANCE GLITCH
Since our index.js file now contains references to the Node’s built-in process and buffer modules, both Browserify and Webpack will automatically include polyfills for those entire modules in our bundle (the final file after bundling). Thus, we have an enormous amount of unnecessary code sitting in our bundle.
With this simple looking 9 line code of our module, Browserify and Webpack will create a bundle weighing 24.7KB minified (7.6KB min+gz). That’s a lot of bytes for something that, in the browser, only needs to be expressed with btao inbuilt browser function.
ENTER: THE BROWSER FIELD IN PACKAGE.JSON
With node-browser-resolve npm package, it lets us include a new field by the key name browser in our package.json file.
Using this technique, we can add the following to our package.json
And then separate the functions into two different files, index.js and browser.js
After this fix, both of the bundlers produce significantly less sized bundles.
Browserify : 511 bytes (315 min + gzipped)
Webpack : 550 bytes (297 bytes + gzipped)
Now when we publish our module, using it in Node environment will get the Node version of our code, and anyone using it for the browser using bundlers like browserify and webpack will get the browser version.
There you go, the simplest way to keep browser and node code separate for those cross environment compatibility for the same functionality!
Got something to add? Drop your thoughts and feedback inside comments below.

Comments

WHAT'S HOT

Samsung Overtakes Fitbit In Wearable Sales For First Time

Fitbit has been a top competitor on the global wearable market for a long time, but the South Korea giant Samsung managed to steal the silver crown of the wearable market from Fitbit. According to Strategy Analytics, Samsung gained the

Supermassive Black Holes Found Orbiting Each Other For The First Time

Image Credit: UCR Researchers from Stanford University have identified super-massive binary black holes at the center of Galaxy 0402+379 about 750 billion light years away. The two of the black holes are just 24 light years apart and one of them is orbiting the other. This is the first

System76 Announces Its Own Linux Distribution Named Pop!_OS

Image: System76 Linux machine vendor System76 has launched their own operating system named Pop!_OS. Based on Ubuntu GNOME, this new Linux distro’s Alpha version is right now available for download. The first final release of Pop!_OS will be shipped

"The corruption is everywhere" - Take a look at what a Nigerian saw in the cupcake he bought

A Nigerian man who bought something he thought was a cupcake, got the shocker of his life after he opened the package to see it was a well packaged "bread".


A Nigerian man who bought something he thought was a cupcake, got the shocker of his life after he opened the package to see it was a well packaged "bread".