Skip to content

Commit be7d99d

Browse files
chore(cursor): alphabetical sorting rule (#1188)
Adding rule to help Cursor enforce proper alphabetical sorting of destructured objects and dependency arrays.
1 parent cd1f17d commit be7d99d

1 file changed

Lines changed: 394 additions & 0 deletions

File tree

Lines changed: 394 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,394 @@
1+
# Alphabetical Sorting Rule
2+
3+
## Overview
4+
5+
This rule ensures consistent code organization and improved readability by requiring alphabetical sorting of dependency arrays and destructured objects throughout the codebase.
6+
7+
## Scope
8+
9+
This rule applies to:
10+
11+
- React hook dependency arrays
12+
- Destructured function parameters
13+
- Destructured variable assignments
14+
- Object destructuring in any context
15+
- Array destructuring when meaningful
16+
17+
## React Hook Dependency Arrays
18+
19+
### useEffect
20+
21+
```typescript
22+
// ✅ Correct - alphabetically sorted
23+
useEffect(() => {
24+
// effect logic
25+
}, [elementA, elementB, onUpdate, options, stylesheets]);
26+
27+
// ❌ Incorrect - not sorted
28+
useEffect(() => {
29+
// effect logic
30+
}, [onUpdate, elementA, stylesheets, options, elementB]);
31+
```
32+
33+
### useCallback
34+
35+
```typescript
36+
// ✅ Correct - alphabetically sorted
37+
const handlePress = useCallback(() => {
38+
// handler logic
39+
}, [element, onSelect, onToggle, value]);
40+
41+
// ❌ Incorrect - not sorted
42+
const handlePress = useCallback(() => {
43+
// handler logic
44+
}, [value, onSelect, element, onToggle]);
45+
```
46+
47+
### useMemo
48+
49+
```typescript
50+
// ✅ Correct - alphabetically sorted
51+
const computedValue = useMemo(() => {
52+
return expensive computation;
53+
}, [data, isLoading, userInput]);
54+
55+
// ❌ Incorrect - not sorted
56+
const computedValue = useMemo(() => {
57+
return expensive computation;
58+
}, [userInput, data, isLoading]);
59+
```
60+
61+
### Custom Hooks
62+
63+
```typescript
64+
// ✅ Correct - alphabetically sorted
65+
const { data, error, loading } = useCustomHook([
66+
apiEndpoint,
67+
filters,
68+
pagination,
69+
sortOrder,
70+
]);
71+
72+
// ❌ Incorrect - not sorted
73+
const { data, error, loading } = useCustomHook([
74+
filters,
75+
apiEndpoint,
76+
sortOrder,
77+
pagination,
78+
]);
79+
```
80+
81+
## Function Parameters
82+
83+
### Props Destructuring
84+
85+
```typescript
86+
// ✅ Correct - alphabetically sorted
87+
const MyComponent = ({ className, id, onPress, title }: Props) => {
88+
// component logic
89+
};
90+
91+
// ❌ Incorrect - not sorted
92+
const MyComponent = ({ onPress, title, id, className }: Props) => {
93+
// component logic
94+
};
95+
```
96+
97+
### Function Arguments
98+
99+
```typescript
100+
// ✅ Correct - alphabetically sorted
101+
const processData = ({
102+
filters,
103+
options = {},
104+
sortBy,
105+
userId,
106+
}: ProcessDataParams) => {
107+
// processing logic
108+
};
109+
110+
// ❌ Incorrect - not sorted
111+
const processData = ({
112+
userId,
113+
filters,
114+
options = {},
115+
sortBy,
116+
}: ProcessDataParams) => {
117+
// processing logic
118+
};
119+
```
120+
121+
## Variable Destructuring
122+
123+
### Object Destructuring
124+
125+
```typescript
126+
// ✅ Correct - alphabetically sorted
127+
const { error, isLoading, response, status } = apiCall();
128+
129+
// ❌ Incorrect - not sorted
130+
const { response, error, status, isLoading } = apiCall();
131+
```
132+
133+
### Nested Destructuring
134+
135+
```typescript
136+
// ✅ Correct - alphabetically sorted
137+
const {
138+
user: { email, id, name },
139+
settings: { notifications, theme },
140+
} = userData;
141+
142+
// ❌ Incorrect - not sorted
143+
const {
144+
user: { name, id, email },
145+
settings: { theme, notifications },
146+
} = userData;
147+
```
148+
149+
### With Default Values
150+
151+
```typescript
152+
// ✅ Correct - alphabetically sorted (ignoring defaults)
153+
const { autoSave = true, maxItems = 100, theme = 'light', userId } = config;
154+
155+
// ❌ Incorrect - not sorted
156+
const { userId, theme = 'light', autoSave = true, maxItems = 100 } = config;
157+
```
158+
159+
## Array Destructuring
160+
161+
### When Meaningful
162+
163+
```typescript
164+
// ✅ Acceptable - positional meaning preserved
165+
const [loading, setLoading] = useState(false);
166+
const [first, second, third] = coordinates;
167+
168+
// ✅ Correct - when renaming, sort alphabetically
169+
const [error, loading, response] = [apiError, isLoading, apiResponse];
170+
171+
// ❌ Incorrect - arbitrary reordering loses meaning
172+
const [setLoading, loading] = useState(false); // Wrong order
173+
```
174+
175+
## Import Statements
176+
177+
### Named Imports
178+
179+
```typescript
180+
// ✅ Correct - alphabetically sorted
181+
import {
182+
Component,
183+
Fragment,
184+
useCallback,
185+
useEffect,
186+
useMemo,
187+
useState,
188+
} from 'react';
189+
190+
// ❌ Incorrect - not sorted
191+
import {
192+
useState,
193+
Component,
194+
useEffect,
195+
Fragment,
196+
useMemo,
197+
useCallback,
198+
} from 'react';
199+
```
200+
201+
### Multiple Import Lines
202+
203+
```typescript
204+
// ✅ Correct - each line sorted internally
205+
import { Button, Input, Modal } from './components';
206+
import { formatDate, parseJson, validateEmail } from './utils';
207+
import { API_ENDPOINTS, THEMES } from './constants';
208+
209+
// ❌ Incorrect - not sorted within lines
210+
import { Modal, Button, Input } from './components';
211+
import { validateEmail, formatDate, parseJson } from './utils';
212+
import { THEMES, API_ENDPOINTS } from './constants';
213+
```
214+
215+
## Special Cases
216+
217+
### With Rest Parameters
218+
219+
```typescript
220+
// ✅ Correct - rest parameter at end, others sorted
221+
const { id, name, ...restProps } = props;
222+
const handleUpdate = useCallback(() => {
223+
// logic
224+
}, [id, name, ...otherDeps]); // Sort before spread
225+
```
226+
227+
### With Renamed Properties
228+
229+
```typescript
230+
// ✅ Correct - sort by destructured name, not original
231+
const { id: userId, email: userEmail, name: userName } = user;
232+
```
233+
234+
### Multiline Arrays
235+
236+
```typescript
237+
// ✅ Correct - one item per line when long, sorted alphabetically
238+
const dependencies = [
239+
'element',
240+
'getCommonProps',
241+
'getScrollViewProps',
242+
'hasInputFields',
243+
'onUpdate',
244+
'options',
245+
'stylesheets',
246+
];
247+
248+
// ✅ Also correct - short arrays can be inline
249+
const deps = [element, onUpdate, options];
250+
```
251+
252+
## Formatting Guidelines
253+
254+
### Line Breaks
255+
256+
```typescript
257+
// ✅ Preferred for long dependency arrays
258+
const value = useMemo(() => {
259+
return computation;
260+
}, [longVariableName, anotherLongVariableName, yetAnotherLongVariableName]);
261+
262+
// ✅ Acceptable for short arrays
263+
const value = useMemo(() => computation, [a, b, c]);
264+
```
265+
266+
### Consistent Spacing
267+
268+
```typescript
269+
// ✅ Consistent spacing
270+
const { element, onUpdate, options, stylesheets } = props;
271+
272+
// ❌ Inconsistent spacing
273+
const { element, onUpdate, options, stylesheets } = props;
274+
```
275+
276+
## Exceptions
277+
278+
### When NOT to Apply
279+
280+
1. **Positional Arrays**: When order has semantic meaning (coordinates, RGB values, etc.)
281+
2. **API Requirements**: When external APIs require specific ordering
282+
3. **Performance Critical**: When reordering would affect performance (rare)
283+
4. **Existing Patterns**: When breaking established patterns in legacy code would cause more harm
284+
285+
### Documentation Required
286+
287+
When exceptions are made, document the reason:
288+
289+
```typescript
290+
// Order maintained for API compatibility - do not sort
291+
const [latitude, longitude, altitude] = coordinates;
292+
293+
// Performance critical - order optimized for execution
294+
const deps = [fastCheck, expensiveCheck]; // Fast check first
295+
```
296+
297+
## Benefits
298+
299+
1. **Consistency**: Uniform code style across the entire codebase
300+
2. **Readability**: Easier to scan and find specific dependencies
301+
3. **Merge Conflicts**: Reduces conflicts when multiple developers modify the same arrays
302+
4. **Debugging**: Easier to spot missing or duplicate dependencies
303+
5. **Code Reviews**: Faster to review when formatting is predictable
304+
305+
## Tools and Automation
306+
307+
### ESLint Rules
308+
309+
Consider configuring ESLint rules for automatic sorting:
310+
311+
- `sort-keys` for object properties
312+
- Custom rules for dependency arrays
313+
- `import/order` for import statements
314+
315+
### IDE Configuration
316+
317+
Set up your IDE to:
318+
319+
- Auto-sort on save
320+
- Highlight unsorted arrays
321+
- Provide quick-fix actions
322+
323+
## Migration Strategy
324+
325+
### Existing Code
326+
327+
1. **Gradual Migration**: Apply rule to new code and files being modified
328+
2. **File-by-File**: Update entire files when making substantial changes
329+
3. **Automated Tools**: Use scripts or IDE features for bulk updates
330+
331+
### Team Adoption
332+
333+
1. **Documentation**: Share this rule with the team
334+
2. **Code Reviews**: Enforce during review process
335+
3. **Linting**: Add automated checks to prevent violations
336+
4. **Examples**: Provide clear examples for common patterns
337+
338+
## Examples by Component Type
339+
340+
### React Components
341+
342+
```typescript
343+
const MyComponent = React.memo(
344+
({ element, onUpdate, options, stylesheets }: Props) => {
345+
const [isLoading, setIsLoading] = useState(false);
346+
347+
const handleClick = useCallback(() => {
348+
// logic
349+
}, [element, onUpdate]);
350+
351+
const computedStyle = useMemo(() => {
352+
// computation
353+
}, [element, options, stylesheets]);
354+
355+
return <div />;
356+
},
357+
);
358+
```
359+
360+
### Custom Hooks
361+
362+
```typescript
363+
export const useCustomHook = (dependencies: string[]) => {
364+
const [data, setData] = useState(null);
365+
366+
useEffect(() => {
367+
// effect logic
368+
}, [data, dependencies, setData]);
369+
370+
return { data, isLoading, error };
371+
};
372+
```
373+
374+
### Utility Functions
375+
376+
```typescript
377+
export const processUserData = ({
378+
email,
379+
id,
380+
name,
381+
preferences = {},
382+
}: UserData) => {
383+
const { notifications, privacy, theme } = preferences;
384+
385+
return {
386+
email,
387+
id,
388+
name,
389+
settings: { notifications, privacy, theme },
390+
};
391+
};
392+
```
393+
394+
This rule should be applied consistently across all TypeScript/JavaScript files in the project to maintain code quality and readability.

0 commit comments

Comments
 (0)