Feature request for product/service
Cursor IDE
Describe the request
I provide a simple task to the AI agent to do in a large workspace, it does 5% of the work, I want an iterate mode that does it once, creates branch or commit, does it again, create branch or commit, does it again, and so on
the use case I’m requesting this feature for is this command
description: Reusability Rules
globs: “**/*.{ts,tsx,js,jsx}”
alwaysApply: true
Reusability Rules
While converting a component to a reusable component, props must not include dynamic code, like classNames, components, functions, icons, etc… in such case, create variants or a structured typescript interface that builds what’s the code was intended to do.
Data-Driven Rendering
Never duplicate components that are based on a list or array of data. Always use array methods (.map(), .filter(), etc.) to render components from data structures instead of manually duplicating JSX elements.
Bad Example:
<div>
<Card>Item 1</Card>
<Card>Item 2</Card>
<Card>Item 3</Card>
</div>
Good Example:
const items = [
{ id: 1, name: "Item 1" },
{ id: 2, name: "Item 2" },
{ id: 3, name: "Item 3" },
];
<div>
{items.map((item) => (
<Card key={item.id}>{item.name}</Card>
))}
</div>;
This approach ensures:
- Single source of truth for data
- Easier maintenance and updates
- Consistent rendering patterns
- Better performance through proper key usage
- Reduced code duplication
Data Storage Location
Always store arrays, configuration data, and static data structures in the components/config directory (or components/config/nodes/ for domain-specific configs). Never define data arrays directly in component files.
Bad Example:
// app/pages/list/page.tsx
export default function ListPage() {
const items = [
{ link: "/accounts", name: "Accounts", description: "..." },
{ link: "/companies", name: "Companies", description: "..." },
];
return <div>{items.map(...)}</div>;
}
Good Example:
// components/config/list-items.config.ts
export interface ListItem {
link: string;
name: string;
description: string;
}
export const LIST_ITEMS: ListItem[] = [
{ link: "/accounts", name: "Accounts", description: "..." },
{ link: "/companies", name: "Companies", description: "..." },
];
// app/pages/list/page.tsx
import { LIST_ITEMS } from "@/components/config/list-items.config";
export default function ListPage() {
return <div>{LIST_ITEMS.map(...)}</div>;
}
Benefits:
- Centralized data management
- Reusable across multiple components
- Easier to maintain and update
- Clear separation of concerns
- Better testability
Code Refactoring Guidelines
When refactoring code, follow these principles to improve maintainability, reduce duplication, and enhance reusability:
1. Delete Unused Code
Identify and remove:
- Unused imports, variables, functions, and components
- Commented-out code blocks
- Dead code paths that are never executed
- Unused type definitions and interfaces
- Unused configuration values
Using the Unused Exports Script:
Before refactoring or cleaning up code, use the automated script to identify unused exports:
pnpm run find-unused-exports
This script:
- Analyzes your TypeScript project using
ts-unused-exports - Generates a report file
unused-exports.txtin the project root - Lists all unused exports with their file paths and line numbers
- Excludes paths configured in
scripts/unused-exports.ts(e.g.,components/ui)
Report Format:
- File paths with unused exports
- Export names and their locations (line:character)
- Total count of unused exports
- Completely unused files (if found)
Before removing code, verify:
- Code is not referenced in other files (use search/grep or check the unused-exports report)
- Code is not used dynamically or via string references
- Code is not part of a public API that external code might use
- Review the
unused-exports.txtreport to identify safe-to-remove exports
2. Convert Duplicate or Similar Code to Reusable Components/Interfaces
When you find duplicate or similar code:
- Extract common patterns into reusable components
- Create interfaces/types for shared data structures
- Use composition to build variations from base components
- Implement variant patterns (using libraries like
class-variance-authorityorcva) instead of duplicating similar components
Process:
- Identify the commonalities between duplicated code
- Extract the shared logic/structure into a reusable component/interface
- Parameterize the differences as props/interface properties
- Replace all duplicates with the new reusable version
- Verify functionality remains unchanged
Example - Duplicate Components:
// Bad: Duplicate components
function UserCardA({ name, email }) {
return (
<Card>
<h3>{name}</h3>
<p>{email}</p>
</Card>
);
}
function UserCardB({ name, email }) {
return (
<Card>
<h3>{name}</h3>
<p>{email}</p>
</Card>
);
}
// Good: Single reusable component
interface UserCardProps {
name: string;
email: string;
}
function UserCard({ name, email }: UserCardProps) {
return (
<Card>
<h3>{name}</h3>
<p>{email}</p>
</Card>
);
}
3. Split Complex Components/Interfaces into Multiple Reusable Components/Interfaces
When to split:
- Component has multiple responsibilities (violates Single Responsibility Principle)
- Component is difficult to understand or test
- Component has grown too large (>300 lines is a common indicator)
- Parts of the component could be reused independently
- Interface has too many optional properties or nested structures
How to split:
- Extract sub-components that represent distinct UI sections
- Create smaller, focused interfaces for specific use cases
- Use composition to combine smaller components
- Extract custom hooks for complex state/logic
- Split large interfaces into base interfaces with extensions
Example - Splitting Complex Component:
// Bad: Monolithic component
function UserProfile({ user, posts, settings, notifications }) {
// 200+ lines of mixed concerns
return <div>{/* User info, posts, settings, notifications all mixed */}</div>;
}
// Good: Split into focused components
function UserProfile({ user }) {
return (
<div>
<UserHeader user={user} />
<UserStats user={user} />
<UserPosts userId={user.id} />
</div>
);
}
function UserHeader({ user }: { user: User }) {
return <div>{/* User header content */}</div>;
}
function UserStats({ user }: { user: User }) {
return <div>{/* Stats content */}</div>;
}
function UserPosts({ userId }: { userId: string }) {
return <div>{/* Posts content */}</div>;
}
Example - Splitting Complex Interface:
// Bad: Complex interface with many concerns
interface User {
id: string;
name: string;
email: string;
profilePicture?: string;
bio?: string;
role: string;
permissions: string[];
address?: {
street: string;
city: string;
country: string;
};
preferences?: {
theme: string;
notifications: boolean;
};
metadata?: Record<string, unknown>;
}
// Good: Split into focused interfaces
interface BaseUser {
id: string;
name: string;
email: string;
}
interface UserProfile extends BaseUser {
profilePicture?: string;
bio?: string;
}
interface UserAddress {
street: string;
city: string;
country: string;
}
interface UserPreferences {
theme: string;
notifications: boolean;
}
interface User extends UserProfile {
role: string;
permissions: string[];
address?: UserAddress;
preferences?: UserPreferences;
metadata?: Record<string, unknown>;
}
4. Combine Similar Reusable Components/Interfaces into One Reusable Component/Interface
When to combine:
- Components share most of their implementation (>70% overlap)
- Components only differ by a few props or variant styles
- Maintaining separate components creates maintenance burden
- Interfaces have significant overlap and could share a base
How to combine:
- Use variant props or discriminated unions for differences
- Make optional props for features that only some variants need
- Use composition patterns where appropriate
- Create base interfaces and extend them for specific cases
- Use conditional rendering based on props rather than separate components
Example - Combining Similar Components:
// Bad: Separate components with minor differences
function PrimaryButton({ children, onClick }) {
return (
<button className="bg-blue-500" onClick={onClick}>
{children}
</button>
);
}
function SecondaryButton({ children, onClick }) {
return (
<button className="bg-gray-500" onClick={onClick}>
{children}
</button>
);
}
function DangerButton({ children, onClick }) {
return (
<button className="bg-red-500" onClick={onClick}>
{children}
</button>
);
}
// Good: Single component with variants
interface ButtonProps {
variant?: "primary" | "secondary" | "danger";
children: React.ReactNode;
onClick: () => void;
}
const buttonVariants = {
primary: "bg-blue-500",
secondary: "bg-gray-500",
danger: "bg-red-500",
};
function Button({ variant = "primary", children, onClick }: ButtonProps) {
return (
<button className={buttonVariants[variant]} onClick={onClick}>
{children}
</button>
);
}
Example - Combining Similar Interfaces:
// Bad: Separate interfaces with overlap
interface CreateUserRequest {
name: string;
email: string;
password: string;
}
interface UpdateUserRequest {
id: string;
name?: string;
email?: string;
password?: string;
}
// Good: Combined with discriminated union or base interface
interface BaseUserRequest {
name?: string;
email?: string;
password?: string;
}
interface CreateUserRequest extends Omit<BaseUserRequest, "name" | "email"> {
name: string;
email: string;
password: string;
}
interface UpdateUserRequest extends BaseUserRequest {
id: string;
}
// Or using discriminated union:
type UserRequest =
| ({ type: "create" } & CreateUserRequest)
| ({ type: "update" } & UpdateUserRequest);
Refactoring Checklist
Before refactoring:
- Identify all usages of code to be refactored
- Understand the full context and dependencies
- Ensure tests exist or write tests before refactoring
- Plan the refactoring approach
During refactoring:
- Make one change at a time
- Keep the code functional at each step
- Update all references to the refactored code
- Maintain backward compatibility if it’s a public API
- Update types/interfaces consistently
After refactoring:
- Verify all functionality works as before
- Run tests to ensure nothing broke
- Check for any new linting errors
- Remove unused code that resulted from refactoring
- Run
pnpm run find-unused-exportsto verify dead code was removed - Update documentation if needed