ty(pe) + pack — pack your TypeScript declarations.
A native TypeScript declaration (.d.ts) bundler built on Oxc.
Bundles one or more .d.ts entry points into a single output file using a three-stage AST pipeline — no TypeScript compiler required.
- Fast — native Rust implementation powered by the Oxc parser, no
tscdependency - Correct — semantic rename using
SymbolId-level analysis to avoid name collisions across modules - Tree-shaking — only emits declarations that are reachable from entry points
- Source maps — optional source map composition back to original
.d.tssources - External packages — configurable external specifiers preserved as imports in the output
- CJS support — optional
export =syntax for CommonJS default exports - Node.js bindings — pre-built N-API bindings for 8 platforms via npm
The bundler runs a three-stage pipeline:
- Scan — parse
.d.tsfiles withoxc_parser, build semantic scoping viaoxc_semantic, resolve imports withoxc_resolver, and produce a topologically sorted module graph - Link — analyze the module graph to build a rename plan (deconflicting names across modules) and a needed-names plan (tree-shaking unused declarations)
- Generate — apply renames, rewrite inline
import()types, wrap namespace imports, filter unused declarations, and emit the bundled output with optional source maps
[dependencies]
typack = "0.1.0"use typack::{TypackBundler, TypackOptions};
let result = TypackBundler::bundle(&TypackOptions {
input: vec!["types/index.d.ts".to_string()],
cwd: std::env::current_dir().unwrap(),
external: vec!["react".to_string()],
sourcemap: true,
..Default::default()
});
match result {
Ok(bundle) => {
println!("{}", bundle.code);
if let Some(map) = bundle.map {
// write source map to disk
let _ = map;
}
for warning in bundle.warnings {
eprintln!("warning: {warning}");
}
}
Err(diagnostics) => {
for diagnostic in diagnostics {
eprintln!("error: {diagnostic}");
}
}
}TypackOptions
| Field | Type | Default | Description |
|---|---|---|---|
input |
Vec<String> |
[] |
Entry .d.ts file paths to bundle |
external |
Vec<String> |
[] |
Module specifiers to keep as external imports |
cwd |
PathBuf |
"." |
Working directory for relative path resolution |
sourcemap |
bool |
false |
Generate source map (.d.ts.map) |
cjs_default |
bool |
false |
Emit export = for single default export |
BundleResult
| Field | Type | Description |
|---|---|---|
code |
String |
The bundled .d.ts output |
map |
Option<SourceMap> |
Source map (when sourcemap: true) |
warnings |
Vec<OxcDiagnostic> |
Non-fatal warnings |
Requires the cli feature:
cargo run --features cli -- [OPTIONS] <ENTRY>...<ENTRY>... Entry .d.ts files to bundle
--external <SPEC> Module specifiers to keep external (repeatable)
--cwd <DIR> Working directory (default: current directory)
--sourcemap Generate source map (.d.ts.map)
--cjs-default Emit `export =` for single default export
-o, --outfile <PATH> Write output to file instead of stdout
cargo run --features cli -- \
--external react \
--external react-dom \
--sourcemap \
-o dist/index.d.ts \
types/index.d.tsnpm install typackRequires Node.js >= 20. Pre-built binaries are available for:
| Platform | Architectures |
|---|---|
| macOS | aarch64, x86_64 |
| Linux (glibc) | aarch64, x86_64 |
| Linux (musl) | aarch64, x86_64 |
| Windows | aarch64, x86_64 |
import { bundle } from "typack";
const result = bundle({
input: ["types/index.d.ts"],
cwd: process.cwd(),
external: ["react"],
sourcemap: true,
});
console.log(result.code);
if (result.map) {
// result.map is a JSON string
fs.writeFileSync("dist/index.d.ts.map", result.map);
}
for (const warning of result.warnings) {
console.warn(`warning: ${warning.message}`);
}interface BundleDtsOptions {
input: Array<string>;
external?: Array<string>;
cwd?: string;
sourcemap?: boolean;
cjsDefault?: boolean;
}
interface BundleDtsResult {
code: string;
map?: string;
warnings: Array<BundleDtsDiagnostic>;
}
interface BundleDtsDiagnostic {
message: string;
file?: string;
span?: Array<number>;
severity: string;
}Module resolution uses oxc_resolver with the "types" export condition enabled, respecting package.json exports and conditional exports.
- External specifiers — specifiers in the
externallist are preserved in the output (exact match) - Unresolved relative specifiers — fatal errors
- Unresolved bare specifiers — automatically externalized with a warning
- Side-effect imports —
import "pkg"statements are preserved when the target contains global or module augmentations - Non-TS assets — imports of CSS, images, etc. are silently skipped
- Reference directives —
/// <reference path="..." />directives are collected and deduplicated
Several tools exist for bundling TypeScript declarations. Here's how they compare:
| typack | rolldown-plugin-dts | @microsoft/api-extractor | dts-bundle-generator | rollup-plugin-dts | |
|---|---|---|---|---|---|
| Language | Rust | TypeScript + Rust | TypeScript | TypeScript | TypeScript |
| Approach | Standalone bundler | Rolldown plugin | Standalone CLI/API | Standalone CLI | Rollup plugin |
| Input | Pre-generated .d.ts |
.ts source files |
Pre-generated .d.ts |
.ts source files |
Pre-generated .d.ts |
Generates .d.ts |
No | Yes (tsc / oxc / tsgo) | No | Yes (in-memory tsc) | No |
| Requires tsc | No | Optional | Yes (for input) | Yes (bundled) | Yes (for input) |
| Multiple entry points | Yes | Yes | No | Yes | Yes |
| Tree-shaking | Yes | Yes | Yes (via trimming) | Yes | Yes |
| Source maps | Yes | Yes | No | No | No |
| Semantic rename | Yes (SymbolId) | Yes (via typack) | Yes | Yes | Yes |
| API report / docs | No | No | Yes | No | No |
| Release trimming | No | No | Yes (@alpha, @beta, etc.) | No | No |
| Status | Active | Active | Active | Active | Maintenance mode |
rolldown-plugin-dts is a Rolldown build plugin that orchestrates the full pipeline — it generates .d.ts files (via tsc, oxc, or tsgo), then calls typack under the hood to bundle them. Use it when you want a single build step that produces both .js and .d.ts output.
@microsoft/api-extractor is Microsoft's tool focused on API governance. Beyond bundling, it generates API reports and supports release trimming (@public, @beta, @internal). It requires pre-generated .d.ts input, supports only a single entry point, and does not produce source maps.
dts-bundle-generator compiles TypeScript in-memory and generates bundled declarations without writing intermediate files. It supports multiple entry points and fine-grained control over which packages to inline vs. import. It does not produce source maps.
rollup-plugin-dts is a Rollup plugin that bundles pre-generated .d.ts files using Rollup's module graph. It is currently in maintenance mode with no new feature development.
typack is a standalone, native bundler that operates directly on .d.ts ASTs. It does not generate declarations from .ts source — it expects pre-generated .d.ts files as input. Its focus is on speed, correctness (semantic-level rename deconfliction), and source map support.
- Rust 1.91.0+
- just (task runner)
just ready # fmt + check + test + lint
just fmt # cargo fmt
just check # cargo check
just test # cargo test
just lint # cargo clippy- Fixture tests — small focused cases in
tests/fixtures/, each with anindex.d.tsinput andsnapshot.d.tsexpected output - Real-world tests — regression tests against declarations from Rolldown, Vue, and Vitest
- Semantic tests — targeted tests for rename deconfliction and tree-shaking logic
The three-stage architecture (scan, link, generate) is inspired by Rolldown. Test fixtures were adapted from rolldown-plugin-dts and rollup-plugin-dts.