Skip to content
857 changes: 857 additions & 0 deletions skills/openrewrite-recipe-authoring-js/SKILL.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright 2025 the original author or authors.
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* https://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {ExecutionContext, Recipe} from "@openrewrite/rewrite";
import {JavaScriptVisitor} from "@openrewrite/rewrite/javascript";
import {J} from "@openrewrite/rewrite/java";
import {produce} from "immer";

/**
* TODO: Add recipe description
*/
export class MyRecipe extends Recipe {
name = "org.openrewrite.javascript.MyRecipe";
displayName = "My Recipe Display Name";
description = "Brief description of what this recipe does.";

async editor(): Promise<JavaScriptVisitor<ExecutionContext>> {
return new class extends JavaScriptVisitor<ExecutionContext> {
// Override visit methods to implement transformation logic
// Example:
override async visitMethodInvocation(
method: J.MethodInvocation,
ctx: ExecutionContext
): Promise<J | undefined> {
// Call super first to visit children (default pattern)
const visited = await super.visitMethodInvocation(method, ctx) as J.MethodInvocation;

// TODO: Add transformation logic here
// Example: Modify the method invocation using produce
// return produce(visited, draft => {
// // Make changes to draft
// });

return visited;
}
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Copyright 2025 the original author or authors.
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* https://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {ExecutionContext, Recipe} from "@openrewrite/rewrite";
import {JavaScriptVisitor, capture, pattern, rewrite, template} from "@openrewrite/rewrite/javascript";
import {J} from "@openrewrite/rewrite/java";

/**
* Example recipe using pattern/template for transformation
*/
export class PatternRewriteExample extends Recipe {
name = "org.openrewrite.javascript.PatternRewriteExample";
displayName = "Pattern Rewrite Example";
description = "Example of using pattern matching and templates for transformations.";

async editor(): Promise<JavaScriptVisitor<ExecutionContext>> {
// Define transformation rule
const rule = rewrite(() => {
const args = capture({ variadic: true });
return {
before: pattern`oldApi.method(${args})`,
after: template`newApi.methodAsync(${args})`
};
});

return new class extends JavaScriptVisitor<ExecutionContext> {
override async visitMethodInvocation(
method: J.MethodInvocation,
ctx: ExecutionContext
): Promise<J | undefined> {
// Try to apply the rule - returns transformed node or undefined
return await rule.tryOn(this.cursor, method) || method;
}
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Copyright 2025 the original author or authors.
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* https://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {RecipeSpec} from "@openrewrite/rewrite/test";
import {javascript, typescript} from "@openrewrite/rewrite/javascript";
import {MyRecipe} from "./MyRecipe"; // TODO: Update import path

describe('MyRecipe', () => {
const spec = new RecipeSpec();

test('transforms code as expected', () => {
spec.recipe = new MyRecipe();

return spec.rewriteRun(
javascript(
// Before transformation
`const x = oldPattern();`,
// After transformation
`const x = newPattern();`
)
);
});

test('does not transform unrelated code', () => {
spec.recipe = new MyRecipe();

return spec.rewriteRun(
// Single argument = no change expected
javascript(`const x = unrelatedPattern();`)
);
});

test('handles edge cases', () => {
spec.recipe = new MyRecipe();

return spec.rewriteRun(
javascript(
`const x = edgeCase();`,
`const x = transformedEdgeCase();`
)
);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* Copyright 2025 the original author or authors.
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* https://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {ExecutionContext, Option, Recipe} from "@openrewrite/rewrite";
import {JavaScriptVisitor} from "@openrewrite/rewrite/javascript";
import {J} from "@openrewrite/rewrite/java";
import {produce} from "immer";

/**
* TODO: Add recipe description
*/
export class MyConfigurableRecipe extends Recipe {
name = "org.openrewrite.javascript.MyConfigurableRecipe";
displayName = "My Configurable Recipe";
description = "Recipe with configurable options.";

@Option({
displayName: "Option Name",
description: "Description of what this option does.",
example: "exampleValue"
})
optionName!: string;

constructor(options: { optionName: string }) {
super(options);
}

async editor(): Promise<JavaScriptVisitor<ExecutionContext>> {
// Capture option values for use in closure
const optionValue = this.optionName;

return new class extends JavaScriptVisitor<ExecutionContext> {
override async visitMethodInvocation(
method: J.MethodInvocation,
ctx: ExecutionContext
): Promise<J | undefined> {
const visited = await super.visitMethodInvocation(method, ctx) as J.MethodInvocation;

// Use captured option value
// TODO: Add transformation logic using optionValue
// Example:
// if (someCondition) {
// return produce(visited, draft => {
// // Use optionValue to make changes
// });
// }

return visited;
}
};
}
}
Loading
Loading