Skip to main content

Quality Gate

All five checks must pass before any commit.
Run these commands before committing:
npm run lint          # ESLint
npm run type-check    # tsc --noEmit
npm run pretty:check  # Prettier check
npm run knip          # Dead code detection
npm test              # Jest
If any check fails, fix the issues and verify again. Only claim what you actually ran.

Linting

Run ESLint

npm run lint
ESLint checks for:
  • Code style violations
  • Unused imports
  • Common programming errors
  • React/React Native best practices

Configuration

ESLint is configured with:
  • eslint-config-expo - Expo’s recommended config
  • eslint-config-prettier - Disables rules that conflict with Prettier
  • eslint-plugin-prettier - Runs Prettier as an ESLint rule
  • eslint-plugin-unused-imports - Detects and removes unused imports
The linter will auto-fix many issues. Check the output for remaining manual fixes.

Type Checking

Run TypeScript Compiler

npm run type-check
This runs tsc --noEmit to check TypeScript types without generating output files.

Common Type Errors

Always import types from coco-cashu-core, never redefine them.
// ✅ Correct
import { Proof, MintKeys } from 'coco-cashu-core';

// ❌ Wrong - don't redefine coco types
type Proof = { amount: number; secret: string };
Import from coco, not the underlying @cashu/cashu-ts library.
// ✅ Correct
import { Proof } from 'coco-cashu-core';

// ❌ Wrong
import { Proof } from '@cashu/cashu-ts';
Check .cursor/rules/zustand-store-scoping.mdc for store type patterns and scoping rules.

Code Formatting

Check Formatting

npm run pretty:check

Auto-Fix Formatting

npm run pretty
Prettier formats:
  • JavaScript/TypeScript files (.js, .jsx, .ts, .tsx)
  • JSON files
  • Module formats (.mjs, .cjs)

Configuration

Prettier is configured with:
  • prettier-plugin-tailwindcss - Automatically sorts Tailwind classes
Most editors can auto-format on save. Configure your editor to run Prettier automatically.

Dead Code Detection

Run Knip

npm run knip
Knip finds:
  • Unused exports
  • Unused dependencies in package.json
  • Unreferenced files
  • Duplicate exports

What to do with Knip results

1

Review the output

Knip will list unused exports, files, and dependencies.
2

Verify before deleting

Some exports may be used by external tools or in ways Knip can’t detect. Verify before removing.
3

Remove dead code

Delete genuinely unused code. Keep the codebase lean.

Testing

Run Tests

npm test
This runs the Jest test suite.

Test Configuration

Tests use:
  • jest - Testing framework
  • jest-expo - Expo-specific Jest preset
  • @types/jest - TypeScript definitions for Jest

Writing Tests

import { renderHook } from '@testing-library/react-hooks';
import { useProcessPaymentString } from '@/hooks/coco/useProcessPaymentString';

describe('useProcessPaymentString', () => {
  it('should parse a Cashu token', () => {
    const { result } = renderHook(() => useProcessPaymentString());
    const parsed = result.current.parse('cashuAey...');
    
    expect(parsed.type).toBe('token');
    expect(parsed.token).toBeDefined();
  });
});

Styling Standards

Use Semantic Tokens

Prefer semantic tokens over raw hex colors.
// ✅ Correct - semantic tokens
className="bg-surface-secondary text-foreground"

// ❌ Wrong - raw hex colors
style={{ backgroundColor: '#1a1a1a', color: '#ffffff' }}

Runtime Color Values

Use useThemeColor for dynamic color access:
import { useThemeColor } from '@/hooks/useThemeColor';

const MyComponent = () => {
  const dangerColor = useThemeColor('danger');
  const [primary, secondary] = useThemeColor(['primary', 'secondary']);
  
  return <View style={{ borderColor: dangerColor }} />;
};
For multiple colors, use the array form to reduce hooks.

Prefer className Over style

// ✅ Correct - Tailwind className
<View className="flex-1 bg-surface p-4" />

// ❌ Avoid - inline styles (use only when dynamic)
<View style={{ flex: 1, backgroundColor: '#fff', padding: 16 }} />

Performance Best Practices

List Performance

// ✅ Correct - stable function reference
const keyExtractor = useCallback((item: Transaction) => item.id, []);

<FlatList
  data={transactions}
  keyExtractor={keyExtractor}
  renderItem={renderItem}
/>

Avoid Unnecessary Memoization

// ✅ Good - expensive computation
const sortedItems = useMemo(
  () => items.sort((a, b) => b.timestamp - a.timestamp),
  [items]
);

// ❌ Unnecessary - simple value
const fullName = useMemo(() => `${first} ${last}`, [first, last]);

Loading States

Use HeroUI Components

import { Spinner } from 'heroui-native';
import { Text } from '@/components/ui/Text';

// ✅ Correct - HeroUI Spinner
{isLoading && <Spinner />}

// ✅ Correct - Text auto-skeleton (nullish children)
<Text>{userName}</Text> {/* Shows skeleton when userName is null/undefined */}
Skeletons must not cause layout shift. Match final dimensions and preserve container structure.

Skeleton Best Practices

// ✅ Correct - auto-skeleton with nullish children
<Text className="text-xl">{balance}</Text>

// ❌ Wrong - separate Skeleton wrapper causes layout shift
{isLoading ? (
  <Skeleton height={24} width={100} />
) : (
  <Text className="text-xl">{balance}</Text>
)}

Comments and JSDoc

No JSDoc that restates TypeScript types.

When to add JSDoc

Add JSDoc only for:
  • Intent (why this exists)
  • Constraints (limits, edge cases)
  • UX rules (user-facing behavior)
  • Async contracts (timing, dependencies)
  • Invariants (must-hold conditions)
/**
 * Processes a payment string (token, invoice, payment request).
 * Returns null if the string is invalid or not a known payment type.
 * 
 * Note: This hook debounces input to avoid excessive parsing.
 */
export function useProcessPaymentString() {
  // ...
}

Quick Reference

CommandPurpose
npm run lintRun ESLint
npm run type-checkRun TypeScript type checking
npm run prettyFormat code with Prettier
npm run pretty:checkCheck formatting without writing
npm run knipFind unused exports/dependencies
npm testRun Jest tests
All five must pass before committing: lint, type-check, pretty:check, knip, test.

Build docs developers (and LLMs) love