vue3-migration
v1.9.0
Published
Automatically migrate Vue 2 mixins to Vue 3 composables — data, computed, methods, watchers, lifecycle hooks, and all
Downloads
2,791
Maintainers
Readme
vue3-migration
Stop rewriting mixins by hand. Automatically migrate your Vue 2 mixins to Vue 3 composables — data, computed, methods, watchers, lifecycle hooks, and all.
One command. Full before/after diffs. No files changed until you say yes.
The Problem
Every Vue 2 project sitting on mixins is a migration bottleneck. Rewriting them by hand means:
- Reading every mixin, understanding every member, tracing every
this.reference - Creating composable files, converting
this.xtox.value, rewriting lifecycle hooks - Updating every component that imports the mixin — removing imports, adding
setup(), destructuring return values - Doing it again for the next mixin. And the next. Across dozens (or hundreds) of components.
vue3-migration does all of this automatically. It reads your mixins, generates (or patches) composables, rewrites your components, and shows you a clean diff before touching a single file.
Install
npm install -D vue3-migrationRequires Node.js >= 14 and Python >= 3.9 (used internally for AST analysis).
Quick Start
npx vue3-migrationThat's it. An interactive menu walks you through four options:
| # | Mode | What it does |
|---|------|-------------|
| 1 | Full project | Migrates every component at once. Generates and patches composables, injects setup(), removes mixin imports. Shows a full change summary before writing. |
| 2 | Pick a component | Choose one component from a list. Migrate just that file. Low blast radius — perfect for large codebases. |
| 3 | Pick a mixin | Choose one mixin to fully retire. Updates the composable and every component that uses it. |
| 4 | Project status | Read-only scan. Generates a detailed markdown report: what's migrated, what's ready, what's blocked and why. |
Direct Commands
npx vue3-migration all # Full project migration
npx vue3-migration component src/components/Foo.vue # One component
npx vue3-migration mixin authMixin # Retire one mixin everywhere
npx vue3-migration status # Status report only
npx vue3-migration --root /path/to/project all # Run from outside project rootWhat It Actually Does
Generates composables from scratch
No existing composable? The tool creates one. Given a mixin like:
// mixins/authMixin.js
export default {
data() {
return { user: null, token: '' }
},
computed: {
isLoggedIn() { return !!this.user }
},
methods: {
async login(credentials) { /* ... */ }
},
mounted() {
this.checkSession()
}
}It generates:
// composables/useAuth.js
import { ref, computed, onMounted } from 'vue'
export function useAuth() {
const user = ref(null)
const token = ref('')
const isLoggedIn = computed(() => !!user.value)
async function login(credentials) { /* ... */ }
onMounted(() => {
checkSession()
})
return { user, token, isLoggedIn, login }
}Every this.x reference is rewritten. Lifecycle hooks are converted. Getter/setter computed properties are handled. Watch expressions with deep, immediate, and handler options are converted.
Patches incomplete composables
Already started writing composables manually? The tool detects what's missing and patches it — adds missing member declarations and updates the return statement. No duplicate work.
Rewrites components automatically
For every component using a mixin, the tool:
- Removes the mixin import line
- Adds the composable import
- Removes the mixin from the
mixins: [...]array - Injects a
setup()function with destructured composable calls - Converts lifecycle hooks to their Composition API equivalents
- Merges into existing
setup()if one already exists
Before:
<script>
import authMixin from '@/mixins/authMixin'
export default {
mixins: [authMixin],
data() { return { localState: true } }
}
</script>After:
<script>
import { useAuth } from '@/composables/useAuth'
export default {
setup() {
const { user, token, isLoggedIn, login } = useAuth()
return { user, token, isLoggedIn, login }
},
data() { return { localState: true } }
}
</script>Handles this.$ patterns
this.$nextTick(cb)→nextTick(cb)(auto-imports from'vue')this.$set(obj, key, val)→obj[key] = valthis.$delete(obj, key)→delete obj[key]this.$emit,this.$router,this.$store,this.$refs, and 15+ more patterns are detected and flagged with actionable migration guidance
Smart about what it can't automate
Some patterns need human judgment. The tool doesn't silently skip them — it flags them clearly:
this.$emit→ "UsedefineEmitsor passemitfromsetup()"this.$router/this.$route→ "ImportuseRouter()/useRoute()fromvue-router"this.$store→ "Import store directly from Pinia/Vuex"this.$refs→ "Use template refs withref()"- Mixin
props,inject,provide→ "Must usedefineProps()/inject()manually" - Nested mixins → "Transitive members may be missed"
filters→ "Removed in Vue 3 — convert to methods"- This-aliasing (
const self = this) → "Manual replacement needed"
Each warning is injected as a comment directly in the generated code, so nothing gets lost.
How It Works
Under the hood, the tool runs a three-phase pipeline — no files are modified until you confirm.
Phase 1 — Analyze. For every .vue file in your project, the tool parses the <script> block, finds which mixins it uses, extracts the mixin's members (data, computed, methods, watch, lifecycle hooks), and searches for a matching composable (authMixin → useAuth.js). It then classifies each member: is it declared in the composable? Is it returned? Does the component override it? This classification determines whether the mixin is ready to migrate, or blocked (and why).
Phase 2 — Prepare composables. Blocked entries get fixed automatically. If a composable is missing members, they're patched in. If no composable exists at all, one is generated from the mixin source — converting data() to ref(), computed to computed(), methods to plain functions, and lifecycle hooks to their Composition API equivalents. All this.x references are rewritten.
Phase 3 — Inject into components. For each ready mixin, the tool removes the mixin import, removes it from the mixins: [...] array, adds the composable import, and injects a setup() function that destructures and returns the composable's members.
Every change is collected as a plan object — original content paired with new content. The CLI shows you a unified diff of the full plan, and only writes files after you type y.
For the full architectural deep-dive — parsing internals, data models, warning system, and module-by-module breakdown — see ARCHITECTURE.md.
Safety Features
Nothing changes until you confirm. Every migration mode shows a complete change summary and asks for explicit y/n confirmation before writing any file.
Full diff report. Every migration writes a migration-diff-<timestamp>.md with unified diffs of every changed file — composables and components.
Override-aware. If a component defines its own data, computed, or methods that overlap with a mixin, the tool knows the component's version takes precedence and won't inject duplicates.
Confidence scoring. Generated composables include a confidence header:
- HIGH — Clean conversion, no remaining issues
- MEDIUM — Has TODOs or warnings that need review
- LOW — Remaining
this.references or structural issues
Blocked status. If a mixin has structural issues — no matching composable, missing member declarations, or members not in the return statement — the tool marks it as blocked and tells you exactly why. Patterns that need manual attention (like this.$emit or this.$router) don't block migration — the tool proceeds and injects warning comments directly in the generated code so you know exactly what to fix.
What It Supports
| Mixin feature | Auto-converted |
|--------------|---------------|
| data() properties | ref() with default values |
| computed (simple) | computed(() => ...) |
| computed (get/set) | computed({ get, set }) |
| methods (sync & async) | Plain functions |
| watch (handler + options) | watch() with deep/immediate |
| mounted, created, etc. | onMounted(), inlined, etc. |
| beforeDestroy / destroyed | onBeforeUnmount() / onUnmounted() |
| this.x references | x.value or x (context-aware) |
| this.$nextTick | nextTick() |
| this.$set / this.$delete | Direct assignment / delete |
| Multiple mixins per component | Multiple composable calls |
| Existing setup() function | Merges (doesn't overwrite) |
| @/ and relative imports | Resolved and rewritten |
Recommended Workflow for Large Projects
- Run
npx vue3-migration statusto see the full picture — which mixins are used where, what's ready, what's blocked. - Start with "Pick a component" (option 2). Migrate one component, test it, commit.
- When a mixin is fully covered, use "Pick a mixin" (option 3) to retire it across all components at once.
- Repeat until the status report shows zero remaining mixins.
For smaller projects, "Full project" (option 1) handles everything in one pass.
How It Finds Your Files
The tool searches recursively from your project root (or --root if specified):
- Components: Every
.vuefile in the project tree - Mixin files: Resolved from import paths (
@/, relative, bare imports) - Composables: First checks directories named
composables/(case-insensitive), then falls back to searching the entire project for anyuse*.js/use*.tsfile
Skips node_modules/, dist/, .git/, and __pycache__/ automatically.
Requirements
- Node.js >= 14 (for the CLI wrapper)
- Python >= 3.9 (for the migration engine — auto-detected on your PATH)
License
MIT
