Thank you for contributing to use-stored-state.
We expect all contributors to be respectful and professional. Please maintain a welcoming and inclusive environment for everyone.
- Node.js (latest LTS version recommended)
- npm (comes with Node.js)
- Git
-
Fork the repository on GitHub
-
Clone your fork locally:
git clone https://github.com/YOUR_USERNAME/useStoredState.git cd useStoredState -
Install dependencies:
npm install
-
Create a branch for your changes:
git checkout -b your-feature-branch
Code quality standards are high and expected to be followed for all new pull requests. We maintain strict quality gates to ensure the codebase remains maintainable and reliable.
Before every commit, run npm run check to validate all quality gates:
- Prettier - Code formatting
- ESLint - Code linting and best practices
- MarkdownLint - Markdown file quality
- TypeScript - Type checking
- Vitest - All tests pass
- Knip - No unused dependencies or exports
- npm audit - No known security vulnerabilities
All checks must pass before your pull request will be accepted.
Mutation testing is mandatory for behavior changes.
Expected mutation outcome:
100%mutation score0surviving mutants0timed out mutants
Recommended process:
- Run mutation tests while iterating:
npm run mutate
- Add targeted tests to kill every surviving mutant.
- Re-run mutation tests until clean.
- Run full mutation suite before opening a PR:
npm run mutate
If a mutant is truly equivalent:
- Prefer rewriting code/tests to make behavior observable.
- If unavoidable, add a narrow Stryker disable comment with a short reason.
You can run individual checks as needed:
npm run prettierornpm run prettier:fixnpm run lintornpm run lint:fixnpm run type-checknpm run testnpm run mutatenpm run knipnpm run markdownlintornpm run markdownlint:fix
- Use strict TypeScript with no implicit
any - Prefer
constoverlet; avoidvar - Use meaningful variable and function names
- Explicitly type function parameters, rely on type inference for return values
- Use interfaces for object shapes, types for unions/primitives
- Keep functions small and focused (single responsibility)
- Files: camelCase (e.g.,
useStoredState.ts) - Functions/Variables: camelCase (e.g.,
parsePrimitiveState) - Types/Interfaces: PascalCase (e.g.,
UseStoredStateOptions) - Test files:
*.test.tsand*.test.tsx
We follow a TDD approach:
- Write tests first before implementing features
- Start with the simplest test case
- Add complexity incrementally
- Ensure all tests pass before moving forward
import { describe, expect, it } from "vitest";
import { functionToTest } from "./module";
describe("functionToTest", () => {
it("should handle basic case", () => {
const result = functionToTest("input");
expect(result).toBe("expected");
});
it("should handle edge case", () => {
const result = functionToTest("");
expect(result).toBe("");
});
});- Use descriptive test names that explain behavior
- Test behavior, not implementation
- Cover happy paths, edge cases, and error cases
- Use arrange-act-assert pattern
- Keep tests independent and isolated
- Co-locate tests with source files (e.g.,
helpers.test.tswithhelpers.ts)
- Ensure your code follows all code standards
- Run
npm run checkand fix any issues - Write or update tests for your changes
- Update documentation if needed
- Commit your changes with clear, descriptive commit messages
- Push to your fork and create a pull request
- Respond to any code review feedback
- Keep PRs focused on a single feature or fix
- Write a clear description of what your PR does
- Reference any related issues
- Ensure all CI checks pass
- Be responsive to review feedback
The use of AI tools (such as GitHub Copilot) is acceptable and encouraged, provided that:
- You review all AI-generated code carefully before submitting
- The code aligns with our code standards and quality requirements
- You understand the code and can explain how it works
- All tests pass and the code is properly tested
AI can assist implementation, but contributors remain responsible for correctness and maintainability.
- Use explicit error checking
- Provide helpful error messages
- Don't swallow errors silently
- Use appropriate error types
- Use
async/awaitover raw promises - Handle promise rejections properly
- Use
Promise.all()for parallel operations
- Avoid type assertions unless absolutely necessary
- Use type guards for runtime type checking
- Prefer union types over
any
- Prefer self-documenting code over comments
- Keep JSDoc accurate for public APIs
- Explain "why" over "what" in comments
- Keep comments up-to-date with code
- ❌ Don't use
anytype without good reason - ❌ Don't skip tests for new functionality
- ❌ Don't commit code that doesn't pass
npm run check - ❌ Don't mutate function parameters
- ❌ Don't use
eval()or similar dangerous functions - ❌ Don't hardcode environment-specific assumptions
- ❌ Don't add dependencies without careful consideration
- ❌ Don't disable TypeScript at all
- ❌ Don't disable ESLint rules without significant justification
- ❌ Don't refactor unrelated code
- ❌ Don't fix unrelated issues in your PR
- Prefer established, well-maintained packages
- Check package size and dependencies
- Ensure license compatibility (MIT preferred)
- Add to correct section in package.json:
dependencies- Runtime dependenciesdevDependencies- Development tools and testing
- Validate all external inputs
- Avoid command injection vulnerabilities
- Don't commit secrets or credentials
- Keep dependencies updated for security patches
- Use
npm auditto check for vulnerabilities
- Check existing issues
- Open a new issue for bugs or feature requests
- Ask questions in your pull request
Thanks for helping improve use-stored-state. 🚀