Skip to content

Instantly share code, notes, and snippets.

@timbenniks
Last active August 23, 2025 02:07
Show Gist options
  • Save timbenniks/28f0eee903eb81eb741e651bd2a8f7bc to your computer and use it in GitHub Desktop.
Save timbenniks/28f0eee903eb81eb741e651bd2a8f7bc to your computer and use it in GitHub Desktop.
Migrate any TypeScript Library to the Void Zero Toolchain using an AI agent

πŸ€– AI Agent Instructions: Migrate TypeScript Library to Void Zero Toolchain

Agent Role: You are tasked with migrating any TypeScript library from its current build system to the high-performance Void Zero toolchain. Follow these instructions systematically.

🎯 Mission Overview

Transform any TypeScript library (regardless of current setup) to use:

  • TSDown for both JavaScript bundling AND TypeScript declarations (primary approach)
  • Oxlint for linting (optional, can keep existing)
  • Vitest for testing (optional, can keep existing)
  • Rolldown as advanced fallback for complex bundling needs (optional)
  • Vite as legacy fallback build option (optional)

Expected Performance: 5-10x faster builds (guaranteed), 100x faster linting (if migrating to Oxlint), framework-compatible output.

Key Insight: For TypeScript libraries, TSDown can handle both bundling and declarations in one tool, eliminating the need for separate bundler configurations!

❓ Pre-Migration Analysis & Questions

Agent Instructions: First analyze the project structure to infer answers, then confirm with the user:

πŸ” STEP 0: Auto-Analysis

Agent Task: Analyze the project before asking questions. Infer these automatically:

# Detect entry points
find . -name "index.ts" -o -name "index.js" | head -5
ls -la src/ lib/ 2>/dev/null || echo "No src/lib found"

# Detect source structure
[ -d "src" ] && echo "Source: src/" || [ -d "lib" ] && echo "Source: lib/" || echo "Source: root level"

# Detect current tools
grep -E "(webpack|rollup|esbuild|jest|eslint|babel)" package.json | head -10

# Detect framework
grep -E "(react|vue|svelte)" package.json | head -5

# Detect test files
find . -name "*.test.*" -o -name "*.spec.*" | head -3

# Check for multiple exports
grep -E "(exports|main|module)" package.json

