forked from Cloud-Pipelines/pipeline-editor
-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy path.cursorrules
More file actions
622 lines (449 loc) · 19.4 KB
/
Copy path.cursorrules
File metadata and controls
622 lines (449 loc) · 19.4 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
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
# Cursor AI Rules for Pipeline Studio App.
## Project Overview
This is a React + TypeScript application for building and running Machine Learning pipelines using drag and drop. The app uses Vite, TailwindCSS v4, ShadCN, Radix UI, React Flow, and Monaco Editor.
## Core Technologies & Standards
### TypeScript
- Use strict TypeScript with proper typing
- Prefer explicit types over `any` (only use `any` when absolutely necessary)
- Use interface for object shapes, type for unions/primitives
- Follow existing patterns for type definitions in `src/types/`
- **Avoid unsafe type casting**: Don't use type assertions (`as`) unless absolutely necessary. Instead prefer:
- Type guards and runtime validation
- Proper typing from the source
- Union types and type narrowing
- Schema validation libraries (e.g., zod) for external data
- Example: Instead of `const something: string = myjson as string`, use type guards or validate the data
### React Patterns
- Use functional components with hooks exclusively
- Use proper dependency arrays in useEffect and useMemo
- Follow the existing component structure
- Do not use barrel exports
- Use React 19 features and patterns
- **Import modules from React**: `import module from react`. Do not use inline `React.[module]`
### File Structure & Imports
- Use absolute imports with `@/` prefix for src directory
- Follow existing folder structure:
- `src/components/` for all React components
- `src/hooks/` for custom hooks
- `src/types/` for TypeScript definitions
- `src/utils/` for utility functions
- `src/services/` for API and business logic
- **Import order**: external packages → internal modules → relative imports
- Use simple-import-sort rules (already configured in ESLint)
- Do not use barrel exports
### UI Components & Styling
- Use ShadCN / Radix UI components from `@/components/ui/` for all UI primitives
- Use TailwindCSS v4 for styling (not CSS modules or styled-components)
- **Do not use inline styling** (`style={styles}`) except where strictly necessary
- Follow existing design patterns in the codebase
- Use `cn()` utility for conditional classes (from `@/lib/utils`)
- Prefer composition over prop drilling for complex components
- **Suggest abstractions for repeated Tailwind patterns**: When you see similar Tailwind class combinations used multiple times (especially for buttons, cards, inputs, wrappers, etc), suggest creating reusable components or utility classes. For example:
- Multiple buttons with similar styling → Create a Button variant or new component
- Repeated container/card patterns → Abstract into reusable Card component
- Common spacing/layout patterns → Suggest utility classes or component abstractions
- Similar form field styling → Create form field components
### UI Primitives (Prefer Over Raw HTML)
**Always prefer our UI primitives over raw HTML elements:**
- **Layout**: Use `BlockStack` and `InlineStack` from `@/components/ui/layout` instead of `<div className="flex ...">`
- `BlockStack` = vertical flex (`flex-col`)
- `InlineStack` = horizontal flex (`flex-row`)
- Both support `gap`, `align`, `blockAlign` props
- Use `as` prop for semantic elements: `<BlockStack as="ul">`, `<InlineStack as="li">`
- **Typography**: Use `Text` from `@/components/ui/typography` instead of raw `<h1-h6>`, `<p>`, `<span>`, `<dt>`, `<dd>`
- `<Text as="h3" size="md" weight="semibold">` instead of `<h3 className="text-md font-semibold">`
- `<Text as="dt" weight="semibold">` instead of `<dt className="font-semibold">`
- `<Text as="p" size="sm">` instead of `<p className="text-sm">`
- Supports: `as`, `size`, `weight`, `tone`, `font` props
- **Buttons**: Use `Button` from `@/components/ui/button`
- **Icons**: Use `Icon` from `@/components/ui/icon`
**When raw HTML is acceptable:**
- Semantic elements not supported by primitives (e.g., `<dl>`, `<ul>`, `<ol>`, `<table>`)
- Complex layouts where primitives don't fit
- Performance-critical sections where abstraction overhead matters
### State Management
- Use Tanstack Query for server state
- Use Tanstack Router for routing
- Use React hooks for local component state
- Use Context providers for app-wide state (see existing providers)
- Follow existing patterns in `src/providers/`
- **Use `useRequiredContext`** to simplify context usage and avoid null checks
### Code Quality
- Follow ESLint rules (configured in eslint.config.js)
- Use Prettier for formatting
- Write tests using Vitest for unit tests, Playwright for E2E
- Use descriptive variable and function names
- Add JSDoc comments for complex functions
- Prefer early returns to reduce nesting
### API & Data
- Use the generated API client in `src/api/`
- Follow existing service patterns in `src/services/`
- Use proper error handling with try/catch
- **Prefer domain types**: Use types from `src/utils/componentSpec.ts`, `src/types/`, etc. for core business logic
- **Use generated API types**: Only use `src/api/types.gen.ts` when directly interfacing with APIs or when domain types don't exist
- **Avoid unsafe type casting**: Don't use type assertions (`as`) unless absolutely necessary. Instead prefer:
- Type guards and runtime validation
- Proper typing from the source
- Union types and type narrowing
- Schema validation libraries for external data
- Example: Instead of `const something: string = myjson as string`, use type guards or validate the data
### React Flow Specific
- Use `@xyflow/react` for flow diagrams
- Follow existing node types and edge patterns
- Keep flow state management consistent with existing patterns
- Use proper node and edge typing
### Testing
#### Unit & Component Tests
- Write unit tests for utilities and hooks using Vitest
- Write component tests for complex components
- Follow existing test patterns and naming
#### E2E Tests with Playwright
- Use Playwright helpers from `tests/e2e/helpers.ts`
- Follow Playwright best practices (see E2E Testing Best Practices section below)
- Use `data-testid` attributes for stable selectors
- Write descriptive test names that explain user behavior
- Ensure tests are isolated and can run independently
### E2E Testing Best Practices (Playwright)
#### Never Use Hard-Coded Timeouts
**❌ Don't:**
```typescript
await element.click();
await page.waitForTimeout(200); // Flaky and slow
```
**✅ Do:**
```typescript
await element.click();
await expect(otherElement).toBeVisible(); // Wait for actual state
```
#### Use Playwright's Auto-Waiting
**❌ Don't:**
```typescript
if (await element.isVisible()) {
await element.click();
}
```
**✅ Do:**
```typescript
await expect(element).toBeVisible();
await element.click(); // Playwright auto-waits
```
#### Never Use Non-Null Assertions
**❌ Don't:**
```typescript
const box = await element.boundingBox();
const x = box!.x; // Crashes if null
```
**✅ Do:**
```typescript
const box = await element.boundingBox();
if (!box) {
throw new Error("Unable to locate element bounding box");
}
const x = box.x;
```
#### Don't Await Locators (They're Lazy)
**❌ Don't:**
```typescript
const button = await page.getByTestId("submit");
const items = await page.locator(".item");
await expect(button).toBeVisible();
```
**✅ Do:**
```typescript
const button = page.getByTestId("submit"); // No await
const items = page.locator(".item"); // No await
await expect(button).toBeVisible(); // Only await assertions
```
#### Use Consistent Assertion Patterns
**❌ Don't:**
```typescript
expect(await element).toHaveText("text");
expect(await element.isVisible()).toBe(true);
```
**✅ Do:**
```typescript
await expect(element).toHaveText("text");
await expect(element).toBeVisible();
```
#### Prefer Semantic Selectors
**Priority order:**
1. `getByRole()` - Best for accessibility
2. `getByTestId()` - Best for test stability (use this for our app)
3. `getByText()` - Good for static content
4. `locator()` with data attributes - When above don't work
5. CSS selectors - Last resort
**❌ Don't:**
```typescript
await page.locator(".button.primary.submit").click();
```
**✅ Do:**
```typescript
await page.getByTestId("submit-button").click();
```
#### Write Isolated, Independent Tests
- Each test should set up its own state
- Don't depend on test execution order (unless using serial mode intentionally)
- Clean up after tests in `afterEach` or `afterAll`
#### Use Helper Functions
- Leverage existing helpers from `tests/e2e/helpers.ts`
- Create new helpers for repeated workflows
- Keep helpers focused and reusable
#### Add Meaningful Error Context
**❌ Don't:**
```typescript
await expect(element).toBeVisible();
```
**✅ Do:**
```typescript
await expect(element, "Component should appear after loading").toBeVisible();
```
#### Test User Behavior, Not Implementation
**❌ Don't:**
```typescript
await expect(button).toHaveClass("bg-blue-500"); // Implementation detail
```
**✅ Do:**
```typescript
await expect(button).toBeVisible();
await expect(button).toBeEnabled();
await expect(button).toHaveText("Submit"); // User-visible behavior
```
### Performance
- Use memo for expensive components
- Use useMemo and useCallback appropriately
- Lazy load heavy components when possible
- Follow existing patterns for optimization
### React Compiler
This project uses the React Compiler for automatic memoization. Files/directories are incrementally adopted in `react-compiler.config.js`.
#### Writing React Compiler Compatible Code
**For new files**, ensure they follow React Compiler rules from the start:
1. **Don't mutate values during render**
```typescript
// ❌ Bad - mutating during render
const items = props.items;
items.push(newItem);
// ✅ Good - create new reference
const items = [...props.items, newItem];
```
2. **Don't read/write refs during render**
```typescript
// ❌ Bad - reading ref during render
const value = myRef.current;
return <div>{value}</div>;
// ✅ Good - read refs in effects or callbacks
useEffect(() => {
const value = myRef.current;
}, []);
```
3. **Follow Rules of Hooks strictly**
- No conditional hooks
- No hooks in loops
- Proper dependency arrays
4. **Avoid patterns the compiler can't optimize**
- Don't spread props with `{...props}` unnecessarily
- Avoid dynamic property access on objects when possible
- Keep component logic predictable
#### Adding Files to React Compiler
When a file is added to `react-compiler.config.js`:
- Remove unnecessary `useCallback` and `useMemo` (compiler handles this)
- Verify no compiler violations with `npm run validate`
- Test the component still works correctly
#### React Compiler Config Structure
Files are organized by cleanup effort in `react-compiler.config.js`:
- Top section: Already enabled directories/files
- Middle: Ready to enable (0 useCallback/useMemo)
- Bottom (commented): Need cleanup before enabling
## Component Architecture
### Component Structure
```typescript
// ComponentName/index.ts
export { ComponentName } from './ComponentName';
// ComponentName/ComponentName.tsx
interface ComponentNameProps {
// props
}
export const ComponentName = ({ }: ComponentNameProps) => {
// component logic
return (
// JSX
);
};
```
### Custom Hooks
- Prefix with `use`
- Return objects for multiple values, not arrays
- Use proper TypeScript return types
- Follow existing patterns in `src/hooks/`
### Provider Pattern
```typescript
const Context = createContext<ContextType | null>(null);
export const Provider = ({ children }: { children: ReactNode }) => {
// provider logic
return <Context.Provider value={value}>{children}</Context.Provider>;
};
export const useContext = () => {
const context = useContext(Context);
if (!context) throw new Error('useContext must be used within Provider');
return context;
};
```
## Naming Conventions
- Components: PascalCase
- Files: PascalCase for components, camelCase for utilities
- Variables/functions: camelCase
- Constants: SCREAMING_SNAKE_CASE
- Types/Interfaces: PascalCase
- Directories: camelCase or PascalCase for component folders
## Error Handling
- Use proper error boundaries
- Handle async errors with try/catch
- Use toast notifications for user-facing errors
- Log errors appropriately
- Follow existing error patterns
## Comments & Documentation
- Use JSDoc for public APIs
- Add comments for complex business logic
- **Explain "why" not "what" in comments**
- **Keep comments up to date with code changes**
- Avoid writing redundant comments for functions and variables that are self-explanatory
## Specific Project Patterns
- Use Monaco Editor for code editing features
- Follow React Flow patterns for pipeline visualization
- Use localforage for client-side storage
- Follow existing authentication patterns
- Use proper task node and pipeline handling patterns
- Follow the existing component library structure
- **Do not modify componentSpec structure** without express permission beforehand
## Don't Do
### General
- Don't use CSS-in-JS or styled-components
- Don't use inline styling (`style={styles}`) except where strictly necessary
- Don't use class components
- Don't ignore TypeScript errors
- Don't use relative imports for `@/components/ui`
- Don't create new global state without good reason
- Don't bypass existing abstractions without discussion
- Don't use `any` type without explicit justification
- Don't use unsafe type casting (`as`) unless absolutely necessary
- Don't create side effects in render functions
- Don't use barrel exports
- Don't modify componentSpec structure without express permission
- Don't use raw HTML elements when UI primitives exist (use `Text`, `BlockStack`, `InlineStack`, etc.)
### E2E Testing
- Don't use `page.waitForTimeout()` - use proper Playwright auto-waiting instead
- Don't use non-null assertions (`!`) - add explicit null checks with error messages
- Don't await locators - only await assertions and actions
- Don't test implementation details (CSS classes, internal state) - test user-visible behavior
- Don't write comments that just restate what the code does - explain "why" not "what"
## Code Review Guidelines
When `@.cursorrules` is referenced alone or without the word "review" (ex: @.cursorrules or @.cursorrules reivew or @.cursorrules sometext some text review), automatically perform a code review of the current PR/commit changes against these rules.
### Validation & Testing
Use these npm scripts for validation and testing:
- **`npm run validate`** - Runs format, lint:fix, typecheck, and knip (use before committing)
- **`npm run validate:test`** - Runs validate + unit tests (full validation)
- **`npm run test:e2e`** - Runs Playwright E2E tests
### Local Development Ports
- **Frontend**: `localhost:3000` (default for `npm start`)
- **Backend API**: `localhost:8000`
- **API Docs**: `localhost:8000/docs`
### Review Process
1. **Scope**: Only review code that has been changed. Do not flag pre-existing issues unless they directly cause problems with the new changes.
2. **Source of truth**: Use the git diff provided in context (if available). If reviewing "the last commit", use `git show HEAD --stat` to see changed files, then read those files. Do NOT use `git diff main...HEAD` as this shows all commits on the branch, not just the last one.
3. **Read files**: Always read the full files being reviewed to understand context, not just the diff.
### What to Check
Review changed code for violations of these `.cursorrules`, prioritizing:
1. **High Priority**
- Unsafe type casting (`as`) - suggest type guards instead
- Potential runtime errors (undefined access, null checks)
- Missing error handling
- Security concerns
- Incorrect use of React hooks (missing deps, unnecessary async)
- React Compiler violations (mutating during render, reading refs during render)
2. **Medium Priority**
- Not using UI primitives (`BlockStack`, `InlineStack`, `Text`, `Icon`)
- Import order violations
- Duplicated logic that should be extracted
3. **Low Priority**
- Redundant code (unnecessary null coalescing, etc.)
- Minor style inconsistencies
- Unused props or parameters
### Review Output Format
Present findings in a clear, actionable format:
```markdown
## Issues Found
### 1. **[Issue Title]** - [File:Line]
[Code snippet showing the problem]
**Why**: [Brief explanation]
**Fix**: [Suggested solution]
---
## What's Good
- [Positive observations about the code]
- Note if file was added to `react-compiler.config.js` (React Compiler enabled)
---
## Summary Table
| Issue | Severity | Location |
| ----- | --------------- | --------- |
| ... | High/Medium/Low | file:line |
```
### Review Principles
- **Be specific**: Include file paths and line numbers
- **Be actionable**: Provide concrete fixes, not vague suggestions
- **Be proportional**: Don't over-engineer simple code
- **Acknowledge good patterns**: Note when code follows best practices
- **Don't nitpick**: Focus on meaningful improvements
- **Offer to fix**: After review, ask "Want me to fix these?"
### What NOT to Do
- Don't review unchanged code (unless it causes issues with new code)
- Don't suggest rewrites of working code just for style preferences
- Don't flag issues that are already present in the codebase (pre-existing)
- Don't recommend adding complexity without clear benefit
- Don't suggest patterns that conflict with existing codebase conventions
### Optional "While We're Here" Cleanup
After completing a code generation task, scan the surrounding area for small, low-risk improvements. **Only offer** if ALL of these conditions are met:
1. **Small scope**: Affects < 30 lines of code
2. **Low risk**: Purely cosmetic or minor refactoring (not logic changes)
3. **Same file**: In a file you already modified
4. **Clear benefit**: Improves readability, removes dead code, or fixes obvious issues
**Examples of good "while we're here" suggestions:**
- Removing an unused import in the file you just edited
- Fixing a typo in a comment nearby
- Removing an unused variable in the same function
- Updating a deprecated pattern you noticed (e.g., `lucide-react` → `Icon` component)
**Do NOT suggest cleanup for:**
- Complex refactors (> 30 lines)
- Logic changes
- Files you didn't touch
- Changes that require testing
- Anything that could break functionality
**How to offer:**
```
---
While we're here, I noticed a small cleanup opportunity:
- [Brief description of the cleanup]
This is optional and low-risk. Want me to fix it?
```
**Don't be naggy**: Only offer once per task, and only if genuinely worthwhile. If in doubt, skip it.
## Planning & Documentation
When asked to create planning documents, architecture decisions, or investigation notes:
- **Always save to `.local/`** - This directory is gitignored for local-only files
- Use descriptive filenames: `.local/feature-name-planning.md`, `.local/bug-investigation.md`
- These files persist locally but won't be committed to the repo
**Example use cases:**
- Feature planning and design docs
- Bug investigation notes
- Architecture decision records (local drafts)
- Task breakdowns and checklists
- Research and exploration notes
**File naming convention:**
```
.local/[feature-or-task]-[type].md
Examples:
.local/pipeline-search-planning.md
.local/auth-refactor-investigation.md
.local/api-migration-checklist.md
```
## Other rules
- Prioritize readability over complexity
- Reuse existing components, types, and styles whenever possible, and avoid over-generating new ones.
- Avoid writing redundant comments for functions and variables that are self-explanatory.
When suggesting code changes, always follow these patterns and maintain consistency with the existing codebase.