code-migration

$npx mdskill add TerminalSkills/skills/code-migration

Migrate codebases between frameworks, languages, and API versions

  • Converts JavaScript to TypeScript, updates deprecated APIs, and modernizes legacy code
  • Works with any modern development environment and supports popular frameworks
  • Analyzes code structure to identify migration scope and compatibility risks
  • Delivers incremental changes with backward compatibility and detailed transformation reports
SKILL.md
.github/skills/code-migrationView on GitHub ↗
---
name: code-migration
description: >-
  Migrate codebases between frameworks, languages, and API versions. Use when
  a user asks to convert JavaScript to TypeScript, migrate React class
  components to hooks, upgrade Vue 2 to Vue 3, migrate Python 2 to 3, update
  deprecated APIs, switch ORMs, convert Express to Fastify, or modernize
  legacy code. Handles incremental migration with backward compatibility.
license: Apache-2.0
compatibility: "Any modern development environment"
metadata:
  author: terminal-skills
  version: "1.0.0"
  category: development
  tags: ["migration", "typescript", "modernization", "refactoring", "upgrade"]
---

# Code Migration

## Overview

Migrate codebases between frameworks, languages, and API versions with automated, incremental transformations. This skill handles JavaScript-to-TypeScript conversions, framework upgrades, deprecated API replacements, and ORM migrations while preserving functionality and maintaining backward compatibility.

## Instructions

When a user asks to migrate or modernize their code, follow these steps:

### Step 1: Assess the migration scope

Analyze the codebase to understand:
- **Source**: Current framework/language version
- **Target**: Desired framework/language version
- **Size**: Number of files and lines affected
- **Dependencies**: Libraries that need updating or replacing
- **Risk areas**: Custom patches, monkey-patches, or framework internals usage

```bash
# Count affected files
find src -name "*.js" | wc -l       # JS→TS migration
grep -rl "React.Component" src/     # Class→hooks migration
grep -rl "Vue.component" src/       # Vue 2 patterns
```

### Step 2: Create a migration plan

Before changing code, produce a migration plan:

```markdown
## Migration Plan: JS → TypeScript

**Files to migrate:** 47
**Estimated effort:** ~2 hours with AI assistance

### Phase 1: Setup (non-breaking)
- Add tsconfig.json with allowJs: true
- Install TypeScript and type packages
- Rename entry point to .ts

### Phase 2: Incremental conversion (file by file)
- Rename .js → .ts/.tsx
- Add type annotations to function signatures
- Replace `any` with proper types
- Fix type errors

### Phase 3: Strict mode
- Enable strict: true in tsconfig
- Resolve remaining `any` types
- Add return type annotations
```

### Step 3: Execute the migration file by file

Process files incrementally, verifying each change:

**JavaScript → TypeScript:**

```typescript
// BEFORE: src/utils/api.js
const fetchUser = async (id) => {
  const response = await fetch(`/api/users/${id}`);
  if (!response.ok) throw new Error('User not found');
  return response.json();
};
module.exports = { fetchUser };

// AFTER: src/utils/api.ts
interface User {
  id: string;
  email: string;
  name: string;
  createdAt: string;
}

export const fetchUser = async (id: string): Promise<User> => {
  const response = await fetch(`/api/users/${id}`);
  if (!response.ok) throw new Error('User not found');
  return response.json() as Promise<User>;
};
```

**React class → hooks:**

```tsx
// BEFORE
class UserProfile extends React.Component {
  state = { user: null, loading: true };

  componentDidMount() {
    fetchUser(this.props.id).then(user =>
      this.setState({ user, loading: false })
    );
  }

  componentDidUpdate(prevProps) {
    if (prevProps.id !== this.props.id) {
      this.setState({ loading: true });
      fetchUser(this.props.id).then(user =>
        this.setState({ user, loading: false })
      );
    }
  }

  render() {
    if (this.state.loading) return <Spinner />;
    return <div>{this.state.user.name}</div>;
  }
}

// AFTER
function UserProfile({ id }: { id: string }) {
  const [user, setUser] = useState<User | null>(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    setLoading(true);
    fetchUser(id).then(user => {
      setUser(user);
      setLoading(false);
    });
  }, [id]);

  if (loading) return <Spinner />;
  return <div>{user?.name}</div>;
}
```

