Skip to content

Latest commit

 

History

History
210 lines (158 loc) · 9.36 KB

File metadata and controls

210 lines (158 loc) · 9.36 KB

Linting Configuration

This monorepo uses a comprehensive linting setup with multiple tools to ensure code quality and consistency across all packages.

Linting Tools Overview

  • ESLint: JavaScript/TypeScript code quality and style enforcement
  • Prettier: Code formatting for JavaScript, TypeScript, JSON, Markdown, YAML, and Solidity
  • Solhint: Solidity-specific linting for smart contracts
  • Forge Lint: Foundry's Solidity linter (for packages using Forge)
  • TODO Check: Reports TODO/FIXME/XXX/HACK comments in Solidity files (informational)
  • Markdownlint: Markdown formatting and style consistency
  • YAML Lint: YAML file validation and formatting

Configuration Architecture

The linting configuration follows a hierarchical structure where packages inherit from root-level configurations.

ESLint Configuration

  • Root Configuration: eslint.config.mjs - Modern flat config format
  • Direct Command: npx eslint '**/*.{js,ts,cjs,mjs,jsx,tsx}' --fix
  • Behavior: ESLint automatically searches up parent directories to find configuration files
  • Package Inheritance: Packages automatically inherit the root ESLint configuration without needing local config files
  • Global Ignores: Configured to exclude autogenerated files (.graphclient-extracted/, lib/) and build outputs

Prettier Configuration

  • Root Configuration: prettier.config.cjs - Base formatting rules for all file types

  • Direct Command: npx prettier -w --cache '**/*.{js,ts,cjs,mjs,jsx,tsx,json,md,sol,yml,yaml}'

  • Package Inheritance: Packages that need Prettier must have a prettier.config.cjs file that inherits from the shared config

  • Example Package Config:

    const baseConfig = require('../../prettier.config.cjs')
    module.exports = { ...baseConfig }
  • Ignore Files: .prettierignore excludes lock files, build outputs, and third-party dependencies

Solidity Linting (Solhint)

  • Root Configuration: .solhint.json - Base Solidity linting rules extending solhint:recommended

  • Direct Command: npx solhint 'contracts/**/*.sol' (add --fix for auto-fixing)

  • List Applied Rules: npx solhint list-rules

  • Package Inheritance: Packages can extend the root config with package-specific rules

  • Configuration Inheritance Limitation: Solhint has a limitation where nested extends don't work properly. When a local config extends a parent config that itself extends solhint:recommended, the built-in ruleset is ignored.

  • Recommended Package Extension Pattern:

    {
      "extends": ["solhint:recommended", "./../../.solhint.json"],
      "rules": {
        "no-console": "off",
        "import-path-check": "off"
      }
    }

Forge Lint

Forge lint is Foundry's built-in Solidity linter. Packages using Foundry can add lint:forge to their lint scripts.

  • Package Configuration: foundry.toml with [lint] section
  • Direct Command: forge lint or forge lint contracts/
  • Available in: Packages with lint:forge script defined (horizon, subgraph-service, issuance)

Markdown Linting (Markdownlint)

  • Root Configuration: .markdownlint.json - Markdown formatting and style rules

  • Direct Command: npx markdownlint '**/*.md' --fix

  • Ignore Files: .markdownlintignore automatically picked up by markdownlint CLI

  • Package Inheritance: Packages that need Markdownlint must have a .markdownlint.json file that extends the root config

  • Example Package Config:

    {
      "extends": "../../.markdownlint.json"
    }

Inline Lint Suppression

When you need to suppress a lint warning for a specific line or item, use the appropriate comment directive.

Solhint Suppression

// Disable for next line (can have intervening comments before target)
// solhint-disable-next-line func-name-mixedcase

// Disable for previous line
function example() {
    // solhint-disable-previous-line no-empty-blocks
}

// Block disable/enable
// solhint-disable no-console
console.log("debug");
// solhint-enable no-console

Forge Lint Suppression

 // Disable for next item (function, struct, etc. - AST-aware)
// forge-lint: disable-next-item(mixed-case-function)

// Note: forge-lint uses "next-item" not "next-line"
// It applies to the entire syntactic construct, not just the next line

Combined Example

