• English
  • Contributing

    Adding a New Rule

    1 — Write the GritQL file

    Create a .grit file in grit/:

    touch grit/my-new-rule.grit

    A minimal rule:

    // [Rationale] One sentence explaining why this pattern is banned.
    //
    // ✗  bad pattern
    // ✓  good alternative
    
    `pattern to match $variable` where {
      register_diagnostic(
        message = "Explain the violation and what to do instead.",
        severity = "error"  // or "warn"
      )
    }

    2 — Document it in AGENTS.md

    Add a ### rule-name section under ## Lint Rules in AGENTS.md. Include: what it checks, the rationale, a ✗/✓ example, and any exception conditions.

    3 — Add a docs page

    Create docs/rules/my-new-rule.mdx following the structure of the existing rule pages.

    4 — Register it in the sidebar

    Add an entry to rspress.config.ts under sidebar['/rules/'].

    5 — Test

    pnpm test

    Rule Naming

    Use kebab-case. Names describe what is disallowed or preferred:

    PrefixWhen to useExample
    no-*Bans a specific patternno-null-type
    prefer-*Promotes an alternativeprefer-union-over-enum
    enforce-*Structural / architectural ruleenforce-pure-src

    GritQL Quick Reference

    Pattern match

    `code $variable { $body }` where { ... }

    Register a diagnostic

    register_diagnostic(
      message = "Human-readable message.",
      severity = "error" | "warn" | "info"
    )

    Conditions

    // File path must match
    $file_path.matches("^src/"),
    
    // Captured variable must match a regex
    $path.matches(".*(storybook|demo).*"),
    
    // Combine with comma (AND)
    $file_path.matches("^src/"),
    $path.matches(".*(storybook).*"),

    Severity Guide

    SeverityUse when
    errorThe pattern is always wrong in this codebase — no ambiguity
    warnThe pattern is usually wrong but has legitimate exceptions
    infoInformational nudge; not a blocker