openrewrite
This skill should be used when implementing or modifying OpenRewrite recipes in the Liftwizard project, when the user asks to "create a recipe", "implement a Refaster template", "convert to Refaster", "add an OpenRewrite transformation", or discusses recipe implementation patterns. Provides guidance on Refaster templates, traditional recipe implementation, type safety, and common patterns.
SKILL.md
| Name | openrewrite |
| Description | This skill should be used when implementing or modifying OpenRewrite recipes in the Liftwizard project, when the user asks to "create a recipe", "implement a Refaster template", "convert to Refaster", "add an OpenRewrite transformation", or discusses recipe implementation patterns. Provides guidance on Refaster templates, traditional recipe implementation, type safety, and common patterns. |
name: openrewrite description: This skill should be used when implementing or modifying OpenRewrite recipes in the Liftwizard project, when the user asks to "create a recipe", "implement a Refaster template", "convert to Refaster", "add an OpenRewrite transformation", or discusses recipe implementation patterns. Provides guidance on Refaster templates, traditional recipe implementation, type safety, and common patterns.
OpenRewrite Recipe Development
Guidance for implementing and modifying OpenRewrite recipes in the Liftwizard project, based on lessons learned from production experience.
When to Use This Skill
Use this skill when:
- Implementing new OpenRewrite recipes
- Converting existing recipes to use Refaster templates
- Debugging type validation issues in recipes
- Deciding between Refaster and traditional Recipe implementations
For test-related guidance, see the separate openrewrite-test skill.
Refaster vs. Traditional Recipe Implementation
When to Use Refaster Templates
✅ Good Use Cases:
- Always prefer Refaster Templates
- Try to implement with Refaster first always
- Don't switch to traditional recipes unless the user confirms
❌ Poor Use Cases:
- Varargs handling (until OpenRewrite issue #4397 adds
@Repeatedannotation support) - Generating method references (they lack proper type information in LST)
Combining Multiple @BeforeTemplate Methods
Rule: Combine multiple @BeforeTemplate methods into one nested class when they produce the same @AfterTemplate output.
Within a single nested class, you can have multiple @BeforeTemplate methods that all share one identical @AfterTemplate. This is useful when semantically different source patterns should transform to the same target pattern:
- Flipped comparison order:
size() == 0vs0 == size()→isEmpty() - Different operators with same meaning:
size() < 1vssize() <= 0→isEmpty() - Other structures with the same meaning:
left == null ? right == null : left.equals(right)vsleft == right || (left != null && left.equals(right))→Objects.equals(left, right)
When to create separate nested classes:
Create separate nested classes when the @AfterTemplate output differs in any way:
- Presence/absence of an optional parameter:
assertThat(x).isNotEmpty()vsassertThat(x).as(message).isNotEmpty()→ separate classes - Different method calls:
foo(x)vsbar(x)→ separate classes - Different parameter types that happen to look similar but aren't actually the same transformation
Refaster Naming Convention
When creating Refaster templates:
- Main class name:
RecipeName(e.g.,ECSimplifyNegatedSatisfies) - Generated class name (used in tests):
RecipeNameRecipes(e.g.,ECSimplifyNegatedSatisfiesRecipes) - Update YAML recipe files to reference the generated
Recipesclass
Type Safety and Validation
Using classpath() vs dependsOn()
Prefer classpath() when possible:
- Add
.classpath("eclipse-collections-api")to JavaTemplate builders for real Eclipse Collections types - Add
.classpath("eclipse-collections")for implementation classes if needed - Almost always works and is better than dependsOn()
Use dependsOn() when necessary:
Verifyclass stubs in AssertJ migration tests (eclipse-collections-testutils classpath doesn't work)
Experiment incrementally:
- Remove one
dependsOn()text block - Run tests - expect failure (missing type information)
- Add one
classpath()entry to fix - Run tests - should pass if real JAR class works
- Stop when tests fail despite class existing in JAR
Removing typeValidationOptions(TypeValidation.none())
Process:
- Remove
.typeValidationOptions(TypeValidation.none())line - Run test
- If passes, commit
- If fails, investigate using OpenRewrite FAQ
Common Fix: Add appropriate classpath() entries to JavaTemplate builders to provide real type information
Fundamental Limitations (cannot remove TypeValidation.none()):
- Refaster-generated method references lack proper type information
- These tests must keep
.typeValidationOptions(TypeValidation.none())
Common Implementation Patterns
Predicates.not() and Negation Patterns
Add support for both explicit negation (!method()) and Predicates.not() wrapping:
!iterable.noneSatisfy(p)→iterable.anySatisfy(p)Iterate.noneSatisfy(iterable, Predicates.not(p))→Iterate.anySatisfy(iterable, p)- Ensure unused
Predicatesimports are correctly removed
Comparator Support in Sorted Collections
For sorted collection transformations, add overloads that accept a Comparator:
TreeSortedSet.newSet(Comparator, Arrays.asList(...))→SortedSets.mutable.with(Comparator, ...)- Test with both
Comparator.naturalOrder()andComparator.reverseOrder() - Create separate arities (1-5) when using Refaster due to varargs limitation
Varargs Handling with Refaster
Until OpenRewrite issue #4397 is resolved:
- Create separate template classes for each arity (typically 0-5)
- Or convert to traditional Recipe using
JavaIsoVisitorwith dynamic placeholder building - Document in code that Refaster could be used once
@Repeatedannotation is supported
Documentation Best Practices
Document Refaster Limitations
When refactoring from traditional Recipe to Refaster, document in class javadoc:
- What context-aware features were lost (e.g., circular transformation prevention)
- Why Refaster cannot handle certain cases
- Reference relevant OpenRewrite issues when applicable
Reference OpenRewrite Issues
- Issue #4397: Lack of
@Repeatedannotation for varargs support in Refaster - Include issue numbers in comments for future refactoring opportunities
Key Takeaways
- Always prefer Refaster templates unless there's a specific limitation
- Always prefer classpath() over dependsOn() when it works
- Test type safety by removing TypeValidation.none() where possible
- Document limitations and trade-offs when converting to Refaster
- Traditional Recipe implementation is better when you need varargs handling or method reference generation