Skip to content

Latest commit

 

History

History
428 lines (306 loc) · 10.8 KB

File metadata and controls

428 lines (306 loc) · 10.8 KB

Contributing to tsgolint

Thank you for your interest in contributing to tsgolint! This document provides guidelines and information for contributors.

Important

tsgolint is a prototype in the early stages of development. This is a community effort. Feel free to ask to be assigned to any of the good first issues.

Getting Started

Prerequisites

  • Go 1.21 or later
  • Git with submodule support
  • just command runner
  • Node.js and pnpm (for oxlint integration testing)
  • Basic understanding of TypeScript and Go

Development Setup

  1. Clone the repository:

    git clone https://github.com/oxc-project/tsgolint.git
    cd tsgolint
  2. Initialize and set up the project:

    just init
  3. Build tsgolint:

    just build
  4. Verify the build:

    ./tsgolint --help
  5. Set up oxlint integration (optional, for integration testing):

    # Install oxlint with type-aware support
    pnpm add -D oxlint-tsgolint@latest
    
    # Test integration with your locally built tsgolint
    OXLINT_TSGOLINT_PATH=./tsgolint pnpm dlx oxlint --type-aware
    
    # Or use the installed version
    pnpm dlx oxlint --type-aware

    Note: Use OXLINT_TSGOLINT_PATH to test your local changes to tsgolint with oxlint.

Development Workflow

Making Changes

  1. Create a feature branch:

    git checkout -b feature/your-feature-name
  2. Make your changes

  3. Test your changes:

    # Run all tests (unit + e2e)
    just test
    
    # Or run tests individually:
    go test ./internal/...  # Unit tests only
    
    # Test oxlint integration with your local tsgolint build
    OXLINT_TSGOLINT_PATH=./tsgolint oxlint --type-aware [test-files]
  4. Commit and push:

    git add .
    git commit -m "feat: your feature description"
    git push origin feature/your-feature-name
  5. Create a Pull Request

Code Style

Use the provided just commands to ensure code quality:

# Format code
just fmt

# Run linter
just lint

# Run everything (format, lint, test)
just ready
  • Follow standard Go conventions
  • Use meaningful variable and function names
  • Add comments for complex logic
  • Ensure all tests pass before submitting

Testing

Running Tests

# Run all tests (recommended)
just test

# Run only unit tests
go test ./internal/...

# Run tests with verbose output
go test -v ./internal/...

# Run tests and update snapshots
just update-snaps

The test suite includes:

  • Unit tests for individual rules
  • End-to-end tests with real TypeScript code
  • Integration tests with fixture files

Testing Individual Rules

When developing or debugging a specific rule:

# Test a specific rule package
go test ./internal/rules/no_unsafe_argument

# Run with verbose output
go test -v ./internal/rules/no_unsafe_argument

# Run with debug logging
OXC_LOG=debug go test ./internal/rules/no_unsafe_argument

Implementing New Rules

Rule Structure

Each rule follows a consistent interface pattern:

package example_rule

import (
   "github.com/oxc-project/tsgolint/internal/rule"
	"github.com/oxc-project/tsgolint/shim/ast"
)

var ExampleRule = rule.Rule{
   Name: "example-rule",
   Run: func(ctx rule.RuleContext, options any) rule.RuleListeners {
      return rule.RuleListeners{
         ast.KindExpressionStatement: func(node *ast.Node) {
            // Handle ExpressionStatement nodes
         }
      }
   }
}

Rule Development Guidelines

  1. Follow typescript-eslint compatibility: Ensure behavior matches the corresponding typescript-eslint rule
  2. Use type-aware analysis: Leverage the TypeScript checker for accurate type information
  3. Provide clear diagnostics: Error messages should be helpful and actionable
  4. Support fixing: When possible, implement automatic code fixes
  5. Add comprehensive tests: Cover edge cases and different TypeScript constructs

Adding a New Rule

  1. Create rule directory:

    mkdir internal/rules/your_rule_name
  2. Implement the rule:

    • Create rule.go with rule implementation
    • Create rule_test.go with comprehensive tests
    • Add test fixtures in appropriate directories
  3. Register the rule: Add your rule to the rule registry in cmd/tsgolint/main.go

  4. Update documentation:

    • Add rule to README.md rule list
    • Update rule count if implementing a new rule

Adding Options to Rules

Rules can define options by creating a JSON schema file that describes the options. Create a file named schema.json in the rule's directory. For example:

rules/
  name_of_rule/
    name_of_rule.go
    name_of_rule_test.go
    schema.json

To generate the Go struct for the options, run:

node tools/gen-json-schemas.mjs

This should create an options.go file in the same directory as the schema.json file. Then, you can use the generated JSON schema code to parse options in your rule implementation:

