Overview
Comprehensive technical overview of the ts-package-template architecture, build system, and publishing workflow
ts-package-template is a future-proof TypeScript package template designed for library authors. It focuses on modern tooling, developer experience, and production-ready configuration for publishing npm packages. The template provides everything needed to quickly start developing TypeScript libraries with minimal setup.
This template is perfect for library authors, offering zero-configuration modern tooling for TypeScript packages. It includes Rolldown bundler, Biome linting, Vitest testing, and np publishing - everything you need to quickly start developing TypeScript libraries with minimal setup.
Architecture
The template follows a minimal but complete structure that balances simplicity with production readiness:
Design Philosophy
The template is built on four key principles:
Minimal but Complete
Only essential files are included, but all necessary configurations are present for a production-ready package.
Modern by Default
Uses latest versions of all tools with sensible, opinionated defaults.
Zero-Configuration
Works out of the box with opinionated but customizable settings.
Production-Ready
Includes testing, linting, changelog generation, and publishing workflows.
Core Technologies
Language & Runtime
Prop
Type
Bundling
Prop
Type
Why Rolldown?
Rolldown is a Rollup-compatible bundler written in Rust, offering: ✅ Excellent performance (Rust-based) ✅ Rollup compatibility (familiar configuration) ✅ Modern ESM support ✅ Growing ecosystem
Code Quality
Prop
Type
Biome Benefits
Biome replaces ESLint + Prettier with a single, faster tool: ✅ Faster linting and formatting ✅ Simpler configuration ✅ Consistent code style ✅ Built-in formatter
Testing
Prop
Type
Vitest Features
✅ Native ESM support ✅ Fast test execution ✅ TypeScript support out of the box ✅ Watch mode for development
Publishing & Versioning
Prop
Type
Key Implementation Details
Build System: Rolldown + tsdown
The template uses Rolldown as the bundler with tsdown for TypeScript integration:
Key build features include:
- Fast builds with Rust-based bundler
- Rollup compatibility for familiar configuration
- Seamless TypeScript integration with tsdown
- ESM-first optimization
Configuration
import { defineConfig } from "tsdown";
export default defineConfig({
entry: {
index: "src/index.ts",
},
dts: true,
tsconfig: "tsconfig.build.json",
format: "esm",
sourcemap: false,
});Prop
Type
Build Process
TypeScript Compilation
tsc runs first with tsconfig.build.json to check types.
Bundling
Rolldown processes the compiled output with tsdown plugin.
Declaration Generation
tsdown generates .d.ts and .d.mts type declaration files.
Build Outputs
Prop
Type
Package Configuration
The package.json is optimized for modern package publishing:
{
"type": "module",
"exports": {
".": {
"import": "./dist/index.mjs",
"types": "./dist/index.d.mts"
}
},
"files": ["dist", "LICENSE", "README.md", "CHANGELOG.md"],
"types": "dist/index.d.ts",
"scripts": {
"dev": "tsdown --watch ./src",
"build": "tsc && tsdown",
"check": "ultracite check",
"test": "vitest run",
"changelog": "git-cliff --config ./cliff.toml --output CHANGELOG.md",
"publish": "np --package-manager npm",
"publish:all": "pnpm run build && pnpm run publish"
}
}Key configuration features:
- ESM-first:
"type": "module"declares the package as ESM - Conditional Exports: Different entry points for ESM and CJS consumers
- Type Exports: Separate type files for ESM and CommonJS
- Selective Publishing: Only essential files are published
TypeScript Configuration
The template uses two TypeScript configuration files for different purposes:
Development Configuration
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"types": ["vitest/globals"]
},
"include": ["src", "test"],
"exclude": ["dist", "node_modules"]
}Development config has noEmit: true to prevent accidental emission during development.
Includes Vitest globals for type-safe testing.
Build Configuration
{
"extends": "./tsconfig.json",
"compilerOptions": {
"noEmit": false,
"emitDeclarationOnly": true,
"outDir": "./dist"
},
"include": ["src"]
}Build config extends development config and enables declaration emission.
Only processes src/ directory (not tests).
Changelog Generation with git-cliff
The template uses git-cliff for automatic changelog generation based on conventional commits:
[changelog]
body = '''
# Changelog
All notable changes to this project will be documented in this file.
'''
[git]
conventional_commits = true
topological_order = true
[commit_parsers]
feat = { message = "^feat", group = "Features" }
fix = { message = "^fix", group = "Bug Fixes" }
doc = { message = "^doc", group = "Documentation" }
perf = { message = "^perf", group = "Performance" }
refactor = { message = "^refactor", group = "Refactor" }
style = { message = "^style", group = "Styling" }
test = { message = "^test", group = "Testing" }
chore = { message = "^chore", group = "Miscellaneous Chores" }
revert = { message = "^revert", group = "Reverts" }Conventional Commits
The template follows Conventional Commits specification:
✅ feat: - A new feature
✅ fix: - A bug fix
✅ docs: - Documentation only changes
✅ style: - Changes that do not affect code meaning
✅ refactor: - Code change that neither fixes a bug nor adds a feature
✅ perf: - A code change that improves performance
✅ test: - Adding missing tests
✅ chore: - Changes to the build process
✅ revert: - Reverts a previous commit
Publishing with np
The template uses np for a better npm publish experience:
np Features
✅ Interactive prompts for version bump and confirmation ✅ Safety checks before publishing ✅ Automatic version bump based on commit messages ✅ Git tag creation (annotated tags) ✅ Package manager support (npm, yarn, pnpm)
# Check code quality first
pnpm check
# Generate changelog
pnpm changelog
# Publish to npm
pnpm publish
# Build and publish (all-in-one)
pnpm publish:allIntended Use Case
This template is ideal for:
Perfect For
✅ Library development (reusable TypeScript packages for npm) ✅ Modern ESM packages with native ES Modules support ✅ Type-safe libraries with full TypeScript declaration support ✅ Production publishing with complete CI/CD ready configuration ✅ Maintainable projects with built-in changelog generation ✅ Developer tools, CLI utilities, and frameworks
Development Workflow
Setup
Clone the repository
git clone https://github.com/LuanRoger/ts-package-template.gitInstall dependencies
cd ts-package-template && pnpm installModify package.json
# Set your package name, version, description, author, etc.Development
Start watch mode
pnpm devRuns: tsdown --watch ./src
Watches for changes in src/ and rebuilds automatically.
Edit source files
Modify files in src/ directory. Changes trigger automatic rebuilds.
Build
Build the package
pnpm buildRuns: tsc && tsdown
- TypeScript compilation check
- Rolldown bundling with tsdown
Code Quality
Auto-fix issues
pnpm check:writeRuns: ultracite fix
Automatically fixes linting and formatting issues.
Testing
Run tests in watch mode
pnpm test:watchRuns: vitest (watch mode)
Automatically re-runs tests on file changes.
Publishing
Generate changelog
pnpm changelogRuns: git-cliff --config ./cliff.toml --output CHANGELOG.md
Generates or updates CHANGELOG.md based on conventional commits.
Build and publish
pnpm publish:allRuns: pnpm run build && pnpm run publish
Complete workflow in one command.
Project Structure Details
Source Code (src/)
The src/ directory contains your package's source code:
Best Practices
✅ Export your public API from index.ts
✅ Use named exports for better tree-shaking
✅ Keep internal modules in separate files
✅ Use TypeScript types for better developer experience
✅ Document all public APIs with JSDoc comments
Tests (test/)
The test/ directory contains your test files:
import { describe, expect, it } from "vitest";
import { hello } from "../src";
describe("hello", () => {
it("should return a greeting", () => {
expect(hello("World")).toBe("Hello, World!");
});
});
// Test async functions
import { greetAsync } from "../src";
describe("greetAsync", () => {
it("should return a greeting asynchronously", async () => {
const result = await greetAsync("World");
expect(result).toBe("Hello, World!");
});
});Testing Features
✅ Vitest: Fast, modern test runner ✅ Native ES Modules support ✅ Full TypeScript support ✅ Watch mode for development ✅ Concurrent test execution
Configuration Files
Prop
Type
Prop
Type
Best Practices
Package Naming
Naming Guidelines
Follow these conventions for package naming:
- Scoped packages: Use
@yourname/package-namefor organization - Naming: Use kebab-case:
my-awesome-package - Length: Keep names short and descriptive
- Uniqueness: Avoid generic names that might conflict
Versioning
Semantic Versioning
Follow SemVer principles:
- Major: Breaking changes
- Minor: New features (backwards-compatible)
- Patch: Bug fixes (backwards-compatible)
Use conventional commits for automatic version bumps with np.
Exports
Export Guidelines
✅ Use named exports for better tree-shaking ✅ Provide TypeScript declarations for all exports ✅ Consider conditional exports for ESM/CJS compatibility ✅ Document all public APIs
// ✅ Good: Named exports with types
export interface Options {
timeout?: number;
retries?: number;
}
export function fetchData(url: string, options?: Options): Promise<Response> {
// Implementation
}
export function postData(url: string, data: unknown): Promise<Response> {
// Implementation
}
// ❌ Bad: Default export only
export default function(url: string) {
// Harder to tree-shake
// Less clear API
}
// ❌ Bad: No types
export function fetch(url: any) {
// No type safety for consumers
}Dependencies
Dependency Management
✅ Minimize dependencies (only include what's necessary) ✅ Use peerDependencies for optional dependencies ✅ Specify exact versions in devDependencies ✅ Test with latest versions of dependencies
TypeScript
TypeScript Best Practices
✅ Enable strict mode (all strict type-checking options)
✅ Use explicit types (avoid any and unknown where possible)
✅ Document types with JSDoc comments
✅ Export types to make them available to consumers
Comparison with Other Templates
Why Choose ts-package-template?
Comparison with traditional templates:
Prop
Type
Troubleshooting
Common Issues & Solutions
Find quick solutions to common problems:
Build Issues
TypeScript errors during build
pnpm checkCheck for linting and formatting issues first.
Missing type definitions
Install required types:
pnpm add -D @types/nodeConfiguration issues
Verify tsconfig.json and tsdown.config.ts are properly configured.
Test Issues
Tests not running
Ensure test files have .test.ts or .spec.ts extension.
Type errors in tests
Add Vitest globals to tsconfig.json:
{ "types": ["vitest/globals"] }Vitest not installed
pnpm add -D vitestChangelog Issues
Changelog not updating
Ensure commits follow conventional commits format.
Configuration issues
Check cliff.toml configuration.
Git history missing
Verify git history is available in the repository.
Publish Issues
Publish fails
Ensure you're logged in to npm:
npm loginPackage name conflict
Check package name is unique on npm.
Missing files
Verify files in package.json includes necessary files.
Additional Resources
- TypeScript Documentation
- Rolldown Documentation
- tsdown Documentation
- Biome Documentation
- Vitest Documentation
- git-cliff Documentation
- np Documentation
- Conventional Commits
- Semantic Versioning
Last updated: 2026-06-26T21:07:13.735Z