Custom Linting Rules
Relevant source files
The following files were used as context for generating this wiki page:
Custom linting rules for the Happy monorepo are centralized in the @happy/lint-rules package. This package provides shared rules that enforce project-specific conventions, naming standards, and architectural constraints across various applications including the CLI, React Native mobile app, and backend servers. These rules are designed for compatibility with both oxlint (via JavaScript plugins) and ESLint (flat config).
The primary goal of these custom rules is to maintain consistency in the codebase, particularly regarding branding (e.g., "GitHub" casing) and the correct usage of shared protocols like @magic-agent/protocol.
Sources: dev/lint-rules/CLAUDE.md:1-12, CLAUDE.md:120-125, knowledge.md:15-20
Architecture and Implementation
The linting system is structured as a standalone package within the dev/lint-rules/ directory. It follows a modular design where each rule is defined in its own file within src/rules/ and exported through a central plugin entry point.
Package Structure
src/index.js: The main entry point that aggregates all rules into a single plugin object.src/rules/: Contains the logic for individual rules and their corresponding unit tests.- Testing Infrastructure: Rules are validated using Vitest and ESLint's
RuleTester.
Sources: dev/lint-rules/CLAUDE.md:73-85, dev/lint-rules/CLAUDE.md:104-110
Rule Logic Flow
Each rule consists of a meta object (describing the rule) and a create function. The create function uses the ESLint AST (Abstract Syntax Tree) visitor pattern to inspect code nodes.
The diagram shows how custom rules are integrated into the development workflow using either oxlint or ESLint to inspect code patterns.
Sources: dev/lint-rules/CLAUDE.md:87-95, dev/lint-rules/src/rules/protocol-helpers.js:77-111
Core Rules
The following table summarizes the custom rules currently implemented in the @happy/lint-rules package:
| Rule Name | Purpose | Fixable | Target Identifiers |
|---|---|---|---|
happy/github-casing | Enforces "GitHub" (capital H) in PascalCase identifiers. | Yes | Interfaces, Classes, Types, Enums |
happy/protocol-helpers | Enforces use of ID accessor helpers instead of direct property access. | No | .sid, .machineId on update bodies |
Sources: dev/lint-rules/CLAUDE.md:14-45, dev/lint-rules/src/rules/protocol-helpers.js:25-39
happy/github-casing
This rule ensures compliance with official branding by requiring a capital "H" when "GitHub" appears in PascalCase identifiers. It specifically ignores camelCase variable names (e.g., githubToken) where the leading "g" is lowercase.
Examples:
- Bad:
interface GithubUser {} - Good:
interface GitHubUser {}
Sources: dev/lint-rules/CLAUDE.md:16-32, CLAUDE.md:308-320, dev/lint-rules/src/rules/github-casing.test.js:41-90
happy/protocol-helpers
This rule mandates the use of accessor functions from @magic-agent/protocol when retrieving session or machine identifiers from update objects. Direct property access is discouraged to maintain a layer of abstraction over the underlying data structure.
Logic Pattern Identification:
The rule specifically looks for MemberExpression nodes that match the pattern *.body.sid or *.body.machineId.
The sequence diagram illustrates how the rule distinguishes between discouraged direct access and the encouraged helper function usage.
Sources: dev/lint-rules/src/rules/protocol-helpers.js:52-75, dev/lint-rules/CLAUDE.md:34-45
Usage and Configuration
Custom rules are configured differently depending on whether the project uses oxlint or ESLint.
Configuration Examples
oxlint (.oxlintrc.json)
{
"jsPlugins": ["@happy/lint-rules"],
"rules": {
"happy/github-casing": "warn",
"happy/protocol-helpers": "warn"
}
}
Sources: dev/lint-rules/CLAUDE.md:49-56, apps/admin/web/CLAUDE.md:120-130
ESLint (eslint.config.js)
import happyPlugin from '@happy/lint-rules';
export default [
{
plugins: { happy: happyPlugin },
rules: {
'happy/github-casing': 'warn',
'happy/protocol-helpers': 'warn'
}
}
];
Sources: dev/lint-rules/CLAUDE.md:58-69
Testing and Development
The package utilizes RuleTester from ESLint combined with Vitest to ensure rule accuracy.
Test Coverage Highlights
- Exclusions: Rules like
happy/protocol-helpersspecifically exclude test files (*.test.ts,*.spec.ts) because these files often need to construct mock objects with direct properties. - Validations: Tests cover valid patterns, invalid patterns, and the effectiveness of auto-fixes for the
github-casingrule.
| Test Suite | Total Tests | Key Scenarios |
|---|---|---|
github-casing | 21 | Interfaces, Types, Classes, camelCase exceptions |
protocol-helpers | 27 | Direct access, helper calls, file exclusions |
Sources: dev/lint-rules/CLAUDE.md:112-126, dev/lint-rules/src/rules/protocol-helpers.test.js:31-100, dev/lint-rules/src/rules/github-casing.test.js:33-100
Custom linting rules serve as a critical component of the Happy project's CI/CD and developer workflow, ensuring that specific architectural decisions and naming conventions are enforced automatically across the monorepo's diverse set of applications.
Summary: Custom linting rules in the @happy/lint-rules package provide automated enforcement of naming conventions (GitHub casing) and architectural patterns (protocol helpers) across the monorepo, supporting both oxlint and ESLint environments.