import (
   "github.com/typescript-eslint/tsgolint/internal/rule"
	"github.com/typescript-eslint/tsgolint/internal/utils"
)

var ExampleRule = rule.Rule{
   Name: "example-rule",
   Run: func(ctx rule.RuleContext, options any) rule.RuleListeners {
      opts := utils.UnmarshalOptions[ExampleRuleOptions](options, "example-rule")

      // ... rest of rule here ...
   }
}

Here is an example JSON schema:

{
  "$schema": "https://json-schema.org/draft-07/schema#",
  "definitions": {
    "example_rule_options": {
      "type": "object",
      "properties": {
        "ignoreSomething": {
          "type": "boolean",
          "default": false
        }
      }
    }
  }
}

Test Fixtures

Create test fixtures that cover:

  • Valid code that should not trigger the rule
  • Invalid code that should trigger the rule
  • Edge cases and complex TypeScript constructs
  • Code that can be auto-fixed

Debugging

Debug Logging

Enable verbose debug output:

OXC_LOG=debug ./tsgolint [files...]

Debug logging provides information about:

  • File assignment to TypeScript programs
  • Worker distribution and execution
  • Performance timing information
  • Internal state details

Common Issues

Build Issues

  • Patch application fails: Ensure you're in the typescript-go directory when applying patches
  • Go build errors: Check Go version (requires 1.21+) and ensure all dependencies are available

Test Issues

  • Integration tests fail: Check that your changes don't break existing functionality
  • Unit tests timeout: Some tests may take longer with debug logging enabled

Oxlint Integration Issues

  • oxlint --type-aware not working: Ensure oxlint-tsgolint is installed and up to date
  • Testing local changes: Use OXLINT_TSGOLINT_PATH=./tsgolint to point oxlint to your locally built binary
  • Performance issues with large repos: This is a known limitation; consider testing with smaller codebases
  • Configuration not working: Check .oxlintrc.json format and rule names

TypeScript Integration

Understanding the Shim Layer

tsgolint uses a shim layer to access internal typescript-go APIs. This is not recommended for production use but enables access to full TypeScript compiler functionality.

Key shim components:

  • shim/ast: TypeScript AST node types and utilities
  • shim/checker: Type checker bindings
  • shim/compiler: Program and compilation host
  • shim/scanner: Source text processing utilities

Working with TypeScript Types

When implementing rules, you can access TypeScript type information:

func (r *YourRule) checkNode(ctx rule.Context, node *ast.Node) {
    // Get type information
    nodeType := ctx.Checker.GetTypeAtLocation(node)

    // Check type properties
    if nodeType.IsString() {
        // Handle string types
    }

    // Get symbol information
    symbol := ctx.Checker.GetSymbolAtLocation(node)
    if symbol != nil {
        // Use symbol information
    }
}

Performance Considerations

Rule Performance

  • Minimize expensive operations in hot paths
  • Cache type checker results when possible
  • Use appropriate AST node listeners (don't listen for all nodes if you only need specific ones)

Testing Performance

Monitor rule performance:

# Profile CPU usage
go test -cpuprofile=cpu.prof ./internal/rules/your_rule

# Profile memory usage
go test -memprofile=mem.prof ./internal/rules/your_rule

Communication

Getting Help

  • Discord: Join our Discord server for real-time discussion
  • Issues: Use GitHub issues for bug reports and feature requests
  • Discussions: Use GitHub discussions for questions and general discussion

Reporting Issues

When reporting issues, please include:

  1. Environment information:

    • tsgolint version
    • Go version
    • Operating system
    • TypeScript version
  2. Reproduction steps:

    • Minimal code example
    • Commands to reproduce
    • Expected vs actual behavior
  3. Debug output (if relevant):

    OXC_LOG=debug ./tsgolint [files...]

Pull Request Guidelines

Before Submitting

  • Run just ready to ensure everything passes
  • Tests pass (just test)
  • Code is formatted (just fmt)
  • Linter passes (just lint)
  • Changes are documented (if user-facing)
  • Commit messages are clear and descriptive

Pull Request Process

  1. Create descriptive title: Use conventional commit format when possible
  2. Provide context: Explain the problem and solution
  3. Link related issues: Reference any related GitHub issues
  4. Request review: Tag relevant maintainers or community members
  5. Address feedback: Respond to review comments promptly

Conventional Commits

We encourage using conventional commit format:

  • feat: for new features
  • fix: for bug fixes
  • docs: for documentation changes
  • test: for test-related changes
  • refactor: for code refactoring
  • perf: for performance improvements

Example: feat: implement no-unnecessary-condition rule

Code of Conduct

Please note that this project follows the Oxc Code of Conduct. By participating, you are expected to uphold this code.

License

By contributing to tsgolint, you agree that your contributions will be licensed under the MIT License.

Additional Resources