A high-performance GraphQL compilation tool built in Rust for building Relay applications with optimized queries and type generation.
—
Relay Compiler includes automated code transformation tools (codemods) for migrating and improving Relay codebases with built-in safety controls and rollout mechanisms.
{ .api }
relay-compiler codemod <CODEMOD> [OPTIONS]{ .api }
--project, -p <PROJECT> # Target specific projects (repeatable)
--config <PATH> # Use specific config fileIdentifies and marks potentially unsafe conditional fragment spreads that could cause runtime errors.
{ .api }
relay-compiler codemod mark-dangerous-conditional-fragment-spreads [OPTIONS]{ .api }
--rollout-percentage, -r <PERCENT> # Percentage or range for gradual rollout
--project, -p <PROJECT> # Target specific projects
--config <PATH> # Use specific config file{ .api }
type RolloutPercentage =
| string // Single percentage: "50"
| string; // Range: "20-30"# 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 mobileThis 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.
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]{ .api }
--project, -p <PROJECT> # Target specific projects
--config <PATH> # Use specific config file# 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 adminThis 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
}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.
# 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>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
}Before running codemods on large codebases:
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;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)
}
}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
}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
}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!Use the --output verbose flag for detailed logging:
relay-compiler codemod mark-dangerous-conditional-fragment-spreads --output verbose --rollout-percentage 25This provides file-by-file transformation details for debugging and verification.
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# 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 pushInstall with Tessl CLI
npx tessl i tessl/npm-relay-compiler