Skip to content

Commit 1baa859

Browse files
doc(rules): add roo code rules
1 parent 6524ebd commit 1baa859

File tree

12 files changed

+600
-2
lines changed

12 files changed

+600
-2
lines changed

.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,6 @@ convex/_generated
8787
astro
8888

8989
# ai, ethereal and auto generated
90-
.roo/rules-code
9190
AGENTS.md
9291

9392
# npm publishing

.roo/rules-code/convex.md

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
# convex
2+
3+
## Validator Patterns
4+
5+
### DRY Validator Definition
6+
7+
Define validators in a reusable, DRY pattern following the example in `convex/generate_image/generate_same/imageGenerationValidator.ts`:
8+
9+
```typescript
10+
import { v } from "convex/values"
11+
12+
// Define base fields as a const object
13+
export const someEntityValidatorFields = {
14+
workspaceId: v.id("workspace"),
15+
name: v.string(),
16+
// ... other fields
17+
}
18+
19+
// Create base validator
20+
export const someEntityValidator = v.object(someEntityValidatorFields)
21+
22+
// Create variant with token for mutations/queries
23+
export const someEntityValidatorWithToken = v.object({
24+
...someEntityValidatorFields,
25+
token: v.string()
26+
})
27+
28+
// Export the type
29+
export type SomeEntityValidatorType = typeof someEntityValidator.type
30+
```
31+
32+
### Type-Safe ID Definitions
33+
34+
Use existing type-safe ID definitions like in `convex/auth/IdUser.ts`:
35+
36+
```typescript
37+
import type { Id } from "../_generated/dataModel"
38+
39+
export type IdUser = Id<"users">
40+
export type IdWorkspace = Id<"workspace">
41+
// Add other ID types as needed
42+
```
43+
44+
## Query/Mutation Patterns
45+
46+
### Function Variants
47+
48+
Each query/mutation/internalQuery/internalMutation should have a corresponding `Fn` function variant, following the pattern in `convex/auth/crud/findUserByEmailQuery.ts`:
49+
50+
```typescript
51+
import type { QueryCtx } from "../../_generated/server"
52+
import { internalQuery } from "../../_generated/server"
53+
import { v } from "convex/values"
54+
55+
// Define the query
56+
export const findUserByEmailQuery = internalQuery({
57+
args: { email: v.string() },
58+
handler: async (ctx, args): Promise<Doc<"users"> | null> => {
59+
return findUserByEmailFn(ctx, args.email)
60+
},
61+
})
62+
63+
// Define the Fn function variant for reuse
64+
export async function findUserByEmailFn(ctx: QueryCtx, email: string): Promise<Doc<"users"> | null> {
65+
return await ctx.db
66+
.query("users")
67+
.withIndex("email", (q) => q.eq("email", email))
68+
.unique()
69+
}
70+
```
71+
72+
### Token Validation
73+
74+
All mutations and queries should include a `token` parameter with validation:
75+
76+
```typescript
77+
import { v } from "convex/values"
78+
import { mutation } from "../../_generated/server"
79+
80+
export const someMutation = mutation({
81+
args: {
82+
// Use the validator with token
83+
...someEntityValidatorWithToken.fields,
84+
},
85+
handler: async (ctx, args) => {
86+
// Validate token first
87+
const user = await validateToken(ctx, args.token)
88+
if (!user) {
89+
throw new Error("Invalid token")
90+
}
91+
92+
// Proceed with mutation logic
93+
return someMutationFn(ctx, args, user)
94+
},
95+
})
96+
97+
export async function someMutationFn(ctx: MutationCtx, args: SomeEntityValidatorType, user: Doc<"users">) {
98+
// Mutation implementation
99+
}
100+
```
101+
102+
## Data Transformation Patterns
103+
104+
### Database to Model Transformation
105+
106+
Create transformation functions to convert database documents to your application models:
107+
108+
```typescript
109+
import type { UserProfile } from "@/auth/model/UserProfile"
110+
import type { Doc } from "../../_generated/dataModel"
111+
112+
export function dbUsersToUserProfile(u: Doc<"users">): UserProfile {
113+
const { _id, _creationTime, ...rest } = u
114+
return { userId: _id, ...rest }
115+
}
116+
```
117+
118+
## Best Practices
119+
120+
1. **Always use validators** for all query/mutation arguments
121+
2. **Include token validation** in all public-facing mutations/queries
122+
3. **Create Fn variants** for all database operations to enable reuse
123+
4. **Use type-safe IDs** instead of string literals
124+
5. **Keep validators close** to where they're used
125+
6. **Export transformation functions** for consistent data conversion
126+
7. **Follow DRY principles** by defining base validators and extending them

