@dbarrett24/tsup-config
v1.0.0
Published
Shared tsup configuration for building component libraries
Maintainers
Readme
@yourname/tsup-config
Shared tsup configuration for building component libraries with automatic workspace dependency resolution and client directive injection.
🎯 What This Solves
When building component libraries in a monorepo, you face these challenges:
- Workspace Dependencies: During development you use
workspace:*, but published packages need actual version numbers - Client Directives: React client components need
'use client';at the top of every file - Build Consistency: Every library needs the same build configuration
- package.json Management: Published packages need a clean
dist/package.jsonwith correct entry points
This package automates all of that.
📦 Installation
pnpm add -D @yourname/tsup-config tsup prettier🚀 Usage
Basic Setup
In your component library's tsup.config.ts:
import { makeConfig } from '@yourname/tsup-config';
export default makeConfig(
// Server-safe entries (no 'use client' needed)
{
index: 'src/index.ts',
utils: 'src/utils/index.ts',
},
// Client component entries (gets 'use client' prepended)
{
button: 'src/components/Button.tsx',
input: 'src/components/Input.tsx',
}
);Advanced Setup with Overrides
import { makeConfig } from '@yourname/tsup-config';
export default makeConfig(
{ index: 'src/index.ts' },
{ button: 'src/components/Button.tsx' },
{
// Optional overrides
format: ['cjs', 'esm'],
external: ['react', 'react-dom'],
onSuccess: async () => {
console.log('Build complete!');
},
}
);⚙️ What It Does
1. Automatic Workspace Dependency Resolution
Your package.json:
{
"dependencies": {
"@yourname/theme-system": "workspace:*"
}
}Generated dist/package.json:
{
"dependencies": {
"@yourname/theme-system": "^1.2.3"
}
}2. Client Directive Injection
Your component:
// src/components/Button.tsx
export const Button = ({ children }) => {
return <button>{children}</button>;
};Built output:
// dist/button.js
'use client';
export const Button = ({ children }) => {
return <button>{children}</button>;
};3. Clean package.json Generation
Automatically creates dist/package.json with only:
nameversiondependenciespeerDependenciessideEffectsmain(entry point)types(type definitions)
4. Consistent Build Configuration
All libraries get:
- TypeScript declaration files (
.d.ts) - CommonJS and ESM formats
- Inline source maps
- Tree-shaking support
- External node_modules
📋 API Reference
makeConfig(entries, clientEntries?, optionOverrides?)
Parameters:
entries(Record<string, string>) - Server-safe entry points- Keys: Output file names
- Values: Source file paths
clientEntries(Record<string, string>, optional) - Client component entry points- Same format as
entries - Gets
'use client'prepended automatically
- Same format as
optionOverrides(Partial<Options>, optional) - Additional tsup options- Any valid tsup configuration option
- Overrides default settings
Returns: Options - tsup configuration object
defaultConfigObject
The base configuration applied to all builds:
{
dts: true, // Generate .d.ts files
format: ['cjs', 'esm'], // Output CommonJS and ESM
legacyOutput: true, // Use legacy output structure
clean: true, // Clean dist before build
sourcemap: 'inline', // Inline source maps
splitting: true, // Enable code splitting
skipNodeModulesBundle: true, // Don't bundle node_modules
}🏗️ Project Structure Example
basketball-training-ui/
├── src/
│ ├── components/
│ │ ├── Button.tsx # Client component
│ │ └── Input.tsx # Client component
│ ├── utils/
│ │ └── helpers.ts # Server-safe utilities
│ └── index.ts # Main entry
├── dist/ # Generated by tsup
│ ├── index.js
│ ├── index.d.ts
│ ├── button.js # Has 'use client'
│ ├── button.d.ts
│ ├── utils.js
│ ├── utils.d.ts
│ └── package.json # Auto-generated with resolved deps
├── package.json
└── tsup.config.ts # Uses makeConfig()🔧 Configuration Tips
Separating Client and Server Code
export default makeConfig(
// Server/universal code
{
index: 'src/index.ts',
hooks: 'src/hooks/index.ts',
utils: 'src/utils/index.ts',
},
// Client components only
{
button: 'src/components/Button.tsx',
input: 'src/components/Input.tsx',
modal: 'src/components/Modal.tsx',
}
);External Dependencies
Mark React and other large dependencies as external:
export default makeConfig(
{ index: 'src/index.ts' },
{ button: 'src/components/Button.tsx' },
{
external: ['react', 'react-dom', 'jotai', '@tanstack/react-query'],
}
);Custom Build Output
export default makeConfig(
{ index: 'src/index.ts' },
{ button: 'src/components/Button.tsx' },
{
format: ['esm'], // ESM only
minify: true, // Minify output
target: 'es2020', // Target specific ECMAScript version
}
);🧪 Verifying the Build
After building, check that:
- dist/package.json exists with resolved workspace dependencies
- Client components have 'use client' at the top
- Type definitions are generated (
.d.tsfiles)
pnpm build
# Check package.json
cat dist/package.json
# Check client directive
head -n 1 dist/button.js
# Should show: 'use client';
# Check types
ls dist/*.d.ts🤝 Integration with Changesets
When using @changesets/cli, this package ensures published versions are correct:
# 1. Make changes to your library
# 2. Create changeset
pnpm changeset
# 3. Version packages (updates version numbers)
pnpm changeset version
# 4. Build (workspace:* gets resolved to actual versions)
pnpm build
# 5. Publish
pnpm changeset publish📚 Related Packages
- @yourname/prettier-config - Used for formatting generated package.json
- @yourname/typescript-config - TypeScript configuration for building
- @yourname/theme-system - Common dependency in component libraries
🐛 Troubleshooting
"Cannot find package.json in lockfile"
Cause: Package not listed in pnpm-workspace.yaml
Fix:
# pnpm-workspace.yaml
packages:
- 'brand-libraries/*'
- 'shared-configs/*'"'use client' not being added"
Cause: Component listed in entries instead of clientEntries
Fix:
// ❌ Wrong
export default makeConfig({
button: 'src/components/Button.tsx', // In entries - no 'use client'
});
// ✅ Correct
export default makeConfig(
{}, // Server entries
{ button: 'src/components/Button.tsx' } // Client entries - gets 'use client'
);"Workspace dependencies not resolved"
Cause: Building outside of monorepo or lockfile not present
Fix:
- Ensure you're building from within the monorepo
- Run
pnpm installto generate/updatepnpm-lock.yaml - Verify
pnpm-workspace.yamlincludes your package
📄 License
MIT © Your Name
