/* * Copyright 2025 the original author or authors. *

* Licensed under the Moderne Source Available License (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

* https://docs.moderne.io/licensing/moderne-source-available-license *

* 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 {Minutes, Recipe, RecipeDescriptor, ScanningRecipe} from "../recipe"; import {RewriteRpc} from "./rewrite-rpc"; import {noopVisitor, TreeVisitor} from "../visitor"; import {ExecutionContext} from "../execution"; import {SourceFile, Tree} from "../tree"; export class RpcRecipe extends ScanningRecipe { name: string = this._descriptor.name; displayName: string = this._descriptor.displayName; description: string = this._descriptor.description; tags: string[] = this._descriptor.tags; estimatedEffortPerOccurrence: Minutes = this._descriptor.estimatedEffortPerOccurrence; constructor(private readonly rpc: RewriteRpc, private readonly remoteId: string, private readonly _descriptor: RecipeDescriptor, readonly editVisitor: string, readonly scanVisitor?: string) { super(); } async descriptor(): Promise { return this._descriptor; } instanceName(): string { return this._descriptor.instanceName; } initialValue(_ctx: ExecutionContext) { return 0 } async editorWithData(_acc: number): Promise> { return this.editVisitor ? new RpcVisitor(this.rpc, this.editVisitor) : noopVisitor(); } async scanner(_acc: number): Promise> { return this.scanVisitor ? new RpcVisitor(this.rpc, this.scanVisitor) : noopVisitor(); } async generate(_acc: number, ctx: ExecutionContext): Promise { return this.rpc.generate(this.remoteId, ctx); } async recipeList(): Promise { const recipeList: Recipe[] = []; for (const r of this._descriptor.recipeList) { const opts = Object.fromEntries(r.options.map(opt => [opt.name, opt.value])); recipeList.push(await this.rpc.prepareRecipe(r.name, opts)); } return recipeList; } async onComplete(ctx: ExecutionContext): Promise { // This will merge data tables from the remote into the local context. // // When multiple recipes ran on the same RPC peer, they will all have been // adding to the same ExecutionContext instance on that peer, and so really // a CHANGE will only be returned for the first of any recipes on that peer. // It doesn't matter which one added data table entries, because they all share // the same view of the data tables. if ("org.openrewrite.rpc.id" in ctx) { const updated = await this.rpc.getObject(ctx.messages["org.openrewrite.rpc.id"]); Object.assign(ctx, updated); } } } export class RpcVisitor extends TreeVisitor { constructor( private readonly rpc: RewriteRpc, private readonly visitorName: string ) { super(); } async isAcceptable(sourceFile: SourceFile, _: ExecutionContext): Promise { return (await this.rpc.languages()).includes(sourceFile.kind); } protected async preVisit(tree: Tree, ctx: ExecutionContext): Promise { this.stopAfterPreVisit(); return this.rpc.visit(tree as SourceFile, this.visitorName, ctx); } }