.roo/rules-code/enums.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Centralized definition of string literals
2+
3+
- Define all magic strings as const objects
4+
- No magic strings scattered throughout the codebase
5+
- Create derived types using `keyof typeof`
6+
- Generate validation schemas using the const object values and `valibot` library
7+
- Export arrays of valid values
8+
9+
```typescript
10+
import { getObjectValues } from "@/utils/obj/getObjectValues"
11+
import * as v from "valibot"
12+
13+
// Type
14+
export type UserRole = keyof typeof userRole
15+
16+
// Values
17+
export const userRole = {
18+
user: "user",
19+
admin: "admin",
20+
dev: "dev",
21+
} as const
22+
23+
// Schema
24+
export const userRoleSchema = v.enum(userRole)
25+
26+
// Type checking to see if schema matches type
27+
function types1(a: v.InferOutput<typeof userRoleSchema>): UserRole {
28+
return a
29+
}
30+
31+
// Usage
32+
function handleLogin(provider: UserRole) {
33+
// Type-safe provider handling
34+
const user = userRole.user === provider
35+
}
36+
```

.roo/rules-code/functions.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Function Modularization
2+
3+
## Overview
4+
5+
When refactoring code, particularly extracting functions from components, follow these guidelines to maintain clean, readable, and maintainable code structure.
6+
7+
- Prefer `function` over `const` for functions
8+
- Prefer early `if(!matching) return` over `if (matching)` checks
9+
- Prefer early `if(!matching) continue` over `if (matching)` checks in for loops
10+
- Break large functions into smaller, focused functions
11+
- Break functions if using try catch
12+
- Each function should have a single responsibility
13+
- Name functions clearly based on their purpose
14+
- Keep functions pure when possible (no side effects)
15+
- Avoid else statements where possible
16+
- Do not use const arrow function to define functions
17+
- Always separate `fetch` calls into a separate function
18+
19+
## General Principles
20+
21+
### 1. Function Placement
22+
23+
- **Component functions first**: Place the main component function at the top of the file, immediately after imports and type definitions.
24+
- **Helper/utility functions last**: Move extracted helper functions to the bottom of the file, after the main component.
25+
- **Dependency order**: Functions that depend on others should come after their dependencies.
26+
27+
### 2. Function Declaration Style
28+
29+
- **Use function declarations**: Prefer `function functionName() {}` over arrow function constants (`const functionName = () => {}`).
30+
- **Explicit return types**: Always specify return types for functions, especially when TypeScript can infer them.
31+
- **Clear parameter naming**: Use descriptive parameter names that indicate their purpose and types.
32+
33+
### 3. Function Naming
34+
35+
- **Descriptive names**: Functions should have names that clearly describe their purpose.
36+
- **PascalCase for components**: Component functions should use PascalCase.
37+
- **camelCase for utilities**: Helper/utility functions should use camelCase.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Import Management Guidelines
2+
3+
Do NOT add import statements before using the corresponding symbols in the code.
4+
Reason: vscode auto optimizes them away on file save because they are unused.
5+
6+
Example:
7+
8+
```typescript
9+
// First, write code that uses the import
10+
const result = someFunction();
11+
12+
// Then, add the import right before usage
13+
import { someFunction } from './library';
14+
```

.roo/rules-code/result.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Result
2+
3+
- Use `Result` types for expected errors
4+
- Always prefer to use Result over try-catch blocks
5+
- If using `fetch`: always use `PromiseResult` return type, validate output with `valibot`
6+
7+
```ts
8+
import { type Result } from "~utils/result/Result"
9+
10+
function processRequest(req: Request): SimpleResult<User> {
11+
const result = validateRequest(req)
12+
if (!result.success) return result
13+
14+
const user = getUser(result.data.id)
15+
if (!result.success) return user
16+
17+
return user
18+
}
19+
```

