將 TypeScript 函式庫專案從 tsup 遷移到 tsdown。提供完整的選項對應、設定轉換規則、預設值差異,以及不支援選項的替代方案,讓 AI 代理可以智慧地執行遷移。
從 tsup 遷移到 tsdown
提供 AI 代理將 tsup 專案遷移到 tsdown(以 Rolldown 為核心的函式庫打包工具)的知識庫。
目標版本:兩階段遷移
tsdown v0.23 移除了所有先前已棄用的 tsup 相容選項——bundle、outExtension、publicDir、removeNodeProtocol、injectStyle 和 skipNodeModulesBundle 不再被辨識。它們會導致 TypeScript 型別檢查失敗,且在執行時會被靜默忽略,因此在 v0.23+ 上遺漏對應會產生錯誤輸出而不會報錯。直接遷移到 v0.23+ 因此不安全。tsdown v0.22.14 是最後一個仍接受這些選項並對每個選項發出棄用警告的版本,使其成為安全的遷移檢查點。請分兩個階段遷移:
- 階段 1——在
tsdown@0.22.14上遷移:安裝tsdown@0.22.14,根據下方表格遷移設定,執行建置,並透過將每個標記的 tsup 選項對應到真正的 tsdown 等效選項來解決每個棄用警告。警告是完整性檢查——直到建置產生零警告,遷移才算完成。 - 階段 2——升級到最新的 tsdown(
^0.23.0或更新版本):僅在 0.22.14 上建置無警告後進行。由於設定不再使用任何已移除的相容選項,v0.23+ 的靜默忽略行為不再構成風險。
執行需求
tsdown 需要 Node.js ^22.18.0 || ^24.11.0 || >=26.0.0 才能執行(僅建置時期)——也就是 Node.js 22.18+、24.11+ 或 26+。奇數版本和 EOL 發行線(例如 Node.js 23、25)不受支援。打包的輸出仍可透過 target 選項指定較低的 Node.js 版本,因此先前使用 tsup 支援 Node.js 18 / 20 的函式庫在遷移後可以繼續支援。
支援 Node.js 18 / 20 時的建議工作流程:
- 在 CI 中使用 Node.js 22+ 建置,設定明確的
target,例如'node18'或'node20'。 - 在需要支援的較低 Node.js 版本上測試建置輸出(或打包的 tarball)。
使用時機
- 將專案從 tsup 遷移到 tsdown
- 了解 tsup 和 tsdown 選項之間的差異
- 檢閱或修正遷移後的設定問題
- 為使用者提供 tsup→tsdown 相容性建議
遷移概覽
請依照以下步驟遷移 tsup 專案:
- 重新命名設定檔:
tsup.config.*→tsdown.config.* - 更新匯入:
'tsup'→'tsdown' - 套用選項對應:根據下方表格重新命名/轉換選項
- 保留 tsup 預設值:明確設定有差異的選項(format、clean、dts、target)
- 更新 package.json:相依套件(階段 1:
tsdown@0.22.14)、腳本、根設定欄位 - 移除不支援的選項:在有替代方案時替換
- 在 0.22.14 上測試建置:執行
tsdown並解決每個棄用警告——需要零警告 - 升級到最新的 tsdown(
^0.23.0或更新版本)並再次驗證建置
設定檔遷移
檔案重新命名
| 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 { defineConfig } from 'tsup'
// 之後
import { defineConfig } from 'tsdown'
取代所有識別碼:tsup → tsdown、TSUP → TSDOWN。
選項對應
屬性重新命名
| tsup | tsdown | 備註 |
|---|---|---|
entryPoints |
entry |
在 tsup 本身也已棄用 |
cjsInterop |
cjsDefault |
CJS 預設匯出處理 |
esbuildPlugins |
plugins |
現在使用 Rolldown/Unplugin 外掛 |
outExtension |
outExtensions |
自訂輸出副檔名 |
publicDir |
copy |
複製靜態檔案到輸出 |
bundle: true |
(移除) | 打包是預設行為 |
bundle: false |
unbundle: true |
保留檔案結構 |
removeNodeProtocol: true |
nodeProtocol: 'strip' |
移除 node: 前綴 |
injectStyle: true |
css: { inject: true } |
CSS 注入 |
injectStyle: false |
(移除) | 預設行為 |
skipNodeModulesBundle |
deps: { neverBundle: true } |
將所有相依套件外部化 |
tsdown v0.23+ 不辨識任何舊名稱——一律使用新名稱。相容選項(outExtension、skipNodeModulesBundle、publicDir、bundle、removeNodeProtocol、injectStyle)在 v0.22.14 之前會接受並發出棄用警告,並在 v0.23 中完全移除;在 v0.23+ 上殘留的選項會被靜默忽略,而不是報錯,因此建置會在沒有警告的情況下行為異常。這就是為什麼階段 1 在 v0.22.14 上執行,每個殘留選項都會被標記。
已棄用但仍接受
external 和 noExternal 是 tsdown v0.23 仍接受的唯一 tsup 選項名稱。它們會發出棄用警告,將在未來版本中移除,且不能與其替代選項結合使用(將 external 與 deps.neverBundle 混合,或將 noExternal 與 deps.alwaysBundle 混合,會拋出錯誤)。一律使用替代選項。
| tsup(已棄用) | tsdown(建議) | 備註 |
|---|---|---|
external: [...] |
deps: { neverBundle: [...] } |
移至 deps 命名空間 |
noExternal: [...] |
deps: { alwaysBundle: [...] } |
移至 deps 命名空間 |
輸出檔名差異
對於 IIFE 建置,tsdown 會產生像 [name].iife.js 這樣的名稱,而 tsup 通常產生 [name].global.js。outExtensions 可以自訂副檔名或後綴,但不會移除內建的 .iife 或 .umd 區段。使用 outputOptions.entryFileNames: '[name].global.js' 來保留舊的 IIFE 檔名。
相依套件命名空間移動
相依套件設定已移至 deps 命名空間下。如果同時存在 external 和 noExternal,請合併為單一 deps 物件:
// 之前(tsup)
export default defineConfig({
external: ['react'],
noExternal: ['lodash-es'],
})
// 之後(tsdown)
export default defineConfig({
deps: {
neverBundle: ['react'],
alwaysBundle: ['lodash-es'],
},
})
tsdown 也新增了 deps.onlyBundle(允許打包的套件白名單)——tsup 沒有對應選項。
tsdown v0.23+ 的相依套件處理預設值:
dependencies、peerDependencies和optionalDependencies預設都會外部化(tsup 只外部化dependencies和peerDependencies,因此optionalDependencies在遷移後可能從打包改為外部化)。deps.resolveDepSubpath(解析外部化套件的子路徑匯入,當沒有exports欄位時)預設停用。
外掛匯入轉換
// 之前(tsup - esbuild 外掛)
import plugin from 'unplugin-example/esbuild'
// 之後(tsdown - Rolldown 外掛)
import plugin from 'unplugin-example/rolldown'
所有 unplugin-*/esbuild 匯入都應改為 unplugin-*/rolldown。
如需每個轉換的完整前後範例,請參閱 guide-option-mappings.md。
預設值差異
tsdown 變更了 tsup 的幾個預設值。遷移時,請明確設定這些選項以保留 tsup 行為,然後讓使用者決定要採用哪些新預設值。
| 選項 | tsup 預設值 | tsdown 預設值 | 遷移動作 |
|---|---|---|---|
format |
'cjs' |
'esm' |
設定 format: 'cjs' 以保留 |
clean |
false |
true |
設定 clean: false 以保留 |
dts |
false |
若 package.json 中有 types/typings 則自動啟用 |
設定 dts: false 以保留 |
target |
(無) | 自動從 package.json 的 engines.node 讀取 |
設定 target: false 以保留 |
遷移後,建議使用者檢閱這些選項——tsdown 的預設值通常更好:
- ESM 是現代標準
- 清理輸出可防止過時檔案
- 從 package.json 自動產生 DTS 可減少設定
- 從 engines.node 自動設定 target 可確保一致性
不支援的選項
這些 tsup 選項在 tsdown 中沒有直接等效選項。請移除並告知使用者。
| tsup 選項 | 狀態 | 替代方案 |
|---|---|---|
splitting |
永遠啟用 | 移除——tsdown 中無法停用程式碼分割 |
metafile |
不可用 | 建議使用 devtools: true 進行 Vite DevTools 套件分析 |
swc |
不支援 | 移除——tsdown 使用 oxc 進行轉換(內建) |
experimentalDts |
不支援 | 改用 dts 選項 |
legacyOutput |
不支援 | 移除——沒有替代方案 |
plugins(tsup 實驗性) |
不相容 | 手動遷移到 Rolldown 外掛;tsup 的外掛 API 與 Rolldown 不同 |
Package.json 遷移
腳本
將所有腳本指令中的 tsup 和 tsup-node 替換為 tsdown:
// 之前
{
"scripts": {
"build": "tsup src/index.ts",
"dev": "tsup --watch"
}
}
// 之後
{
"scripts": {
"build": "tsdown src/index.ts",
"dev": "tsdown --watch"
}
}
相依套件
| 位置 | 動作 |
|---|---|
dependencies.tsup |
重新命名為 dependencies.tsdown |
devDependencies.tsup |
重新命名為 devDependencies.tsdown |
optionalDependencies.tsup |
重新命名為 optionalDependencies.tsdown |
peerDependencies.tsup |
重新命名為 peerDependencies.tsdown |
peerDependenciesMeta.tsup |
重新命名為 peerDependenciesMeta.tsdown |
根設定欄位
如果 package.json 有根層級的 tsup 欄位(內聯設定),請重新命名為 tsdown:
// 之前
{ "tsup": { "entry": ["src/index.ts"] } }
// 之後
{ "tsdown": { "entry": ["src/index.ts"] } }
如需詳細的 package.json 範例,請參閱 guide-package-json.md。
新的 tsdown 功能
遷移後,建議使用者使用這些 tsdown 專屬功能:
| 功能 | 設定 | 描述 |
|---|---|---|
| Node 協定 | nodeProtocol: true | 'strip' |
在內建匯入上新增或移除 node: 前綴 |
| Workspace | workspace: 'packages/*' |
在 monorepo 中建置多個套件 |
| 套件匯出 | exports: true |
自動產生 package.json 中的 exports 欄位 |
| 套件驗證 | publint: true、attw: true |
檢查套件並驗證型別正確性 |
| 可執行檔 | exe: true |
打包為 Node.js 獨立可執行檔(SEA) |
| DevTools | devtools: true |
Vite DevTools 整合,用於套件分析 |
| Hooks | hooks: { 'build:done': ... } |
生命週期 hooks:build:prepare、build:before、build:done |
| CSS modules | css: { modules: { ... } } |
為 .module.css 檔案提供作用域類別名稱 |
| Glob 匯入 | globImport: true |
支援 import.meta.glob(Vite 風格) |
如需詳細比較,請參閱 guide-differences-detailed.md。
參考資料
| 主題 | 描述 | 參考 |
|---|---|---|
| 選項對應 | 每個選項轉換的完整前後對照 | guide-option-mappings |
| 詳細差異 | 架構、功能、相容性比較 | guide-differences-detailed |
| Package.json | 相依套件、腳本和設定欄位遷移 | guide-package-json |
遷移檢查清單
執行遷移時請使用此檢查清單:
- [ ] 重新命名 tsup.config.* → tsdown.config.*
- [ ] 將匯入從 'tsup' 更新為 'tsdown'
- [ ] 將 tsup/TSUP 識別碼替換為 tsdown/TSDOWN
- [ ] 套用屬性重新命名(cjsInterop→cjsDefault、esbuildPlugins→plugins、outExtension→outExtensions、publicDir→copy、bundle→unbundle、removeNodeProtocol→nodeProtocol、injectStyle→css.inject)
- [ ] 將 external/noExternal 移至 deps 命名空間,並將 skipNodeModulesBundle 替換為 `deps.neverBundle: true`
- [ ] 將 unplugin 匯入從 /esbuild 更新為 /rolldown
- [ ] 設定明確的預設值以保留 tsup 行為(format、clean、dts、target)
- [ ] 移除不支援的選項(splitting、metafile、swc 等)
- [ ] 更新 package.json 腳本(tsup→tsdown)
- [ ] 更新 package.json 相依套件(階段 1:tsdown@0.22.14)
- [ ] 如果存在,重新命名根層級的 tsup 設定欄位
- [ ] 在 0.22.14 上執行 tsdown 並解決每個棄用警告(需要零警告)
- [ ] 升級到最新的 tsdown(^0.23.0 或更新版本)並再次驗證建置
- [ ] 向使用者建議新的 tsdown 功能






