Generated banner for Tsdown
Generated logo for Tsdown

Tsdown

JetBrainsOfficialCreated by Anthony FuDistributed by JetBrains

  • Skill
  • Tool integration
  • Software development
  • MIT
  • No issues found

From the author

Bundle TypeScript and JavaScript libraries with blazing-fast speed powered by Rolldown. Use when building libraries, generating type declarations, bundling for multiple formats, or migrating from tsup.
— the description by Anthony Fu

AutoGineer summary

Written by AutoGineer from the skill’s own files, not by the author

tsdown is a library bundler designed for TypeScript and JavaScript projects. It leverages Rolldown and Oxc to handle bundling tasks, including generating type declarations, supporting multiple output formats, and managing dependencies. It provides a configuration-driven approach to library development, supporting various frameworks and runtime environments, and includes a migration path for users moving from tsup.

What it does

  • Bundles TypeScript and JavaScript libraries into multiple formats like ESM, CJS, IIFE, and UMD.
  • Generates TypeScript declaration files (.d.ts) automatically.
  • Supports dependency management, including externalization and bundling options.
  • Provides hooks for custom build logic and supports plugins from the Rolldown and Rollup ecosystems.
  • Offers specialized configurations for component libraries using React, Vue, Solid, or Svelte.
  • Includes a migration tool to assist with transitioning from tsup.

When to use it

  • When building TypeScript or JavaScript libraries for distribution via npm.
  • When you need to generate type declarations alongside your bundled code.
  • When your project requires bundling for multiple module formats.
  • When migrating an existing library project from tsup.
  • When developing component libraries for frameworks like React, Vue, or Svelte.

Advantages

  • Simplifies the bundling process for library authors with a clear configuration API.
  • Provides built-in support for common library tasks like package exports and ESM/CJS shims.
  • Offers CI-aware configuration options to validate packages before publishing.
  • Supports advanced features like standalone executable bundling and CSS processing.
  • Allows for flexible build configurations including monorepo and workspace support.

Usage examples

“How do I bundle my TypeScript library for both ESM and CJS formats?”
You can use a tsdown.config.ts file with the format option set to ['esm', 'cjs'] and dts set to true to generate declarations.
“How can I migrate my existing tsup project to tsdown?”
You can run the npx tsdown-migrate command to assist with the transition.
“How do I configure tsdown to exclude React from my bundle?”
In your configuration file, use the deps.neverBundle option to specify ['react', 'react-dom'].
“How do I enable watch mode for development?”
Run the command npx tsdown --watch to enable watch mode for your build process.

Install

  1. Download the skill as a ZIP:

    https://mp-staging.autogineer.com/agent-skills/jetbrains/tsdown/download
  2. Unzip it into the cross-client skills folder of your project:

    mkdir -p .agents/skills && unzip tsdown.zip -d .agents/skills

Documentation by Anthony Fu

tsdown - The Elegant Library Bundler

Blazing-fast bundler for TypeScript/JavaScript libraries powered by Rolldown and Oxc.

When to Use

  • Building TypeScript/JavaScript libraries for npm

  • Generating TypeScript declaration files (.d.ts)

  • Bundling for multiple formats (ESM, CJS, IIFE, UMD)

  • Optimizing bundles with tree shaking and minification

  • Migrating from tsup with minimal changes

  • Building React, Vue, Solid, or Svelte component libraries

Quick Start

# Install
pnpm add -D tsdown

# Basic usage
npx tsdown

# With config file
npx tsdown --config tsdown.config.ts

# Watch mode
npx tsdown --watch

# Migrate from tsup
npx tsdown-migrate

Basic Configuration

import { defineConfig } from 'tsdown'

export default defineConfig({
  entry: ['./src/index.ts'],
  format: ['esm', 'cjs'],
  dts: true,
  clean: true,
})

Core References

