# Migration Guide: TSDX v0.x to v2.0
This guide helps you migrate from the original TSDX (v0.x) to the modern TSDX 2.0.
## What's Changed
TSDX 2.0 is a complete rewrite that replaces the original toolchain with modern, high-performance alternatives:
| Old (v0.x) | New (v2.0) | Why |
|------------|------------|-----|
| Rollup + Babel | [bunchee](https://github.com/huozhi/bunchee) | Zero-config, SWC-powered, faster |
| Jest | [vitest](https://vitest.dev/) | Vite-native, faster, Jest-compatible |
| ESLint | [oxlint](https://oxc.rs/) | 50-100x faster, Rust-powered |
| Prettier | [oxfmt](https://oxc.rs/) | 35x faster, Rust-powered |
| yarn/npm | [bun](https://bun.sh/) | Faster installs and execution |
| Node 10+ | Node 20+ | LTS only |
## Quick Migration
For most projects, follow these steps:
### 1. Install Bun
```bash
# macOS/Linux
curl -fsSL https://bun.sh/install | bash
# Windows
powershell -c "irm bun.sh/install.ps1 | iex"
```
### 2. Update package.json
Replace your scripts:
```json
{
"scripts": {
"dev": "tsdx dev",
"build": "tsdx build",
"test": "tsdx test",
"lint": "tsdx lint",
"format": "tsdx format",
"typecheck": "tsdx typecheck",
"prepublishOnly": "bun run build"
}
}
```
### 3. Update Dependencies
Remove old dependencies and add new ones:
```bash
# Remove old dependencies
bun remove tsdx rollup @rollup/plugin-* babel-* @babel/* jest ts-jest eslint @typescript-eslint/* prettier husky lint-staged
# Add new tsdx
bun add -D tsdx typescript
```
### 4. Replace Jest with Vitest
Create `vitest.config.ts`:
```typescript
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
globals: true,
environment: 'node', // or 'jsdom' for React/DOM testing
},
});
```
Update test files:
- Change `import { describe, it, expect } from 'jest'` to `import { describe, it, expect } from 'vitest'`
- Or use `globals: true` in vitest.config.ts to avoid imports
**Jest to Vitest Cheatsheet:**
| Jest | Vitest |
|------|--------|
| `jest.fn()` | `vi.fn()` |
| `jest.mock()` | `vi.mock()` |
| `jest.spyOn()` | `vi.spyOn()` |
| `jest.useFakeTimers()` | `vi.useFakeTimers()` |
| `beforeAll/afterAll` | Same |
| `beforeEach/afterEach` | Same |
| `describe/it/test` | Same |
| `expect()` | Same |
### 5. Remove Old Config Files
Delete these files (they're no longer needed):
```bash
rm -f tsdx.config.js
rm -f jest.config.js
rm -f .babelrc babel.config.js babel.config.json
rm -f .eslintrc .eslintrc.js .eslintrc.json .eslintignore
rm -f .prettierrc .prettierrc.js .prettierrc.json .prettierignore
rm -f rollup.config.js
rm -f yarn.lock package-lock.json
```
### 6. Update tsconfig.json
Update to modern settings:
```json
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"lib": ["ES2022", "DOM"],
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"declaration": true,
"declarationMap": true,
"outDir": "./dist",
"rootDir": "./src",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true
},
"include": ["src"],
"exclude": ["node_modules", "dist"]
}
```
### 7. Update package.json Exports
Ensure your package.json has modern exports:
```json
{
"type": "module",
"main": "./dist/index.cjs",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"import": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
},
"require": {
"types": "./dist/index.d.cts",
"default": "./dist/index.cjs"
}
},
"./package.json": "./package.json"
},
"files": ["dist", "src"],
"engines": {
"node": ">=20"
}
}
```
### 8. Install and Test
```bash
# Install dependencies
bun install
# Run tests
bun run test
# Build
bun run build
# Lint
bun run lint
```
## Detailed Migration
### Build Configuration
**Old TSDX (tsdx.config.js):**
```javascript
module.exports = {
rollup(config, options) {
// Custom rollup config
return config;
},
};
```
**New TSDX:** No configuration needed! bunchee reads your `package.json` exports field.
For advanced customization, create `bunchee.config.ts`:
```typescript
import { BuncheeConfig } from 'bunchee';
export default {
// See bunchee documentation
} satisfies BuncheeConfig;
```
### Testing
**Old Jest test:**
```typescript
import { sum } from './index';
describe('sum', () => {
it('adds numbers', () => {
expect(sum(1, 2)).toBe(3);
});
});
```
**New Vitest test (same syntax!):**
```typescript
import { describe, it, expect } from 'vitest';
import { sum } from './index';
describe('sum', () => {
it('adds numbers', () => {
expect(sum(1, 2)).toBe(3);
});
});
```
Or with `globals: true` in vitest.config.ts:
```typescript
import { sum } from './index';
describe('sum', () => {
it('adds numbers', () => {
expect(sum(1, 2)).toBe(3);
});
});
```
### React Testing
**Old (enzyme/react-testing-library with Jest):**
```typescript
import { render, screen } from '@testing-library/react';
import { MyComponent } from './MyComponent';
test('renders', () => {
render();
expect(screen.getByText('Hello')).toBeInTheDocument();
});
```
**New (same, just with Vitest!):**
```typescript
import { describe, it, expect } from 'vitest';
import { render, screen } from '@testing-library/react';
import { MyComponent } from './MyComponent';
describe('MyComponent', () => {
it('renders', () => {
render();
expect(screen.getByText('Hello')).toBeDefined();
});
});
```
Note: Replace `toBeInTheDocument()` with `toBeDefined()` or add `@testing-library/jest-dom` and configure in vitest setup.
### Linting
**Old ESLint (.eslintrc.js):**
```javascript
module.exports = {
extends: ['react-app', 'prettier'],
rules: {
'no-unused-vars': 'warn',
},
};
```
**New oxlint (.oxlintrc.json) - optional:**
```json
{
"rules": {
"no-unused-vars": "warn"
}
}
```
Most ESLint rules have equivalents in oxlint. Check [oxlint rules documentation](https://oxc.rs/docs/guide/usage/linter/rules.html).
### Formatting
**Old Prettier (.prettierrc):**
```json
{
"semi": true,
"singleQuote": true,
"tabWidth": 2
}
```
**New oxfmt (.oxfmtrc.json) - optional:**
```json
{
"indentWidth": 2,
"lineWidth": 100
}
```
### GitHub Actions
**Old workflow:**
```yaml
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node: [12, 14, 16]
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node }}
- run: yarn install
- run: yarn build
- run: yarn test
```
**New workflow:**
```yaml
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
node: ['20', '22']
os: [ubuntu-latest, windows-latest, macos-latest]
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
- run: bun install
- run: bun run lint
- run: bun run typecheck
- run: bun run build
- run: bun run test
```
## Breaking Changes
### Removed Features
1. **Storybook template** - Use [Storybook CLI](https://storybook.js.org/docs/get-started/install) directly
2. **Custom Rollup config** - Use bunchee config or raw rollup if needed
3. **tsdx lint** - Now wraps oxlint instead of ESLint
4. **Node.js < 20** - Only Node.js 20+ (LTS) is supported
### Changed Behavior
1. **Build output** - Slightly different but compatible
2. **Watch mode** - Now uses bunchee's watch, may have different behavior
3. **Test runner** - Vitest instead of Jest (mostly compatible API)
4. **Default branch** - Uses `main` instead of `master` in templates
## Compatibility
### Your Library Consumers
**No changes needed!** The build output format is compatible:
- ESM and CommonJS dual publish
- TypeScript declarations
- Same export patterns
### Your Development Workflow
| Task | Old Command | New Command |
|------|-------------|-------------|
| Create project | `npx tsdx create mylib` | `bunx tsdx create mylib` |
| Development | `yarn start` | `bun run dev` |
| Build | `yarn build` | `bun run build` |
| Test | `yarn test` | `bun run test` |
| Lint | `yarn lint` | `bun run lint` |
| Format | `yarn prettier --write .` | `bun run format` |
## Troubleshooting
### "bun: command not found"
Install bun:
```bash
curl -fsSL https://bun.sh/install | bash
```
### Tests fail with "vi is not defined"
Add vitest imports:
```typescript
import { describe, it, expect, vi } from 'vitest';
```
Or enable globals in `vitest.config.ts`:
```typescript
export default defineConfig({
test: {
globals: true,
},
});
```
### TypeScript errors with moduleResolution
Update tsconfig.json:
```json
{
"compilerOptions": {
"moduleResolution": "bundler"
}
}
```
### ESM/CJS interop issues
Ensure your package.json has:
```json
{
"type": "module"
}
```
### "Cannot find module" in tests
Check your vitest.config.ts has correct paths:
```typescript
export default defineConfig({
test: {
include: ['test/**/*.test.ts'],
},
});
```
## Getting Help
- [TSDX GitHub Issues](https://github.com/jaredpalmer/tsdx/issues)
- [Vitest Documentation](https://vitest.dev/)
- [bunchee Documentation](https://github.com/huozhi/bunchee)
- [oxlint Documentation](https://oxc.rs/docs/guide/usage/linter.html)
- [Bun Documentation](https://bun.sh/docs)