| name | ts-builds |
| description | Guide for creating TypeScript libraries using ts-builds and applying its standards to existing projects. Use when setting up new npm packages, standardizing build scripts, configuring tooling (tsdown, Vitest, ESLint, Prettier), or applying ESM patterns. |
ts-builds
Overview
This skill helps you create professional TypeScript libraries using the ts-builds package and apply these standards to existing projects. It provides a modern, production-ready setup with ESM-only output, comprehensive testing, and consistent code quality tooling.
ts-builds bundles all tooling (ESLint, Prettier, Vitest, TypeScript) and provides a CLI for running standardized commands across projects.
When to Use This Skill
Trigger this skill when:
- Creating a new TypeScript library or npm package
- Standardizing build scripts across TypeScript projects
- Setting up ESM-only TypeScript projects
- Configuring modern tooling (tsdown, Vitest, ESLint, Prettier)
- Applying consistent code quality standards
- Publishing packages to npm
- Migrating from older build tools (webpack, rollup, tsc alone)
Quick Start
Scenario 1: Creating a New Project
mkdir my-library && cd my-library
pnpm init
pnpm add -D ts-builds tsdown
npx ts-builds init
npx ts-builds config
mkdir src test
echo 'export const hello = () => "Hello!"' > src/index.ts
npx ts-builds validate
Scenario 2: Applying Standards to Existing Project
pnpm add -D ts-builds tsdown
npx ts-builds init
npx ts-builds config
npx ts-builds cleanup
npx ts-builds validate
CLI Reference
Setup Commands
npx ts-builds
npx ts-builds init
npx ts-builds config
npx ts-builds config --force
npx ts-builds info
npx ts-builds cleanup
npx ts-builds cleanup --yes
npx ts-builds help
Script Commands
npx ts-builds validate
npx ts-builds format
npx ts-builds format:check
npx ts-builds lint
npx ts-builds lint:check
npx ts-builds typecheck
npx ts-builds ts-types
npx ts-builds test
npx ts-builds test:watch
npx ts-builds test:coverage
npx ts-builds test:ui
npx ts-builds build
npx ts-builds build:watch
npx ts-builds dev
npx ts-builds preview
Analysis Commands
npx ts-builds size
npx ts-builds size --save
npx ts-builds doctor
npx ts-builds changelog
npx ts-builds changelog --since v1.0.0 --version 2.0.0
npx ts-builds changelog --output CHANGELOG.md
Named Chains and Custom Commands
Run custom validation chains or commands defined in config:
npx ts-builds validate
npx ts-builds validate:core
npx ts-builds my-custom-cmd
Core Standards
Package.json Scripts
Add these scripts to delegate all commands to ts-builds:
{
"scripts": {
"validate": "ts-builds validate",
"format": "ts-builds format",
"format:check": "ts-builds format:check",
"lint": "ts-builds lint",
"lint:check": "ts-builds lint:check",
"typecheck": "ts-builds typecheck",
"test": "ts-builds test",
"test:watch": "ts-builds test:watch",
"build": "ts-builds build",
"dev": "ts-builds dev",
"prepublishOnly": "pnpm validate"
}
}
This ensures consistency across all projects using ts-builds.
Configuration (ts-builds.config.json)
Create ts-builds.config.json in your project root to customize behavior:
Basic configuration:
{
"srcDir": "./src",
"validateChain": ["format", "lint", "typecheck", "test", "build"]
}
Advanced configuration (monorepos, custom commands):
{
"srcDir": "./src",
"commands": {
"compile": "tsc",
"docs:validate": "pnpm docs:build && pnpm docs:check",
"landing:validate": { "run": "pnpm validate", "cwd": "./landing" }
},
"chains": {
"validate": ["validate:core", "validate:landing"],
"validate:core": ["format", "lint", "compile", "test", "docs:validate", "build"],
"validate:landing": ["landing:validate"]
}
}
Configuration options:
srcDir - Source directory for linting (default: ./src)
testDir - Test directory (default: ./test)
buildMode - Build tool: "tsdown" (default, libraries) or "vite" (SPAs)
lint.useProjectEslint - Use project's ESLint instead of bundled (default: false)
validateChain - Default validate sequence (backward compat)
commands - Custom commands (string or { run, cwd })
chains - Named command chains (can reference other chains)
Using Vite for SPAs/React Apps:
Set buildMode: "vite" to use Vite instead of tsdown:
{
"srcDir": "./src",
"buildMode": "vite"
}
With Vite mode:
ts-builds build โ vite build
ts-builds dev โ vite (dev server with HMR)
ts-builds preview โ vite preview
Requires vite as peer dependency instead of tsdown.
Using custom ESLint plugins:
If your project uses ESLint plugins not bundled with ts-builds (e.g., eslint-plugin-react-hooks), set lint.useProjectEslint: true:
{
"srcDir": "./src",
"lint": {
"useProjectEslint": true
}
}
This tells ts-builds to use your project's ESLint installation instead of the bundled version, allowing custom plugins to be resolved correctly.
Named chains usage:
npx ts-builds validate
npx ts-builds validate:core
npx ts-builds validate:landing
ESM-Only Format
The template outputs ESM-only (no CommonJS):
package.json exports:
{
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js",
"default": "./dist/index.js"
}
}
}
Build outputs:
lib/ - Development builds (NODE_ENV !== "production")
dist/ - Production builds (NODE_ENV === "production")
- Both directories published to npm
Build Configuration (tsdown)
Key features:
- Environment-based output (lib/ vs dist/)
- ESM-only format
- TypeScript declarations (.d.ts)
- Source maps in development
- Minification in production
- Watch mode for development
Testing (Vitest)
Configuration highlights:
- Node.js environment
- v8 coverage provider
- Multiple reporters (text, json, html)
- Hot reload in watch mode
- UI mode available
Code Quality
ESLint:
- Native flat config (eslint.config.js) with ESLint 10
- TypeScript support via unified
typescript-eslint package
- Prettier integration via
eslint-plugin-prettier/recommended
- Import sorting with simple-import-sort
Prettier:
- No semicolons
- Trailing commas
- Double quotes
- 120 character width
- 2 space tabs
TypeScript:
- Strict mode enabled
- Pragmatic exceptions:
noImplicitAny: false
strictPropertyInitialization: false
- ESNext target
- Declaration files only (tsdown handles transpilation)
pnpm 11 Defaults
Projects using ts-builds are pinned to pnpm 11 via the packageManager field. Settings live in pnpm-workspace.yaml, NOT .npmrc (auth/registry only under pnpm 11) or the package.json pnpm field (no longer read by pnpm 11).
Settings relocated to pnpm-workspace.yaml: publicHoistPattern, overrides, peerDependencyRules, minimumReleaseAgeExclude, allowBuilds.
Supply-chain protections (intentionally left ON)
pnpm 11 enables supply-chain protection by default. Do not disable these globally โ approve specific packages instead.
minimumReleaseAge (default: 1440, i.e. 1 day) โ pnpm refuses dependency versions published less than 24h ago. CI installs from the committed pnpm-lock.yaml, so pinned versions are unaffected; this only bites a fresh pnpm add or a lockfile re-resolution of a just-published package. One-off override: pnpm install --config.minimumReleaseAge=0. When a dep's semver range has no aged-enough version (e.g. @types/node@^24.13.1), add a pinned minimumReleaseAgeExclude entry โ do not downgrade.
strictDepBuilds: true โ installs fail hard (ERR_PNPM_IGNORED_BUILDS) on un-approved dependency build scripts. pnpm 10 only warned; pnpm 11 errors.
blockExoticSubdeps: true โ blocks exotic sub-dependency resolutions.
allowBuilds (replaces legacy build-allow family)
allowBuilds (added in pnpm v10.26.0) replaces the entire legacy family: onlyBuiltDependencies, onlyBuiltDependenciesFile, neverBuiltDependencies, ignoredBuiltDependencies, and ignoreDepScripts. It is a map of package matcher โ boolean in pnpm-workspace.yaml.
allowBuilds:
esbuild: false
some-native-pkg: true
Use allowBuilds.<pkg>: true to approve a package's build script; false to explicitly decline. esbuild: false is correct and is the ts-builds default โ esbuild's build script is NOT needed because its binary ships via @esbuild/<platform> optional dependencies.
Doctor and migration
ts-builds doctor reports pnpm 11 readiness: it flags public-hoist-pattern[] lines in .npmrc and a package.json pnpm field (both ignored by pnpm 11). ts-builds doctor --fix migrates them to pnpm-workspace.yaml, strips inert .npmrc lines, and prunes the pnpm field. Exotic keys (e.g. packageExtensions) and pre-existing target keys are reported for manual migration rather than altered.
For a complete step-by-step runbook covering the full 2.x โ 3.0.0 / pnpm 10 โ 11 upgrade โ including the ERR_PNPM_ABORTED_REMOVE_MODULES_DIR_NO_TTY, ERR_PNPM_NO_MATURE_MATCHING_VERSION, and ERR_PNPM_IGNORED_BUILDS gotchas with exact fixes โ see references/standardization.md ยง "Migrating 2.x โ 3.0.0 (pnpm 10 โ 11)".
Common Workflows
Creating a New Library
- Clone and customize (see references/template-setup.md)
- Develop with
pnpm dev (watch mode)
- Test with
pnpm test:watch
- Validate with
pnpm validate before commits
- Publish with
npm publish (prepublishOnly auto-validates)
Standardizing an Existing Project
- Audit current setup - identify gaps
- Update package.json - scripts and dependencies
- Copy configurations - tsdown, vitest, eslint
- Migrate build - switch to tsdown with ESM-only
- Update exports - proper ESM exports
- Test thoroughly - ensure all builds work
- Update documentation - new commands and workflows
Publishing to npm
The template includes safety checks:
{
"scripts": {
"prepublishOnly": "pnpm validate"
}
}
This ensures every publish:
- Formats code correctly
- Passes all linting
- Passes all tests
- Builds successfully
Publishing workflow:
npm version patch
npm publish --access public
Architecture Patterns
Environment-Based Builds
The tsdown configuration (line 3 in tsdown.config.ts) checks NODE_ENV:
const isDev = process.env.NODE_ENV !== "production"
export default defineConfig({
outDir: isDev ? "lib" : "dist",
minify: !isDev,
sourcemap: isDev,
})
Why two output directories?
lib/ - Fast development builds, easier debugging
dist/ - Optimized production builds, what npm gets
File Organization
project/
โโโ src/
โ โโโ index.ts # Main entry point
โ โโโ **/*.ts # All source files
โโโ test/
โ โโโ *.spec.ts # Vitest tests
โโโ lib/ # Dev builds (gitignored)
โโโ dist/ # Prod builds (gitignored)
โโโ tsdown.config.ts # Build config
โโโ vitest.config.ts # Test config
โโโ eslint.config.js # Lint config
โโโ .prettierrc # Format config (optional)
โโโ package.json # Scripts + exports
Troubleshooting
Build Issues
"Cannot find module" errors:
- Check package.json exports match build outputs
- Verify .js files exist in dist/
- Ensure types field points to .d.ts file
Watch mode not working:
- Check tsdown.config.ts has watch: true for dev
- Verify NODE_ENV is not set to "production"
Test Issues
Tests not found:
- Check vitest.config.ts includes correct pattern
- Verify test files end in .spec.ts or .test.ts
- Ensure test/ directory exists
Coverage incomplete:
- Check coverage.include in vitest.config.ts
- Add exclude patterns for generated files
Import Issues
ESM import problems:
- Verify package.json has
"type": "module"
- Check package.json exports use correct paths
- Verify .js files are generated in dist/
Type definitions missing:
- Ensure tsdown config has
dts: true
- Check .d.ts files generated in dist/
- Verify types field in package.json
Migration Checklist
When applying these standards to an existing project:
Resources
Reference Documents
- references/template-setup.md - Complete guide for using the template
- references/standardization.md - Detailed migration guide for existing projects; includes the "Migrating 2.x โ 3.0.0 (pnpm 10 โ 11)" runbook at the end of the file
- references/tooling-reference.md - Configuration examples and patterns
External Links
Key Files to Reference
When working with ts-builds, these files contain the canonical configurations:
ts-builds.config.json - Project-specific configuration
tsdown.config.ts - Build configuration with environment logic
vitest.config.ts - Test configuration with coverage
eslint.config.js - Linting rules and TypeScript integration
package.json - Scripts, exports, and dependency versions
Best Practices
Development Workflow
- Always use
pnpm dev during development for fast rebuilds
- Run
pnpm validate before committing changes
- Use
pnpm test:watch while writing tests
- Check
pnpm test:coverage to ensure adequate coverage
Code Quality
- Enable strict TypeScript - catches issues early
- Fix linting issues - don't disable rules without good reason
- Write tests - aim for high coverage on critical paths
- Format consistently - let Prettier handle style
Publishing
- Test locally - use
npm link to test before publishing
- Version semantically - follow semver (major.minor.patch)
- Update changelog - document changes for users
- Verify ESM works - test in ESM projects
Documentation
- Keep CLAUDE.md updated - helps Claude Code assist you
- Document commands - clear examples for all scripts
- Explain architecture - help future maintainers
- Link to references - point to this skill for standards