TopicDescriptionReference
Getting StartedInstallation, first bundle, CLI basicsguide-getting-started
Configuration FileConfig file formats, multiple configs, workspaceoption-config-file
CLI ReferenceAll CLI commands and optionsreference-cli
Migrate from tsupMigration guide and compatibility notesguide-migrate-from-tsup
PluginsRolldown, Rollup, Unplugin supportadvanced-plugins
HooksLifecycle hooks for custom logicadvanced-hooks
Programmatic APIBuild from Node.js scriptsadvanced-programmatic
Rolldown OptionsPass options directly to Rolldownadvanced-rolldown-options
CI EnvironmentCI detection, 'ci-only' / 'local-only' valuesadvanced-ci

Build Options

OptionUsageReference
Entry pointsentry: ['src/*.ts', '!**/*.test.ts']option-entry
Output formatsformat: ['esm', 'cjs', 'iife', 'umd']option-output-format
Output directoryoutDir: 'dist', outExtensionsoption-output-directory
Type declarationsdts: true, dts: { sourcemap, compilerOptions, vue }option-dts
Target environmenttarget: 'es2020', target: 'esnext'option-target
Platformplatform: 'node', platform: 'browser'option-platform
Tree shakingtreeshake: true, custom optionsoption-tree-shaking
Minificationminify: true, minify: 'dce-only'option-minification
Source mapssourcemap: true, 'inline', 'hidden'option-sourcemap
Watch modewatch: true, watch optionsoption-watch-mode
Cleaningclean: true, clean patternsoption-cleaning
Log levellogLevel: 'silent', failOnWarn: falseoption-log-level

Dependency Handling

