Skip to content

Commit cc50dff

Browse files
committed
docs: update coding standards and folder structure guidelines
- Clarified variable/function naming rules for descriptive effect-based names. - Expanded docstring requirements to include file usage location. - Overhauled React and React Router guidelines including folder conventions, prop typing, mock data loading, and routing meta tags. - Added new refactor rule file prompting application of all rules on request. Generated-by: aiautocommit
1 parent 86c9432 commit cc50dff

5 files changed

Lines changed: 101 additions & 24 deletions

File tree

.cursor/rules/general.mdc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ Coding instructions for all programming languages:
1414
- When I ask you to write code, prioritize simplicity and legibility over covering all edge cases, handling all errors, etc.
1515
- When a particular need can be met with a mature, reasonably adopted and maintained package, I would prefer to use that package rather than engineering my own solution.
1616
- Never add error handling to recover gracefully from an error without being asked to do so. Fail hard and early with assertions and allowing exceptions to propagate whenever possible
17+
- When naming variables or functions, use names that describe the effect. For example, instead of `function handleClaimFreeTicket` (a function which opens a dialog box) use `function openClaimFreeTicketDialog`.
1718

1819
**DO NOT FORGET**: keep your responses short, dense, and without fluff. I am a senior, well-educated software engineer, and do not need long explanations.
1920

.cursor/rules/react-router.mdc

Lines changed: 63 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,26 +5,75 @@ alwaysApply: false
55
---
66
## React Router
77

8+
- You are using the latest version of React Router (v7).
89
- The primary export in a routes file should specify `loaderData` like `export default function RouteNamePage({ loaderData }: Route.ComponentProps)`. `loaderData` is the return value from `clientLoader`.
9-
- When using an import from `~/configuration/client` (1) use `body:` for request params and (2) always `const { data, error } = await theCall()` (3) add `invariant(data, "error loading $xyz")`
10-
- Use `export async function clientLoader(loaderArgs: Route.ClientLoaderArgs)` to define a clientLoader on a route.
11-
- Use `loaderArgs.params.$THE_KEY` to use a query string parameter.
10+
- Use `href("/products/:id", { id: "abc123" })` to generate a url path for a route managed by the application.
11+
- Look at [routes.ts](mdc:web/app/routes.ts) to determine what routes and path parameters exist.
12+
- Use `export async function clientLoader(loaderArgs: Route.ClientLoaderArgs)` to define a `clientLoader` on a route.
1213
- Do not define `Route.*` types, these are autogenerated and can be imported from `import type { Route } from "./+types/routeFileName"`
13-
- Each non-layout route should define a meta function:
14+
- If URL parameters or query string values need to be checked before rendering the page, do this in a `clientLoader` and not in a `useEffect`
15+
- Never worry about generating types using `pnpm`
16+
- Each route should include `<meta/>` elements:
1417

1518
```typescript
16-
export const meta: MetaFunction = () => {
17-
return [
18-
{ title: "Page Title" },
19-
{
20-
name: "description",
21-
content: "Page Description",
22-
},
23-
]
19+
export default function MyRouteComponent() {
20+
return (
21+
<div>
22+
<title>Very cool app</title>
23+
<meta property="og:title" content="Very cool app" />
24+
<meta
25+
name="description"
26+
content="This app is the best"
27+
/>
28+
</div>
29+
)
2430
}
2531
```
2632

33+
- Use the following pattern to reference query string values (i.e. `?theQueryStringParam=value`)
34+
35+
```typescript
36+
const [searchParams, _setSearchParams] = useSearchParams();
37+
// searchParams contains the value of all query string parameters
38+
const queryStringValue = searchParams.get("theQueryStringParam")
39+
```
40+
41+
### Loading Mock Data
42+
43+
Don't load mock data in the component function with `useEffect`. Instead, load data in a `clientLoader`:
44+
45+
```typescript
46+
47+
// in mock.ts
48+
export async function getServerData(options: any) {
49+
// ...
50+
}
51+
52+
// in web/app/routes/**/*.ts
53+
export async function clientLoader(loaderArgs: Route.ClientLoaderArgs) {
54+
// no error reporting is needed, this will be handled by the `getServerData`
55+
// mock loading functions should return result in a `data` key
56+
const { data } = await getServerData({ /* ... */ })
57+
58+
// the return result here is available in `loaderData`
59+
return data
60+
}
61+
```
62+
63+
### How to use clientLoader
64+
65+
- `export async function clientLoader(loaderArgs: Route.ClientLoaderArgs) {`
66+
- Load any server data required for page load here, not in the component function.
67+
- Use `return redirect(href("/the/url"))` to redirect users
68+
- Use [getQueryParam](web/app/lib/utils.ts) to get query string variables
69+
- `throw new Response` if you need to mimic a 400, 500, etc error
70+
- `loaderArgs` and all sub-objects are all fully typed
71+
- `loaderArgs.params.id` to get URL parameters
72+
2773
### Using API Data
2874

