{"skill":{"slug":"vue-composition-analyzer","displayName":"Vue Composition Analyzer","summary":"Analyze Vue 3 Composition API usage — audit reactivity patterns, composable design, ref vs reactive choices, computed properties, watcher usage, and componen...","description":"---\nname: vue-composition-analyzer\ndescription: Analyze Vue 3 Composition API usage — audit reactivity patterns, composable design, ref vs reactive choices, computed properties, watcher usage, and component lifecycle hooks. Use when reviewing Vue 3 codebases, migrating from Options API, or enforcing Composition API best practices.\nmetadata:\n  tags: [\"vue\", \"vue3\", \"composition-api\", \"reactivity\", \"frontend\", \"code-quality\"]\n---\n\n# Vue Composition API Analyzer\n\nDeep analysis of Vue 3 Composition API usage. Detects anti-patterns in reactivity, composable design, watcher usage, lifecycle hooks, and component structure. Produces actionable findings with severity levels.\n\nUse when: reviewing Vue 3 quality, migrating from Options API, auditing composable architecture, or enforcing Composition API conventions.\n\n## Analysis Steps\n\n### 1. Project Discovery & API Style Census\n\n```bash\ncat package.json 2>/dev/null | jq '{vue: .dependencies.vue, nuxt: .dependencies.nuxt, pinia: .dependencies.pinia}'\nfind . -name \"*.vue\" -not -path '*/node_modules/*' | wc -l\n\n# Options API components\ngrep -rl \"export default {\" --include=\"*.vue\" . 2>/dev/null | while read f; do\n  grep -l \"data()\\|methods:\\|computed:\" \"$f\" 2>/dev/null\ndone | sort -u | head -20\n\n# Script setup (preferred) vs setup()\ngrep -rl \"<script setup\" --include=\"*.vue\" . 2>/dev/null | wc -l\ngrep -rl \"setup()\" --include=\"*.vue\" . 2>/dev/null | wc -l\n```\n\nClassify each component as Options API, `setup()`, or `<script setup>`. Flag mixed-style codebases.\n\n### 2. Reactivity Pattern Audit\n\n```bash\ngrep -rn \"ref(\\|reactive(\\|shallowRef\\|shallowReactive\" --include=\"*.vue\" --include=\"*.ts\" . 2>/dev/null | head -30\n\n# Reactivity loss: destructuring reactive without toRefs\ngrep -rn \"const {.*} = .*reactive\\|let {.*} = .*reactive\" --include=\"*.vue\" --include=\"*.ts\" . 2>/dev/null | head -15\n\n# toRefs usage\ngrep -rn \"toRefs\\|toRef(\" --include=\"*.vue\" --include=\"*.ts\" . 2>/dev/null | head -15\n```\n\nCheck for:\n- **Reactivity loss**: destructuring `reactive()` without `toRefs()` silently breaks reactivity\n- **ref vs reactive confusion**: `ref` for primitives and replaceable objects, `reactive` for stable complex objects\n- **Unnecessary reactive**: wrapping a primitive in `reactive({count: 0})` instead of `ref(0)`\n- **Missing shallowRef**: large objects that don't need deep reactivity (DOM elements, third-party instances)\n- **Double wrapping**: `ref(reactive({...}))` — always a bug\n\n### 3. Computed & Watcher Audit\n\n```bash\ngrep -rn \"computed(\" --include=\"*.vue\" --include=\"*.ts\" . 2>/dev/null | head -20\n\n# Side effects in computed\ngrep -A5 \"computed(\" --include=\"*.vue\" --include=\"*.ts\" . 2>/dev/null \\\n  | grep -i \"console\\.\\|fetch(\\|emit(\\|\\.value =\" | head -15\n\n# Watchers\ngrep -rn \"watch(\\|watchEffect(\" --include=\"*.vue\" --include=\"*.ts\" . 2>/dev/null | head -20\n\n# Immediate watchers (often replaceable with watchEffect)\ngrep -A10 \"watch(\" --include=\"*.vue\" --include=\"*.ts\" . 2>/dev/null | grep \"immediate: true\" | head -10\n\n# Deep watchers on large objects\ngrep -A10 \"watch(\" --include=\"*.vue\" --include=\"*.ts\" . 2>/dev/null | grep \"deep: true\" | head -10\n```\n\nFlag:\n- **Side effects in computed**: computed must be pure derivations, never mutate state or trigger I/O\n- **Non-reactive dependencies in computed**: reading `localStorage`/`window` won't trigger re-computation\n- **watch + immediate: true**: usually replaceable with `watchEffect()`\n- **deep: true on large objects**: performance risk, prefer watching specific nested paths\n- **Async watchers without onCleanup**: race conditions on rapid value changes\n\n### 4. Composable Design Review\n\n```bash\nfind . -name \"use*.ts\" -o -name \"use*.js\" -not -path '*/node_modules/*' 2>/dev/null | head -20\nfind . -path \"*/composables/*\" -not -name \"use*\" -not -path '*/node_modules/*' -name \"*.ts\" 2>/dev/null | head -10\n\n# Composables using component-specific APIs\ngrep -rn \"defineProps\\|defineEmits\\|useSlots\" --include=\"*.ts\" . 2>/dev/null | grep -i \"composable\\|use\" | head -10\n```\n\nEvaluate:\n- **Naming**: composables must start with `use` prefix\n- **Single responsibility**: one concern per composable\n- **Return type**: should return refs (not raw values) so consumers maintain reactivity\n- **Side effect cleanup**: event listeners, timers, subscriptions must clean up via `onUnmounted`\n- **Testability**: composables should be testable outside components\n\n### 5. Lifecycle & Resource Leaks\n\n```bash\n# onMounted without matching onUnmounted\nfor f in $(grep -rl \"onMounted\" --include=\"*.vue\" . 2>/dev/null); do\n  if ! grep -q \"onUnmounted\\|onBeforeUnmount\" \"$f\"; then\n    echo \"MISSING_CLEANUP: $f\"\n  fi\ndone | head -15\n\n# addEventListener without removeEventListener\ngrep -l \"addEventListener\" --include=\"*.vue\" --include=\"*.ts\" . 2>/dev/null | while read f; do\n  if ! grep -q \"removeEventListener\" \"$f\"; then echo \"LISTENER_LEAK: $f\"; fi\ndone | head -15\n\n# setInterval without clearInterval\ngrep -l \"setInterval\" --include=\"*.vue\" . 2>/dev/null | while read f; do\n  if ! grep -q \"clearInterval\" \"$f\"; then echo \"INTERVAL_LEAK: $f\"; fi\ndone | head -10\n```\n\n### 6. Props & Dependency Injection\n\n```bash\ngrep -rn \"defineProps\" --include=\"*.vue\" . 2>/dev/null | head -15\ngrep -A5 \"defineProps(\\[\" --include=\"*.vue\" . 2>/dev/null | head -15\ngrep -rn \"provide(\\|inject(\" --include=\"*.vue\" --include=\"*.ts\" . 2>/dev/null | head -15\n```\n\nFlag:\n- **Array-style defineProps**: `defineProps(['foo'])` has no type safety; use object or TypeScript generic syntax\n- **Untyped provide/inject**: use `InjectionKey<T>` for type safety\n- **Prop mutation**: directly modifying props (Vue warns at runtime, static analysis catches earlier)\n\n## Output Template\n\n```markdown\n# Vue Composition API Analysis — [Project Name]\n\n## Summary\n- Components: N | Script setup: N% | Options API remaining: N\n- Critical: N | Warnings: N\n\n## API Style Migration\n| Style | Count | Files |\n|-------|-------|-------|\n| `<script setup>` | N | — |\n| `setup()` function | N | file1.vue |\n| Options API | N | legacy1.vue |\n\n## Critical Findings\n### [C1] Reactivity Loss — destructuring reactive without toRefs\n- **File**: src/components/UserForm.vue:24\n- **Code**: `const { name, email } = userState`\n- **Fix**: `const { name, email } = toRefs(userState)`\n\n### [C2] Side Effect in Computed\n- **File**: src/composables/useCart.ts:45\n- **Fix**: Move localStorage write to a watcher\n\n## Warnings\n### [W1] Missing Cleanup — addEventListener in onMounted, no onUnmounted\n### [W2] Deep Watcher on 1000+ item array — watch specific properties instead\n\n## Composable Health\n| Composable | Issues |\n|-----------|--------|\n| useAuth | Returns raw values instead of refs |\n| useCart | No cleanup for event subscription |\n\n## Recommendations\n1. Convert N Options API components to `<script setup>`\n2. Add `InjectionKey<T>` types to provide/inject pairs\n3. Replace `watch(..., { immediate: true })` with `watchEffect()` in N places\n4. Add `onUnmounted` cleanup to N components with resource subscriptions\n```\n\n## Anti-Pattern Quick Reference\n\n| Anti-Pattern | Severity | Description |\n|-------------|----------|-------------|\n| Destructure reactive | Critical | Breaks reactivity silently |\n| Side effects in computed | Critical | Non-deterministic, SSR-incompatible |\n| ref(reactive(...)) | Critical | Double-wrapping, confusing unwrap |\n| Missing watcher cleanup | High | Memory leaks, race conditions |\n| Deep watch large objects | Medium | Performance degradation |\n| watch + immediate | Low | Prefer watchEffect |\n| Array-style defineProps | Low | No type safety or defaults |\n\n## Tips\n\n- Use Vue DevTools \"Performance\" tab to visualize reactive dependency chains\n- Run `npx vue-tsc --noEmit` to type-check all SFC files including template expressions\n- Consider `effectScope()` in composables that create many watchers/effects for batch cleanup\n- Nuxt 3: check `useAsyncData`/`useFetch` for proper key usage and deduplication\n","tags":{"latest":"1.0.0"},"stats":{"comments":0,"downloads":359,"installsAllTime":13,"installsCurrent":0,"stars":0,"versions":1},"createdAt":1777685754398,"updatedAt":1778492826217},"latestVersion":{"version":"1.0.0","createdAt":1777685754398,"changelog":"Initial release of **vue-composition-analyzer** — a tool for auditing Vue 3 Composition API usage and enforcing best practices.\n\n- Deep static analysis of reactivity patterns, composable design, computed and watcher usage, and lifecycle resource cleanup.\n- Detects anti-patterns such as reactivity loss, side effects in computed, wrong ref/reactive usage, and memory/resource leaks.\n- Summarizes component API styles (Options API, `setup()`, `<script setup>`) and flags mixed-style codebases.\n- Provides actionable findings with severity levels (Critical, Warning, etc.) and recommendations for code improvements.\n- Output includes component statistics, specific issues found, and remediation tips for improving overall code quality.","license":"MIT-0"},"metadata":null,"owner":{"handle":"charlie-morrison","userId":"s17cttbdxry5kkyafjw983mq8s83p4y3","displayName":"charlie-morrison","image":"https://avatars.githubusercontent.com/u/271589886?v=4"},"moderation":null}