--- description: Code style guidelines for Twenty CRM globs: [] alwaysApply: true --- # Code Style Guidelines ## Formatting Standards - **Prettier**: 2-space indentation, single quotes, trailing commas, semicolons - **Print width**: 80 characters - **Oxlint**: No unused imports, consistent import ordering, prefer const over let ## Naming Conventions ```typescript // ✅ Variables and functions - camelCase const userAccountBalance = 1000; const calculateMonthlyPayment = () => {}; // ✅ Constants - SCREAMING_SNAKE_CASE const API_ENDPOINTS = { USERS: '/api/users', ORDERS: '/api/orders', } as const; // ✅ Types and Classes - PascalCase class UserService {} type UserAccountData = {}; type ButtonProps = {}; // Component props suffix with 'Props' // ✅ Files and directories - kebab-case // user-profile.component.tsx // user-profile.styles.ts // ❌ NEVER use abbreviations in variable names // Bad const users = data.map((u) => u.name); const field = items.find((f) => f.id === id); // Good const users = data.map((user) => user.name); const field = items.find((item) => item.id === id); const fieldMetadata = inlineFields.find( (fieldMetadataItem) => fieldMetadataItem.name === fieldName, ); ``` ## Import Organization ```typescript // ✅ Correct import order // 1. External libraries import React from 'react'; import { useCallback } from 'react'; import styled from 'styled-components'; // 2. Internal modules (absolute paths) import { Button } from '@/components/ui'; import { UserService } from '@/services'; // 3. Relative imports import { UserCardProps } from './types'; ``` ## Function Structure ```typescript // ✅ Small, focused functions // ✅ Required parameters first, optional last const processUserData = ( user: User, options: ProcessingOptions, callback?: (result: ProcessedUser) => void ): ProcessedUser => { const processedUser = transformUserData(user); applyOptions(processedUser, options); if (callback) { callback(processedUser); } return processedUser; }; ``` ## Comments ```typescript // ✅ Use short-form comments, NOT JSDoc blocks // ✅ Explain business logic and non-obvious intentions (WHY, not WHAT) // Apply 15% discount for premium users with orders > $100 const discount = isPremiumUser && orderTotal > 100 ? 0.15 : 0; // TODO: Replace with proper authentication service const isAuthenticated = localStorage.getItem('token') !== null; // ✅ Multi-line comments use multiple // lines (NOT /** */ blocks) // Calculates the total price after applying tax and discount // Returns the final price that should be charged to the customer const calculateTotalPrice = (basePrice: number): number => { // Implementation }; // ❌ AVOID obvious comments that just describe what code does // Bad: Get all inline fields dynamically const { inlineFieldMetadataItems } = useFieldListFieldMetadataItems({...}); // Bad: Define standard fields in display order const standardFieldOrder = ['startsAt', 'endsAt', 'conferenceLink']; // Bad: Split fields into standard and custom const standardFields = standardFieldOrder.map(...) // ✅ GOOD: Only comment if explaining non-obvious business logic // Calendar events display standard fields first, then custom fields after participants // to maintain consistency with the legacy UI behavior const standardFields = standardFieldOrder.map(...) // ❌ AVOID JSDoc blocks - use short comments instead /** * This style is NOT preferred in this codebase */ ``` **Comment Guidelines:** - **DO** comment complex business rules or domain-specific logic - **DO** comment non-obvious algorithmic decisions - **DO** add TODOs for future improvements - **DON'T** comment obvious variable declarations or function calls - **DON'T** comment what is already clear from well-named variables/functions - **DON'T** add comments that just repeat what the code says ## Utility Helpers ```typescript // ✅ Use existing utility helpers instead of manual checks import { isDefined } from 'twenty-shared/utils'; import { isNonEmptyString, isNonEmptyArray } from '@sniptt/guards'; // ❌ Manual type guards const validItems = items.filter((item): item is Item => item !== undefined); const hasValue = value !== null && value !== undefined; // ✅ Use utility helpers const validItems = items.filter(isDefined); const hasValue = isDefined(value); // Other useful helpers: // - isDefined(value) - checks !== null && !== undefined // - isNonEmptyString(value) - checks string is defined and not empty // - isNonEmptyArray(value) - checks array is defined and has items ``` ## Security Patterns ```typescript // ✅ CSV Export: Always apply security first, then formatting const safeValue = formatValueForCSV(sanitizeValueForCSVExport(userInput)); // ✅ Input validation before processing const sanitizedInput = validateAndSanitize(userInput); const result = processData(sanitizedInput); ``` ## Error Handling ```typescript // ✅ Proper error types and meaningful messages try { const user = await userService.findById(userId); if (!user) { throw new UserNotFoundError(`User with ID ${userId} not found`); } return user; } catch (error) { logger.error('Failed to fetch user', { userId, error }); throw error; } ```