πŸ€– Smart Questions (Ask only what you can't infer)

Format: "I detected [INFERENCE], is this correct? If not, please clarify:"

  1. Entry Point: "I found these potential entry points: [detected files]. Main entry appears to be [best guess] - correct?"

  2. Source Directory: "Source code appears to be in [detected directory] - is this right?"

  3. Current Tools: "I detected you're using [found tools] for building/testing/linting - accurate?"

  4. Framework Type: "Based on dependencies, this appears to be a [React/Vue/vanilla/other] library - correct?"

  5. Multiple Exports: "Package.json suggests [single/multiple] export structure - is this right?"

🎯 Clarification Questions (Only ask if analysis is unclear)

  1. External Dependencies: "Should these dependencies stay external (not bundled)? [list from peerDependencies + common libs]"

  2. Migration Scope: "Do you want to migrate just the build system, or also linting/testing to Void Zero tools?"

  3. Output Formats: "I see you currently output [detected formats] - keep both ESM and CJS?"

  4. Special Build Steps: "I noticed [custom scripts/configs] - any special build requirements I should preserve?"

  5. Breaking Changes: "This migration may change your build scripts - are you okay with that?"

Agent Decision Matrix Based on Analysis + Answers:

  • Always migrate: TSDown (build + types) - single tool for everything!
  • Auto-configure: Entry points (Q1), source paths (Q2), externals (Q6), output formats (Q8)
  • Conditionally migrate: Oxlint (based on Q7), Vitest (based on Q7)
  • Advanced needs only: Rolldown (complex bundling), Vite (legacy fallback)
  • Preserve existing: Custom build steps (Q9), existing tools (if user prefers in Q7)

πŸ“‹ Step-by-Step Agent Instructions

STEP 1: Analyze Current Setup πŸ”

Task: Examine the existing project structure and identify what needs to be replaced (this should build on your STEP 0 auto-analysis).

Actions:

  1. Verify auto-analysis findings with the user using smart questions above

  2. Document current setup based on analysis + user confirmation:

    • Entry point: [confirmed path]
    • Source directory: [confirmed directory]
    • Current bundler: [detected tool]
    • Current linter: [detected tool or none]
    • Current test framework: [detected tool or none]
    • Framework type: [React/Vue/vanilla/other]
    • External dependencies: [from peerDependencies + user input]
  3. Plan replacement strategy:

    • webpack.config.js β†’ Replace with Rolldown
    • rollup.config.js β†’ Replace with Rolldown
    • esbuild.config.js β†’ Replace with Rolldown
    • .eslintrc.* β†’ Replace with Oxlint (if user agrees)
    • jest.config.* β†’ Replace with Vitest (if user agrees)
    • babel.config.* β†’ Remove (Rolldown handles transpilation)
    • Multiple tsconfig.*.json β†’ Simplify to one
    • Build scripts in /scripts/ β†’ Replace with simple npm scripts

Expected Output: Clear migration plan based on actual project structure rather than assumptions.

STEP 2: Install Void Zero Dependencies πŸ“¦

Task: Replace old dependencies with Void Zero ecosystem (install only what you need).

Actions:

  1. Remove old dependencies (only uninstall what exists in the current project):
# Check package.json first, then remove only existing bundlers
npm uninstall -D webpack webpack-cli webpack-dev-server || true
npm uninstall -D rollup @rollup/plugin-* || true
npm uninstall -D esbuild || true
npm uninstall -D parcel || true

# Remove existing linters only if found
npm uninstall -D eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin || true
npm uninstall -D eslint-plugin-* || true

# Remove existing test runners only if found
npm uninstall -D jest @types/jest ts-jest || true
npm uninstall -D mocha @types/mocha || true

# Remove transpilation tools only if found
npm uninstall -D babel-core @babel/core @babel/preset-* || true
npm uninstall -D swc @swc/core || true
  1. Install Void Zero tools (TSDown-first approach):
# Core essentials (always install - TSDown handles both JS and types!)
npm install -D tsdown@^0.14.1
npm install -D typescript@^5.8.3

# Choose linting approach:
npm install -D oxlint@^1.11.2  # OR keep existing ESLint setup

# Choose testing approach:
npm install -D vitest@^3.2.4   # OR keep existing test framework

# Advanced bundling (only if TSDown isn't sufficient):
npm install -D rolldown@^1.0.0-beta.32

# Legacy fallback build (install if complex build needs):
npm install -D vite@^6.0.0 vite-plugin-dts@^4.3.0

# Utilities (install if needed):
npm install -D rimraf@^6.0.1 @types/node@^24.0.10

STEP 3: Create Core Configuration Files πŸ“

Task: Create the essential configuration (TSDown-first approach for libraries).

Action 3A: Create tsdown.config.js (Primary build - handles both JS and types!)

// TSDown can handle both bundling and declarations for libraries
export default {
  entry: "src/index.ts", // Adjust based on your project structure
  outDir: "dist",
  format: ["esm", "cjs"], // Outputs both ES modules and CommonJS
  dts: true, // Generate TypeScript declarations
  sourcemap: true,
  external: [], // Add external dependencies here (will be updated in Step 8)
  clean: true,
};

Action 3B: Create rolldown.config.js (Only if you need advanced bundling features)

// Only create this if TSDown doesn't meet your complex bundling needs
export default {
  input: "src/index.ts", // Common alternatives: "lib/index.ts", "index.ts"
  output: [
    {
      file: "dist/esm/index.js",
      format: "esm",
      sourcemap: true,
    },
    {
      file: "dist/cjs/index.cjs",
      format: "cjs",
      sourcemap: true,
    },
  ],
  external: [], // Add external dependencies here
};

Action 3C: Create oxlint.json (only if migrating from ESLint)

{
  "rules": {}
}

Action 3D: Create vite.config.ts (only if you need legacy fallback build)

import { defineConfig } from "vite";
import dts from "vite-plugin-dts";

export default defineConfig({
  plugins: [
    dts({
      outDir: "dist/types",
      include: ["src/**/*"], // Adjust path if needed: ["lib/**/*"], etc.
      exclude: ["**/*.test.ts", "**/*.spec.ts"],
    }),
  ],
  build: {
    lib: {
      entry: "src/index.ts", // Adjust to match your entry point
      formats: ["es", "cjs"],
      fileName: (format) =>
        format === "es" ? "esm/index.js" : "cjs/index.cjs",
    },
    minify: false, // Libraries should not be minified
    target: "esnext",
    rollupOptions: {
      output: {
        compact: false, // Clean output for frameworks
      },
    },
  },
});

STEP 4: Update TypeScript Configuration πŸ”§

Task: Simplify TypeScript setup to one modern config.

Actions:

  1. Remove old tsconfig files: tsconfig.*.json (except main one)
  2. Replace tsconfig.json content with:
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "ESNext",
    "moduleResolution": "bundler",
    "declaration": true,
    "declarationMap": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "isolatedModules": true,
    "noEmit": true
  },
  "include": ["src/**/*", "vite.config.ts"],
  "exclude": ["node_modules", "dist", "**/*.test.ts", "**/*.spec.ts"]
}

