Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 129 additions & 0 deletions .cursorrules
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
# Hocuspocus Cursor Rules

## Testing Conventions

### Test Organization
- Place test files in `__tests__` directories next to the code they test
- Use the naming convention: `<filename-no-extension>.spec.ts`
- Example: For `packages/server/src/Hocuspocus.ts`, create `packages/server/src/__tests__/Hocuspocus.spec.ts`

### Test Framework
- Use Vitest as the test runner
- Import test utilities from `vitest`:
```typescript
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
```

### Test Structure
- Use `describe` blocks to group related tests
- Use descriptive test names that explain what is being tested
- Follow the Arrange-Act-Assert pattern:
```typescript
describe('MyClass', () => {
it('should do something specific', () => {
// Arrange: Set up test data and dependencies
const input = 'test';

// Act: Execute the code being tested
const result = myFunction(input);

// Assert: Verify the expected outcome
expect(result).toBe('expected');
});
});
```

### Mocking
- Use `vi.fn()` for mock functions
- Use `vi.mock()` for module mocking
- Use `vi.spyOn()` for spying on existing methods
- Clean up mocks in `afterEach` hooks

### Async Testing
- Use `async/await` for asynchronous tests
- Always await promises in tests
- Use `waitFor` utilities when testing async behavior

### Coverage
- Aim for high test coverage
- Focus on testing public APIs and critical paths
- Don't test trivial code (getters, setters, simple formatters)
- Test edge cases and error conditions

## Type Safety

### Strict Type Checking
- **Never use `any` type** - it defeats the purpose of TypeScript
- Use specific types or generics instead of `any`
- If the type is truly unknown, use `unknown` and narrow it with type guards
- Use `Record<string, SomeType>` for objects with dynamic keys
- Use union types (`string | number`) instead of `any`

### Type Inference
- Let TypeScript infer types when possible
- Add explicit types for:
- Function parameters
- Public API return types
- Complex types that benefit from clarity

### Type Guards
- Use type guards to narrow `unknown` types:
```typescript
function isString(value: unknown): value is string {
return typeof value === 'string';
}
```

### Generic Types
- Use generics for reusable, type-safe code:
```typescript
function identity<T>(value: T): T {
return value;
}
```

### Utility Types
- Use TypeScript utility types:
- `Partial<T>` - Make all properties optional
- `Required<T>` - Make all properties required
- `Pick<T, K>` - Pick specific properties
- `Omit<T, K>` - Omit specific properties
- `Record<K, T>` - Object type with specific key/value types

## Code Quality

### Linting
- Run `npm run lint` before committing
- Fix all linting errors before submitting PR
- Use `npm run lint:fix` for auto-fixable issues

### Best Practices
- Write self-documenting code with clear variable and function names
- Keep functions small and focused on a single responsibility
- Use early returns to reduce nesting
- Prefer const over let, never use var
- Use optional chaining (`?.`) and nullish coalescing (`??`) operators
- Avoid deep nesting - extract complex logic into separate functions

### Comments
- Write comments for complex algorithms or non-obvious code
- Don't comment obvious code
- Use JSDoc for public APIs
- Keep comments up-to-date with code changes

## Git Workflow

### Commits
- Write clear, descriptive commit messages
- Use conventional commit format when possible:
- `feat:` for new features
- `fix:` for bug fixes
- `refactor:` for code refactoring
- `test:` for test changes
- `docs:` for documentation changes

### Pull Requests
- Include tests for new features and bug fixes
- Ensure all tests pass before submitting
- Include a clear description of changes
- Link to related issues
42 changes: 42 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: CI

on:
pull_request:
branches:
- master
- main

jobs:
lint-and-test:
name: Lint and Test
runs-on: ubuntu-latest

strategy:
matrix:
node-version: [22.x]

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'

- name: Install dependencies
run: npm ci

- name: Run linter
run: npm run lint

- name: Run tests with coverage
run: npm run test:coverage

- name: Print coverage summary
run: |
echo "## Test Coverage Summary" >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
cat coverage/coverage-summary.json | node -e "const data = JSON.parse(require('fs').readFileSync('/dev/stdin', 'utf8')); console.log('Lines: ' + data.total.lines.pct + '%'); console.log('Statements: ' + data.total.statements.pct + '%'); console.log('Functions: ' + data.total.functions.pct + '%'); console.log('Branches: ' + data.total.branches.pct + '%');" 2>/dev/null || echo "Coverage summary not available" >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
10 changes: 9 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
{
"editor.defaultFormatter": "dbaeumer.vscode-eslint"
"editor.defaultFormatter": "dbaeumer.vscode-eslint",
"files.exclude": {
"**/node_modules": true,
"**/dist": true,
"**/build": true,
"**/out": true,
"**/target": true,
"**/tmp": true,
}
}
12 changes: 11 additions & 1 deletion biome.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,17 @@
"linter": {
"rules": {
"suspicious": {
"noAsyncPromiseExecutor": "off"
"noAsyncPromiseExecutor": "off",
"noConfusingVoidType": "off",
"noExportsInTest": "off"
},
"complexity": {
"noForEach": "off",
"noBannedTypes": "off"
},
"style": {
"noParameterAssign": "off",
"noNonNullAssertion": "off"
}
}
}
Expand Down
Loading
Loading