**Vue 2 → Vue 3 (Options → Composition API):**

```vue
<!-- BEFORE: Vue 2 Options API -->
<script>
export default {
  data() {
    return { count: 0, items: [] };
  },
  computed: {
    total() { return this.items.reduce((s, i) => s + i.price, 0); }
  },
  mounted() { this.fetchItems(); },
  methods: {
    async fetchItems() {
      this.items = await api.getItems();
    }
  }
};
</script>

<!-- AFTER: Vue 3 Composition API -->
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue';

const count = ref(0);
const items = ref<Item[]>([]);
const total = computed(() => items.value.reduce((s, i) => s + i.price, 0));

async function fetchItems() {
  items.value = await api.getItems();
}

onMounted(fetchItems);
</script>
```

### Step 4: Update dependencies and configuration

```bash
# For JS→TS
npm install -D typescript @types/node @types/react
npx tsc --init

# For Vue 2→3
npm install vue@3 @vue/compiler-sfc
npm uninstall vue-template-compiler

# Update import paths, build config (vite/webpack), and aliases
```

### Step 5: Verify after migration

After each file or batch:
1. Run the type checker: `npx tsc --noEmit`
2. Run existing tests: `npm test`
3. Run the linter: `npx eslint src/`
4. Build the project: `npm run build`

Report any errors and fix them before proceeding.

## Examples

### Example 1: Migrate an Express.js project from JavaScript to TypeScript

**User request:** "Convert my Express app from JS to TS"

**Actions taken:**
1. Analyzed project: 23 JS files, Express 4.18, no existing types
2. Installed TypeScript, `@types/express`, `@types/node`, `ts-node`
3. Created `tsconfig.json` with `allowJs: true` for incremental migration
4. Renamed files `.js` → `.ts` starting from leaf modules (utils, models)
5. Added interfaces for request/response bodies
6. Converted `require` → `import`, `module.exports` → `export`
7. Added type annotations to route handlers

**Result:**
```
Files migrated: 23/23
Type errors fixed: 41
New interfaces created: 8 (User, Product, Order, ApiError, etc.)
Build: ✅ passing
Tests: ✅ 34/34 passing
```

### Example 2: Update deprecated React Router v5 to v6

**User request:** "Upgrade our app from React Router 5 to 6"

**Actions taken:**
1. Identified 12 files using React Router v5 APIs
2. Applied transformations:
   - `<Switch>` → `<Routes>`
   - `<Route component={X}>` → `<Route element={<X />}>`
   - `useHistory()` → `useNavigate()`
   - `<Redirect to="/">` → `<Navigate to="/" replace>`
   - Nested routes restructured with `<Outlet />`
3. Updated `react-router-dom` to v6.20

**Before/After:**
```tsx
// v5
<Switch>
  <Route exact path="/" component={Home} />
  <Route path="/users/:id" component={UserProfile} />
  <Redirect to="/" />
</Switch>

// v6
<Routes>
  <Route path="/" element={<Home />} />
  <Route path="/users/:id" element={<UserProfile />} />
  <Route path="*" element={<Navigate to="/" replace />} />
</Routes>
```

**Result:** 12 files updated, 0 type errors, all 28 tests passing.

## Guidelines

- Always migrate incrementally — one file or module at a time. Never attempt a big-bang rewrite.
- Create the migration plan before writing any code. Share it with the user for approval on large projects.
- For JS→TS, start with `strict: false` and `allowJs: true`, then tighten after all files are converted.
- Preserve existing behavior exactly. Migration should not change functionality.
- Run tests after every file migration. If tests break, fix before proceeding.
- When migrating frameworks, check the official migration guide first (e.g., Vue 2→3, Angular upgrade guide).
- Handle deprecated APIs by checking the library's changelog for the recommended replacement.
- For large codebases (100+ files), suggest migrating in phases across multiple PRs.
- Keep `require`/`import` style consistent within each file during migration — don't mix.
- Add type annotations starting with function signatures, then variables, then generics. Don't over-type.
More from TerminalSkills/skills