.roo/rules-code/solid.md

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
# solid.js
2+
3+
## general
4+
5+
- use `function` over const function expression
6+
- do not use default exports
7+
8+
## use solid.js build-in components instead of react patterns
9+
10+
### loops
11+
12+
Instead of using `map()` inside JSX or manual `for` loops:
13+
14+
- Prefer the `For` component from **solid-js** for list rendering.
15+
- Move repeating UI blocks into a dedicated function, defined just below the current component within the same file.
16+
17+
### conditions
18+
19+
Instead of adding conditional logic or early returns in React-like patterns:
20+
21+
- Prefer using the `Show` component from solid-js for conditional rendering.
22+
- Place fallback component in a dedicated function, defined just below the current component within the same file.
23+
24+
---
25+
26+
## component classes
27+
28+
### classMerge and classArr
29+
30+
- **Standardize class organization** in Solid components using `classMerge` and `classArr` utility functions
31+
- **Always use `classMerge`** instead of template strings
32+
- **Group classes** by functional category:
33+
```tsx
34+
;[
35+
"flex flex-col items-center justify-center", // layout
36+
"min-h-screen", // sizing
37+
"bg-gray-50 dark:bg-gray-900", // background
38+
"p-4", // spacing
39+
]
40+
```
41+
- **Comment each group** with its purpose
42+
- **Order groups logically**:
43+
- Layout/Positioning
44+
- Sizing/Dimensions
45+
- Backgrounds
46+
- Borders/Shadows
47+
- Typography
48+
- Spacing/Margin/Padding
49+
- Transitions/Animations
50+
- State modifiers (hover/focus)
51+
- **Place props.class last** to enable proper override
52+
- Prefer `classArr` for static class lists without dynamic props
53+
- Use `classMerge` when dynamic class props are needed
54+
55+
Incorrect Approach (String Templates):
56+
57+
```tsx
58+
// ErrorPage.tsx
59+
<div class="flex flex-col items-center justify-center min-h-screen bg-gray-50 dark:bg-gray-900 p-4">
60+
```
61+
62+
Correct Approach (ClassMerge + Grouped Arrays):
63+
64+
```tsx
65+
import { classMerge } from "@/ui/utils/classMerge"
66+
67+
<div class={classMerge(
68+
"flex flex-col items-center justify-center", // layout
69+
"min-h-screen", // sizing
70+
"bg-gray-50 dark:bg-gray-900", // background
71+
"p-4", // spacing
72+
props.class,
73+
)}>
74+
```
75+
76+
### text color classes
77+
78+
Never use `text-gray-400`, `text-gray-500` or `text-gray-600` - use `text-muted-foreground` instead.
79+
80+
---
81+
82+
## component props
83+
84+
### use and extend existing Interfaces
85+
86+
Always include base utility types for exported general purpose components in "src/ui" folder:
87+
88+
```ts
89+
import type { MayHaveChildren } from "@/ui/utils/MayHaveChildren"
90+
import type { MayHaveClassName } from "@/ui/utils/MayHaveClassName"
91+
92+
export interface ComponentProps extends MayHaveClassName, MayHaveChildren {
93+
// component-specific props
94+
}
95+
96+
// usage example
97+
function MyComponent(p: ComponentProps) {}
98+
```
99+
100+
if the component has an `icon` property have the props extend existing `MayHaveIcon` interface
101+
if the component has an `title` property have the props extend existing `MayHaveTitle` interface
102+
if the component has an `subtitle` property have the props extend existing `MayHaveSubtitle` interface
103+
104+
### never use object deconstruction
105+
106+
Always name the component properties, named `p`:
107+
108+
```ts
109+
// bad
110+
function SessionButton({ session }: { session: UserSession }) {}
111+
112+
// good
113+
function SessionButton(p: { session: UserSession }) {
114+
// usage: how to access properties
115+
p.session
116+
}
117+
```
118+
### use existing components defined in `src/ui`
119+
120+
examples `ButtonIcon`

0 commit comments

Comments
 (0)