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:
nx add @nx/rollupMake 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:
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/
- ...
- ...
Configure Package Exports
Section titled “Configure Package Exports”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.
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:
{ "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.
Verify Your Package
Section titled “Verify Your Package”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/clito check packages locally or in CI:
npx @arethetypeswrong/cli ./dist/my-awesome-libThis tool will identify common issues like missing type declarations for specific export conditions, helping you catch problems before publishing.