Documentation Index Fetch the complete documentation index at: https://mintlify.com/toeverything/AFFiNE/llms.txt
Use this file to discover all available pages before exploring further.
These guidelines help maintain code quality and consistency across the AFFiNE codebase.
Code Style
We use automated tools to enforce consistent formatting:
Prettier : JavaScript, TypeScript, CSS, JSON, Markdown
Rustfmt : Rust code
Taplo : TOML files
# Format all files
yarn lint:fix
# Format specific files
yarn prettier --write path/to/file.ts
Linting
We use ESLint with strict rules:
# Run linter
yarn lint
# Auto-fix issues
yarn lint:eslint:fix
TypeScript Guidelines
Type Safety
Always use explicit types, avoid any:
// Good
function greet ( name : string ) : string {
return `Hello, ${ name } ` ;
}
interface User {
id : string ;
name : string ;
email : string ;
}
// Bad
function greet ( name ) {
return `Hello, ${ name } ` ;
}
const user : any = { id: '123' };
Type Inference
Use type inference where obvious:
// Good: Type is inferred
const name = 'John' ;
const age = 30 ;
const user = { name , age };
// Bad: Unnecessary annotation
const name : string = 'John' ;
const age : number = 30 ;
Strict Null Checks
Handle null/undefined explicitly:
// Good
function getUserName ( user : User | null ) : string {
return user ?. name ?? 'Anonymous' ;
}
// Bad
function getUserName ( user : User ) : string {
return user . name ; // May crash if user is null
}
Generics
Use generics for reusable type-safe code:
// Good
function getById < T >( items : T [], id : string ) : T | undefined {
return items . find ( item => item . id === id );
}
// Bad
function getById ( items : any [], id : string ) : any {
return items . find ( item => item . id === id );
}
React Guidelines
Component Structure
import { type ReactNode } from 'react' ;
interface ButtonProps {
children : ReactNode ;
onClick ?: () => void ;
disabled ?: boolean ;
variant ?: 'primary' | 'secondary' ;
}
export function Button ({
children ,
onClick ,
disabled = false ,
variant = 'primary'
} : ButtonProps ) {
return (
< button
onClick = { onClick }
disabled = { disabled }
className = { variant }
>
{ children }
</ button >
);
}
Hooks
Follow React hooks rules:
// Good
function useWorkspace ( id : string ) {
const [ workspace , setWorkspace ] = useState < Workspace | null >( null );
useEffect (() => {
fetchWorkspace ( id ). then ( setWorkspace );
}, [ id ]);
return workspace ;
}
// Bad: Conditional hooks
function useWorkspace ( id : string | null ) {
if ( ! id ) return null ; // Don't call hooks conditionally
const [ workspace , setWorkspace ] = useState < Workspace | null >( null );
return workspace ;
}
State Management
Use Jotai for global state:
import { atom , useAtom } from 'jotai' ;
const userAtom = atom < User | null >( null );
export function useUser () {
return useAtom ( userAtom );
}
// Usage
function Profile () {
const [ user , setUser ] = useUser ();
return < div >{user?. name } </ div > ;
}
Naming Conventions
Variables & Functions
// camelCase for variables and functions
const userName = 'John' ;
function getUserById ( id : string ) {}
// PascalCase for classes and components
class UserService {}
function UserProfile () {}
// SCREAMING_SNAKE_CASE for constants
const MAX_RETRY_COUNT = 3 ;
const API_BASE_URL = 'https://api.example.com' ;
Files & Folders
// kebab-case for files and folders
user-service.ts
workspace-list.tsx
api/auth/login.ts
// PascalCase for component files
UserProfile.tsx
WorkspaceList.tsx
Booleans
Use clear boolean naming:
// Good
const isLoading = true ;
const hasPermission = false ;
const canEdit = true ;
// Bad
const loading = true ;
const permission = false ;
const edit = true ;
// Good: Explain WHY, not WHAT
function calculateScore ( attempts : number ) : number {
// Penalize excessive retries to prevent brute force
return Math . max ( 0 , 100 - attempts * 10 );
}
// Bad: Comment explains obvious code
function add ( a : number , b : number ) : number {
// Add a and b
return a + b ;
}
JSDoc for Public APIs
/**
* Fetches a workspace by ID.
*
* @param id - The workspace ID
* @returns The workspace object, or null if not found
* @throws {NetworkError} If the request fails
*
* @example
* const workspace = await fetchWorkspace('abc123');
*/
export async function fetchWorkspace ( id : string ) : Promise < Workspace | null > {
// Implementation
}
Error Handling
Throw Meaningful Errors
// Good
function divide ( a : number , b : number ) : number {
if ( b === 0 ) {
throw new Error ( 'Division by zero is not allowed' );
}
return a / b ;
}
// Bad
function divide ( a : number , b : number ) : number {
return a / b ; // May return Infinity or NaN
}
Handle Errors Gracefully
// Good
async function saveWorkspace ( data : Workspace ) {
try {
await api . save ( data );
} catch ( error ) {
if ( error instanceof NetworkError ) {
toast . error ( 'Network error. Please try again.' );
} else {
toast . error ( 'Failed to save workspace.' );
logger . error ( 'Save failed' , error );
}
}
}
// Bad
async function saveWorkspace ( data : Workspace ) {
await api . save ( data ); // Errors crash the app
}
Avoid Unnecessary Re-renders
import { memo , useCallback , useMemo } from 'react' ;
// Memoize expensive components
export const WorkspaceList = memo ( function WorkspaceList ({ workspaces } : Props ) {
return < div >{ /* ... */ } </ div > ;
});
// Memoize callbacks
function Parent () {
const handleClick = useCallback (() => {
console . log ( 'clicked' );
}, []);
return < Child onClick ={ handleClick } />;
}
// Memoize expensive computations
function Stats ({ data } : Props ) {
const stats = useMemo (() => {
return calculateStats ( data ); // Heavy computation
}, [ data ]);
return < div >{ stats } </ div > ;
}
Lazy Loading
import { lazy , Suspense } from 'react' ;
const Settings = lazy (() => import ( './Settings' ));
function App () {
return (
< Suspense fallback = {<Loading />} >
< Settings />
</ Suspense >
);
}
Security
import DOMPurify from 'dompurify' ;
// Good: Sanitize HTML
function RenderHTML ({ html } : Props ) {
const clean = DOMPurify . sanitize ( html );
return < div dangerouslySetInnerHTML ={{ __html : clean }} />;
}
// Bad: XSS vulnerability
function RenderHTML ({ html } : Props ) {
return < div dangerouslySetInnerHTML ={{ __html : html }} />;
}
import { z } from 'zod' ;
const userSchema = z . object ({
name: z . string (). min ( 1 ). max ( 100 ),
email: z . string (). email (),
age: z . number (). min ( 0 ). max ( 150 )
});
function createUser ( data : unknown ) {
const validated = userSchema . parse ( data );
// Safe to use validated data
}
Protect Sensitive Data
// Good: Don't log sensitive data
logger . info ( 'User logged in' , { userId: user . id });
// Bad: Logs password
logger . info ( 'User logged in' , { user });
Git Workflow
Commit Messages
Follow Conventional Commits :
feat: add workspace sharing
fix: resolve sync conflict
docs: update API documentation
style: format code with prettier
refactor: simplify auth logic
test: add workspace tests
chore: update dependencies
Branch Naming
feat/workspace-sharing
fix/sync-conflict
docs/api-documentation
refactor/auth-logic
Pull Requests
Good PR:
Clear title and description
Linked to issue
Small, focused changes
Tests included
Documentation updated
Example:
## Description
Adds workspace sharing functionality allowing users to invite collaborators.
## Changes
- Added share dialog component
- Implemented invite API endpoint
- Added permission checks
## Testing
- [ x ] Unit tests added
- [ x ] E2E tests added
- [ x ] Manually tested
Fixes #123
Code Review
As a Reviewer
Be respectful and constructive
Focus on code, not the person
Explain your suggestions
Approve when ready, don’t nitpick
As an Author
Respond to all comments
Don’t take feedback personally
Ask questions if unclear
Update and re-request review
Documentation
Code Documentation
/**
* Service for managing workspaces.
*
* Handles workspace creation, updates, deletion,
* and permission management.
*/
export class WorkspaceService {
/**
* Creates a new workspace.
*
* @param name - The workspace name
* @param ownerId - The owner's user ID
* @returns The created workspace
*/
async createWorkspace ( name : string , ownerId : string ) : Promise < Workspace > {
// Implementation
}
}
README Files
Every package should have a README:
# Package Name
Brief description of the package.
## Installation
How to install and use the package.
## Usage
Code examples showing common use cases.
## API
List of main exports and their purpose.
Development Setup Set up your local environment
Testing Guide Learn how to write tests
Architecture Understand the codebase
TypeScript Handbook TypeScript documentation