Skip to content

base

npm version License: MIT Node.js tsup

> Base tsup configuration for jmlweb projects. Provides sensible defaults and a clean API for creating consistent build configurations.

  • 🎯 Sensible Defaults: Pre-configured with common settings for TypeScript libraries
  • 📦 Dual Format: Generates both CommonJS and ESM outputs by default
  • 📝 TypeScript Declarations: Automatic .d.ts generation 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
Terminal window
pnpm add -D @jmlweb/tsup-config-base tsup

> 💡 Upgrading from a previous version? See the Migration Guide for breaking changes and upgrade instructions.

Create a tsup.config.ts file in your project root:

import { createTsupConfig } from '@jmlweb/tsup-config-base';
export default createTsupConfig();
tsup.config.ts
import { createTsupConfig } from '@jmlweb/tsup-config-base';
export default createTsupConfig();
tsup.config.ts
import { createTsupConfig } from '@jmlweb/tsup-config-base';
export default createTsupConfig({
external: ['eslint', 'typescript-eslint', '@eslint/js'],
});
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',
],
});
tsup.config.ts
import { createTsupConfig } from '@jmlweb/tsup-config-base';
export default createTsupConfig({
entry: ['src/main.ts'],
external: ['some-dependency'],
});
tsup.config.ts
import { createTsupConfig } from '@jmlweb/tsup-config-base';
export default createTsupConfig({
external: ['vitest'],
options: {
minify: true,
sourcemap: true,
splitting: true,
},
});
tsup.config.ts
import { createTsupConfig } from '@jmlweb/tsup-config-base';
export default createTsupConfig({
entry: ['src/index.ts', 'src/cli.ts'],
external: ['commander'],
});
tsup.config.ts
import { createTsupConfig } from '@jmlweb/tsup-config-base';
export default createTsupConfig({
entry: {
index: 'src/index.ts',
utils: 'src/utils/index.ts',
},
});

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 shebang
tsup.config.ts
import { createTsupCliConfig } from '@jmlweb/tsup-config-base';
export default createTsupCliConfig({
entry: { bin: 'src/bin.ts' },
external: ['commander'],
});
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'],
});

> 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.

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 external parameter 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
SettingDefault ValueDescription
entry['src/index.ts']Entry point(s) for the build
format['cjs', 'esm']Output formats (dual publishing)
dtstrueGenerate TypeScript declarations
cleantrueClean output directory before build
outDir'dist'Output directory
external[]External packages to exclude

createTsupConfig(options?: TsupConfigOptions): Options

Section titled “createTsupConfig(options?: TsupConfigOptions): Options”

Creates a tsup configuration with sensible defaults for library packages.

Parameters:

OptionTypeDefaultDescription
entrystring[] | Record<string, string>['src/index.ts']Entry point files
format('cjs' | 'esm' | 'iife')[]['cjs', 'esm']Output formats
dtsbooleantrueGenerate declaration files
cleanbooleantrueClean output before build
outDirstring'dist'Output directory
external(string | RegExp)[][]Packages to exclude from bundle
optionsPartial<Options>{}Additional tsup options to merge

Returns: A complete tsup Options object.

createTsupCliConfig(options?: TsupCliConfigOptions): Options | Options[]

Section titled “createTsupCliConfig(options?: TsupCliConfigOptions): Options | Options[]”

Creates a CLI-specific tsup configuration with shebang support.

Parameters:

OptionTypeDefaultDescription
entrystring[] | Record<string, string>{ cli: 'src/cli.ts' }Entry point files
format('cjs' | 'esm' | 'iife')[]['esm']Output formats (ESM only by default)
dtsbooleantrueGenerate declaration files
cleanbooleantrueClean output before build
outDirstring'dist'Output directory
external(string | RegExp)[][]Packages to exclude from bundle
targetNodeTarget'node18'Node.js target version
shebangboolean | string | string[]trueAdd shebang to entries (true = all entries)
optionsPartial<Options>{}Additional tsup options to merge

Returns: A tsup Options object, or an array of Options when selective shebang is used.

  • BASE_DEFAULTS - Default library configuration values
  • CLI_DEFAULTS - Default CLI configuration values
  • Options - Re-exported from tsup
  • EntryConfig - Entry point configuration type (string[] | Record<string, string>)
  • NodeTarget - Node.js target version type ('node16' | 'node18' | 'node20' | 'node22' | ...)
  • TsupConfigOptions - Options for createTsupConfig
  • TsupCliConfigOptions - Options for createTsupCliConfig

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

You can extend the configuration for your specific needs:

tsup.config.ts
import { createTsupConfig } from '@jmlweb/tsup-config-base';
export default createTsupConfig({
external: ['vitest'],
options: {
minify: true,
sourcemap: true,
splitting: true,
target: 'es2022',
},
});
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
});

When configuring external, include:

  1. Peer dependencies: Packages that consumers should install themselves
  2. Workspace dependencies: Internal @jmlweb/* packages used via workspace:*
  3. 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',
],
});

Add build scripts to your package.json:

{
"scripts": {
"build": "tsup",
"clean": "rm -rf dist",
"prepublishOnly": "pnpm build"
}
}

Then run:

Terminal window
pnpm build # Build the package
pnpm clean # Clean build output
  • Node.js >= 18.0.0
  • tsup >= 8.0.0

This package requires the following peer dependency:

  • tsup (>=8.0.0)
  • 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

> 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.

See CHANGELOG.md for version history and release notes.

MIT