CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-relay-compiler

A high-performance GraphQL compilation tool built in Rust for building Relay applications with optimized queries and type generation.

Pending
Overview
Eval results
Files

codemods.mddocs/

Codemods

Relay Compiler includes automated code transformation tools (codemods) for migrating and improving Relay codebases with built-in safety controls and rollout mechanisms.

Running Codemods

{ .api }
relay-compiler codemod <CODEMOD> [OPTIONS]

Global Options

{ .api }
--project, -p <PROJECT>     # Target specific projects (repeatable)
--config <PATH>             # Use specific config file

Available Codemods

Mark Dangerous Conditional Fragment Spreads

Identifies and marks potentially unsafe conditional fragment spreads that could cause runtime errors.

{ .api }
relay-compiler codemod mark-dangerous-conditional-fragment-spreads [OPTIONS]

Options

{ .api }
--rollout-percentage, -r <PERCENT>    # Percentage or range for gradual rollout
--project, -p <PROJECT>               # Target specific projects
--config <PATH>                       # Use specific config file

Percentage Formats

{ .api }
type RolloutPercentage = 
  | string          // Single percentage: "50"
  | string;         // Range: "20-30"

Usage Examples

# Apply to 50% of applicable files
relay-compiler codemod mark-dangerous-conditional-fragment-spreads --rollout-percentage 50

# Apply to files in a percentage range
relay-compiler codemod mark-dangerous-conditional-fragment-spreads -r 20-30

# Apply to specific project only
relay-compiler codemod mark-dangerous-conditional-fragment-spreads --project web

# Apply with custom config
relay-compiler codemod mark-dangerous-conditional-fragment-spreads --config ./custom-relay.config.json

# Apply to multiple projects
relay-compiler codemod mark-dangerous-conditional-fragment-spreads -p web -p mobile

What It Does

This codemod identifies conditional fragment spreads that might cause runtime errors due to missing alias information:

Before:

fragment UserProfile on User {
  name
  ... on AdminUser {
    adminLevel
  }
  ... on RegularUser {
    memberSince
  }
}

After:

fragment UserProfile on User {
  name
  ... on AdminUser @__dangerouslyUnaliasedConditional {
    adminLevel
  }
  ... on RegularUser @__dangerouslyUnaliasedConditional {
    memberSince
  }
}

The @__dangerouslyUnaliasedConditional directive serves as a marker for manual review and potential refactoring.

Remove Unnecessary Required Directives

Removes @required directives from fields that are already non-null in the schema, cleaning up redundant code.

{ .api }
relay-compiler codemod remove-unnecessary-required-directives [OPTIONS]

Options

{ .api }
--project, -p <PROJECT>     # Target specific projects
--config <PATH>             # Use specific config file

Usage Examples

# Apply to all projects
relay-compiler codemod remove-unnecessary-required-directives

# Apply to specific project
relay-compiler codemod remove-unnecessary-required-directives --project web

# Apply with custom configuration  
relay-compiler codemod remove-unnecessary-required-directives --config ./relay.config.json

# Apply to multiple projects
relay-compiler codemod remove-unnecessary-required-directives -p web -p mobile -p admin

What It Does

This codemod removes unnecessary @required directives from fields that are already non-null according to the GraphQL schema:

Before:

fragment UserInfo on User {
  id @required(action: LOG)        # id is ID! in schema
  name @required(action: THROW)    # name is String! in schema  
  email                            # email is String? in schema
}

After:

fragment UserInfo on User {
  id           # @required removed - field is already non-null
  name         # @required removed - field is already non-null
  email        # @required preserved - field is nullable
}

Rollout Strategy

Percentage-based Rollouts

Codemods support gradual rollouts to minimize risk:

{ .api }
interface RolloutStrategy {
  percentage: number;           // 0-100
  range?: [number, number];     // Alternative: range rollout
  fileSelection: "random" | "deterministic";
}

Deterministic Selection: Files are selected based on a hash of their path, ensuring consistent selection across runs.

Random Selection: Files are selected randomly each time the codemod runs.

Project-based Rollouts

# Start with a small project
relay-compiler codemod <CODEMOD> --project small-app

# Expand to larger projects
relay-compiler codemod <CODEMOD> --project web --project mobile

# Apply to all projects (no --project flag)
relay-compiler codemod <CODEMOD>

Safety Mechanisms

Dry Run Mode

All codemods include built-in validation to ensure they only make safe transformations:

