Aspecto blog

On microservices, OpenTelemetry, and anything in between

Make your npm package work on both Node.js and browser

Make your npm package work on both Node.js and browser

Share this post

Share on facebook
Share on twitter
Share on linkedin

I recently worked on an npm package that is supposed to be consumed on both Node.js environment and the browser (using React/Angular/etc..).

Things started getting complicated when the node branch of the code needed to require native Node.js packages — like fs.

Consider the following (simplified) code:

// node-handler.js
const fs = require('fs');

export const handle = () => { 
  fs.readFileSync(…);
  …
};

// browser-handler.js
export const handle = () => { … };

// index.js
const nodeHandler = require('./node-handler').handle;
const browserHandle = require('./browser-handler').handle;
const isNode = require('./config').isNode;

const handle = isNode ? nodeHandler : browserHandle;

When testing the module on my React + Webpack app, the app crashed:

This happened even though the node-handler file wasn’t executed, this is due to Webpack nature of creating a bundle containing all the code.

Step 1: Postpone your requires

Instead of requiring fs in the global scope, we can require it only where we actually need it, that way we don’t require it when running in the browser:

// node-handler.js
export const handle = () => { 
  require('fs').readFileSync(…);
  …
};

// browser-handler.js
export const handle = () => { … };

// index.js
const nodeHandler = require('./node-handler').handle;
const browserHandle = require('./browser-handler').handle;
const isNode = require('./config').isNode;

const handle = isNode ? nodeHandler : browserHandle;

Cool! Our React app is not crashing anymore! But we do get an annoying compilation warning right now:

While we can live with a warning ⚠️ , our end-users will probably not like this too much and will end up not installing our package.

Step 2: eval your require

This is not the most elegant solution (to say the least..), but it’s keeping Webpack quiet and your end-users happy. Instead of using require('fs'), we’re gonna use eval('require')('fs'):

// node-handler.js
export const handle = () => { 
  eval('require')('fs').readFileSync(…);
  …
};

// browser-handler.js
export const handle = () => { … };

// index.js
const nodeHandler = require('./node-handler').handle;
const browserHandle = require('./browser-handler').handle;
const isNode = require('./config').isNode;

const handle = isNode ? nodeHandler : browserHandle;

And that’s it! Just two simple steps to make your npm package work on both Node.js and browser.

Good luck and may you be blessed with tons of stars 🌟

Spread the word

Share on facebook
Share on twitter
Share on linkedin
Subscribe for more distributed applications tutorials and insights that will help you boost microservices troubleshooting.