-
Notifications
You must be signed in to change notification settings - Fork 35
Expand file tree
/
Copy path.cursorrules
More file actions
361 lines (297 loc) · 16.8 KB
/
.cursorrules
File metadata and controls
361 lines (297 loc) · 16.8 KB
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
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
# General Info
This is a Wasp full-stack web application project. Always consider the following background information and rules when planning and implementing app features.
## Quick Reference
### Common Patterns
- Define app structure in `main.wasp` or `main.wasp.ts`
- Define data models in `schema.prisma`
- Group feature code in `src/features/{featureName}`
- Group feature config definitions (e.g. routes, pages, operations, etc.) into sections within the Wasp config file using the `//#region` directive.
- e.g. `//#region {FeatureName}` ... `//#endregion`
- Use operations (queries/actions) for client-server communication
- Import from 'wasp/...' not '@wasp/...'
- Document features in `./ai/docs` with:
- One markdown file per feature (e.g. `./ai/docs/{featureName}.md`)
- Operations specifications and data models
- User workflows and business logic
- Update documentation when implementing feature changes
- Reference the relevant `./ai/docs` files when writing or modifying feature code
### Common Issues
- If client imports aren't working, check path format ('wasp/...' not '@wasp/...')
- Wasp config file imports should start with "@src/"
- Client-side Wasp Actions should be called directly WITHOUT using the useAction hook. Instead, use async/await within event handlers.
- e.g. `import { deleteTask } from 'wasp/client/operations'`, `await deleteTask({ taskId })`.
- If entities aren't available in operations, check they're listed in the operation definition
- If auth isn't working, ensure auth config is properly set up in main.wasp
## Background Information
### What is Wasp
- Wasp (Web Application SPecification language) is a declarative, statically typed, domain-specific language (DSL) for building modern, full-stack web applications.
- Unlike traditional frameworks that are sets of libraries, Wasp is a simple programming language that understands web app concepts and generates code for you.
- Wasp integrates with React (frontend), Node.js (backend), and Prisma (database ORM) to create full-stack web applications with minimal boilerplate.
- The Wasp compiler reads your declarative configuration and generates all the necessary code for a working web application.
- For the most up-to-date and comprehensive information, always refer to the [official Wasp documentation](https://wasp-lang.com/docs).
### Wasp Project Structure
- A Wasp project consists of a `main.wasp` (or `main.wasp.ts`) file in the root directory that defines the app's configuration.
- The `schema.prisma` file in the root directory defines your database models.
- Your custom code lives in the `src/` directory, which contains client-side and server-side code.
- Wasp generates additional code that connects everything together when you run your app.
- Feature code should be organized in `src/features/{featureName}` directories.
### The main.wasp File
- The `main.wasp` file is the central configuration file that defines your application structure.
- It contains declarations for app settings, pages, routes, authentication, database entities, and operations (queries and actions).
- Example structure:
```
app myApp {
wasp: {
version: "^0.16.0"
},
title: "My App",
}
route HomeRoute { path: "/", to: HomePage }
page HomePage {
component: import { HomePage } from "@src/client/pages/HomePage.tsx"
}
query getTasks {
fn: import { getTasks } from "@src/server/queries.js",
entities: [Task]
}
```
### Wasp Database and Entities
- Wasp uses Prisma for database access, with models defined in `schema.prisma`.
- Prisma models automatically become Wasp Entities that can be used in operations.
- Wasp reads the Prisma schema to understand your data model and generate appropriate code.
- Example Prisma model:
```
model Task {
id Int @id @default(autoincrement())
description String
isDone Boolean @default(false)
user User @relation(fields: [userId], references: [id])
userId Int
}
```
### Wasp Operations
- Operations are how Wasp handles client-server communication.
- Queries (read operations) and Actions (write operations) are defined in the main.wasp file.
- Operations automatically handle data fetching, caching, and updates.
- Operations reference entities to establish proper access patterns and dependencies.
- Example query and action:
```
query getTasks {
fn: import { getTasks } from "@src/server/operations",
entities: [Task]
}
action createTask {
fn: import { createTask } from "@src/server/operations",
entities: [Task]
}
```
### Wasp Auth
- Wasp provides built-in authentication with minimal configuration.
- Auth can be configured with username/password, social providers (Google, GitHub, etc.), or verified email and password .
- Wasp generates all necessary auth routes, middleware, and UI components.
- Example auth configuration:
```
app myApp {
// ... other config
auth: {
userEntity: User,
methods: {
usernameAndPassword: {},
google: {}
},
onAuthFailedRedirectTo: "/login"
}
}
```
- Wasp Auth methods handle essential identity fields internally.
- Your Prisma `User` model (specified in `auth.userEntity`) typically only needs the `id` field for Wasp to link the auth identity.
- Avoid adding `email`, `emailVerified`, or `password` fields to your Prisma `User` model unless you have specific customization needs that require overriding Wasp's default behavior.
- Add other *non-auth* related fields as needed (e.g., profile info, relations)
- Note on Dummy Email Provider: When `emailSender: { provider: Dummy }` is configured in `main.wasp`, Wasp does not send actual emails. Instead, the content of verification/password reset emails, including the clickable link, will be printed directly to the server console where `wasp start` is running.
### Advanced Features
#### Jobs and Workers
- Wasp supports background jobs with the `job` declaration in main.wasp.
- Jobs can be scheduled to run at specific intervals or triggered programmatically.
- Example:
```
job emailSender {
executor: PgBoss,
perform: {
fn: import { sendEmail } from "@src/server/jobs/emailSender.js"
},
entities: [User, Email]
}
```
#### API Routes
- Custom API endpoints can be defined with the `api` declaration.
- Useful for webhooks, or third-party integrations.
- Example:
```
api stripeWebhook {
fn: import { handleStripeWebhook } from "@src/server/apis/stripe.js",
httpRoute: (POST, "/stripe-webhook"),
entities: [User, Payment]
}
```
#### Middleware
- Wasp supports custom middleware for both API routes and pages.
- Middleware can be used for logging, authentication, or request transformation.
- Example:
```
api myApi {
fn: import { myApiHandler } from "@src/server/apis.js",
middlewares: [myMiddleware]
}
middleware myMiddleware {
fn: import { myMiddlewareFn } from "@src/server/middleware.js"
}
```
### Deployment
- Wasp applications can be deployed to various hosting providers.
- Wasp has a built-in one-command deployment to fly.io, e.g. `wasp deploy fly`. See the [Wasp CLI Deployment docs](https://wasp-lang.com/docs/deployment/fly) for more information.
### Error Handling
- Client-side: Use try/catch blocks with async/await
- Server-side: Use try/catch in operation functions and return appropriate error responses
- Use HTTP status codes appropriately in API responses
- Consider implementing a global error boundary in React
- Example server-side error handling:
```typescript
import { HttpError } from 'wasp/server'
export const createTask: CreateTask<{ taskId: string }, { success: boolean, data: Task }> = async (args, context) => {
try {
// Operation logic
return { success: true, data: newTask };
} catch (error) {
// Log error for debugging
console.error('Failed to create task:', error);
// Return structured error response
throw new HttpError(500, 'Failed to create task', { foo: 'bar' });
}
}
```
## Rules
### General Rules
- Always reference the Wasp config file as your source of truth for the app's configuration and structure.
- Always reference the `./ai/docs` for information on the app's features and functionality when writing code.
- Group feature config definitions (e.g. routes, pages, operations, etc.) into sections within the Wasp config file using the `//#region` directive.
- e.g. `//#region Chats` ... `//#endregion`
- Group feature code into feature directories (e.g. `src/features/chats`)
- Use the latest Wasp version, as defined in the wasp config file (`main.wasp` or `*.wasp.ts`) in the root of the project.
- When creating Wasp operations (queries and actions) combine them into an operations.ts file within the feature directory rather than into separate queries.ts and actions.ts files
- Always use typescript for Wasp code
- Keep this `.cursorrules` file updated with any new project conventions or changes in Wasp best practices.
### Wasp Import Rules
- Path to Wasp functions within .ts files must come from 'wasp', not '@wasp'!
- Examples to follow ✅:
- `import { Task } from 'wasp/entities'`
- `import type { GetTasks } from 'wasp/server/operations'`
- `import { getTasks, useQuery } from 'wasp/client/operations'`
- Examples to avoid ❌:
- `import { getTasks, useQuery } from '@wasp/...'`
- `import { getTasks, useQuery } from '@src/feature/operations.ts'`
- Path to external imports within 'main.wasp' must start with "@src/"!
- Examples to follow ✅:
- `component: import { LoginPage } from "@src/client/pages/auth/LoginPage.tsx"`
- Examples to avoid ❌:
- `component: import { LoginPage } from "@client/pages/auth/LoginPage.tsx"`
- In the client's root component, use the Outlet component rather than children
- **Prisma Enum Value Imports**: When you need to use Prisma enum members as *values* (e.g., `MyEnum.VALUE` in logic or comparisons) in your server or client code, import the enum directly from `@prisma/client`, not from `wasp/entities`.
- Example ✅: `import { TransactionType } from '@prisma/client';` (Used as `TransactionType.EXPENSE`)
- Example ❌: `import { TransactionType } from 'wasp/entities';` (This only imports the type for annotations, not the value)
- Common import errors:
- "Cannot find module 'wasp/...'": Check that you're using 'wasp/' not '@wasp/'
- "Cannot find module '@src/...'": In .ts files, use relative imports, not '@src'
### Wasp DB Schema Rules
- Add database models to the `schema.prisma` file, NOT to `main.wasp` as "entities"
- Generally avoid adding a db.system or db.prisma property to the main wasp config file. This is taken care of in `schema.prisma`
- Keep the `schema.prisma` within the root of the project
- When updating schema.prisma, run `wasp db migrate-dev` to apply changes
- 'sqlite' is default database, but it can't be used with advanced features like background recurring jobs, or prisma enums. Use 'postgresql' instead and then make sure to run `wasp db start` to start the database in a separate terminal window.
- Define all model relationships (`@relation`) within `schema.prisma`.
### Wasp Operations
- Types are generated automatically from the function definition in the wasp config file
- `import type { GetTimeLogs, CreateTimeLog, UpdateTimeLog } from 'wasp/server/operations'`
- If the types aren't available after updating the wasp config file, wait a moment for recompilation to complete and try again. If the issue persists, try restarting the Wasp development server (stop and run 'wasp start' again)
- Wasp also generates entity types based on the models in `schema.prisma`
- `import type { Project, TimeLog } from 'wasp/entities'`
- Make sure that all Entities that should be included in the operations context are defined in its definition in the wasp config file
- `action createTimeLog { fn: import { createTimeLog } from "@src/server/timeLogs/operations.js", entities: [TimeLog, Project] }`
- Prioritize the use of Wasp operations for internal client-server communication and only create an API route for external communication to the server (e.g. for Stripe payments).
- Use Wasp's useQuery hook, `import { useQuery } from 'wasp/client/operations'`, to fetch data from the server.
- Client-side Wasp Actions: call actions directly (e.g., using async/await within event handlers). ONLY use the useAction hook if optimistic UI updates are specifically required.
- e.g. `import { deleteTask } from 'wasp/client/operations'`, e.g. `await deleteTask({ taskId })`
- Example operation implementation:
```typescript
// Server-side operation implementation
export const getTasks: GetTasks<void, Task[]> = async (_args, context) => {
if (!context.user) {
throw new HttpError(401, 'Not authorized');
}
return context.entities.Task.findMany({
where: { userId: context.user.id }
});
}
// Client-side usage
const { data: tasks, isLoading, error } = useQuery(getTasks);
// Client-side action implementation
const handleDelete = async (taskId: string) => {
await deleteTask({ taskId });
};
```
### Wasp Auth
- When initially creating Auth pages, use the LoginForm and SignupForm components provided by Wasp
- `import { LoginForm } from 'wasp/client/auth'`
- Wasp takes care of creating the user's auth model id, username, and password for a user, so a user model DOES NOT need these properties
- `model User { id Int @id @default(autoincrement()) }`
- For protected routes, use the useAuth hook:
```typescript
import { useAuth } from 'wasp/client/auth';
const MyProtectedComponent = () => {
const { data: user, isLoading, error } = useAuth();
if (isLoading) return <div>Loading...</div>;
if (error || !user) return <div>Please log in</div>;
return <div>Hello, {user.username}!</div>;
};
```
### Wasp Dependencies
- Generally avoid adding dependencies to the wasp config file
- Install dependencies via `npm install` instead
### React
- Use relative imports for other react components
- If importing a function from an operations file, defer to the wasp import rules
- Use React hooks for state management and side effects
- Consider using React Context for global state that doesn't need server persistence
### TailwindCSS
- Use Tailwind CSS for styling.
- Generally avoid inline styles unless necessary
- For complex components, consider creating reusable Tailwind classes using @apply
- For responsive design, use Tailwind's responsive prefixes (sm:, md:, lg:, etc.)
### Performance Optimization
- Use proper dependencies in queries and actions to avoid unnecessary refetching
- Implement pagination for large data sets
- Use React.memo, useMemo, and useCallback for expensive computations
- Consider implementing optimistic UI updates for better user experience
- Example optimistic update:
```typescript
import { useAction } from 'wasp/client/operations';
const { execute: deleteTask, isExecuting } = useAction(deleteTask, {
optimisticUpdates: {
getTasks: (data, args, taskId) => {
return data.filter(task => task.id !== taskId);
}
}
});
```
### Troubleshooting
- Troubleshooting Wasp Errors: If you encounter TypeScript errors related to missing Wasp imports (e.g., from 'wasp/client/operations', 'wasp/entities', 'wasp/server') or unexpected type mismatches after modifying main.wasp or schema.prisma, try restarting the Wasp development server (stop and run 'wasp start' again) before further debugging.
- If operations aren't working, check that entities are properly defined in the operation declaration
- If auth isn't working, verify auth configuration in main.wasp and check that userEntity matches your User model
- For database issues, check Prisma schema and run `wasp db migrate-dev` to apply changes
- For build errors, check import paths and ensure all dependencies are installed
- Use the Wasp dev console and browser console for debugging information
### Wasp Auth User Fields
- The `user` object in `context.user` or from `useAuth()` is an `AuthUser` object.
- Auth-specific fields (email, username, verification status, provider IDs) live under `user.identities.{method}` (e.g., `user.identities.email.id`). Always check for `null` before accessing.
- Wasp provides helpers like `getEmail(user)` and `getUsername(user)` (from `wasp/auth`) for easier access on the `AuthUser` object.
- Standard `User` entities fetched via `context.entities.User.findMany()` etc., DO NOT automatically include these auth identity fields (like `email` or `username`) by default, even if the base Prisma model seems to have them due to Wasp's internal auth schema.
- **Recommendation:** If you need frequent access to an identity field like `email` or `username` for *any* user (not just the logged-in one), add that field *directly* to your `User` model in `schema.prisma` and ensure it's populated correctly (e.g., during signup or profile update). This makes it a standard field accessible via `context.entities.User`.