29-
* `~/configuration/client` re-exports all types and functions from `client/*`. Import from `~/configuration/client` instead of anything you find in the `client/` folder/package.
30-
* For each API endpoint, there's a fully typed async function that can be used to call it. Never attempt to call an API endpoint directly.
75+
- `~/configuration/client` re-exports all types and functions from `client/*`. Import from `~/configuration/client` instead of anything you find in the `client/` folder/package.
76+
- For each API endpoint, there's a fully typed async function that can be used to call it. Never attempt to call an API endpoint directly.
77+
- When using an import from `~/configuration/client`:
78+
- use `body:` for request params
79+
- always `const { data, error } = await theCall()`

.cursor/rules/react.mdc

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,31 +5,49 @@ alwaysApply: false
55
---
66
## React
77

8+
- You are using the latest version of React (v19)
89
- Do not write any backend code. Just frontend logic.
9-
- For any backend requirements, create mock responses. Use a function to return mock data so I can easily swap it out later.
10-
- When creating mock data, always specify it in a dedicated `mock.ts` file
11-
- Load mock data using a react router `clientLoader`. Use the Skeleton component to present a loading state.
1210
- If a complex skeleton is needed, create a component function `LoadingSkeleton` in the same file.
13-
- Store components for each major page or workflow in `src/components/$WORKFLOW_OR_PAGE_NAME`.
11+
- Store components for each major page or workflow in `app/components/$WORKFLOW/$COMPONENT.tsx`.
12+
- If a single page has more than two dedicated components, create a subfolder `app/components/$WORKFLOW/$PAGE/$COMPONENT.tsx`
1413
- Use lowercase dash separated words for file names.
1514
- Use React 19, TypeScript, Tailwind CSS, and ShadCN components.
1615
- Prefer function components, hooks over classes.
1716
- Break up large components into smaller components, but keep them in the same file unless they can be generalized.
1817
- Put any "magic" strings like API keys, hosts, etc into a "constants.ts" file.
19-
- Only use a separate interface for component props if there are more than 4 props.
20-
- Put the interface definition right above the related function
18+
- For React functional components with three or fewer props, always inline the prop types as an object literal directly in the function signature after the destructured parameters (e.g., `function Component({ prop1, prop2 }: { prop1: string; prop2?: number }) { ... })`. Include default values in destructuring and mark optional props with ? in the type object. Do not use separate interfaces or type aliases; keep types inline. For complex types, add inline comments if needed.
19+
- Put the interface definition right above the related function
2120
- Internally, store all currency values as integers and convert them to floats when rendering visually
22-
- Never edit (or add) `components/ui/*.tsx` files
21+
- Never edit the `components/ui/*.tsx` files
2322
- When building forms use React Hook Form.
2423
- Include a two line breaks between any `useHook()` calls and any `useState()` definitions for a component.
25-
- Use `href("/products/:id", { id: "abc123" })` to generate a url path for a route managed by the application.
26-
- Look at @routes.ts to determine what routes and path parameters exist.
24+
- When using a function prop inside a `useEffect`, please use a pattern that avoids including the function in the dependency array, like the `useRef` trick.s
25+
- Use the following pattern to reference query string values (i.e. `?theQueryStringParam=value`):
26+
27+
```typescript
28+
const [searchParams, _setSearchParams] = useSearchParams();
29+
// searchParams contains the value of all query string parameters
30+
const queryStringValue = searchParams.get("theQueryStringParam")
31+
```
32+
33+
### Mock Data
34+
35+
- For any backend communication, create mock responses. Use a async function to return mock data that I will swap out later for a async call to an API.
36+
- When creating mock data, always specify it in a dedicated `web/app/mock.ts` file
37+
- Load mock data using a react router `clientLoader`. Use the Skeleton component to present a loading state.
2738

2839
### React Hook Form
2940

3041
Follow this structure when generating a form.
3142

3243
```tsx
44+
45+
// add a mock function simulating server communication
46+
async function descriptiveServerSendFunction(values: any) {
47+
const mockData = getMockReturnData(/* ... */)
48+
return new Promise(resolve => setTimeout(() => resolve(mockData), 500));
49+
}
50+
3351
const formSchema = z.object({
3452
field_name: z.string(),
3553
// additional schema definition
@@ -41,6 +59,8 @@ const form = useForm<z.infer<typeof formSchema>>({
4159

4260
async function onSubmit(values: z.infer<typeof formSchema>) {
4361
// ...
62+
await descriptiveSendFunction(values)
63+
// ...
4464
}
4565

4666
return (
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
description:
3+
globs:
4+
alwaysApply: false
5+
---
6+
7+
Refactor this code following all the established coding rules. Carefully review each rule.
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
## TypeScript DocString
22

3-
Add a file-level docstring with a simple description of what this file does.
3+
Add a file-level docstring with a simple description of what this file does and where this is used.

0 commit comments

Comments
 (0)