Skip to content

How It Works

This page gives a high-level overview of what tsdown does out of the box and which options let you adjust each behavior. For full details, follow the links to the dedicated option pages.

Smart Defaults at a Glance

tsdown reads your package.json and tsconfig.json to infer sensible defaults. Here's what happens automatically:

When tsdown detects...It will...
dependencies / peerDependencies in package.jsonExternalize them (not bundled)
A devDependency imported in your codeBundle it into the output
types or typings field in package.jsonEnable .d.ts generation
isolatedDeclarations in tsconfig.jsonUse the fast oxc-transform path for dts
engines.node in package.jsonInfer the compilation target from it
type: "module" in package.jsonUse .js extension for ESM output (instead of .mjs)
No entry specified, but src/index.ts existsUse it as the default entry point
platform: "node" (the default)Enable fixedExtension (.mjs/.cjs)
Dual-format build with exports: trueGenerate main/module legacy fields in package.json
Config file changes in watch modeRestart the entire build

The sections below explain each area in more detail.

Dependencies

When you publish a library, your consumers install its dependencies and peerDependencies alongside it. There's no need to bundle those packages into your output — they'll already be available at runtime.

Default behavior:

  • dependencies and peerDependencies are externalized — they appear as import / require statements in the output and are not included in the bundle.
  • devDependencies are bundled if imported. Since they won't be installed by consumers, any code you import from a devDependency is inlined into your output automatically.
  • Phantom dependencies (installed in node_modules but not listed in your package.json) follow the same rule as devDependencies — bundled only if used.

Key options:

OptionWhat it does
deps.onlyAllowBundleWhitelist of dependencies allowed to be bundled. Any unlisted dependency that ends up in the bundle causes an error. Useful for catching accidental inlining in large projects.
deps.neverBundleExplicitly mark additional packages as external (never bundled).
deps.alwaysBundleForce specific packages to be bundled, even if they're in dependencies.
deps.skipNodeModulesBundleSkip resolving and bundling everything from node_modules.

See Dependencies for details.

Output Format

tsdown produces ESM output by default. You can generate multiple formats in a single build, and even override options per format.

Key options:

OptionWhat it does
formatSet to esm, cjs, iife, or umd. Pass multiple values (e.g. format: ['esm', 'cjs']) for dual-format builds.
shimsInject compatibility shims (e.g. __dirname for ESM, import.meta for CJS).

See Output Format for details.

Declaration Files (dts)

tsdown generates .d.ts files so consumers get full TypeScript support.

Default behavior:

  • If your package.json has a types or typings field, dts generation is enabled automatically.
  • With isolatedDeclarations enabled in your tsconfig.json, tsdown uses the fast oxc-transform path. Otherwise, it falls back to the TypeScript compiler.

Key options:

OptionWhat it does
dtsEnable/disable dts, or pass an object for advanced settings like resolver and sourcemap.

See Declaration Files for details.

Package Exports

When publishing a library, the exports field in package.json tells consumers and bundlers how to resolve your package's entry points.

Default behavior:

  • Auto-generation of exports is off by default. You manage the exports field in your package.json yourself.

With exports: true:

  • tsdown analyzes your entry points and output files, then writes the exports, main, module, and types fields in your package.json automatically.
  • For dual-format builds (ESM + CJS), it generates conditional exports with import and require conditions.

Key options:

OptionWhat it does
exportsSet to true to enable auto-generation, or pass an object for fine-tuning.
exports.allExport all output files, not just entry points.
exports.devExportsPoint exports to source files during development for better editor support.
exports.customExportsA function that lets you modify or extend the generated exports.

See Package Exports for details.

Package Validation

tsdown integrates with publint and attw to catch publishing mistakes before they reach npm.

Default behavior:

  • Both tools are disabled by default and are optional peer dependencies.

What they check:

  • publint validates your package.json configuration — it checks that exports, main, module, and types point to files that actually exist, that module formats are correct, and flags common misconfigurations.
  • attw (Are the types wrong?) verifies that your TypeScript declarations resolve correctly under different module resolution strategies (node10, node16, bundler), catching issues like false ESM/CJS type declarations.

Key options:

OptionWhat it does
publintSet to true or 'ci-only' to enable.
attwSet to true or pass an object with profile, level, and ignoreRules.

See Package Validation for details.

Other Defaults

A few more things tsdown handles for you:

  • Output directory — Defaults to dist/. The output directory is cleaned before each build. Use --no-clean to keep existing files. See Cleaning.
  • Tree-shaking — Enabled by default. Dead code is removed from the output. See Tree-shaking.
  • Platform — Defaults to node. See Platform.
  • Target — Inferred from your package.json engines field, or defaults to the latest stable Node.js version. See Target.

Released under the MIT License.