| name | tsdown-migrate |
| description | Migrate TypeScript library projects from tsup to tsdown. Provides complete option mappings, config transformation rules, default value differences, and unsupported option alternatives so AI agents can intelligently perform migrations. |
Migrating from tsup to tsdown
Knowledge base for AI agents to migrate tsup projects to tsdown — the Rolldown-powered library bundler.
Runtime Requirement
tsdown requires Node.js 22.18.0 or higher to run (build-time only). The bundled output can still target lower Node.js versions via the target option, so a library that previously supported Node.js 18 / 20 with tsup can continue to do so after migrating.
Recommended workflow when supporting Node.js 18 / 20:
- Build with Node.js 22+ in CI, setting an explicit
target such as 'node18' or 'node20'.
- Test the built output (or the packed tarball) on the lower Node.js versions you need to support.
When to Use
- Migrating a project from tsup to tsdown
- Understanding differences between tsup and tsdown options
- Reviewing or fixing post-migration configuration issues
- Advising users on tsup→tsdown compatibility
Migration Overview
Follow these steps to migrate a tsup project:
- Rename config file:
tsup.config.* → tsdown.config.*
- Update imports:
'tsup' → 'tsdown'
- Apply option mappings: Rename/transform options per tables below
- Preserve tsup defaults: Explicitly set options that differ (format, clean, dts, target)
- Update package.json: Dependencies, scripts, root config field
- Remove unsupported options: Replace with alternatives where available
- Test build: Run
tsdown and verify output
Config File Migration
File Rename
| tsup | tsdown |
|---|
tsup.config.ts | tsdown.config.ts |
tsup.config.cts | tsdown.config.cts |
tsup.config.mts | tsdown.config.mts |
tsup.config.js | tsdown.config.js |
tsup.config.cjs | tsdown.config.cjs |
tsup.config.mjs | tsdown.config.mjs |
tsup.config.json | tsdown.config.json |
Import and Identifier Changes
import { defineConfig } from 'tsup'
import { defineConfig } from 'tsdown'
Replace all identifiers: tsup → tsdown, TSUP → TSDOWN.
Option Mappings
Property Renames
| tsup | tsdown | Notes |
|---|
cjsInterop | cjsDefault | CJS default export handling |
esbuildPlugins | plugins | Now uses Rolldown/Unplugin plugins |
outExtension | outExtensions | Custom output extensions |
Deprecated but Compatible
These tsup options still work in tsdown for backward compatibility, but emit deprecation warnings and will be removed in a future version. Migrate them immediately.
| tsup (deprecated) | tsdown (preferred) | Notes |
|---|
entryPoints | entry | Also deprecated in tsup itself |
publicDir | copy | Copy static files to output |
bundle: true | (remove) | Bundle is default behavior |
bundle: false | unbundle: true | Preserve file structure |
removeNodeProtocol: true | nodeProtocol: 'strip' | Strip node: prefix |
injectStyle: true | css: { inject: true } | CSS injection |
injectStyle: false | (remove) | Default behavior |
external: [...] | deps: { neverBundle: [...] } | Moved to deps namespace |
noExternal: [...] | deps: { alwaysBundle: [...] } | Moved to deps namespace |
skipNodeModulesBundle | deps: { skipNodeModulesBundle: true } | Moved to deps namespace |
Output Filename Differences
For IIFE builds, tsdown emits names like [name].iife.js, while tsup commonly emitted [name].global.js. outExtensions customizes extensions or suffixes, but it does not remove the built-in .iife or .umd segment. Use outputOptions.entryFileNames: '[name].global.js' to preserve old IIFE filenames.
Dependency Namespace Moves
Dependencies config moved under deps namespace. If both external and noExternal exist, merge into a single deps object:
export default defineConfig({
external: ['react'],
noExternal: ['lodash-es'],
})
export default defineConfig({
deps: {
neverBundle: ['react'],
alwaysBundle: ['lodash-es'],
},
})
tsdown also adds deps.onlyBundle (whitelist of allowed bundled packages) — no tsup equivalent.
Plugin Import Transforms
import plugin from 'unplugin-example/esbuild'
import plugin from 'unplugin-example/rolldown'
All unplugin-*/esbuild imports should change to unplugin-*/rolldown.
For complete before/after examples of every transformation, see guide-option-mappings.md.
Default Value Differences
tsdown changes several defaults from tsup. When migrating, explicitly set these to preserve tsup behavior, then let the user decide which new defaults to adopt.
| Option | tsup Default | tsdown Default | Migration Action |
|---|
format | 'cjs' | 'esm' | Set format: 'cjs' to preserve |
clean | false | true | Set clean: false to preserve |
dts | false | Auto-enabled if types/typings in package.json | Set dts: false to preserve |
target | (none) | Auto-reads from engines.node in package.json | Set target: false to preserve |
After migration, suggest the user review these — tsdown's defaults are generally better:
- ESM is the modern standard
- Cleaning output prevents stale files
- Auto DTS from package.json reduces config
- Auto target from engines.node ensures consistency
Unsupported Options
These tsup options have no direct equivalent in tsdown. Remove them and inform the user.
| tsup Option | Status | Alternative |
|---|
splitting | Always enabled | Remove — code splitting cannot be disabled in tsdown |
metafile | Not available | Suggest devtools: true for Vite DevTools bundle analysis |
swc | Not supported | Remove — tsdown uses oxc for transformation (built-in) |
experimentalDts | Not supported | Use the dts option instead |
legacyOutput | Not supported | Remove — no alternative |
plugins (tsup experimental) | Incompatible | Migrate to Rolldown plugins manually; tsup's plugin API differs from Rolldown's |
Package.json Migration
Scripts
Replace tsup and tsup-node with tsdown in all script commands:
{
"scripts": {
"build": "tsup src/index.ts",
"dev": "tsup --watch"
}
}
{
"scripts": {
"build": "tsdown src/index.ts",
"dev": "tsdown --watch"
}
}
Dependencies
| Location | Action |
|---|
dependencies.tsup | Rename to dependencies.tsdown |
devDependencies.tsup | Rename to devDependencies.tsdown |
optionalDependencies.tsup | Rename to optionalDependencies.tsdown |
peerDependencies.tsup | Rename to peerDependencies.tsdown |
peerDependenciesMeta.tsup | Rename to peerDependenciesMeta.tsdown |
Root Config Field
If package.json has a root-level tsup field (inline config), rename to tsdown:
{ "tsup": { "entry": ["src/index.ts"] } }
{ "tsdown": { "entry": ["src/index.ts"] } }
For detailed package.json examples, see guide-package-json.md.
New tsdown Features
After migration, suggest these tsdown-exclusive features to the user:
| Feature | Config | Description |
|---|
| Node protocol | nodeProtocol: true | 'strip' | Add or strip node: prefix on built-in imports |
| Workspace | workspace: 'packages/*' | Build multiple packages in a monorepo |
| Package exports | exports: true | Auto-generate exports field in package.json |
| Package validation | publint: true, attw: true | Lint package and check type correctness |
| Executable | exe: true | Bundle as Node.js standalone executable (SEA) |
| DevTools | devtools: true | Vite DevTools integration for bundle analysis |
| Hooks | hooks: { 'build:done': ... } | Lifecycle hooks: build:prepare, build:before, build:done |
| CSS modules | css: { modules: { ... } } | Scoped class names for .module.css files |
| Glob import | globImport: true | Support import.meta.glob (Vite-style) |
For detailed comparisons, see guide-differences-detailed.md.
References
Migration Checklist
Use this checklist when performing a migration:
- [ ] Rename tsup.config.* → tsdown.config.*
- [ ] Update import from 'tsup' to 'tsdown'
- [ ] Replace tsup/TSUP identifiers with tsdown/TSDOWN
- [ ] Apply property renames (cjsInterop→cjsDefault, esbuildPlugins→plugins, outExtension→outExtensions)
- [ ] Migrate deprecated options (publicDir→copy, bundle→unbundle, removeNodeProtocol→nodeProtocol, injectStyle→css.inject)
- [ ] Move external/noExternal/skipNodeModulesBundle into deps namespace
- [ ] Update unplugin imports from /esbuild to /rolldown
- [ ] Set explicit defaults to preserve tsup behavior (format, clean, dts, target)
- [ ] Remove unsupported options (splitting, metafile, swc, etc.)
- [ ] Update package.json scripts (tsup→tsdown)
- [ ] Update package.json dependencies
- [ ] Rename root-level tsup config field if present
- [ ] Run tsdown and verify build output
- [ ] Suggest new tsdown features to the user