
Tsdown
- 55 installs
- 14 repo stars
- Updated March 2, 2026
- oakoss/agent-skills
Helps with ai & agent building tasks during AI-assisted development.
About
tsdown is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted coding.
- tsdown
- AI & Agent Building
- AI-coding skill
Tsdown by the numbers
- 55 all-time installs (skills.sh)
- +1 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #6,793 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/oakoss/agent-skills --skill tsdownAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 55 |
|---|---|
| repo stars | ★ 14 |
| Last updated | March 2, 2026 |
| Repository | oakoss/agent-skills ↗ |
What it does
Helps with ai & agent building tasks during AI-assisted development.
Files
tsdown
Overview
tsdown is an elegant library bundler built on Rolldown (Rust-based), providing a complete out-of-the-box solution for building TypeScript and JavaScript libraries. It handles source transformation, multiple output formats (ESM, CJS, IIFE, UMD), TypeScript declaration file generation, and package.json exports field generation with sensible defaults.
When to use: Building TypeScript libraries for npm, generating declaration files, producing dual ESM/CJS packages, bundling CLI tools, library development with watch mode, migrating from tsup.
When NOT to use: Application bundling (use Vite/Rolldown directly), server-side rendering frameworks (use framework bundlers), projects that need Webpack-specific features, simple scripts that need no bundling.
Quick Reference
| Pattern | API | Key Points |
|---|---|---|
| Basic build | npx tsdown | Auto-detects src/index.ts entry |
| Config file | tsdown.config.ts | defineConfig() for type safety |
| Entry points | entry: ['src/index.ts'] | String, array, or object for named entries |
| Output formats | format: ['esm', 'cjs'] | ESM, CJS, IIFE, UMD supported |
| Declaration files | dts: true | Auto-detects from package.json types field |
| Fixed extensions | fixedExtension: true | Forces .mjs/.cjs and .d.mts/.d.cts |
| Externals | external: ['react'] | node_modules external by default |
| Bundle deps | noExternal: ['lodash-es'] | Force-bundle specific dependencies |
| Target | target: 'node18' | ES version or Node version |
| Platform | platform: 'node' | node, browser, or neutral |
| Minification | minify: true | Tree shaking enabled separately |
| Tree shaking | treeshake: true | Dead code elimination |
| Source maps | sourcemap: true | Inline or external source maps |
| Watch mode | tsdown --watch | Auto-rebuild on file changes |
| Clean output | clean: true | Remove output directory before build |
| Validation | --publint --attw | Package quality checks post-build |
| Size report | --report | Bundle size with gzip/brotli stats |
| Multiple configs | defineConfig([...]) | Array of configs for different outputs |
| Dynamic config | defineConfig((opts) => {}) | Function receiving CLI options |
| Programmatic API | import { build } | Use in Node.js scripts |
| Banner/Footer | banner: { js: '...' } | Prepend/append to output files |
| onSuccess hook | onSuccess: async () => {} | Run after successful build |
| Node protocol | nodeProtocol: true | Add/strip node: prefix on built-in imports |
Common Mistakes
| Mistake | Correct Pattern |
|---|---|
Not setting fixedExtension with dual formats | Use fixedExtension: true for .mjs/.cjs when publishing both ESM and CJS |
| Bundling all node_modules into library output | Dependencies are external by default; use noExternal only for specific deps |
Using dts: true without TypeScript configured | Ensure tsconfig.json exists with proper compilerOptions |
Setting target too low for modern syntax | Match target to your minimum supported Node/browser version |
Missing types field in package.json | Add types or exports types condition for declaration resolution |
| Not cleaning output before format changes | Use clean: true to avoid stale files from previous builds |
Inline external for Node built-ins | Node built-ins (node:fs, etc.) are auto-externalized on platform: 'node' |
| Using interactive CLI flags in CI | Use config file or non-interactive CLI flags for CI builds |
Delegation
- Build configuration review: Use
Taskagent to analyze tsdown config for correctness - Package publishing: Use
Exploreagent to verify package.json exports and types fields - Migration from tsup: Use
Taskagent to map tsup options to tsdown equivalents
References
- Configuration: config file, entry points, output formats, target, platform, externals
- Declaration files: DTS generation, isolated declarations, type bundling
- Advanced features: plugins, code splitting, tree shaking, watch mode, validation
Advanced Features
Plugins
tsdown supports the entire Rolldown plugin ecosystem and is compatible with most Rollup plugins.
import { defineConfig } from 'tsdown';
import somePlugin from 'rolldown-plugin-example';
export default defineConfig({
entry: ['src/index.ts'],
plugins: [somePlugin()],
});Customizing Rolldown Options
For advanced scenarios, use inputOptions and outputOptions to access Rolldown's full configuration.
import { defineConfig } from 'tsdown';
export default defineConfig({
entry: ['src/index.ts'],
inputOptions: {
resolve: {
alias: {
'@': './src',
},
},
},
outputOptions: {
banner: '/* Built with tsdown */',
},
});Tree Shaking
Enable dead code elimination to reduce bundle size.
import { defineConfig } from 'tsdown';
export default defineConfig({
entry: ['src/index.ts'],
treeshake: true,
});Tree shaking works with ESM output by default. Mark side-effect-free packages in package.json:
{
"sideEffects": false
}Or specify files with side effects:
{
"sideEffects": ["./src/polyfills.ts", "*.css"]
}Minification
Reduce output size for production builds.
import { defineConfig } from 'tsdown';
export default defineConfig({
entry: ['src/index.ts'],
minify: true,
});Combine with tree shaking for maximum size reduction:
import { defineConfig } from 'tsdown';
export default defineConfig({
entry: ['src/index.ts'],
treeshake: true,
minify: true,
});Source Maps
Generate source maps for debugging.
import { defineConfig } from 'tsdown';
export default defineConfig({
entry: ['src/index.ts'],
sourcemap: true,
});Watch Mode
Automatically rebuild when source files change. Useful during development.
npx tsdown --watchimport { defineConfig } from 'tsdown';
export default defineConfig({
entry: ['src/index.ts'],
watch: true,
});onSuccess Hook
Run a callback after each successful build. Receives the config and an abort signal for watch mode.
import { defineConfig } from 'tsdown';
export default defineConfig({
entry: ['src/index.ts'],
watch: true,
onSuccess: async (_config, _signal) => {
console.log('Build completed!');
},
});Clean Output
Remove the output directory before each build to avoid stale files.
import { defineConfig } from 'tsdown';
export default defineConfig({
entry: ['src/index.ts'],
outDir: 'dist',
clean: true,
});Banner and Footer
Prepend or append content to output files. Useful for CLI tools or license headers.
import { defineConfig } from 'tsdown';
export default defineConfig({
entry: ['src/cli.ts'],
format: 'cjs',
banner: {
js: '#!/usr/bin/env node',
},
});Build Validation
publint
Validate package.json configuration and exports against common publishing mistakes.
npx tsdown --publintimport { defineConfig } from 'tsdown';
export default defineConfig({
entry: ['src/index.ts'],
dts: true,
publint: {
level: 'warning',
strict: true,
},
});Are The Types Wrong (attw)
Check that TypeScript declaration files resolve correctly for all export conditions.
npx tsdown --attwimport { defineConfig } from 'tsdown';
export default defineConfig({
entry: ['src/index.ts'],
dts: true,
attw: {
format: 'table-flipped',
},
});Size Report
Generate bundle size reports with compression statistics.
npx tsdown --reportimport { defineConfig } from 'tsdown';
export default defineConfig({
entry: ['src/index.ts'],
report: {
gzip: true,
brotli: true,
},
});Combined Validation
npx tsdown --publint --attw --reportProgrammatic API
Use tsdown programmatically in Node.js scripts for custom build workflows.
import { build } from 'tsdown';
await build({
entry: ['src/index.ts'],
format: ['esm', 'cjs'],
outDir: 'dist',
dts: true,
clean: true,
});Advanced Programmatic Build
import { build } from 'tsdown';
await build({
entry: {
index: 'src/index.ts',
cli: 'src/cli.ts',
},
format: ['esm', 'cjs'],
outDir: 'dist',
clean: true,
dts: {
bundle: true,
resolve: true,
},
sourcemap: true,
treeshake: true,
platform: 'node',
target: 'node18',
external: ['react', 'vue'],
});Build with Error Handling
import { build } from 'tsdown';
try {
await build({
entry: ['src/index.ts'],
format: ['esm'],
logLevel: 'info',
failOnWarn: true,
});
} catch (error) {
console.error('Build failed:', error);
process.exit(1);
}CLI Quick Reference
npx tsdown # Build with defaults
npx tsdown src/index.ts src/cli.ts # Specific entries
npx tsdown --format esm,cjs --dts # Dual format with declarations
npx tsdown --minify --sourcemap # Production build
npx tsdown --watch # Development watch mode
npx tsdown --target es2020 --platform node # Target environment
npx tsdown --clean # Clean before build
npx tsdown --publint --attw --report # Full validationNode.js Version Requirement
tsdown requires Node.js version 20.19 or higher.
Getting Started with create-tsdown
Scaffold a new library project with the create-tsdown CLI.
npx create-tsdown my-libraryProvides starter templates for pure TypeScript libraries and frontend libraries (React, Vue, Solid, Svelte).
Configuration
Config File
tsdown searches for configuration files in the current working directory and parent directories. Supported file names:
tsdown.config.ts(recommended)tsdown.config.mts,tsdown.config.ctstsdown.config.js,tsdown.config.mjs,tsdown.config.cjstsdown.config.json
Configuration can also be defined in the tsdown field of package.json.
Basic Config
import { defineConfig } from 'tsdown';
export default defineConfig({
entry: ['src/index.ts'],
format: ['esm', 'cjs'],
outDir: 'dist',
dts: true,
clean: true,
});Custom Config Path
npx tsdown --config custom.config.ts
npx tsdown --no-configEntry Points
Entry points can be a string, array, or object for named entries.
import { defineConfig } from 'tsdown';
export default defineConfig({
entry: ['src/index.ts'],
});import { defineConfig } from 'tsdown';
export default defineConfig({
entry: {
index: 'src/index.ts',
cli: 'src/cli.ts',
utils: 'src/utils/index.ts',
},
});When no entry is specified, tsdown auto-detects src/index.ts.
Output Formats
tsdown supports four output formats: esm, cjs, iife, and umd.
Dual ESM/CJS Output
import { defineConfig } from 'tsdown';
export default defineConfig({
entry: ['src/index.ts'],
format: ['esm', 'cjs'],
fixedExtension: true,
});Setting fixedExtension: true ensures .mjs/.cjs extensions regardless of package.json type field. This is recommended for dual-format packages.
Custom Extensions
import { defineConfig } from 'tsdown';
export default defineConfig({
entry: ['src/index.ts'],
format: ['esm', 'cjs'],
outExtensions({ format, pkgType }) {
return {
js: format === 'esm' ? '.mjs' : '.cjs',
dts: format === 'esm' ? '.d.mts' : '.d.cts',
};
},
});IIFE with Global Name
import { defineConfig } from 'tsdown';
export default defineConfig({
entry: ['src/index.ts'],
format: 'iife',
globalName: 'MyLib',
});Target Environment
Set the compilation target for syntax downleveling.
import { defineConfig } from 'tsdown';
export default defineConfig({
entry: ['src/index.ts'],
target: 'node18',
});Valid targets include ES versions (es2020, es2022) and Node versions (node18, node20).
Platform
Controls module resolution and built-in handling.
import { defineConfig } from 'tsdown';
export default defineConfig({
entry: ['src/index.ts'],
platform: 'node',
});| Platform | Behavior |
|---|---|
node | Node built-ins (node:fs, etc.) auto-externalized |
browser | No built-in externalization, browser-compatible output |
neutral | No platform-specific behavior |
External Dependencies
By default, all node_modules dependencies are externalized. Use external, noExternal, and inlineOnly to customize.
Explicit Externals
import { defineConfig } from 'tsdown';
export default defineConfig({
entry: ['src/index.ts'],
external: ['react', 'vue'],
});Bundle Specific Dependencies
import { defineConfig } from 'tsdown';
export default defineConfig({
entry: ['src/index.ts'],
noExternal: ['lodash-es', 'tiny-invariant'],
});Strict Inline Mode
import { defineConfig } from 'tsdown';
export default defineConfig({
entry: ['src/index.ts'],
inlineOnly: ['lodash-es', 'debug'],
});With inlineOnly, tsdown throws an error if any other dependencies are imported but not listed.
Regex Patterns
import { defineConfig } from 'tsdown';
export default defineConfig({
entry: ['src/index.ts'],
external: [/^node:/, /^@types\//],
noExternal: [/^lodash/],
});Dynamic Resolution
import { defineConfig } from 'tsdown';
export default defineConfig({
entry: ['src/index.ts'],
noExternal: (id, _importer) => {
if (id.startsWith('lodash/')) return true;
return false;
},
});Skip All node_modules
import { defineConfig } from 'tsdown';
export default defineConfig({
entry: ['src/index.ts'],
skipNodeModulesBundle: true,
});Node Protocol Handling
Control how Node.js built-in module imports are handled in output.
import { defineConfig } from 'tsdown';
export default defineConfig({
entry: ['src/index.ts'],
nodeProtocol: true,
});| Value | Behavior |
|---|---|
true | Adds node: prefix to built-ins (fs becomes node:fs) |
'strip' | Removes node: prefix (node:fs becomes fs) |
false | Keeps imports as-is (default) |
Use true for modern Node.js targets (v16+), 'strip' for older runtimes.
Multiple Configurations
Build multiple outputs with different settings in a single run.
import { defineConfig } from 'tsdown';
export default defineConfig([
{
entry: ['src/index.ts'],
format: ['esm'],
outDir: 'dist/esm',
},
{
entry: ['src/cli.ts'],
format: ['cjs'],
outDir: 'dist/cjs',
banner: {
js: '#!/usr/bin/env node',
},
},
]);Dynamic Configuration
Access CLI options in the config function.
import { defineConfig } from 'tsdown';
export default defineConfig((cliOptions) => {
return {
entry: ['src/index.ts'],
format: ['esm', 'cjs'],
minify: process.env.NODE_ENV === 'production',
dts: cliOptions.dts ?? true,
};
});Recommended package.json Setup
{
"name": "my-library",
"type": "module",
"exports": {
".": {
"import": {
"types": "./dist/index.d.mts",
"default": "./dist/index.mjs"
},
"require": {
"types": "./dist/index.d.cts",
"default": "./dist/index.cjs"
}
}
},
"main": "./dist/index.cjs",
"module": "./dist/index.mjs",
"types": "./dist/index.d.mts",
"scripts": {
"build": "tsdown"
}
}Declaration Files
Basic DTS Generation
Enable declaration file generation with dts: true. tsdown auto-detects the output path from the types field in package.json.
import { defineConfig } from 'tsdown';
export default defineConfig({
entry: ['src/index.ts'],
format: ['esm', 'cjs'],
dts: true,
});This generates .d.ts files alongside JavaScript output. With fixedExtension: true, it produces .d.mts and .d.cts for ESM and CJS respectively.
import { defineConfig } from 'tsdown';
export default defineConfig({
entry: ['src/index.ts'],
format: ['esm', 'cjs'],
dts: true,
fixedExtension: true,
});Advanced DTS Configuration
Pass an object to dts for fine-grained control.
Bundle Declarations
Bundle all declaration files into a single .d.ts file, inlining types from dependencies.
import { defineConfig } from 'tsdown';
export default defineConfig({
entry: ['src/index.ts'],
dts: {
bundle: true,
},
});Resolve External Types
Include type declarations from external packages in the bundled output.
import { defineConfig } from 'tsdown';
export default defineConfig({
entry: ['src/index.ts'],
dts: {
bundle: true,
resolve: true,
},
});Custom Entry Point
Specify a different entry point for declaration generation.
import { defineConfig } from 'tsdown';
export default defineConfig({
entry: ['src/index.ts'],
dts: {
entry: './src/types.ts',
},
});Custom Compiler Options
Override TypeScript compiler options for declaration generation.
import { defineConfig } from 'tsdown';
export default defineConfig({
entry: ['src/index.ts'],
dts: {
compilerOptions: {
composite: false,
declaration: true,
},
},
});Dual Format Declarations
When publishing both ESM and CJS, generate separate declaration files with proper extensions.
import { defineConfig } from 'tsdown';
export default defineConfig({
entry: ['src/index.ts'],
format: ['esm', 'cjs'],
dts: true,
fixedExtension: true,
});This produces:
dist/
index.mjs # ESM bundle
index.cjs # CJS bundle
index.d.mts # ESM declarations
index.d.cts # CJS declarationspackage.json Types Configuration
Ensure your package.json exports map includes type conditions for proper resolution.
{
"exports": {
".": {
"import": {
"types": "./dist/index.d.mts",
"default": "./dist/index.mjs"
},
"require": {
"types": "./dist/index.d.cts",
"default": "./dist/index.cjs"
}
}
},
"types": "./dist/index.d.mts"
}The types condition must come first in each export block for TypeScript to resolve it correctly.
Multiple Entry Point Declarations
When using multiple entry points, declaration files are generated for each entry.
import { defineConfig } from 'tsdown';
export default defineConfig({
entry: {
index: 'src/index.ts',
utils: 'src/utils/index.ts',
types: 'src/types.ts',
},
format: ['esm'],
dts: true,
});Map each entry in package.json exports:
{
"exports": {
".": {
"types": "./dist/index.d.mts",
"import": "./dist/index.mjs"
},
"./utils": {
"types": "./dist/utils.d.mts",
"import": "./dist/utils.mjs"
},
"./types": {
"types": "./dist/types.d.mts",
"import": "./dist/types.mjs"
}
}
}Validating Declarations
Use the built-in --attw flag (Are The Types Wrong) to validate that declaration files resolve correctly for all export conditions.
npx tsdown --attwCombine with --publint for full package validation:
npx tsdown --publint --attwCommon DTS Issues
| Issue | Solution |
|---|---|
| Missing types in output | Verify dts: true is set and tsconfig.json exists |
| Wrong extension for declarations | Use fixedExtension: true for dual ESM/CJS |
| Types not resolving for consumers | Ensure types condition is first in exports map |
| Declaration bundling too slow | Use dts: { bundle: false } for faster builds during development |
| External types not included | Use dts: { resolve: true } to inline external types |