-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy path.cursorrules
241 lines (210 loc) · 7.25 KB
/
.cursorrules
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
# Overview
You are a TypeScript, Node.js, Wasp, React, Shadcn UI, Radix UI, Zod, React Hook Form, and TailwindCSS expert with deep knowledge on each topic.
This project is a Wasp app template. It includes a bunch of examples and guides.
You are helping a developer set up the project and add new pages while using the project's conventions as an example.
# Wasp Rules
## main.wasp and general
- Wasp is on ^0.15.x
- Try not to remove things from main.wasp unless the user asks you to, if you do, use the existing code as an example.
- When you add a route, page, action, query, or job to main.wasp the app will not work
until you add the related file to the directory, so make sure to add one.
## Actions and Queries
- Store actions and queries together in feature/operations.ts
- For queries, always use the satisfies keyword so we can dynamically type the response later with
```
export interface ExampleProps {
example: Awaited<ReturnType<typeof getExample>>
}
```
## Routing
- Use Wasp's built-in Link component from wasp/client/router for internal links
- Use <a> elements for external links and anchor links
# Import Rules
## Wasp Imports
- Always import Wasp functions from 'wasp' package, not '@wasp'
✓ import { Task } from 'wasp/entities'
✓ import { type GetTasks } from 'wasp/server/operations'
✓ import { getTasks, useQuery } from 'wasp/client/operations'
✗ import { ... } from '@wasp/...'
## React Imports
- Do not import React from 'react' because it's automatically imported
- Import hooks directly from packages:
✓ import { useState } from 'react'
✗ import React, { useState } from 'react'
## Motion Imports
- Import from 'motion/react' instead of 'framer-motion'
✓ import { motion } from 'motion/react'
✗ import { motion } from 'framer-motion'
## General Imports
- Never use the @ alias, use relative imports only.
- When the user manually updates the import you suggest, please don't change it back.
# Schema & Data Models
## Prisma Schema
- Add new entities (models) to 'schema.prisma', NOT 'main.wasp'
- Use meaningful names for models and fields
- Add proper relations between models
- Include field validations where needed
- Provide default values where it makes common sense (fields like createdAt and updatedAt can default to now)
- Follow Prisma naming conventions:
✓ Model names: PascalCase (User, Task)
✓ Field names: camelCase (firstName, createdAt)
# Dependencies & Package Management
## Installation
- Do NOT add dependencies to 'main.wasp'
- Install dependencies via 'npm install' instead
- Use exact versions for critical dependencies
- Keep dependencies up to date
- Keep dependencies in package.json
# TypeScript
## General
- Always use TypeScript for new files
- Enable strict mode in tsconfig
- Define proper types for all variables and functions
- Use type inference when types are obvious
- Use .tsx extension for React components
- Use .ts extension for non-React files
- Define proper types for operations:
✓ type GetTasks = QueryFn<void, Task[]>
✓ type CreateTask = ActionFn<{ description: string }, Task>
## Type Definitions
- Place shared types in the same file they will be used in.
- Use interface for object types that can be extended
- Use type for unions, intersections, and mapped types
# Styling
## Icon library
- Use @phosphor-icons/react for icons
## Tailwind CSS
- Use Tailwind CSS for styling
- Do not use inline styles unless absolutely necessary
- Follow semantic color naming:
✓ text-destructive, text-muted-foreground
✗ text-red-500, text-gray-600
- Let the theme handle colors and spacing
- Follow mobile-first approach
## Lint and format
- Follow these prettier rules
{
"arrowParens": "avoid",
"bracketSpacing": true,
"htmlWhitespaceSensitivity": "css",
"insertPragma": false,
"bracketSameLine": false,
"jsxSingleQuote": true,
"printWidth": 80,
"proseWrap": "always",
"quoteProps": "as-needed",
"requirePragma": false,
"semi": false,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "all",
"useTabs": false,
}
- Use ' for apostrophes in content
## Component Structure
- Group related styles together
- Use meaningful class names
- Leverage Tailwind's responsive utilities
- Use good UI spacing via the tailwind gap class.
# State Management
- Use Wasp's built-in auth state management
- Use Wasp's built-in useQuery for server state
- Use local state (useState) for UI state
- Use context sparingly and only for truly global state
- Use appropriate hooks for state management
- Keep state as local as possible
- Avoid prop drilling with context when needed
- Implement proper error boundaries
# Performance
- Use proper key props in lists
- Use precaching for routes where it makes sense
- Use proper caching strategies
- Implement proper data fetching patterns
- Use proper React Query configurations
- Use proper code splitting
# Accessibility
- Use semantic HTML elements
- Include proper ARIA labels
- Ensure keyboard navigation works
- Maintain sufficient color contrast
- Support reduced motion preferences
- Support screen readers
- Implement proper ARIA attributes
# Error Handling
- Implement proper error boundaries
- Use type-safe error handling
- Log errors appropriately
- Provide user-friendly error messages
- Handle operation errors appropriately:
✓ try { await createTask(...) } catch (e) { handleError(e) }
# Project Structure
- Organize code by features (vertically) rather than technical layers
✓ src/tasks/{actions.ts,queries.ts,TaskList.tsx}
✓ src/auth/{actions.ts,LoginPage.tsx}
✗ src/client/components/TaskList.tsx
- Keep source files in src/
- Keep static files in public/
- Use feature-based organization: ```
src/
├── tasks/
│ ├── actions.ts
│ ├── queries.ts
│ └── TaskList.tsx
├── auth/
│ ├── actions.ts
│ ├── LoginPage.tsx
│ └── RegisterPage.tsx
└── shared/
└── utils.ts ```
# Forms
- Use React Hook Form for form actions like useForm and zodresolver
- Use Zod to validate form schemas
- Here is an example form using both
```
import { zodResolver } from "@hookform/resolvers/zod"
import { useForm } from "react-hook-form"
import { z } from "zod"
import { Button } from "@/components/ui/button"
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form"
import { Input } from "@/components/ui/input"
const formSchema = z.object({
username: z.string().min(2, {
message: "Username must be at least 2 characters.",
}),
})
export function ProfileForm() {
// ...
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
<FormField
control={form.control}
name="username"
render={({ field }) => (
<FormItem>
<FormLabel>Username</FormLabel>
<FormControl>
<Input placeholder="shadcn" {...field} />
</FormControl>
<FormDescription>
This is your public display name.
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<Button type="submit">Submit</Button>
</form>
</Form>
)
}
```
- Type validate all form inputs.