@jmlweb/tsup-config-base
v1.1.4
Published
Base tsup configuration for jmlweb projects with sensible defaults
Maintainers
Readme
@jmlweb/tsup-config-base
Base tsup configuration for jmlweb projects. Provides sensible defaults and a clean API for creating consistent build configurations.
✨ Features
- 🎯 Sensible Defaults: Pre-configured with common settings for TypeScript libraries
- 📦 Dual Format: Generates both CommonJS and ESM outputs by default
- 📝 TypeScript Declarations: Automatic
.d.tsgeneration enabled - 🔧 Clean API: Simple function to create configurations with external dependencies
- ⚡ Zero Config: Works out of the box with minimal setup
- 🛠️ Fully Typed: Complete TypeScript support with exported types
- 🖥️ CLI Preset: Specialized configuration for CLI packages with shebang support
📦 Installation
pnpm add -D @jmlweb/tsup-config-base tsup💡 Upgrading from a previous version? See the Migration Guide for breaking changes and upgrade instructions.
🚀 Quick Start
Create a tsup.config.ts file in your project root:
import { createTsupConfig } from '@jmlweb/tsup-config-base';
export default createTsupConfig();💡 Examples
Basic Setup (No Externals)
// tsup.config.ts
import { createTsupConfig } from '@jmlweb/tsup-config-base';
export default createTsupConfig();With External Dependencies
// tsup.config.ts
import { createTsupConfig } from '@jmlweb/tsup-config-base';
export default createTsupConfig({
external: ['eslint', 'typescript-eslint', '@eslint/js'],
});With Internal Workspace Dependencies
// tsup.config.ts
import { createTsupConfig } from '@jmlweb/tsup-config-base';
export default createTsupConfig({
external: [
// Internal packages
'@jmlweb/eslint-config-base-js',
// External peer dependencies
'@eslint/js',
'eslint',
'eslint-config-prettier',
'typescript-eslint',
],
});Custom Entry Point
// tsup.config.ts
import { createTsupConfig } from '@jmlweb/tsup-config-base';
export default createTsupConfig({
entry: ['src/main.ts'],
external: ['some-dependency'],
});With Additional Options
// tsup.config.ts
import { createTsupConfig } from '@jmlweb/tsup-config-base';
export default createTsupConfig({
external: ['vitest'],
options: {
minify: true,
sourcemap: true,
splitting: true,
},
});Multiple Entry Points
// tsup.config.ts
import { createTsupConfig } from '@jmlweb/tsup-config-base';
export default createTsupConfig({
entry: ['src/index.ts', 'src/cli.ts'],
external: ['commander'],
});Object-Style Entry Points
// tsup.config.ts
import { createTsupConfig } from '@jmlweb/tsup-config-base';
export default createTsupConfig({
entry: {
index: 'src/index.ts',
utils: 'src/utils/index.ts',
},
});CLI Package Configuration
For CLI packages that need shebang injection, use createTsupCliConfig:
// tsup.config.ts
import { createTsupCliConfig } from '@jmlweb/tsup-config-base';
export default createTsupCliConfig();
// Output: dist/cli.js with #!/usr/bin/env node shebangCLI with Custom Entry
// tsup.config.ts
import { createTsupCliConfig } from '@jmlweb/tsup-config-base';
export default createTsupCliConfig({
entry: { bin: 'src/bin.ts' },
external: ['commander'],
});CLI with Library API
// tsup.config.ts
import { createTsupCliConfig } from '@jmlweb/tsup-config-base';
export default createTsupCliConfig({
entry: {
cli: 'src/cli.ts',
index: 'src/index.ts',
},
shebang: 'cli', // Only add shebang to cli entry
external: ['commander'],
});🤔 Why Use This?
Philosophy: TypeScript libraries should publish both CommonJS and ESM formats with type declarations for maximum compatibility across all consuming projects.
This package provides a tsup configuration optimized for building TypeScript libraries with dual-format output. It handles the complexity of generating both CommonJS and ESM builds while maintaining proper type definitions and ensuring external dependencies aren't bundled.
Design Decisions
Dual Format Output (format: ['cjs', 'esm']): Support both module systems
- Why: The JavaScript ecosystem is in transition from CommonJS to ESM. Publishing both formats ensures your library works in all environments - legacy Node.js projects using require(), modern ESM projects using import, and bundlers that optimize based on format. This maximizes compatibility
- Trade-off: Slightly larger published package size (two builds). But consumers only use one format, and the compatibility benefit is essential
- When to override: For internal packages consumed only by ESM projects, you can use
['esm']only. But dual publishing is safer for public libraries
Type Declaration Generation (dts: true): Automatic .d.ts files
- Why: TypeScript consumers need type definitions for intellisense, type checking, and developer experience. Generating declarations automatically ensures your library provides first-class TypeScript support without manual maintenance
- Trade-off: Slightly slower builds due to declaration generation. But type safety for consumers is non-negotiable
- When to override: Never for libraries meant to be consumed - types are essential for modern JavaScript development
External Dependencies (external array): Control bundling
- Why: Libraries shouldn't bundle their dependencies - this leads to duplicate code, version conflicts, and bloated packages. The
externalparameter lets you specify which dependencies should remain as imports, letting consumers manage versions - Trade-off: Must carefully specify which dependencies are external (typically all dependencies and peerDependencies)
- When to override: For standalone CLI tools, you might bundle all dependencies. But for libraries, externals are crucial
Clean Builds (clean: true): Fresh output on every build
- Why: Cleaning the output directory prevents stale files from previous builds causing issues. Ensures deterministic builds and prevents shipping renamed or deleted files
- Trade-off: Very slightly slower builds (must delete files first). But the reliability is worth it
- When to override: Rarely - clean builds prevent an entire class of mysterious bugs
📋 Configuration Details
Default Settings
| Setting | Default Value | Description |
| ---------- | ------------------ | ----------------------------------- |
| entry | ['src/index.ts'] | Entry point(s) for the build |
| format | ['cjs', 'esm'] | Output formats (dual publishing) |
| dts | true | Generate TypeScript declarations |
| clean | true | Clean output directory before build |
| outDir | 'dist' | Output directory |
| external | [] | External packages to exclude |
API Reference
createTsupConfig(options?: TsupConfigOptions): Options
Creates a tsup configuration with sensible defaults for library packages.
Parameters:
| Option | Type | Default | Description |
| ---------- | ------------------------------------ | ------------------ | -------------------------------- |
| entry | string[] \| Record<string, string> | ['src/index.ts'] | Entry point files |
| format | ('cjs' \| 'esm' \| 'iife')[] | ['cjs', 'esm'] | Output formats |
| dts | boolean | true | Generate declaration files |
| clean | boolean | true | Clean output before build |
| outDir | string | 'dist' | Output directory |
| external | (string \| RegExp)[] | [] | Packages to exclude from bundle |
| options | Partial<Options> | {} | Additional tsup options to merge |
Returns: A complete tsup Options object.
createTsupCliConfig(options?: TsupCliConfigOptions): Options | Options[]
Creates a CLI-specific tsup configuration with shebang support.
Parameters:
| Option | Type | Default | Description |
| ---------- | ------------------------------------ | ----------------------- | ------------------------------------------- |
| entry | string[] \| Record<string, string> | { cli: 'src/cli.ts' } | Entry point files |
| format | ('cjs' \| 'esm' \| 'iife')[] | ['esm'] | Output formats (ESM only by default) |
| dts | boolean | true | Generate declaration files |
| clean | boolean | true | Clean output before build |
| outDir | string | 'dist' | Output directory |
| external | (string \| RegExp)[] | [] | Packages to exclude from bundle |
| target | NodeTarget | 'node18' | Node.js target version |
| shebang | boolean \| string \| string[] | true | Add shebang to entries (true = all entries) |
| options | Partial<Options> | {} | Additional tsup options to merge |
Returns: A tsup Options object, or an array of Options when selective shebang is used.
Exported Constants
BASE_DEFAULTS- Default library configuration valuesCLI_DEFAULTS- Default CLI configuration valuesOptions- Re-exported from tsup
Type Exports
EntryConfig- Entry point configuration type (string[] | Record<string, string>)NodeTarget- Node.js target version type ('node16' | 'node18' | 'node20' | 'node22' | ...)TsupConfigOptions- Options forcreateTsupConfigTsupCliConfigOptions- Options forcreateTsupCliConfig
🎯 When to Use
Use this configuration when you want:
- ✅ Consistent build configuration across multiple packages
- ✅ Dual-format output (CommonJS + ESM) for maximum compatibility
- ✅ Automatic TypeScript declaration generation
- ✅ A clean, simple API for specifying externals
- ✅ Easy customization without repeating boilerplate
- ✅ CLI packages with proper shebang injection
🔧 Extending the Configuration
You can extend the configuration for your specific needs:
Adding Custom Options
// tsup.config.ts
import { createTsupConfig } from '@jmlweb/tsup-config-base';
export default createTsupConfig({
external: ['vitest'],
options: {
minify: true,
sourcemap: true,
splitting: true,
target: 'es2022',
},
});Overriding Defaults
// tsup.config.ts
import { createTsupConfig } from '@jmlweb/tsup-config-base';
export default createTsupConfig({
format: ['esm'], // ESM only
dts: false, // Disable type declarations
outDir: 'build', // Custom output directory
});Externals Strategy
When configuring external, include:
- Peer dependencies: Packages that consumers should install themselves
- Workspace dependencies: Internal
@jmlweb/*packages used viaworkspace:* - Optional dependencies: Packages that may or may not be present
// Example: ESLint config package
export default createTsupConfig({
external: [
// Internal workspace dependency
'@jmlweb/eslint-config-base-js',
// Peer dependencies
'@eslint/js',
'eslint',
'eslint-config-prettier',
'eslint-plugin-simple-import-sort',
'typescript-eslint',
],
});📝 Usage with Scripts
Add build scripts to your package.json:
{
"scripts": {
"build": "tsup",
"clean": "rm -rf dist",
"prepublishOnly": "pnpm build"
}
}Then run:
pnpm build # Build the package
pnpm clean # Clean build output📋 Requirements
- Node.js >= 18.0.0
- tsup >= 8.0.0
📦 Peer Dependencies
This package requires the following peer dependency:
tsup(>=8.0.0)
🔗 Related Packages
Internal Packages
@jmlweb/eslint-config-base- ESLint config for TypeScript projects@jmlweb/prettier-config-base- Prettier config for consistent formatting@jmlweb/tsconfig-base- TypeScript configuration@jmlweb/vitest-config- Vitest configuration for testing
External Tools
- tsup - Bundle TypeScript libraries with zero config
- esbuild - Extremely fast JavaScript bundler (used by tsup)
- TypeScript - JavaScript with syntax for types
- Rollup - Alternative bundler for complex configurations
🔄 Migration Guide
Upgrading to a New Version
Note: If no breaking changes were introduced in a version, it's safe to upgrade without additional steps.
No breaking changes have been introduced yet. This package follows semantic versioning. When breaking changes are introduced, detailed migration instructions will be provided here.
For version history, see the Changelog.
Need Help? If you encounter issues during migration, please open an issue.
📜 Changelog
See CHANGELOG.md for version history and release notes.
📄 License
MIT
