This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
npm start- Start development server (IMPORTANT: Never run this command directly; ask the user to start the server as needed)npm run build- Build production versionnpm run lint- Run ESLintnpm run lint-fix- Run ESLint with auto-fixnpm run format- Run Prettiernpm run test- Run all testsnpm run test:unit- Run unit testsnpm run test:integration- Run integration testsnpm run test:cpu- Run CPU compatibility testsnpm run ci-checks- Run linting checks for CIvitest run tests/unit/test-gzip.js- Run a single test file
npm run coverage:unit- Run unit tests with coveragenpm run coverage:all-tests- Run all tests with coverage- Coverage reports are generated in the
coveragedirectory - HTML report includes line-by-line coverage visualization
- Formatting: Uses Prettier, configured in package.json
- Linting: ESLint with eslint-config-prettier integration
- Modules: ES modules with import/export syntax (type: "module")
- JavaScript Target: ES2020 with strict null checks
- Error Handling: Use try/catch with explicit error messages that provide context about what failed
- Naming: camelCase for variables and functions, PascalCase for classes
- Imports: Group by source (internal/external) with proper separation
- Documentation: Use JSDoc for public APIs and complex functions, add comments for non-obvious code
- Error Messages: Use consistent, specific error messages (e.g., "Track buffer overflow" instead of "Overflow in disc building")
- Test Consolidation: All tests for a specific component should be consolidated in a single test file.
For example, all tests for
emulator.jsshould be intest-emulator.js- do not create separate test files for different aspects of the same component. - Test Structure: Use nested describe blocks to organize tests by component features
- Test Isolation: When mocking components in tests, use
vi.spyOn()withvi.restoreAllMocks()inafterEachhooks rather than globalvi.mock()to prevent memory leaks and test pollution - Memory Management: Avoid global mocks that can leak between tests and accumulate memory usage over time
- Test philosophy
- Mock as little as possible: Try and rephrase code not to require it.
- Try not to rely on internal state: don't manipulate objects' inner state in tests
- Use idiomatic vitest assertions (expect/toBe/toEqual) instead of node assert
- Never commit code unless asked: Very often we'll work on code and iterate. After you think it's complete, let me check it before you commit.
-
General Principles:
- Follow the existing code style and structure
- Use
constandletinstead ofvar - Avoid global variables; use module scope
- Use arrow functions for callbacks
- Prefer template literals over string concatenation
- Use destructuring for objects and arrays when appropriate
- Use async/await for asynchronous code instead of callbacks or promises
- Minimise special case handling - prefer explicit over implicit behaviour
- Consider adding tests first before implementing features
-
When simplifying existing code
- Prefer helper functions for repetitive operations (like the
appendParamfunction) - Remove unnecessary type checking where types are expected to be correct
- Replace complex conditionals with more readable alternatives when possible
- Ensure simplifications don't break existing behavior or assumptions
- Try and modernise the code to use ES6+ features where possible
- Prefer helper functions for repetitive operations (like the
-
Prefer helper functions for repetitive operations (like the
appendParamfunction) -
Remove unnecessary type checking where types are expected to be correct
-
Replace complex conditionals with more readable alternatives when possible
-
Ensure simplifications don't break existing behavior or assumptions
-
Constants and Magic Numbers:
- Local un-exported properties should be used for shared constants
- Local constants should be used for temporary values
- Always use named constants instead of magic numbers in code
- Use PascalCase for module-level constants (e.g.,
const MaxHfeTrackPulses = 3132;) - Prefer module-level constants over function-local constants for shared values
- Define constants at the beginning of functions or at the class/module level as appropriate
- Add comments explaining what the constant represents, especially for non-obvious values
-
Pre-commit Hooks:
- The project uses lint-staged with ESLint
- Watch for unused variables and ensure proper error handling
- YOU MUST NEVER bypass git commit hooks on checkins. This leads to failures in CI later on
- The snapshot JSON format is documented in
docs/snapshot-format.md— keep it up to date if you add new fields to any component'ssnapshotState()or change the format version - Each component owns its own
snapshotState()/restoreState()methods - Scheduler tasks are not serialized directly — each component saves its task offset relative to
scheduler.epochand re-registers on restore interruptis not saved in the CPU snapshot — it is reconstructed by VIA/ACIArestoreState()callssoundChip.lastRunEpochis always synced to the scheduler epoch on restore, not saved
- When creating branches with Claude, use the
claude/prefix (e.g.,claude/fix-esm-import-error)
This project uses Conventional Commits for automated versioning and changelog generation via release-please.
Commit types for user-facing changes (appear in changelog, affect version):
fix:- Bug fixes (bumps patch version: 0.0.7 → 0.0.8)feat:- New features (bumps minor version: 0.0.7 → 0.1.0)fix!:orfeat!:- Breaking changes (bumps major version: 0.0.7 → 1.0.0)
Commit types for internal changes (do NOT appear in changelog, do NOT affect version):
chore:- Maintenance tasks, tooling updates, dependency updatesci:- CI/CD configuration changes (GitHub Actions, etc.)build:- Build system changes (webpack, vite, electron-builder config)docs:- Documentation-only changesstyle:- Code style changes (formatting, whitespace)refactor:- Code refactoring without behavior changetest:- Test additions or modifications
Guidelines:
- Use
fix:andfeat:ONLY for changes that affect end users - Use
chore:,ci:, orbuild:for internal tooling/infrastructure changes - Example: A release-please workflow fix should be
ci:orchore:, notfix: - Breaking changes must include
BREAKING CHANGE:in the commit body or use!(e.g.,feat!:)
Examples:
fix: enable window scaling in Electron app
feat: add joystick support for gamepads
chore: update dependencies to latest versions
ci: fix release-please workflow configuration
build: configure electron-builder icon settings
docs: update README with installation instructions