STEP 5: Transform Package.json πŸ“‹

Task: Update package.json to use Void Zero toolchain.

Action 5A: Replace scripts section (TSDown-first approach):

{
  "scripts": {
    "clean": "rimraf dist", // OR "rm -rf dist" if rimraf not installed
    "build": "tsdown", // Single command builds both JS and types!
    "build:rolldown": "rolldown -c rolldown.config.js", // Only if using Rolldown
    "build:vite": "npm run clean && vite build", // Legacy fallback
    "lint": "oxlint", // OR keep your existing linter: "eslint src"
    "lint:fix": "oxlint --fix", // OR "eslint src --fix"
    "test": "vitest run", // OR keep existing: "jest", "mocha", etc.
    "test:watch": "vitest", // OR existing watch command
    "prepublishOnly": "npm run build"
  }
}

Action 5B: Update/add package configuration fields (TSDown output structure):

{
  "type": "module",
  "main": "./dist/index.cjs", // TSDown output structure
  "module": "./dist/index.js",
  "types": "./dist/index.d.ts",
  "sideEffects": false,
  "exports": {
    ".": {
      "import": {
        "types": "./dist/index.d.ts",
        "default": "./dist/index.js"
      },
      "require": {
        "types": "./dist/index.d.ts",
        "default": "./dist/index.cjs"
      }
    }
  },
  "files": ["dist"]
}

STEP 6: Handle Test Files πŸ§ͺ

Task: Migrate tests to Vitest (optional - you can keep existing test setup).

Actions (only if migrating to Vitest):

  1. Find test files: Look for *.test.*, *.spec.*, /test/, /__tests__/
  2. Update imports (if migrating from Jest):
    • Change import { describe, it, expect } from 'jest'
    • To import { describe, it, expect } from 'vitest'
  3. Remove old test configs: jest.config.*, .mocharc.* (if migrating)
  4. Create vitest.config.ts only if you have complex testing needs:
import { defineConfig } from "vitest/config";

export default defineConfig({
  test: {
    globals: true,
    environment: "node", // or "jsdom" for browser-like tests
  },
});

Note: You can keep your existing test setup (Jest, Mocha, etc.) and only migrate the build system.

STEP 7: Clean Up Legacy Files πŸ—‘οΈ

Task: Remove obsolete configuration and build files (only remove what exists).

Actions - Check and remove these files if they exist:

# Remove old bundler configs (check first)
[ -f webpack.config.js ] && rm webpack.config.js
[ -f rollup.config.js ] && rm rollup.config.js
[ -f esbuild.config.js ] && rm esbuild.config.js
[ -f parcel.config.js ] && rm parcel.config.js

# Remove old linter configs (only if migrating to Oxlint)
[ -f .eslintrc.js ] && rm .eslintrc.js
[ -f .eslintrc.json ] && rm .eslintrc.json
[ -f .eslintrc.yml ] && rm .eslintrc.yml

# Remove old test configs (only if migrating to Vitest)
[ -f jest.config.js ] && rm jest.config.js
[ -f jest.config.json ] && rm jest.config.json
[ -f .mocharc.json ] && rm .mocharc.json

# Remove transpilation configs (check first)
[ -f babel.config.js ] && rm babel.config.js
[ -f .babelrc ] && rm .babelrc
[ -f .swcrc ] && rm .swcrc

# Remove multiple tsconfig files (keep main tsconfig.json)
[ -f tsconfig.cjs.json ] && rm tsconfig.cjs.json
[ -f tsconfig.esm.json ] && rm tsconfig.esm.json
[ -f tsconfig.types.json ] && rm tsconfig.types.json

# Remove build scripts directory (only if it contains build scripts)
[ -d scripts ] && [ "$(ls -A scripts)" ] && echo "Check scripts/ directory manually"

# Remove old package files
rm -f *.tgz

STEP 8: Handle External Dependencies πŸ”—

Task: Configure external dependencies for the bundler.

Actions:

  1. Read package.json dependencies and peerDependencies
  2. Add them to rolldown.config.js external array:
external: ["react", "react-dom", "lodash"]; // Example
  1. Rule: Never bundle dependencies that consumers should provide

STEP 9: Test Migration βœ…

Task: Verify everything works correctly.

Actions:

# Test build (core requirement)
npm run build

# Test linting (if migrated to Oxlint)
npm run lint

# Test tests (with existing or new test framework)
npm test

# Create test package
npm pack

Expected Results:

  • Build completes in <1 second (major improvement)
  • Single tsdown command handles everything!
  • Linting completes in <50ms (if using Oxlint)
  • All tests pass (with existing or migrated test framework)
  • Package creates successfully
  • Output structure (TSDown approach):
dist/
β”œβ”€β”€ index.js (ESM)
β”œβ”€β”€ index.cjs (CommonJS)
β”œβ”€β”€ index.d.ts (TypeScript declarations)
└── *.map files (sourcemaps)

STEP 10: Validate Framework Compatibility 🎯

Task: Ensure the package works across different frameworks.

Actions:

  1. Check that dist/ contains both .d.ts AND .js/.cjs files
  2. Verify package.json has proper exports field
  3. Test import in a simple test file:
// Test ESM
import { yourFunction } from "./package-name";

// Test CJS
const { yourFunction } = require("./package-name");

// Test CJS const { yourFunction } = require("./package-name");


## 🚨 Common Edge Cases to Handle

### Multiple Entry Points

If the library has multiple exports, update configs:

