963 markdown files reformatted with the repository's pinned prettier so pnpm format:check covers the folded tree like every other repo file. The formatter's embedded-language pass also normalized code fences (TS semicolons, closed HTML tags in examples, lowercased CSS hex colors, one renumbered list that skipped an index). Alphanumeric token deltas vs the fold commit were audited file-by-file; all are formatter-equivalent markup normalizations plus the four sanitized skills.
5.6 KiB
Unbundle Mode
Preserve source directory structure in output.
Overview
Unbundle mode (also called "bundleless" or "transpile-only") outputs files that mirror your source structure, rather than bundling everything into single files. Each source file is compiled individually with a one-to-one mapping.
Basic Usage
CLI
tsdown --unbundle
Config File
export default defineConfig({
entry: ['src/**/*.ts', '!**/*.test.ts'],
unbundle: true,
});
How It Works
Source Structure
src/
├── index.ts
├── utils/
│ ├── helper.ts
│ └── format.ts
└── components/
└── button.ts
With Unbundle
Config:
export default defineConfig({
entry: ['src/index.ts'],
unbundle: true,
});
Output:
dist/
├── index.mjs
├── utils/
│ ├── helper.mjs
│ └── format.mjs
└── components/
└── button.mjs
All imported files are output individually, preserving structure.
Without Unbundle (Default)
Output:
dist/
└── index.mjs (all code bundled together)
When to Use
Use Unbundle When:
✅ Building monorepo packages with shared utilities ✅ Users need to import individual modules ✅ Want clear source-to-output mapping ✅ Library with many independent utilities ✅ Debugging requires tracing specific files ✅ Incremental builds for faster development
Use Standard Bundling When:
❌ Single entry point application ❌ Want to optimize bundle size ❌ Need aggressive tree shaking ❌ Creating IIFE/UMD bundles ❌ Deploying to browsers directly
Common Patterns
Utility Library
export default defineConfig({
entry: ['src/**/*.ts', '!**/*.test.ts'],
format: ['esm', 'cjs'],
unbundle: true,
dts: true,
});
Benefits:
- Users import only what they need
- Tree shaking still works at user's build
- Clear module boundaries
Usage:
// Users can import specific utilities
import { helper } from 'my-lib/utils/helper';
import { Button } from 'my-lib/components/button';
Monorepo Shared Package
export default defineConfig({
entry: ['src/index.ts'],
format: ['esm'],
unbundle: true,
outDir: 'dist',
});
TypeScript Compilation Only
export default defineConfig({
entry: ['src/**/*.ts'],
format: ['esm'],
unbundle: true,
minify: false,
treeshake: false,
dts: true,
});
Pure TypeScript to JavaScript transformation.
Development Mode
export default defineConfig((options) => ({
entry: ['src/**/*.ts'],
unbundle: options.watch, // Unbundle in dev only
minify: !options.watch,
}));
Fast rebuilds during development, optimized for production.
With Entry Patterns
Include/Exclude
export default defineConfig({
entry: ['src/**/*.ts', '!**/*.test.ts', '!**/*.spec.ts', '!**/fixtures/**'],
unbundle: true,
});
Multiple Entry Points
export default defineConfig({
entry: {
index: 'src/index.ts',
cli: 'src/cli.ts',
},
unbundle: true,
});
Both entry files and all imports preserved.
Output Control
Custom Extension
export default defineConfig({
entry: ['src/**/*.ts'],
unbundle: true,
outExtensions: () => ({ js: '.js' }),
});
Preserve Directory
export default defineConfig({
entry: ['src/**/*.ts'],
unbundle: true,
outDir: 'lib',
});
Output:
lib/
├── index.js
├── utils/
│ └── helper.js
└── components/
└── button.js
Package.json Setup
{
"name": "my-library",
"type": "module",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": "./dist/index.js",
"./utils/*": "./dist/utils/*.js",
"./components/*": "./dist/components/*.js"
},
"files": ["dist"]
}
Or use exports: true to auto-generate.
Comparison
| Feature | Bundled | Unbundled |
|---|---|---|
| Output files | Few | Many |
| File size | Smaller | Larger |
| Build speed | Slower | Faster |
| Tree shaking | Build time | User's build |
| Source mapping | Complex | Simple |
| Module imports | Entry only | Any module |
| Dev rebuilds | Slower | Faster |
Performance
Build Speed
Unbundle is typically faster:
- No bundling overhead
- Parallel file processing
- Incremental builds possible
Bundle Size
Unbundle produces larger output:
- Each file has its own overhead
- No cross-module optimizations
- User's bundler handles final optimization
Tips
- Use with glob patterns for multiple files
- Enable in development for faster rebuilds
- Let users bundle for production optimization
- Preserve structure for utilities/components
- Combine with DTS for type definitions
- Use with monorepos for shared code
Troubleshooting
Too Many Files
- Adjust entry patterns
- Exclude unnecessary files
- Use specific entry points
Missing Files
- Check entry patterns
- Verify files are imported
- Look for excluded patterns
Import Paths Wrong
- Check relative paths
- Verify output structure
- Update package.json exports
CLI Examples
# Enable unbundle
tsdown --unbundle
# With specific entry
tsdown src/**/*.ts --unbundle
# With other options
tsdown --unbundle --format esm --dts
Related Options
- Entry - Entry patterns
- Output Directory - Output location
- Output Format - Module formats
- DTS - Type declarations