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.json | Externalize them (not bundled) |
A devDependency imported in your code | Bundle it into the output |
types or typings field in package.json | Enable .d.ts generation |
isolatedDeclarations in tsconfig.json | Use the fast oxc-transform path for dts |
engines.node in package.json | Infer the compilation target from it |
type: "module" in package.json | Use .js extension for ESM output (instead of .mjs) |
No entry specified, but src/index.ts exists | Use it as the default entry point |
platform: "node" (the default) | Enable fixedExtension (.mjs/.cjs) |
Dual-format build with exports: true | Generate main/module legacy fields in package.json |
| Config file changes in watch mode | Restart 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:
dependenciesandpeerDependenciesare externalized — they appear asimport/requirestatements in the output and are not included in the bundle.devDependenciesare 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_modulesbut not listed in yourpackage.json) follow the same rule as devDependencies — bundled only if used.
Key options:
| Option | What it does |
|---|---|
deps.onlyAllowBundle | Whitelist 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.neverBundle | Explicitly mark additional packages as external (never bundled). |
deps.alwaysBundle | Force specific packages to be bundled, even if they're in dependencies. |
deps.skipNodeModulesBundle | Skip 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:
| Option | What it does |
|---|---|
format | Set to esm, cjs, iife, or umd. Pass multiple values (e.g. format: ['esm', 'cjs']) for dual-format builds. |
shims | Inject 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.jsonhas atypesortypingsfield, dts generation is enabled automatically. - With
isolatedDeclarationsenabled in yourtsconfig.json, tsdown uses the fast oxc-transform path. Otherwise, it falls back to the TypeScript compiler.
Key options:
| Option | What it does |
|---|---|
dts | Enable/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
exportsis off by default. You manage theexportsfield in yourpackage.jsonyourself.
With exports: true:
- tsdown analyzes your entry points and output files, then writes the
exports,main,module, andtypesfields in yourpackage.jsonautomatically. - For dual-format builds (ESM + CJS), it generates conditional exports with
importandrequireconditions.
Key options:
| Option | What it does |
|---|---|
exports | Set to true to enable auto-generation, or pass an object for fine-tuning. |
exports.all | Export all output files, not just entry points. |
exports.devExports | Point exports to source files during development for better editor support. |
exports.customExports | A 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.jsonconfiguration — it checks thatexports,main,module, andtypespoint 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:
| Option | What it does |
|---|---|
publint | Set to true or 'ci-only' to enable. |
attw | Set 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-cleanto 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.jsonenginesfield, or defaults to the latest stable Node.js version. See Target.