For functions that need both Solhint and Forge lint suppression (e.g., OpenZeppelin-style initializers):

// solhint-disable-next-line func-name-mixedcase
// forge-lint: disable-next-item(mixed-case-function)
/**
 * @notice Internal function to initialize the contract
 */
function __ContractName_init(address param) internal {
  // initialization code
}

Note: Place suppression comments before natspec to avoid warnings about comments not directly preceding the function.

Linting Scripts

Root Level Scripts

# Run all linting tools
pnpm lint

# Individual linting commands
pnpm lint:ts      # ESLint + Prettier for TypeScript/JavaScript
pnpm lint:sol     # TODO check + Solhint + Prettier for Solidity (runs recursively)
pnpm lint:forge   # Forge lint for packages that support it
pnpm lint:md      # Markdownlint + Prettier for Markdown
pnpm lint:json    # Prettier for JSON files
pnpm lint:yaml    # YAML linting + Prettier

# Lint only staged files (useful for manual pre-commit checks)
pnpm lint:staged  # Run linting on git-staged files only

Package Level Scripts

Each package can define its own linting scripts that work with the inherited configurations:

# Example from packages/contracts
pnpm lint:sol   # Solhint for contracts in this package only
pnpm lint:ts    # ESLint for TypeScript files in this package

Pre-commit Hooks (lint-staged)

The repository uses lint-staged with Husky to run linting on staged files before commits:

  • Automatic: Runs automatically on git commit via Husky pre-commit hook
  • Manual: Run pnpm lint:staged to manually check staged files before committing
  • Configuration: Root package.json contains lint-staged configuration
  • Custom Script: scripts/lint-staged-run.sh filters out generated files that shouldn't be linted
  • File Type Handling:
    • .{js,ts,cjs,mjs,jsx,tsx}: ESLint + Prettier
    • .sol: TODO check + Solhint + Prettier
    • .md: Markdownlint + Prettier
    • .json: Prettier only
    • .{yml,yaml}: YAML lint + Prettier

Usage: pnpm lint:staged is particularly useful when you want to check what linting changes will be applied to your staged files before actually committing.

TODO Comment Checking

The repository reports TODO comments in Solidity files to help track technical debt:

  • Scope: Applies only to Solidity (.sol) files
  • Detection: Finds TODO, FIXME, XXX, and HACK comments (case-insensitive)
  • Behavior: Informational only - does not block commits or fail linting
  • Included in: lint:sol and lint:staged scripts
  • Script: scripts/check-todos.sh (must be run from repository root)

Configuration Files Reference

Tool Root Config Package Config Ignore Files
ESLint eslint.config.mjs Auto-inherited Built into config
Prettier prettier.config.cjs prettier.config.cjs (inherits) .prettierignore
Solhint .solhint.json .solhint.json (array extends) N/A
Forge Lint N/A foundry.toml [lint] section N/A
TODO Check scripts/check-todos.sh N/A N/A
Markdownlint .markdownlint.json .markdownlint.json (extends) .markdownlintignore
Lint-staged package.json N/A scripts/lint-staged-run.sh

Troubleshooting

  • ESLint not finding config: ESLint searches up parent directories automatically - no local config needed
  • Prettier not working: Packages need a prettier.config.cjs that inherits from root config
  • Markdownlint not working: Packages need a .markdownlint.json that extends root config
  • Solhint missing rules: If extending a parent config, use array format: ["solhint:recommended", "./../../.solhint.json"] to ensure all rules are loaded
  • Solhint inheritance not working: Nested extends don't work - parent config's solhint:recommended won't be inherited with simple string extends
  • Solhint rule reference: Use npx solhint list-rules to see all available rules and their descriptions
  • Generated files being linted: Check ignore patterns in .prettierignore, .markdownlintignore, and ESLint config
  • Preview lint changes before commit: Use pnpm lint:staged to see what changes will be applied to staged files
  • Commit blocked by linting: Fix the linting issues or use git commit --no-verify to bypass (not recommended)
  • Forge lint symlink errors: Forge follows symlinks when scanning for files, which can cause "Too many levels of symbolic links" errors in packages with nested workspace dependencies. If a package has a test/ subproject with workspace symlinks that create loops, rename the directory (e.g., to testing/) so forge doesn't scan it by default.