/tsdown-migrate
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.
$ npx -y skills add rolldown/tsdown --skill tsdown-migrate --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/tsdown-migrate
Context preview
The summary Claude sees to decide when to auto-load this skill.
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.
SKILL.md
tsdown-migrate.SKILL.mdname: 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.
Target Version: Two-Stage Migration
tsdown v0.23 removed all previously-deprecated tsup compatibility options — `bundle`, `outExtension`, `publicDir`, `removeNodeProtocol`, `injectStyle`, and `skipNodeModulesBundle` are no longer recognized. They fail TypeScript type checking and are **silently ignored at runtime**, so a missed mapping on v0.23+ produces wrong output without any error. Migrating directly to v0.23+ is therefore unsafe. tsdown **v0.22.14** is the last version that still accepts these options and flags each one with a deprecation warning, making it the safe migration checkpoint. Migrate in **two stages**:
1. **Stage 1 — migrate on `tsdown@0.22.14`**: Install `tsdown@0.22.14`, migrate the config per the tables below, run a build, and resolve **every** deprecation warning by mapping each flagged tsup option to its real tsdown equivalent. The warnings are the completeness check — the migration is not done until the build produces **zero warnings**. 2. **Stage 2 — upgrade to the latest tsdown (`^0.23.0` or newer)**: Only after a warning-free build on 0.22.14. Since the config no longer uses any removed compat options, the silent-ignore behavior of v0.23+ is no longer a risk.
Runtime Requirement
`tsdown` requires **Node.js `^22.18.0 || ^24.11.0 || >=26.0.0` to run** (build-time only) — that is, Node.js 22.18+, 24.11+, or 26+. Odd-numbered and EOL release lines (e.g. Node.js 23, 25) are not supported. The bundled output can still target lower Node.js versions via the [`target`](../tsdown/references/option-target.md) 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:
1. **Rename config file**: `tsup.config.*` → `tsdown.config.*` 2. **Update imports**: `'tsup'` → `'tsdown'` 3. **Apply option mappings**: Rename/transform options per tables below 4. **Preserve tsup defaults**: Explicitly set options that differ (format, clean, dts, target) 5. **Update package.json**: Dependencies (Stage 1: `tsdown@0.22.14`), scripts, root config field 6. **Remove unsupported options**: Replace with alternatives where available 7. **Test build on 0.22.14**: Run `tsdown` and resolve every deprecation warning — zero warnings required 8. **Upgrade to latest tsdown** (`^0.23.0` or newer) and verify the build again
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
// Before
import { defineConfig } from 'tsup'
// After
import { defineConfig } from 'tsdown'Replace all identifiers: `tsup` → `tsdown`, `TSUP` → `TSDOWN`.
Option Mappings
Property Renames
| tsup | tsdown | Notes | |------|--------|-------| | `entryPoints` | `entry` | Also deprecated in tsup itself | | `cjsInterop` | `cjsDefault` | CJS default export handling | | `esbuildPlugins` | `plugins` | Now uses Rolldown/Unplugin plugins | | `outExtension` | `outExtensions` | Custom output extensions | | `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 | | `skipNodeModulesBundle` | `deps: { neverBundle: true }` | Externalize all dependencies |
None of the old names are recognized by tsdown v0.23+ — always emit the new names. The compatibility options (`outExtension`, `skipNodeModulesBundle`, `publicDir`, `bundle`, `removeNodeProtocol`, `injectStyle`) were accepted with deprecation warnings up to v0.22.14 and removed entirely in v0.23; on v0.23+ leftovers are silently ignored, not errors, so builds misbehave without warning. This is why Stage 1 runs on v0.22.14, where every leftover is flagged.
Deprecated but Still Accepted
`external` and `noExternal` are the **only** tsup option names tsdown v0.23 still accepts. They emit deprecation warnings, will be removed in a future version, and cannot be combined with their replacements (mixing `external` with `deps.neverBundle`, or `noExternal` with `deps.alwaysBundle`, throws an error). Always emit the replacements.
| tsup (deprecated) | tsdown (preferred) | Notes | |--------------------|--------------------|-------| | `external: [...]` | `deps: { neverBundle: [...] }` | Moved to deps namespace | | `noExternal: [...]` | `deps: { alwaysBundle: [...] }` | 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
Read more
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.
Target Version: Two-Stage Migration
tsdown v0.23 removed all previously-deprecated tsup compatibility options — `bundle`, `outExtension`, `publicDir`, `removeNodeProtocol`, `injectStyle`, and `skipNodeModulesBundle` are no longer recognized. They fail TypeScript type checking and are **silently ignored at runtime**, so a missed mapping on v0.23+ produces wrong output without any error. Migrating directly to v0.23+ is therefore unsafe. tsdown **v0.22.14** is the last version that still accepts these options and flags each one with a deprecation warning, making it the safe migration checkpoint. Migrate in **two stages**:
1. **Stage 1 — migrate on `tsdown@0.22.14`**: Install `tsdown@0.22.14`, migrate the config per the tables below, run a build, and resolve **every** deprecation warning by mapping each flagged tsup option to its real tsdown equivalent. The warnings are the completeness check — the migration is not done until the build produces **zero warnings**. 2. **Stage 2 — upgrade to the latest tsdown (`^0.23.0` or newer)**: Only after a warning-free build on 0.22.14. Since the config no longer uses any removed compat options, the silent-ignore behavior of v0.23+ is no longer a risk.
Runtime Requirement
`tsdown` requires **Node.js `^22.18.0 || ^24.11.0 || >=26.0.0` to run** (build-time only) — that is, Node.js 22.18+, 24.11+, or 26+. Odd-numbered and EOL release lines (e.g. Node.js 23, 25) are not supported. The bundled output can still target lower Node.js versions via the [`target`](../tsdown/references/option-target.md) 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:
1. **Rename config file**: `tsup.config.*` → `tsdown.config.*` 2. **Update imports**: `'tsup'` → `'tsdown'` 3. **Apply option mappings**: Rename/transform options per tables below 4. **Preserve tsup defaults**: Explicitly set options that differ (format, clean, dts, target) 5. **Update package.json**: Dependencies (Stage 1: `tsdown@0.22.14`), scripts, root config field 6. **Remove unsupported options**: Replace with alternatives where available 7. **Test build on 0.22.14**: Run `tsdown` and resolve every deprecation warning — zero warnings required 8. **Upgrade to latest tsdown** (`^0.23.0` or newer) and verify the build again
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
// Before
import { defineConfig } from 'tsup'
// After
import { defineConfig } from 'tsdown'Replace all identifiers: `tsup` → `tsdown`, `TSUP` → `TSDOWN`.
Option Mappings
Property Renames
| tsup | tsdown | Notes | |------|--------|-------| | `entryPoints` | `entry` | Also deprecated in tsup itself | | `cjsInterop` | `cjsDefault` | CJS default export handling | | `esbuildPlugins` | `plugins` | Now uses Rolldown/Unplugin plugins | | `outExtension` | `outExtensions` | Custom output extensions | | `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 | | `skipNodeModulesBundle` | `deps: { neverBundle: true }` | Externalize all dependencies |
None of the old names are recognized by tsdown v0.23+ — always emit the new names. The compatibility options (`outExtension`, `skipNodeModulesBundle`, `publicDir`, `bundle`, `removeNodeProtocol`, `injectStyle`) were accepted with deprecation warnings up to v0.22.14 and removed entirely in v0.23; on v0.23+ leftovers are silently ignored, not errors, so builds misbehave without warning. This is why Stage 1 runs on v0.22.14, where every leftover is flagged.
Deprecated but Still Accepted
`external` and `noExternal` are the **only** tsup option names tsdown v0.23 still accepts. They emit deprecation warnings, will be removed in a future version, and cannot be combined with their replacements (mixing `external` with `deps.neverBundle`, or `noExternal` with `deps.alwaysBundle`, throws an error). Always emit the replacements.
| tsup (deprecated) | tsdown (preferred) | Notes | |--------------------|--------------------|-------| | `external: [...]` | `deps: { neverBundle: [...] }` | Moved to deps namespace | | `noExternal: [...]` | `deps: { alwaysBundle: [...] }` | 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
![Open on npmx][npmx-href] ![npm downloads][npmx-href] ![Unit Test][unit-test-href] ![tsdown Starter StackBlitz][tsdown-starter-stackblitz-href] ✨ The elegant bundler for libraries powered by Rolldown.
Repo: rolldown/tsdown

