Skip to content

It can be difficult to set up a TypeScript library to compile to ESM and CommonJS. You can use @nx/rollup to take care of it for you.

Use Rollup to Compile your TypeScript Project

Section titled “Use Rollup to Compile your TypeScript Project”

If you do not use Rollup already, install the corresponding Nx plugin as follows:

Terminal window
nx add @nx/rollup

Make sure the version of @nx/rollup matches your other @nx/* package versions.

Configure Rollup to Create Multiple Formats

Section titled “Configure Rollup to Create Multiple Formats”

Create a rollup.config.cjs file in your project with the following configuration:

packages/my-awesome-lib/rollup.config.cjs
const { withNx } = require('@nx/rollup/with-nx');
module.exports = withNx(
{
main: './src/index.ts',
outputPath: './dist',
tsConfig: './tsconfig.lib.json',
compiler: 'swc',
format: ['esm', 'cjs'],
additionalEntryPoints: ['./src/foo.ts'],
},
{
// Additional rollup configuration options
}
);

The @nx/rollup/plugin will automatically infer a build target for any project with a rollup.config.cjs file.

After compiling our package using nx build my-awesome-lib we'll get the following output in our dist folder.

  • Directorymy-awesome-lib/
    • Directorydist/
      • foo.cjs.js
      • foo.esm.js
      • foo.d.ts
      • index.cjs.js
      • index.esm.js
      • index.d.ts
      • Directorysrc/
        • Directorylib/
          • my-awesome-lib.d.ts
    • Directorysrc/
      • ...
    • ...

To ensure your package works correctly with TypeScript's module resolution, you need to configure the exports field in your source package.json with proper types entries. This is critical for TypeScript 4.7+ which requires explicit type declarations for each export condition.

Types Must Be Specified for Each Condition

When using conditional exports with both ESM and CJS formats, you must include types entries for each condition. Without this, TypeScript may fail to resolve types correctly, causing compilation errors for consumers of your package. See the TypeScript 4.7 release notes for more details.

Update your source package.json to include the exports configuration:

packages/my-awesome-lib/package.json
{
"name": "my-awesome-lib",
"version": "0.0.1",
"type": "commonjs",
"main": "./dist/index.cjs.js",
"module": "./dist/index.esm.js",
"types": "./dist/index.d.ts",
"exports": {
"./package.json": "./package.json",
".": {
"import": {
"types": "./dist/index.d.ts",
"default": "./dist/index.esm.js"
},
"require": {
"types": "./dist/index.d.ts",
"default": "./dist/index.cjs.js"
}
},
"./foo": {
"import": {
"types": "./dist/foo.d.ts",
"default": "./dist/foo.esm.js"
},
"require": {
"types": "./dist/foo.d.ts",
"default": "./dist/foo.cjs.js"
}
}
}
}

Now consumers of your package can access the appropriate format for their codebase and TypeScript will correctly resolve types regardless of whether they're using ESM or CommonJS.

After building your package, verify that the types are correctly configured. A common mistake is misconfiguring the exports field, which can cause TypeScript compilation errors for consumers of your package.

Use Are the types wrong? to check your package:

  • Web app: Upload your built package or check a published npm package at arethetypeswrong.github.io
  • CLI tool: Install @arethetypeswrong/cli to check packages locally or in CI:
Terminal window
npx @arethetypeswrong/cli ./dist/my-awesome-lib

This tool will identify common issues like missing type declarations for specific export conditions, helping you catch problems before publishing.