Skip to content

Commit 31628ca

Browse files
committed
chore: merge main into release for new releases
2 parents 98907bd + 8d2a811 commit 31628ca

File tree

328 files changed

+43631
-2721
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

328 files changed

+43631
-2721
lines changed

.cursor/rules/after-changes.mdc

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
---
2+
description: Always run typecheck and lint after making code changes
3+
globs: **/*.{ts,tsx}
4+
alwaysApply: true
5+
---
6+
7+
# After Code Changes
8+
9+
After making any code changes, **always run these checks**:
10+
11+
```bash
12+
# TypeScript type checking
13+
bun run typecheck
14+
15+
# ESLint linting
16+
bun run lint
17+
```
18+
19+
## Fix Errors Before Committing
20+
21+
If either check fails:
22+
23+
1. Fix all TypeScript errors first (they break the build)
24+
2. Fix ESLint errors/warnings
25+
3. Re-run checks until both pass
26+
4. Only then commit or deploy
27+
28+
## Common TypeScript Fixes
29+
30+
- **Property does not exist**: Check interface/type definitions, ensure correct property names
31+
- **Type mismatch**: Verify the expected type vs actual type being passed
32+
- **Empty interface extends**: Use `type X = SomeType` instead of `interface X extends SomeType {}`
33+
34+
## Common ESLint Fixes
35+
36+
- **Unused variables**: Remove or prefix with `_`
37+
- **Any type**: Add proper typing
38+
- **Empty object type**: Use `type` instead of `interface` for type aliases

.cursor/rules/better-auth.mdc

Lines changed: 0 additions & 28 deletions
This file was deleted.

.cursor/rules/code-standards.mdc

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
---
2+
description: General code quality standards and file size limits
3+
globs: **/*.{ts,tsx}
4+
alwaysApply: true
5+
---
6+
7+
# Code Standards
8+
9+
## File Size Limits
10+
11+
**Files must not exceed 300 lines.** When a file approaches this limit, split it.
12+
13+
### ✅ How to Split Large Files
14+
15+
```
16+
# Before: One 400-line file
17+
components/TaskList.tsx (400 lines)
18+
19+
# After: Multiple focused files
20+
components/
21+
├── TaskList.tsx # Main component (~100 lines)
22+
├── TaskListItem.tsx # Individual item (~80 lines)
23+
├── TaskListFilters.tsx # Filter controls (~60 lines)
24+
└── TaskListEmpty.tsx # Empty state (~40 lines)
25+
```
26+
27+
### Splitting Strategies
28+
29+
| File Type | Split By |
30+
| --------- | -------------------------------------------- |
31+
| Component | Extract sub-components, hooks, utils |
32+
| Hook | Extract helper functions, split by concern |
33+
| Utils | Group by domain (dates, strings, validation) |
34+
| Types | Split by entity (Task, User, Organization) |
35+
36+
### ❌ Warning Signs
37+
38+
```tsx
39+
// ❌ File is too long
40+
// TaskList.tsx - 450 lines with inline helpers, multiple components
41+
42+
// ❌ Multiple components in one file
43+
export function TaskList() { ... }
44+
export function TaskCard() { ... } // Should be separate file
45+
export function TaskBadge() { ... } // Should be separate file
46+
```
47+
48+
## Code Quality
49+
50+
### ✅ Always Do This
51+
52+
```tsx
53+
// Early returns for readability
54+
function processTask(task: Task | null) {
55+
if (!task) return null;
56+
if (task.deleted) return null;
57+
58+
return <TaskCard task={task} />;
59+
}
60+
61+
// Descriptive names
62+
const handleTaskComplete = (taskId: string) => { ... };
63+
const isTaskOverdue = (task: Task) => task.dueDate < new Date();
64+
65+
// Const arrow functions with types
66+
const formatDate = (date: Date): string => {
67+
return date.toLocaleDateString();
68+
};
69+
```
70+
71+
### ❌ Never Do This
72+
73+
```tsx
74+
// No early returns - deeply nested
75+
function processTask(task) {
76+
if (task) {
77+
if (!task.deleted) {
78+
return <TaskCard task={task} />;
79+
}
80+
}
81+
return null;
82+
}
83+
84+
// Vague names
85+
const handleClick = () => { ... }; // Click on what?
86+
const data = fetchStuff(); // What data?
87+
88+
// Function keyword when const works
89+
function formatDate(date) { ... }
90+
```
91+
92+
## Function Parameters
93+
94+
**Use named parameters (object destructuring) for functions with 2+ parameters.**
95+
96+
### ✅ Always Do This
97+
98+
```tsx
99+
// Named parameters - clear at call site
100+
const createTask = ({ title, assigneeId, dueDate }: CreateTaskParams) => { ... };
101+
createTask({ title: 'Review PR', assigneeId: user.id, dueDate: tomorrow });
102+
103+
// Hook with options object
104+
const useTasks = ({ organizationId, initialData }: UseTasksOptions) => { ... };
105+
const { tasks } = useTasks({ organizationId: orgId, initialData: serverTasks });
106+
107+
// Component props (always named)
108+
function TaskCard({ task, onComplete, showDetails }: TaskCardProps) { ... }
109+
<TaskCard task={task} onComplete={handleComplete} showDetails={true} />
110+
```
111+
112+
### ❌ Never Do This
113+
114+
```tsx
115+
// Positional parameters - unclear at call site
116+
const createTask = (title: string, assigneeId: string, dueDate: Date) => { ... };
117+
createTask('Review PR', user.id, tomorrow); // What's the 2nd param?
118+
119+
// Multiple positional args are confusing
120+
const formatRange = (start: Date, end: Date, format: string, timezone: string) => { ... };
121+
formatRange(startDate, endDate, 'MM/dd', 'UTC'); // Hard to read
122+
123+
// Boolean positional params are the worst
124+
fetchTasks(orgId, true, false, true); // What do these booleans mean?
125+
```
126+
127+
### Exception: Single Parameter
128+
129+
```tsx
130+
// Single param is fine as positional
131+
const getTask = (taskId: string) => { ... };
132+
const formatDate = (date: Date) => { ... };
133+
const isOverdue = (task: Task) => { ... };
134+
```
135+
136+
## Accessibility
137+
138+
### ✅ Always Include
139+
140+
```tsx
141+
// Interactive elements need keyboard support
142+
<div
143+
role="button"
144+
tabIndex={0}
145+
onClick={handleClick}
146+
onKeyDown={(e) => e.key === 'Enter' && handleClick()}
147+
aria-label="Delete task"
148+
>
149+
<TrashIcon />
150+
</div>
151+
152+
// Form inputs need labels
153+
<label htmlFor="task-name">Task Name</label>
154+
<input id="task-name" type="text" />
155+
156+
// Images need alt text
157+
<img src={avatar} alt={`${user.name}'s avatar`} />
158+
```
159+
160+
## DRY Principle
161+
162+
### ✅ Extract Repeated Logic
163+
164+
```tsx
165+
// Before: Duplicated validation
166+
if (email && email.includes('@') && email.length > 5) { ... }
167+
if (email && email.includes('@') && email.length > 5) { ... }
168+
169+
// After: Extracted helper
170+
const isValidEmail = (email: string) =>
171+
email?.includes('@') && email.length > 5;
172+
173+
if (isValidEmail(email)) { ... }
174+
```
175+
176+
## Checklist
177+
178+
Before committing:
179+
180+
- [ ] No file exceeds 300 lines
181+
- [ ] Uses early returns for conditionals
182+
- [ ] Variable/function names are descriptive
183+
- [ ] Functions with 2+ params use named parameters
184+
- [ ] Interactive elements have keyboard support
185+
- [ ] No duplicated logic (DRY)
186+
- [ ] Const arrow functions with types

0 commit comments

Comments
 (0)