Back to News & Insights
Web Development October 7, 2026 Β· 7 min read

Migrating a Real TypeScript OSS Library from tsup to tsdown πŸ”§

The config migration was small. Preserving the package contract was the interesting part. Hoi hoi!...

Migrating a Real TypeScript OSS Library from tsup to tsdown πŸ”§

The config migration was small. Preserving the package contract was the interesting part.

I'm @nyaomaru, a frontend engineer who enjoyed looking for fungus this season. πŸ˜ΈπŸ„

Just remove tsup. Just install tsdown. Just change the config. Run the build. Done! πŸŽ‰

The first tsdown build succeeded while silently changing files that were already part of the package's public contract.

Instead, I want to show what happened when I migrated a real published TypeScript library from tsup to tsdown, what actually changed, and how I verified that consumers would still receive the same package.

The package had: one entry point ESM and CJS outputs bundled declaration files explicit package.json exports a declaration banner a packed-package smoke test

| Environment | Value | | ------- | -------------------------------- | | Package | is-kit@1.14.2 | | Bundler | tsup@8.5.1 | | Entry | src/index.ts | | Output | ESM + CJS + bundled declarations | | Target | esnext |

tsdown is built around Rolldown, has an active ecosystem, and is explicitly designed as a migration path for projects currently using tsup.

Can I move the build setup to tsdown without changing what existing consumers receive?

along with the existing ESM/CJS runtime behavior, declaration compatibility, export set, and declaration banner.

The package itself is not "type": "module", and using an ESM-specific config extension avoids having Node reinterpret the config file and emit related warnings.

| Build | Main generated files | | -------------------------- | ------------------------------------------------------ | | tsup | index.mjs, index.js, index.d.ts | | default tsdown migration | index.mjs, index.cjs, index.d.mts, index.d.cts |

It doesn't automatically mean that output still matches every promise your already-published package makes.

The tsdown migration guide explicitly calls out the rename from outExtension to outExtensions.

tsdown chooses extensions based on the package type and output format to avoid ambiguous module interpretation.

If consumers already depend on your filenames, those filenames are part of your compatibility surface.

I already had a pnpm test:package command that builds the package, runs npm pack, installs the generated tarball into a temporary project, and verifies it from a real consumer's perspective.

A library can work perfectly inside its own repository while still publishing a broken package, so this smoke test exercises the artifact that users would actually install.

Want to discuss this further?

Book a free strategy call with our team to see how these insights apply to your specific business goals.

Book a consultation