0
# Codemods
1
2
Relay Compiler includes automated code transformation tools (codemods) for migrating and improving Relay codebases with built-in safety controls and rollout mechanisms.
3
4
## Running Codemods
5
6
```bash
7
{ .api }
8
relay-compiler codemod <CODEMOD> [OPTIONS]
9
```
10
11
### Global Options
12
13
```bash
14
{ .api }
15
--project, -p <PROJECT> # Target specific projects (repeatable)
16
--config <PATH> # Use specific config file
17
```
18
19
## Available Codemods
20
21
### Mark Dangerous Conditional Fragment Spreads
22
23
Identifies and marks potentially unsafe conditional fragment spreads that could cause runtime errors.
24
25
```bash
26
{ .api }
27
relay-compiler codemod mark-dangerous-conditional-fragment-spreads [OPTIONS]
28
```
29
30
#### Options
31
32
```bash
33
{ .api }
34
--rollout-percentage, -r <PERCENT> # Percentage or range for gradual rollout
35
--project, -p <PROJECT> # Target specific projects
36
--config <PATH> # Use specific config file
37
```
38
39
#### Percentage Formats
40
41
```typescript
42
{ .api }
43
type RolloutPercentage =
44
| string // Single percentage: "50"
45
| string; // Range: "20-30"
46
```
47
48
#### Usage Examples
49
50
```bash
51
# Apply to 50% of applicable files
52
relay-compiler codemod mark-dangerous-conditional-fragment-spreads --rollout-percentage 50
53
54
# Apply to files in a percentage range
55
relay-compiler codemod mark-dangerous-conditional-fragment-spreads -r 20-30
56
57
# Apply to specific project only
58
relay-compiler codemod mark-dangerous-conditional-fragment-spreads --project web
59
60
# Apply with custom config
61
relay-compiler codemod mark-dangerous-conditional-fragment-spreads --config ./custom-relay.config.json
62
63
# Apply to multiple projects
64
relay-compiler codemod mark-dangerous-conditional-fragment-spreads -p web -p mobile
65
```
66
67
#### What It Does
68
69
This codemod identifies conditional fragment spreads that might cause runtime errors due to missing alias information:
70
71
**Before:**
72
```graphql
73
fragment UserProfile on User {
74
name
75
... on AdminUser {
76
adminLevel
77
}
78
... on RegularUser {
79
memberSince
80
}
81
}
82
```
83
84
**After:**
85
```graphql
86
fragment UserProfile on User {
87
name
88
... on AdminUser @__dangerouslyUnaliasedConditional {
89
adminLevel
90
}
91
... on RegularUser @__dangerouslyUnaliasedConditional {
92
memberSince
93
}
94
}
95
```
96
97
The `@__dangerouslyUnaliasedConditional` directive serves as a marker for manual review and potential refactoring.
98
99
### Remove Unnecessary Required Directives
100
101
Removes `@required` directives from fields that are already non-null in the schema, cleaning up redundant code.
102
103
```bash
104
{ .api }
105
relay-compiler codemod remove-unnecessary-required-directives [OPTIONS]
106
```
107
108
#### Options
109
110
```bash
111
{ .api }
112
--project, -p <PROJECT> # Target specific projects
113
--config <PATH> # Use specific config file
114
```
115
116
#### Usage Examples
117
118
```bash
119
# Apply to all projects
120
relay-compiler codemod remove-unnecessary-required-directives
121
122
# Apply to specific project
123
relay-compiler codemod remove-unnecessary-required-directives --project web
124
125
# Apply with custom configuration
126
relay-compiler codemod remove-unnecessary-required-directives --config ./relay.config.json
127
128
# Apply to multiple projects
129
relay-compiler codemod remove-unnecessary-required-directives -p web -p mobile -p admin
130
```
131
132
#### What It Does
133
134
This codemod removes unnecessary `@required` directives from fields that are already non-null according to the GraphQL schema:
135
136
**Before:**
137
```graphql
138
fragment UserInfo on User {
139
id @required(action: LOG) # id is ID! in schema
140
name @required(action: THROW) # name is String! in schema
141
email # email is String? in schema
142
}
143
```
144
145
**After:**
146
```graphql
147
fragment UserInfo on User {
148
id # @required removed - field is already non-null
149
name # @required removed - field is already non-null
150
email # @required preserved - field is nullable
151
}
152
```
153
154
## Rollout Strategy
155
156
### Percentage-based Rollouts
157
158
Codemods support gradual rollouts to minimize risk:
159
160
```typescript
161
{ .api }
162
interface RolloutStrategy {
163
percentage: number; // 0-100
164
range?: [number, number]; // Alternative: range rollout
165
fileSelection: "random" | "deterministic";
166
}
167
```
168
169
**Deterministic Selection**: Files are selected based on a hash of their path, ensuring consistent selection across runs.
170
171
**Random Selection**: Files are selected randomly each time the codemod runs.
172
173
### Project-based Rollouts
174
175
```bash
176
# Start with a small project
177
relay-compiler codemod <CODEMOD> --project small-app
178
179
# Expand to larger projects
180
relay-compiler codemod <CODEMOD> --project web --project mobile
181
182
# Apply to all projects (no --project flag)
183
relay-compiler codemod <CODEMOD>
184
```
185
186
## Safety Mechanisms
187
188
### Dry Run Mode
189
190
All codemods include built-in validation to ensure they only make safe transformations:
191
192
```typescript
193
{ .api }
194
interface CodemodSafety {
195
validateBeforeTransform: boolean; // Validate AST before changes
196
validateAfterTransform: boolean; // Validate AST after changes
197
preserveComments: boolean; // Maintain code comments
198
preserveFormatting: boolean; // Maintain code style
199
}
200
```
201
202
### Backup Recommendations
203
204
Before running codemods on large codebases:
205
206
1. **Commit all changes**: Ensure clean working directory
207
2. **Run tests**: Verify baseline functionality
208
3. **Use version control**: Enable easy rollback
209
4. **Start small**: Test on subset before full rollout
210
211
## Custom Codemods
212
213
While the CLI doesn't expose custom codemod registration, you can use the programmatic API to build custom transformations:
214
215
```rust
216
{ .api }
217
// Codemod trait for custom implementations
218
pub trait Codemod {
219
fn name(&self) -> &'static str;
220
fn description(&self) -> &'static str;
221
fn transform(&self, program: &Program) -> Result<Program, CodemodError>;
222
}
223
224
// Built-in codemod implementations
225
pub struct MarkDangerousConditionalFragmentSpreads {
226
pub rollout_percentage: Option<f32>,
227
}
228
229
pub struct RemoveUnnecessaryRequiredDirectives;
230
```
231
232
### Custom Codemod Example
233
234
```rust
235
use relay_compiler::{Codemod, Program, CodemodError};
236
237
pub struct CustomCodemod;
238
239
impl Codemod for CustomCodemod {
240
fn name(&self) -> &'static str {
241
"custom-transformation"
242
}
243
244
fn description(&self) -> &'static str {
245
"Applies custom transformation logic"
246
}
247
248
fn transform(&self, program: &Program) -> Result<Program, CodemodError> {
249
// Custom transformation logic
250
let mut transformed_program = program.clone();
251
252
// Apply transformations to fragments, operations, etc.
253
// ...
254
255
Ok(transformed_program)
256
}
257
}
258
```
259
260
## Error Handling
261
262
Codemods provide detailed error reporting for various failure scenarios:
263
264
```typescript
265
{ .api }
266
interface CodemodErrors {
267
ParseError: string; // GraphQL parsing failures
268
ValidationError: string; // Schema validation failures
269
TransformationError: string; // Transformation logic failures
270
FileSystemError: string; // File read/write failures
271
ConfigurationError: string; // Invalid configuration
272
}
273
```
274
275
### Common Error Scenarios
276
277
1. **Invalid GraphQL syntax**: Codemod skips malformed files with warning
278
2. **Schema mismatch**: Reports fields not found in schema
279
3. **Permission errors**: File system access issues
280
4. **Configuration errors**: Invalid project names or paths
281
282
## Reporting and Logging
283
284
### Transformation Summary
285
286
After running a codemod, you'll receive a detailed summary:
287
288
```typescript
289
{ .api }
290
interface CodemodSummary {
291
filesProcessed: number; // Total files examined
292
filesTransformed: number; // Files that were modified
293
transformationsApplied: number; // Total number of changes made
294
errors: CodemodError[]; // Any errors encountered
295
warnings: string[]; // Non-fatal issues
296
}
297
```
298
299
### Example Output
300
301
```
302
Codemod: mark-dangerous-conditional-fragment-spreads
303
Rollout: 50% (deterministic selection)
304
Projects: web, mobile
305
306
Files processed: 234
307
Files transformed: 42
308
Transformations applied: 156
309
Errors: 0
310
Warnings: 3
311
312
Warnings:
313
- src/components/UserProfile.js: Fragment already marked (line 23)
314
- src/pages/AdminPanel.js: No conditional spreads found
315
- src/utils/fragments.js: Skipped - not a GraphQL file
316
317
Transformation complete!
318
```
319
320
### Verbose Logging
321
322
Use the `--output verbose` flag for detailed logging:
323
324
```bash
325
relay-compiler codemod mark-dangerous-conditional-fragment-spreads --output verbose --rollout-percentage 25
326
```
327
328
This provides file-by-file transformation details for debugging and verification.
329
330
## Integration with CI/CD
331
332
### Validation Mode
333
334
Run codemods in validation mode to check if transformations are needed without making changes:
335
336
```bash
337
# Check if codemod would make changes (exit code 2 if changes needed)
338
relay-compiler codemod remove-unnecessary-required-directives --validate
339
```
340
341
### Automated Rollouts
342
343
```yaml
344
# GitHub Actions example
345
name: Gradual Codemod Rollout
346
on:
347
schedule:
348
- cron: '0 2 * * MON' # Weekly rollout
349
350
jobs:
351
codemod:
352
runs-on: ubuntu-latest
353
steps:
354
- uses: actions/checkout@v2
355
- run: npm install
356
- run: |
357
# Gradual rollout: increase percentage weekly
358
WEEK=$(date +%U)
359
PERCENTAGE=$((WEEK * 10))
360
if [ $PERCENTAGE -le 100 ]; then
361
relay-compiler codemod mark-dangerous-conditional-fragment-spreads --rollout-percentage $PERCENTAGE
362
fi
363
- run: npm test
364
- run: git add -A && git commit -m "Codemod rollout: ${PERCENTAGE}%" && git push
365
```