Support multiple JS module formats with rollup

Having recently needed to produce a shared JavaScript npm package for internal sharing of functionality between applications I naively failed to consider the impact of the various competing module formats at large in the JS world, but luckily the solution is straight forward with the help of ‘rollup’.

The requirement was to make a shared JavaScript library to be shared between numerous React/Node applications, and make it available internally via an npm/yarn install by publishing it to an internal registry. All straight forward in theory, however there are competing module formats and you need to either pick one or support both.

The various formats are mainly CommonJS (CJS)(the original NodeJS module system), Async Module Definition (AMD), Universal Module Definition (UMD)(a combination of CommonJS & AMD) and ES6 Modules (ESM). Most require the use of “require” keyword to import modules, or in the case of ES6 modules the use of “import”. So why do I need to care about all this? Wouldn’t it be easier if just use the modern ES6 module format only. Well usually you would pick one to use for your app and mostly ignore the others, but when building a shared library you need to consider what’s used by the consuming application so that it can use your new library. Just using ES6 Modules makes it difficult for apps built using CommonJS to consume the library, and in my case I also found that consuming the library from a majority ES6 module based app was problematic and there is often something that requires the CommonJS format (I’m looking at you Node).

So whilst over time everything will standardise (maybe) on ES6 modules in the meantime I used rollup (https://rollupjs.org) to solve the problem. Rollup module bundler allows you to code in modern ES6 standard and then compile it down into other formats (e.g. CommonJS) in one bundle (or different bundles if you wish).

To make life even easier I found this excellent ts-library-template repo for a rollup based TypeScript JS library which fitted my needs perfectly. Rollup is configured to output both CJS and ESM formats.

output: [
  {
     dir: `dist/${dirname(pkg.main)}`,
     entryFileNames: '[name].js',
     format: 'cjs'
  },
  {
     dir: `dist/${dirname(pkg.module)}`,
     entryFileNames: '[name].mjs',
     format: 'esm'
  }
]

So when the library is compiled it will be able to be be consumed by both CJS and ESM based applications.