FeatureUsageReference
Never bundledeps: { neverBundle: ['react', /^@myorg\//] }option-dependencies
Always bundledeps: { alwaysBundle: ['dep-to-bundle'] }option-dependencies
Only bundledeps: { onlyBundle: ['cac', 'bumpp'] } - Whitelistoption-dependencies
Skip node_modulesdeps: { skipNodeModulesBundle: true }option-dependencies
Auto externalAutomatic peer/dependency externalizationoption-dependencies

Output Enhancement

FeatureUsageReference
Shimsshims: true - Add ESM/CJS compatibilityoption-shims
CJS defaultcjsDefault: true (default) / falseoption-cjs-default
Package exportsexports: true - Auto-generate exports fieldoption-package-exports
CSS handling[experimental] css: { ... } — full pipeline with preprocessors, Lightning CSS, PostCSS, code splitting; requires @tsdown/cssoption-css
CSS injectcss: { inject: true } — preserve CSS imports in JS outputoption-css
Unbundle modeunbundle: true - Preserve directory structureoption-unbundle
Root directoryroot: 'src' - Control output directory mappingoption-root
Executable[experimental] exe: true - Bundle as standalone executable, cross-platform via @tsdown/exeoption-exe
Package validationpublint: true, attw: true - Validate packageoption-lint

Framework & Runtime Support

FrameworkGuideReference
ReactJSX transform, React Compilerrecipe-react
VueSFC support, JSXrecipe-vue
SolidSolidJS JSX transformrecipe-solid
SvelteSvelte component libraries (source distribution recommended)recipe-svelte
WASMWebAssembly modules via rolldown-plugin-wasmrecipe-wasm

Common Patterns

Basic Library Bundle
export default defineConfig({
  entry: ['src/index.ts'],
  format: ['esm', 'cjs'],
  dts: true,
  clean: true,
})
Multiple Entry Points
export default defineConfig({
  entry: {
    index: 'src/index.ts',
    utils: 'src/utils.ts',
    cli: 'src/cli.ts',
  },
  format: ['esm', 'cjs'],
  dts: true,
})
Browser Library (IIFE/UMD)
export default defineConfig({
  entry: ['src/index.ts'],
  format: ['iife'],
  globalName: 'MyLib',
  platform: 'browser',
  minify: true,
})
React Component Library
export default defineConfig({
  entry: ['src/index.tsx'],
  format: ['esm', 'cjs'],
  dts: true,
  deps: {
    neverBundle: ['react', 'react-dom'],
  },
  inputOptions: {
    jsx: { runtime: 'automatic' },
  },
})
Preserve Directory Structure
export default defineConfig({
  entry: ['src/**/*.ts', '!**/*.test.ts'],
  unbundle: true, // Preserve file structure
  format: ['esm'],
  dts: true,
})
CI-Aware Configuration
export default defineConfig({
  entry: ['src/index.ts'],
  format: ['esm', 'cjs'],
  dts: true,
  failOnWarn: 'ci-only',  // opt-in: fail on warnings in CI
  publint: 'ci-only',
  attw: 'ci-only',
})
WASM Support
import { wasm } from 'rolldown-plugin-wasm'
import { defineConfig } from 'tsdown'

export default defineConfig({
  entry: ['src/index.ts'],
  plugins: [wasm()],
})
Library with CSS and Sass
export default defineConfig({
  entry: ['src/index.ts'],
  format: ['esm', 'cjs'],
  dts: true,
  target: 'chrome100',
  css: {
    preprocessorOptions: {
      scss: {
        additionalData: `@use "src/styles/variables" as *;`,
      },
    },
  },
})
Standalone Executable
export default defineConfig({
  entry: ['src/cli.ts'],
  exe: true,
})
Cross-Platform Executable (requires @tsdown/exe)
export default defineConfig({
  entry: ['src/cli.ts'],
  exe: {
    targets: [
      { platform: 'linux', arch: 'x64', nodeVersion: '25.7.0' },
      { platform: 'darwin', arch: 'arm64', nodeVersion: '25.7.0' },
      { platform: 'win', arch: 'x64', nodeVersion: '25.7.0' },
    ],
  },
})
Advanced with Hooks
export default defineConfig({
  entry: ['src/index.ts'],
  format: ['esm', 'cjs'],
  dts: true,
  hooks: {
    'build:before': async (context) => {
      console.log('Building...')
    },
    'build:done': async (context) => {
      console.log('Build complete!')
    },
  },
})

Configuration Features

Multiple Configs

Export an array for multiple build configurations:

export default defineConfig([
  {
    entry: ['src/index.ts'],
    format: ['esm', 'cjs'],
    dts: true,
  },
  {
    entry: ['src/cli.ts'],
    format: ['esm'],
    platform: 'node',
  },
])
Conditional Config

Use functions for dynamic configuration:

export default defineConfig((options) => {
  const isDev = options.watch
  return {
    entry: ['src/index.ts'],
    format: ['esm', 'cjs'],
    minify: !isDev,
    sourcemap: isDev,
  }
})
Workspace/Monorepo

Use glob patterns to build multiple packages:

export default defineConfig({
  workspace: 'packages/*',
  entry: ['src/index.ts'],
  format: ['esm', 'cjs'],
  dts: true,
})

CLI Quick Reference

# Basic commands
tsdown                          # Build once
tsdown --watch                  # Watch mode
tsdown --config custom.ts       # Custom config
npx tsdown-migrate              # Migrate from tsup

# Output options
tsdown --format esm,cjs        # Multiple formats
tsdown -d lib                  # Custom output directory (--out-dir)
tsdown --minify                # Enable minification
tsdown --dts                   # Generate declarations
tsdown --exe                   # Bundle as standalone executable
tsdown --unbundle              # Bundleless mode

# Entry options
tsdown src/index.ts            # Single entry
tsdown src/*.ts                # Glob patterns
tsdown src/a.ts src/b.ts       # Multiple entries

# Workspace / Monorepo
tsdown -W                      # Enable workspace mode
tsdown -W -F my-package        # Filter specific package
tsdown --filter /^pkg-/        # Filter by regex

# Development
tsdown --watch                 # Watch mode
tsdown --sourcemap             # Generate source maps
tsdown --clean                 # Clean output directory
tsdown --from-vite             # Reuse Vite config
tsdown --tsconfig tsconfig.build.json  # Custom tsconfig

Best Practices

  1. Always generate type declarations for TypeScript libraries:

    { dts: true }
  2. Externalize dependencies to avoid bundling unnecessary code:

    { deps: { neverBundle: [/^react/, /^@myorg\//] } }
  3. Use tree shaking for optimal bundle size:

    { treeshake: true }
  4. Enable minification for production builds:

    { minify: true }
  5. Add shims for better ESM/CJS compatibility:

    { shims: true }  // Adds __dirname, __filename, etc.
  6. Auto-generate package.json exports:

    { exports: true }  // Creates proper exports field
  7. Use watch mode during development:

    tsdown --watch
  8. Preserve structure for utilities with many files:

    { unbundle: true }  // Keep directory structure
  9. Validate packages in CI before publishing:

    { publint: 'ci-only', attw: 'ci-only' }

Resources

Files

references/advanced-hooks.md

Lifecycle Hooks

Extend the build process with lifecycle hooks.

Overview

Hooks provide a way to inject custom logic at specific stages of the build lifecycle. Inspired by unbuild.

Recommendation: Use plugins for most extensions. Use hooks for simple custom tasks or Rolldown plugin injection.

Usage Patterns

Object Syntax
export default defineConfig({
  entry: ['src/index.ts'],
  hooks: {
    'build:prepare': async (context) => {
      console.log('Build starting...')
    },
    'build:done': async (context) => {
      console.log('Build complete!')
    },
  },
})
Function Syntax
export default defineConfig({
  entry: ['src/index.ts'],
  hooks(hooks) {
    hooks.hook('build:prepare', () => {
      console.log('Preparing build...')
    })

    hooks.hook('build:before', (context) => {
      console.log(`Building format: ${context.format}`)
    })
  },
})

Available Hooks

build:prepare

Called before the build process starts.

When: Once per build session

Context:

{
  options: ResolvedConfig,
  hooks: Hookable
}

Use cases:

  • Setup tasks

  • Validation

  • Environment preparation

Example:

hooks: {
  'build:prepare': async (context) => {
    console.log('Starting build for:', context.options.entry)
    await cleanOldFiles()
  },
}
build:before

Called before each Rolldown build.

When: Once per format (ESM, CJS, etc.)

Context:

{
  options: ResolvedConfig,
  buildOptions: BuildOptions,
  hooks: Hookable
}

Use cases:

  • Modify build options per format

  • Inject plugins dynamically

  • Format-specific setup

Example:

hooks: {
  'build:before': async (context) => {
    console.log(`Building ${context.buildOptions.format} format...`)

    // Add format-specific plugin
    if (context.buildOptions.format === 'iife') {
      context.buildOptions.plugins.push(browserPlugin())
    }
  },
}
build:done

Called after the build completes.

When: Once per build session

Context:

{
  options: ResolvedConfig,
  chunks: RolldownChunk[],
  hooks: Hookable
}

Use cases:

  • Post-processing

  • Asset copying

  • Notifications

  • Deployment

Example:

hooks: {
  'build:done': async (context) => {
    console.log(`Built ${context.chunks.length} chunks`)

    // Copy additional files
    await copyAssets()

    // Send notification
    notifyBuildComplete()
  },
}

Common Patterns

Build Notifications
export default defineConfig({
  hooks: {
    'build:prepare': () => {
      console.log('🚀 Starting build...')
    },
    'build:done': (context) => {
      const size = context.chunks.reduce((sum, c) => sum + c.code.length, 0)
      console.log(`✅ Build complete! Total size: ${size} bytes`)
    },
  },
})
Conditional Plugin Injection
export default defineConfig({
  hooks(hooks) {
    hooks.hook('build:before', (context) => {
      // Add minification only for production
      if (process.env.NODE_ENV === 'production') {
        context.buildOptions.plugins.push(minifyPlugin())
      }
    })
  },
})
Custom File Copy
import { copyFile } from 'fs/promises'

export default defineConfig({
  hooks: {
    'build:done': async (context) => {
      // Copy README to dist
      await copyFile('README.md', `${context.options.outDir}/README.md`)
    },
  },
})
Build Metrics
export default defineConfig({
  hooks: {
    'build:prepare': (context) => {
      context.startTime = Date.now()
    },
    'build:done': (context) => {
      const duration = Date.now() - context.startTime
      console.log(`Build took ${duration}ms`)

      // Log chunk sizes
      context.chunks.forEach((chunk) => {
        console.log(`${chunk.fileName}: ${chunk.code.length} bytes`)
      })
    },
  },
})
Format-Specific Logic
export default defineConfig({
  format: ['esm', 'cjs', 'iife'],
  hooks: {
    'build:before': (context) => {
      const format = context.buildOptions.format

      if (format === 'iife') {
        // Browser-specific setup
        context.buildOptions.globalName = 'MyLib'
      } else if (format === 'cjs') {
        // Node-specific setup
        context.buildOptions.platform = 'node'
      }
    },
  },
})
Deployment Hook
export default defineConfig({
  hooks: {
    'build:done': async (context) => {
      if (process.env.DEPLOY === 'true') {
        console.log('Deploying to CDN...')
        await deployToCDN(context.options.outDir)
      }
    },
  },
})

Advanced Usage

Multiple Hooks
export default defineConfig({
  hooks(hooks) {
    // Register multiple hooks
    hooks.hook('build:prepare', setupEnvironment)
    hooks.hook('build:prepare', validateConfig)

    hooks.hook('build:before', injectPlugins)
    hooks.hook('build:before', logFormat)

    hooks.hook('build:done', generateManifest)
    hooks.hook('build:done', notifyComplete)
  },
})
Async Hooks
export default defineConfig({
  hooks: {
    'build:prepare': async (context) => {
      await fetchRemoteConfig()
      await initializeDatabase()
    },
    'build:done': async (context) => {
      await uploadToS3(context.chunks)
      await invalidateCDN()
    },
  },
})
Error Handling
export default defineConfig({
  hooks: {
    'build:done': async (context) => {
      try {
        await riskyOperation()
      } catch (error) {
        console.error('Hook failed:', error)
        // Don't throw - allow build to complete
      }
    },
  },
})

Hookable API

tsdown uses hookable for hooks. Additional methods:

export default defineConfig({
  hooks(hooks) {
    // Register hook
    hooks.hook('build:done', handler)

    // Register hook once
    hooks.hookOnce('build:prepare', handler)

    // Remove hook
    hooks.removeHook('build:done', handler)

    // Clear all hooks for event
    hooks.removeHooks('build:done')

    // Call hooks manually
    await hooks.callHook('build:done', context)
  },
})

Tips

  1. Use plugins for most extensions

  2. Hooks for simple tasks like notifications or file copying

  3. Async hooks supported for I/O operations

  4. Don't throw errors unless you want to fail the build

  5. Context is mutable in build:before for advanced use cases

  6. Multiple hooks allowed for the same event

Troubleshooting

Hook Not Called
  • Verify hook name is correct

  • Check hook is registered in config

  • Ensure async hooks are awaited

Build Fails in Hook
  • Add try/catch for error handling

  • Don't throw unless intentional

  • Log errors for debugging

Context Undefined
  • Check which hook you're using

  • Verify context properties available for that hook

Requirements and compatibility

Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta

“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.

Needs: Needs Node.js

Security

No issues found by our static scan· No issues found

A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.

Attribution and licence

Created by
Anthony Fu
Publisher
JetBrains (Official)
Distributed by
JetBrains
Source
JetBrains/skills · tsdown · commit 3f53782
Licence
MIT License (redistribution allowed)
  • ATTRIBUTION.md (written by AutoGineer)
Synced

AutoGineer is not affiliated with or endorsed by Anthony Fu or JetBrains.

Download ZIP

The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download tsdown.zip