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

Amazon Cuts Huawei Watch 2 Price To $194.99

Huawei's latest smartwatch has received a temporary price cut in the United States. There is no word on how long the promotion will last, but those interested

This Medical Camera Can See Right Through Human Body

Now, a team at the University of Edinburgh has developed a medical camera that can see right through your body. This new camera works by detecting light sources inside the body, such as

Google Search About To Receive A New “Speed Test Tool”, Here Is How To Use It

I use the Speed Test tool by Ookla to check the speed of my broadband connection. Generally, I use Google Search to visit the website. But the last time when I Googled the term “speed test,” I didn’t have to go much further than the search result itself.

Microsoft Has Released The First Windows 10 Build 16353 For Insiders

As Fall Creators Update is nearing its release, Skip Ahead was announced last month. It enables fast ring users to continue receiving new features, though the RS_PRERELEASE