```javascript
// rolldown.config.js
export default [
  { input: 'src/index.ts', output: [...] },
  { input: 'src/utils.ts', output: [...] }
]

React/Vue Libraries

Add JSX/Vue support:

// rolldown.config.js
export default {
  // ... other config
  plugins: [
    // Add react() or vue() plugin if needed
  ],
};

Complex Directory Structure

If src files are not in /src/, adjust all config file paths accordingly.

Monorepo Setup

Apply this migration to each package individually, not at the root level.

🎯 Success Criteria

The migration is complete when:

  • βœ… Build time reduced by 5-10x (core requirement)
  • βœ… TSDown successfully handles both bundling AND declarations (primary goal)
  • βœ… Package exports both ESM and CJS
  • βœ… Single npm run build command works
  • βœ… No old bundler tools remain
  • βœ… All tests pass (regardless of test framework)
  • βœ… Package size optimized
  • βœ… Framework compatibility verified

Optional completions (depending on what you migrated):

  • βœ… Linting time reduced by 100x (if migrated to Oxlint)
  • βœ… Test performance improved (if migrated to Vitest)
  • βœ… Rolldown available for advanced bundling needs (if installed)

🎯 Using Auto-Analysis + User Confirmation

Agent Reference: Use the auto-detected information + user confirmations to customize the migration:

Path Configuration (Based on Auto-Detection + Q1-2)

// tsdown.config.js - use confirmed paths (primary approach)
export default {
  entry: "[detected/confirmed entry point]", // e.g., "src/index.ts"
  outDir: "dist",
  format: ["esm", "cjs"],
  dts: true,
  // For multiple exports (detected in analysis), specify multiple entries
  // entry: ["src/index.ts", "src/utils.ts"]
};

// rolldown.config.js - only if TSDown isn't sufficient
export default {
  input: "[detected/confirmed entry point]", // e.g., "src/index.ts"
  // For multiple exports (detected in analysis), create array of configs
  // input: ["src/index.ts", "src/utils.ts"]
};

Framework Support (Based on Auto-Detection + Q4)

// If React/Vue library detected, add to tsdown.config.js:
export default {
  entry: "src/index.ts",
  outDir: "dist",
  format: ["esm", "cjs"],
  dts: true,
  esbuildOptions: {
    jsx: "automatic", // For React
    // or configure for Vue/Svelte
  },
};

// OR for complex cases, use rolldown.config.js:
import react from "@rolldown/plugin-react";
import vue from "@rolldown/plugin-vue";

export default {
  plugins: [react()], // Add appropriate plugin based on detection
  // ...
};

External Dependencies (Based on Auto-Detection + Q6)

// tsdown.config.js - use detected peerDependencies + user confirmation
export default {
  entry: "src/index.ts",
  outDir: "dist",
  format: ["esm", "cjs"],
  dts: true,
  external: ["react", "react-dom", "vue"], // From peerDeps + user input
};

Conditional Script Generation (Based on Q7)

// package.json scripts - TSDown-first approach
{
  "scripts": {
    "build": "tsdown", // Primary: single command for everything!
    "build:rolldown": "rolldown -c", // Only if using Rolldown for complex needs
    "lint": "oxlint", // If user chose to migrate linting
    "test": "vitest run" // If user chose to migrate testing
    // Otherwise keep existing: "eslint src", "jest", etc.
  }
}

πŸ”„ Final Output Structure

Every migrated library should have (TSDown-first approach):

β”œβ”€β”€ src/                    # Source code (unchanged location may vary)
β”œβ”€β”€ dist/                   # Generated by TSDown
β”‚   β”œβ”€β”€ index.js           # ES modules
β”‚   β”œβ”€β”€ index.cjs          # CommonJS
β”‚   β”œβ”€β”€ index.d.ts         # TypeScript declarations
β”‚   └── *.map              # Sourcemaps
β”œβ”€β”€ package.json           # Updated configuration
β”œβ”€β”€ tsconfig.json          # Simplified TypeScript config
β”œβ”€β”€ tsdown.config.js       # Primary build config (TSDown)
β”œβ”€β”€ rolldown.config.js     # Advanced build config (optional)
β”œβ”€β”€ vite.config.ts         # Legacy fallback build config (optional)
β”œβ”€β”€ oxlint.json           # Linting config (optional)
└── README.md             # Updated documentation

Agent Note: This migration should work for ANY TypeScript library regardless of its current setup. The key principle is:

  1. Always install: TSDown + TypeScript (TSDown handles both bundling AND declarations!)
  2. Optionally install: Oxlint (linting), Vitest (testing), Rolldown (complex bundling), Vite (legacy fallback)
  3. Adapt paths: Check actual entry points (src/, lib/, index.ts, etc.)
  4. Preserve choices: Keep existing test/lint setups if preferred
  5. Remove safely: Only delete files that actually exist

Focus on the TSDown-first transformation for libraries - it's simpler and handles both JS bundling and TypeScript declarations in one tool!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment