Skip to content

Latest commit

 

History

History
188 lines (148 loc) · 8.8 KB

File metadata and controls

188 lines (148 loc) · 8.8 KB

Project Overview

React Native + Expo application using TypeScript. All code must be type-safe with zero any types outside test files. Theme system is mandatory for all styling. Server state uses React Query exclusively. Navigation is centralized in a single router file.

Code Style

Naming Conventions

  • File names: kebab-case for components (user-profile.tsx), kebab-case for utilities (format-date.ts), UPPER_SNAKE_CASE for constants (API_BASE_URL.ts)
  • Component files must export exactly one default export: the component itself
  • Hook files start with use- prefix and export named function: export function useUserData() {}
  • Utility files export named functions only, never default exports
  • Variable names: camelCase for all variables, functions, and object keys
  • Boolean variables must start with is, has, can, or should: isLoading, hasError, canSubmit
  • Private functions in files start with underscore: _formatInternalDate()
  • Type names: PascalCase for all types, interfaces, and enums
  • Enum values: UPPER_SNAKE_CASE inside enums

TypeScript Requirements

  • Never use any type. Use unknown with type guards if needed
  • Never use as type assertions outside .test.ts files. Use type guards or Zod validation instead
  • All function parameters must have explicit types. No implicit any
  • All function return types must be explicit. No implicit inference
  • Use satisfies operator for object literals that must match a type
  • All object literals in code must have explicit type annotations: const config: AppConfig = {}
  • Discriminated unions required for error states: type Result = { status: 'success'; data: T } | { status: 'error'; error: Error }

Formatting

  • Max line length: 100 characters
  • Indent: 2 spaces
  • Trailing commas in multi-line objects, arrays, and function parameters
  • No semicolons at end of statements
  • Use single quotes for strings
  • Use template literals for any string with variables

File Structure

Directory Layout

src/
├── app/                    # Navigation and app entry
│   └── router.tsx         # ONLY file with navigation logic
├── components/            # Reusable UI components
│   ├── buttons/
│   ├── inputs/
│   ├── layouts/
│   └── [component-name].tsx
├── hooks/                 # Custom React hooks
│   └── use-[hook-name].ts
├── screens/              # Full-screen components
│   └── [screen-name].tsx
├── services/             # API clients and external services
│   ├── api-client.ts
│   └── [service-name].ts
├── store/                # React Query and state management
│   ├── queries.ts
│   └── mutations.ts
├── theme/                # Design tokens and styling
│   ├── colors.ts
│   ├── spacing.ts
│   ├── typography.ts
│   └── theme-context.tsx
├── types/                # Shared TypeScript types
│   └── [domain].types.ts
├── utils/                # Utility functions
│   └── [utility-name].ts
└── constants/            # App-wide constants
    └── [constant-name].ts
  • No nested component directories deeper than 2 levels
  • No component files in utils/ or services/
  • No utility functions in components/
  • All theme values must live in theme/ directory only

Import Rules

  • Absolute imports only: import { Button } from '@/components/buttons/button'
  • No relative imports (../../../)
  • Group imports: React/Expo first, then third-party, then local (blank line between groups)
  • Import types with import type: import type { User } from '@/types/user.types'

Patterns

Styling

  • All colors must come from useTheme() hook, never hardcoded hex values
  • All spacing must use theme.spacing values, never arbitrary numbers
  • All font sizes must use theme.typography, never arbitrary sizes
  • StyleSheet.create() must reference theme values via closure: const styles = (theme: Theme) => StyleSheet.create({...})
  • Every screen component must wrap content in <KeyboardAvoidingView behavior={Platform.OS === 'ios' ? 'padding' : 'height'} />
  • Platform-specific code must use Platform.select() or Platform.OS === 'ios' checks, never assume iOS behavior

Server State Management

  • All API calls must use React Query hooks from store/queries.ts
  • Never use useState for server data. Use useQuery instead
  • Mutations must use useMutation from store/mutations.ts
  • Query keys must be defined in store/queries.ts as constants: export const userKeys = { all: ['users'], detail: (id: string) => ['users', id] }
  • All queries must have explicit staleTime and cacheTime set
  • Infinite queries must use useInfiniteQuery with getNextPageParam defined

Navigation

  • All navigation logic must live in app/router.tsx only
  • No useNavigation() calls in components for navigation decisions
  • Components receive navigation callbacks as props: onPress={() => navigation.navigate('Detail', { id })}
  • Screen components must not import navigation types directly
  • Deep linking configuration must be in app/router.tsx with explicit type-safe route params

Error Handling

  • Create typed error class: class AppError extends Error { constructor(public code: string, message: string) {} }
  • Every async function must have try/catch with typed error handling
  • Caught errors must be checked: catch (err) { if (err instanceof AppError) { ... } else if (err instanceof NetworkError) { ... } }
  • Never catch with catch (e: any) or bare catch
  • API responses must validate with Zod before use: const user = userSchema.parse(response.data)
  • Error messages shown to users must come from constants/messages.ts, never inline strings
  • Network errors must be caught separately and mapped to user-friendly messages

Input Validation

  • All form inputs must validate with Zod schemas defined in types/[domain].types.ts
  • Validation must happen at component boundary before API calls
  • API responses must validate with Zod before storing in React Query cache
  • Never trust API response shape — always parse with schema

Async Operations

  • All async operations in components must use useEffect with cleanup
  • Async operations in useEffect must be wrapped in named function: useEffect(() => { async function load() {} load() }, [])
  • Never use async directly on useEffect callback
  • All promises must be awaited or explicitly handled with .catch()
  • Abort controllers required for fetch calls: const controller = new AbortController(); fetch(url, { signal: controller.signal })

Component Structure

  • Functional components only, no class components
  • Props interface must be defined above component: interface UserCardProps { userId: string; onPress: () => void }
  • Component must destructure props in signature: export default function UserCard({ userId, onPress }: UserCardProps)
  • Hooks must be called at top level before any conditional logic
  • Component must return JSX directly, no intermediate variables for JSX
  • Max component file size: 300 lines (split into smaller components if larger)

Testing

Test File Location and Naming

  • Test files must be colocated with source: src/components/button.tsxsrc/components/button.test.tsx
  • Test file naming: [filename].test.ts or [filename].test.tsx
  • No separate __tests__ directories
  • Integration tests in src/__tests__/integration/ with [feature].integration.test.ts naming

Test Requirements

  • Every utility function must have unit tests covering happy path and error cases
  • Every custom hook must have tests using @testing-library/react-native
  • Every screen component must have at least one integration test
  • API integration points must have tests mocking React Query
  • Error handling code must have explicit tests for each error type
  • Navigation logic in app/router.tsx must have tests for all route transitions

Test Patterns

  • Use describe() blocks to group related tests
  • Test names must start with should: it('should return user data when API succeeds')
  • Mock React Query with QueryClient in test setup
  • Mock navigation with @react-navigation/native test utilities
  • Mock theme with ThemeProvider wrapper in test setup
  • All async tests must use waitFor() from testing library
  • Never use act() directly — use waitFor() instead
  • Mock API calls with msw (Mock Service Worker), never inline mocks

Coverage Requirements

  • Minimum 80% line coverage for utils/, hooks/, and services/
  • Minimum 60% coverage for components and screens
  • 100% coverage for error handling paths
  • All error branches must be explicitly tested

Source: Codelibrium — the marketplace for AI behaviour files. Browse multiple rulesets at codelibrium.com or install via CLI: npx codelibrium-cli install <ruleset-name>