Skip to content

RoninForge/roninforge-vue-nuxt

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

roninforge-vue-nuxt

License: MIT

Cursor plugin for Vue 3.5 + Nuxt 4 + TypeScript. Pinned to vue ^3.5.34 and nuxt ^4.4.5. Teaches the Vue 3.5 trio that LLMs trained pre-September 2024 do not know (useTemplateRef('name'), useId(), reactive props destructuring with defaults), the Nuxt 4 app/ srcDir flatten (with server/, shared/, public/, modules/ at the root), the useAsyncData / useFetch shallowRef + singleton-by-key behaviour, Pinia 3 setup stores, VueUse 14 foundational composables, and Vitest 4 + Vue Test Utils 2 + @nuxt/test-utils. Catches 38 LLM regressions with BAD / CORRECT TypeScript + Vue pairs.

The Problem

LLMs trained on Vue 3.0 / 3.2 / 3.3 / 3.4 and Nuxt 2 / 3.x emit code that does not match a fresh Vue 3.5 + Nuxt 4 install. They write:

  • ref<HTMLInputElement | null>(null) template refs (Vue 3.5 introduced useTemplateRef('name'))
  • withDefaults(defineProps<{}>(), {...}) wrapping (Vue 3.5 made it redundant - destructure with defaults is the documented form)
  • toRefs(props) boilerplate after defineProps (Vue 3.5 destructure is reactive)
  • Options API blocks (data() / methods / mounted) in new components
  • Vue.use(plugin) / new Vue({}) (Vue 2 globals; Vue 3 has none)
  • {{ x | currency }} template filters (removed in Vue 3)
  • .sync modifier (use v-model:propName + defineModel)
  • created() / beforeDestroy() lifecycle hooks (renamed / removed)
  • Vuex createStore for new projects (Pinia is the official state library)
  • Pinia options-style stores (defineStore('id', { state, getters, actions }) instead of the setup form)
  • MODULE-SCOPE ref() in a Nuxt composable - cross-request SSR leak; CRITICAL bug
  • Manual import { ref, computed } from 'vue' inside a Nuxt SFC (auto-imported)
  • pages/, components/, composables/ at the project root in a Nuxt 4 project (must be under app/)
  • future: { compatibilityVersion: 3 } in a Nuxt 4 nuxt.config.ts (flag was REMOVED in Nuxt 4)
  • store/ directory (Vuex / Nuxt 2 naming; should be stores/ and in Nuxt 4 app/stores/)
  • readBody(event) in a GET handler (GET has no body in HTTP)
  • server/api/posts.ts without method suffix (.get.ts / .post.ts / etc.)
  • throw new Error('...') from a server route (use createError({ statusCode, statusMessage, data }))
  • queryContent('/blog') in a @nuxt/content v3 project (use queryCollection('posts').path(...).first())
  • Mutating data.value.someField on a useAsyncData payload (Nuxt 4 default is shallowRef)
  • Two useFetch('/api/x') calls without explicit key: (Nuxt 4 makes them singleton-by-key)
  • Watchers with await fetch(...) but no onWatcherCleanup() to abort (in-flight fetch leak)
  • Hand-rolled addEventListener + onUnmounted cleanup instead of VueUse useEventListener
  • Hand-rolled click-outside / debounce instead of VueUse onClickOutside / useDebounceFn
  • Static <Teleport to="#modal"> when the target may not exist yet (Vue 3.5 added <Teleport defer>)
  • Eager imports of below-the-fold heavy components (Vue 3.5 added lazy hydration via defineAsyncComponent({ hydrate: ... }))
  • vitest.config.ts without explicit pool: 'forks' (Vitest 4 default flipped from v3's 'threads')
  • Cypress component tests as the recommended path (Vitest + Vue Test Utils + @nuxt/test-utils is documented)

Why this plugin (vs the existing community Vue / Nuxt rules)

A handful of Vue and Nuxt rules already live on cursor.directory: vue.mdc, vue3-composition-api-cursorrules-prompt-file.mdc, vue-3-nuxt-3-development-cursorrules-prompt-file.mdc, vue-pinia-cursorrules-prompt-file.mdc, vue-claude-stack.mdc, and the largest single file sanjeed5/nuxt.mdc (387 lines). They are shallow and have three structural problems this plugin fixes:

  1. They pin nothing or pin pre-3.5 / pre-4. Across all of these, zero mentions of useTemplateRef, useId, reactive props destructure with defaults, Nuxt 4, the app/ srcDir layout, the shallowRef default on useAsyncData, the singleton-by-key behaviour, or onWatcherCleanup(). Vue 3.5 (Sep 2024) and Nuxt 4 (Jul 2025) post-date all of them. This plugin pins vue ^3.5.34 and nuxt ^4.4.5 explicitly.

  2. The largest single file (sanjeed5/nuxt.mdc, 387 lines) still teaches the store/ directory - deprecated for stores/ even in Nuxt 3, and app/stores/ in Nuxt 4. The vue-pinia-cursorrules-prompt-file.mdc teaches Pinia options-style stores rather than the documented setup-store shape.

  3. They ship flat .cursorrules text without globs, fixtures, skills, or an agent. This plugin ships:

    • 10 MDC rules with proper globs so the server-route check fires on server/api/**, the Pinia check on app/stores/**, the VueUse check on app/**/*.vue, etc.
    • 38 documented anti-patterns with BAD / CORRECT TypeScript + Vue pairs (the existing rules have zero)
    • 5 skills: /vue-new-component, /nuxt-new-server-route, /nuxt-new-pinia-store, /vue-nuxt-migrate-to-3-5-and-4, /vue-nuxt-validate
    • 1 reviewer agent with severity grouping (CRITICAL / ERROR / WARN / NIT)
    • 2 fixture projects: correct-sample (gold-standard Vue 3.5.34 + Nuxt 4.4.5 + Pinia 3 + Nuxt UI 4 + Zod) and anti-pattern-sample (16 distinct numbered violations across 6 files, pinned to vue ^3.3.0 + nuxt ^3.0.0 + vuex ^4.1.0 to demonstrate the v3.3 / v3 / Vuex hangover)

Install

Copy the rules, skills, and agent into your project's Cursor configuration. Back up your existing files first; cp -r will overwrite same-named rules.

git clone https://github.com/RoninForge/roninforge-vue-nuxt.git

# Use -n to avoid clobbering an existing customised rule of the same name.
cp -rn roninforge-vue-nuxt/rules/*  your-project/.cursor/rules/
cp -rn roninforge-vue-nuxt/skills/* your-project/.cursor/skills/
cp -rn roninforge-vue-nuxt/agents/* your-project/.cursor/agents/

Or vendor the whole repo as a git submodule under your-project/.cursor/plugins/. Refer to the Cursor plugin docs for the current global-install path on your Cursor version.

What's Included

Rules (10 files)

Rule Scope (globs) What it does
vue-nuxt-anti-patterns **/*.vue,**/*.ts,**/*.js,nuxt.config.ts 38 LLM regressions with BAD / CORRECT pairs. Each entry annotates which Vue / Nuxt version dropped or renamed the BAD form
vue-3-5-core **/*.vue,**/*.ts,**/*.js Vue 3.5 trio: useTemplateRef, useId, reactive props destructure. Plus onWatcherCleanup, <Teleport defer>, lazy hydration
vue-script-setup **/*.vue <script setup lang="ts"> canonical: defineProps / defineEmits (tuple) / defineModel / defineSlots / defineOptions / defineExpose, generic components, no setup() nesting
nuxt-4-core nuxt.config.ts,nuxt.config.js,app/**/*,server/**/*,shared/**/*,modules/**/* app/ srcDir for client code, server/ shared/ public/ modules/ at root, no compatibilityVersion, split TypeScript projects, auto-imports
nuxt-data-fetching app/**/*.vue,app/**/*.ts,server/**/*.ts,composables/**/*.ts,app/composables/**/*.ts useFetch / useAsyncData Nuxt 4 shallowRef default, singleton-by-key, reactive keys, transform / pick, error handling, lazy / useLazyFetch
nuxt-state-management app/stores/**/*.ts,stores/**/*.ts,app/composables/**/*.ts,composables/**/*.ts,app/**/*.vue Decision tree: ref / useState / Pinia. Why module-scope ref() in a Nuxt composable is a SSR cross-request leak
nuxt-server-routes server/api/**/*.ts,server/routes/**/*.ts,server/middleware/**/*.ts,server/utils/**/*.ts Filename method suffixes, defineEventHandler, getValidatedQuery + readValidatedBody with Zod, createError, server middleware
pinia app/stores/**/*.ts,stores/**/*.ts Pinia 3 setup-store shape, storeToRefs, no mapState / mapGetters, acceptHMRUpdate, $patch / $reset / $subscribe, isolated unit testing
vueuse app/**/*.vue,app/**/*.ts,components/**/*.vue,composables/**/*.ts VueUse 14 foundational composables (useEventListener, onClickOutside, useDebounceFn, useIntersectionObserver, useDark, useStorage)
vue-nuxt-testing tests/**/*,test/**/*,**/*.test.ts,**/*.spec.ts,vitest.config.ts Vitest 4 + Vue Test Utils 2 + @nuxt/test-utils + Playwright. Vitest 4 default pool 'forks' (was 'threads' in v3)

Skills (5 commands)

Skill Command What it does
New component /vue-new-component Scaffold a <script setup lang="ts"> component using useTemplateRef, useId, reactive props destructure with defaults, defineEmits tuple form, defineExpose, plus matching Vitest test
New server route /nuxt-new-server-route Scaffold a server/api/<resource>/<segment>.<method>.ts handler with Zod-validated query / body, createError for failures, typed return value referencing ~~/shared/types, plus matching @nuxt/test-utils test
New Pinia store /nuxt-new-pinia-store Scaffold a defineStore('name', () => { ... }) setup store with state refs + computed getters + action functions, acceptHMRUpdate block, plus a test using setActivePinia(createPinia())
Migrate to 3.5 + 4 /vue-nuxt-migrate-to-3-5-and-4 Stage-by-stage migration: bump pins, move srcDir to app/, drop compatibilityVersion, rename store/ to stores/, replace Vuex with Pinia setup stores, swap ref(null) for useTemplateRef, drop withDefaults / toRefs(props), add onWatcherCleanup(), add server route method suffixes, swap queryContent for queryCollection, set vitest pool: 'forks'
Validate /vue-nuxt-validate Run vue-tsc + ESLint + Vitest in parallel and grep-audit the codebase for tracked Vue 3.5 + Nuxt 4 anti-patterns

Agent (1 subagent)

Agent What it does
vue-nuxt-reviewer Reviews Vue 3.5 + Nuxt 4 + TypeScript code by severity. CRITICAL: module-scope ref() in Nuxt composable, readBody in GET handler, throw new Error from server route, Vuex createStore, compatibilityVersion: 3 in Nuxt 4 config, ref(null) template refs, pages/components/composables at root in Nuxt 4. ERROR: Options API in new SFC, withDefaults wrapping, toRefs(props), Pinia options-style, server route without method suffix, queryContent in v3 project, store/ directory, manual Vue imports inside Nuxt SFC. WARN: missing onWatcherCleanup, useFetch without explicit key, mutating data.value.* on shallowRef, missing useId for ARIA, hand-rolled addEventListener over useEventListener, hand-rolled click-outside / debounce, eager imports of heavy below-fold components, static <Teleport> to lazy target, Vitest pool not declared. NIT: missing defineExpose surface, missing storeToRefs around state destructure, missing acceptHMRUpdate

Fixtures

tests/fixtures/correct-sample/ is a slim Vue 3.5.34 + Nuxt 4.4.5 + Pinia 3 + Nuxt UI 4 + Zod project demonstrating the gold-standard shape: app/ srcDir layout (with server/, shared/, public/ at root), <script setup lang="ts"> everywhere, useTemplateRef('name') instead of ref(null) for DOM handles, useId() for SSR-safe ARIA, reactive props destructure with defaults (no withDefaults, no toRefs(props)), onWatcherCleanup() to abort in-flight fetches inside watchers, useAsyncData with explicit keys + reactive keys for route params (respecting Nuxt 4's shallowRef default), Pinia 3 setup stores with acceptHMRUpdate, server routes with method-suffixed filenames using getValidatedQuery / readValidatedBody + Zod and createError({ statusCode, statusMessage, data }) for failures, and a shared/types.ts referenced from both client and server.

tests/fixtures/anti-pattern-sample/ is the inverse. Every file violates a numbered anti-pattern. package.json pins vue ^3.3.0 + nuxt ^3.0.0 + vuex ^4.1.0 on purpose - the v3.3 / v3 / Vuex combination is a valid peer-dep window where Vuex was the only state library and the Nuxt 3 srcDir layout was the default. Tracked violations: #1 (Options API), #3 (filter), #4 (event-bus shape), #6 (created / beforeDestroy), #7 ($set), #8 ($refs), #11 (Vuex), #13 (module-scope ref leak), #14 (manual Vue import), #17 (defineComponent in SFC), #22 (readBody in GET), #23 (no method suffix), #24 (throw new Error), #25 (root-level pages/components/composables), #26 (compatibilityVersion: 3), #27 (store/ directory). The fixture's README maps every violation to file:line.

Versioning

Rules target vue ^3.5.34 on nuxt ^4.4.5 with Node 20+ (22 recommended). Most patterns work back to Vue 3.4 / Nuxt 3.12 with the deltas called out inline. Where the rule cites a version (useTemplateRef 3.5, useId 3.5, reactive props destructure 3.5, onWatcherCleanup 3.5, <Teleport defer> 3.5, lazy hydration 3.5, app/ srcDir Nuxt 4 default, shallowRef default Nuxt 4, singleton-by-key Nuxt 4, compatibilityVersion removed Nuxt 4), verify against the changelog for the version you have installed before adopting.

License

MIT - see LICENSE

Links

About

Cursor plugin for Vue 3.5 + Nuxt 4. Catches 38 LLM regressions (Options API in new code, ref(null) template refs vs useTemplateRef, toRefs/withDefaults redundancy, module-scope ref SSR leaks, readBody in GET, store/ vs app/stores/, etc) with BAD/CORRECT pairs.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages