Thank you for your interest in contributing to LRE! This document outlines the coding standards and style guidelines enforced in this project.
- Use yarn for package management (not npm)
- Run
yarn checkcodeto validate TypeScript files
This project uses Prettier for code formatting and ESLint with TypeScript support for linting.
| Rule | Value | Description |
|---|---|---|
trailingComma |
all |
Add trailing commas wherever possible (ES5+) |
tabWidth |
2 |
Use 2 spaces for indentation |
semi |
true |
Always use semicolons |
singleQuote |
false |
Use double quotes " for strings |
printWidth |
80 |
Maximum line length of 80 characters |
Explicit return types are required for all functions.
// ✅ Good
function calculateSum(a: number, b: number): number {
return a + b;
}
// ❌ Bad - missing return type
function calculateSum(a: number, b: number) {
return a + b;
}Note: Expressions (like callbacks in array methods) are exempt from this rule.
Unused variables must be prefixed with _ to indicate they are intentionally unused.
// ✅ Good - unused parameter prefixed with _
function handler(_event: Event, data: Data): void {
console.log(data);
}
// ❌ Bad - unused parameter without prefix
function handler(event: Event, data: Data): void {
console.log(data);
}This applies to:
- Function arguments (
argsIgnorePattern: "^_") - Variables (
varsIgnorePattern: "^_") - Caught errors (
caughtErrorsIgnorePattern: "^_")
Between statements: Always add a blank line before and after block-like statements (if, for, while, etc.) and functions.
// ✅ Good
const x = 1;
if (x > 0) {
doSomething();
}
const y = 2;// ❌ Bad - missing blank lines
const x = 1;
if (x > 0) {
doSomething();
}
const y = 2;Between class members: Always add a blank line before and after methods.
// ✅ Good
class MyClass {
private value: number;
constructor() {
this.value = 0;
}
getValue(): number {
return this.value;
}
setValue(v: number): void {
this.value = v;
}
}Empty blocks are not allowed, except for catch blocks.
// ✅ Good - empty catch is allowed
try {
riskyOperation();
} catch {
// Intentionally empty
}
// ❌ Bad - empty if block
if (condition) {
}Short-circuit (&&, ||) and ternary expressions are allowed as statements.
// ✅ Good
condition && doSomething();
isValid ? process() : skip();All test titles must be unique within their suite.
// ✅ Good
it("should handle empty input", () => {});
it("should handle null input", () => {});
// ❌ Bad - duplicate titles
it("should work", () => {});
it("should work", () => {});Always add blank lines around test blocks (describe, it, test, beforeEach, etc.).
// ✅ Good
describe("MyComponent", () => {
beforeEach(() => {
setup();
});
it("should render", () => {
expect(true).toBe(true);
});
it("should update", () => {
expect(true).toBe(true);
});
});The following are explicitly allowed in this codebase:
- Using
anytype (@typescript-eslint/no-explicit-anyis off) - Using
argumentsobject (prefer-rest-paramsis off) - Using
Function.applywith arrays (prefer-spreadis off)
This project uses Vitest for testing. All code contributions must include comprehensive tests.
The goal is confidence: every function, API, and intended behavior should be automatically tested so code changes never cause unexpected regressions.
Workflow: Code → Tests → Documentation → Review. Never skip the tests step.
-
Cover all cases - This is the most important requirement. Every possible scenario must be tested:
- All user inputs and possible values
- Edge cases and corner cases
- Error scenarios and invalid states
- Boundary conditions
-
Maximize code coverage - Strive for exhaustive, high-coverage tests while focusing on correctness.
-
Test all public APIs - For any public method or exported function/class, create targeted tests.
| Practice | Description |
|---|---|
| Small, focused tests | Prefer smaller test cases to isolate failures and speed up runs |
| Parameterized tests | Use parameterized or property-based tests to cover many cases efficiently |
| Shared setups | Use helper functions or shared setups to avoid duplication |
| Coverage reports | Use coverage reports to discover untested branches |
| Skip trivial code | Avoid over-testing simple getters/setters unless they contain logic |
| Maintain tests | Refactor tests when code changes to keep them relevant |
describe("MyComponent", () => {
describe("myMethod", () => {
it("should handle normal input", () => {
// Test the happy path
});
it("should handle empty input", () => {
// Edge case
});
it("should handle null/undefined", () => {
// Error case
});
it("should handle boundary values", () => {
// Boundary case
});
it.each([
[input1, expected1],
[input2, expected2],
[input3, expected3],
])("should transform %s to %s", (input, expected) => {
// Parameterized test for multiple cases
});
});
});Ask yourself: "What tests need to be created or updated?"
- Did I test all the new code paths?
- Did I cover all edge cases?
- Did I test error scenarios?
- Are my tests focused and readable?
yarn checkall before every commit. It must pass with zero errors.
yarn checkallThis command runs all quality checks:
- ts-unused-exports - Detects unused exports
- cspell - Spell checking
- tsc - TypeScript type checking
- eslint - Linting and formatting
- vitest - All tests
❌ Do not commit if yarn checkall fails. Fix all issues first.
# Full check (REQUIRED before commit)
yarn checkall
# Code checks only (no tests)
yarn checkcode
# Run tests
yarn test
# Run tests with coverage
yarn test --coverageThe following directories are excluded from linting:
out/build/coverage/assemble/lre*.jsfiles in root