-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathsay-hello-recipe.ts
More file actions
83 lines (72 loc) · 2.87 KB
/
say-hello-recipe.ts
File metadata and controls
83 lines (72 loc) · 2.87 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
import { Recipe, ExecutionContext, TreeVisitor } from '@openrewrite/rewrite';
import { JavaScriptVisitor, template } from '@openrewrite/rewrite/javascript';
import { J } from '@openrewrite/rewrite/java';
import { create, Draft } from 'mutative';
/**
* Recipe that adds a hello() method to JavaScript/TypeScript classes.
*
* This recipe demonstrates:
* - Visiting class declarations
* - Checking for existing methods
* - Using the template API to add new methods
*/
export class SayHelloRecipe extends Recipe {
readonly name = "com.yourorg.SayHelloRecipe";
readonly displayName = "Say Hello";
readonly description = "Adds a hello() method to classes that don't have one";
async editor(): Promise<TreeVisitor<any, ExecutionContext>> {
return new SayHelloVisitor();
}
}
/**
* Visitor that traverses the LST and adds the hello method to classes.
*/
class SayHelloVisitor extends JavaScriptVisitor<ExecutionContext> {
protected async visitClassDeclaration(
classDecl: J.ClassDeclaration,
ctx: ExecutionContext
): Promise<J | undefined> {
// First, continue visiting children
const visited = await super.visitClassDeclaration(classDecl, ctx);
if (!visited) {
return visited;
}
const updatedClass = visited as J.ClassDeclaration;
// Check if the class already has a hello method
const hasHelloMethod = this.classHasHelloMethod(updatedClass);
if (!hasHelloMethod) {
// Create a temporary class with just the hello method using template
const tempClass = await template`class Temp {
hello() {
return "Hello from " + this.constructor.name + "!";
}
}`.apply(updatedClass, this.cursor);
if (tempClass && (tempClass as J.ClassDeclaration).body) {
// Extract the hello method from the temporary class
const helloMethod = (tempClass as J.ClassDeclaration).body.statements[0];
// Add the hello method to the existing class body using create
return create(updatedClass, (draft: Draft<J.ClassDeclaration>) => {
draft.body.statements = [...draft.body.statements, helloMethod];
});
}
}
return updatedClass;
}
/**
* Checks if a class already has a hello method.
*/
private classHasHelloMethod(classDecl: J.ClassDeclaration): boolean {
if (!classDecl.body || !classDecl.body.statements) {
return false;
}
return classDecl.body.statements.some((stmt) => {
// Properly unwrap RightPadded wrapper
const element = stmt.element;
if (element.kind === J.Kind.MethodDeclaration) {
const method = element as J.MethodDeclaration;
return method.name.simpleName === 'hello';
}
return false;
});
}
}