{ .api }
interface CodemodSafety {
  validateBeforeTransform: boolean;    // Validate AST before changes
  validateAfterTransform: boolean;     // Validate AST after changes
  preserveComments: boolean;           // Maintain code comments
  preserveFormatting: boolean;         // Maintain code style
}

Backup Recommendations

Before running codemods on large codebases:

  1. Commit all changes: Ensure clean working directory
  2. Run tests: Verify baseline functionality
  3. Use version control: Enable easy rollback
  4. Start small: Test on subset before full rollout

Custom Codemods

While the CLI doesn't expose custom codemod registration, you can use the programmatic API to build custom transformations:

{ .api }
// Codemod trait for custom implementations
pub trait Codemod {
    fn name(&self) -> &'static str;
    fn description(&self) -> &'static str;
    fn transform(&self, program: &Program) -> Result<Program, CodemodError>;
}

// Built-in codemod implementations
pub struct MarkDangerousConditionalFragmentSpreads {
    pub rollout_percentage: Option<f32>,
}

pub struct RemoveUnnecessaryRequiredDirectives;

Custom Codemod Example

use relay_compiler::{Codemod, Program, CodemodError};

pub struct CustomCodemod;

impl Codemod for CustomCodemod {
    fn name(&self) -> &'static str {
        "custom-transformation"
    }
    
    fn description(&self) -> &'static str {
        "Applies custom transformation logic"
    }
    
    fn transform(&self, program: &Program) -> Result<Program, CodemodError> {
        // Custom transformation logic
        let mut transformed_program = program.clone();
        
        // Apply transformations to fragments, operations, etc.
        // ...
        
        Ok(transformed_program)
    }
}

Error Handling

Codemods provide detailed error reporting for various failure scenarios:

{ .api }
interface CodemodErrors {
  ParseError: string;              // GraphQL parsing failures
  ValidationError: string;         // Schema validation failures  
  TransformationError: string;     // Transformation logic failures
  FileSystemError: string;         // File read/write failures
  ConfigurationError: string;      // Invalid configuration
}

Common Error Scenarios

  1. Invalid GraphQL syntax: Codemod skips malformed files with warning
  2. Schema mismatch: Reports fields not found in schema
  3. Permission errors: File system access issues
  4. Configuration errors: Invalid project names or paths

Reporting and Logging

Transformation Summary

After running a codemod, you'll receive a detailed summary:

{ .api }
interface CodemodSummary {
  filesProcessed: number;         // Total files examined
  filesTransformed: number;       // Files that were modified
  transformationsApplied: number; // Total number of changes made
  errors: CodemodError[];         // Any errors encountered
  warnings: string[];             // Non-fatal issues
}

Example Output

Codemod: mark-dangerous-conditional-fragment-spreads
Rollout: 50% (deterministic selection)
Projects: web, mobile

Files processed: 234
Files transformed: 42
Transformations applied: 156
Errors: 0
Warnings: 3

Warnings:
- src/components/UserProfile.js: Fragment already marked (line 23)
- src/pages/AdminPanel.js: No conditional spreads found
- src/utils/fragments.js: Skipped - not a GraphQL file

Transformation complete!

Verbose Logging

Use the --output verbose flag for detailed logging:

relay-compiler codemod mark-dangerous-conditional-fragment-spreads --output verbose --rollout-percentage 25

This provides file-by-file transformation details for debugging and verification.

Integration with CI/CD

Validation Mode

Run codemods in validation mode to check if transformations are needed without making changes:

# Check if codemod would make changes (exit code 2 if changes needed)
relay-compiler codemod remove-unnecessary-required-directives --validate

Automated Rollouts

# GitHub Actions example
name: Gradual Codemod Rollout
on:
  schedule:
    - cron: '0 2 * * MON'  # Weekly rollout

jobs:
  codemod:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - run: npm install
      - run: |
          # Gradual rollout: increase percentage weekly
          WEEK=$(date +%U)
          PERCENTAGE=$((WEEK * 10))
          if [ $PERCENTAGE -le 100 ]; then
            relay-compiler codemod mark-dangerous-conditional-fragment-spreads --rollout-percentage $PERCENTAGE
          fi
      - run: npm test
      - run: git add -A && git commit -m "Codemod rollout: ${PERCENTAGE}%" && git push

Install with Tessl CLI

npx tessl i tessl/npm-relay-compiler

docs

cli-usage.md

codemods.md

configuration.md

index.md

language-server.md

programmatic-api.md

tile.json