From cd9fe6ccabc998a500dfccd767c99c1a7f3f8bd3 Mon Sep 17 00:00:00 2001 From: Timo Stamm Date: Wed, 27 Nov 2024 13:16:21 +0100 Subject: [PATCH] Transform static fromBinary, fromJson, fromJsonString Signed-off-by: Timo Stamm --- .../v2.0.0-transform-class-refs.spec.ts | 38 ++++++++++ .../migrations/v2.0.0-transform-class-refs.ts | 74 +++++++++++++++++-- 2 files changed, 104 insertions(+), 8 deletions(-) diff --git a/packages/connect-migrate/src/migrations/v2.0.0-transform-class-refs.spec.ts b/packages/connect-migrate/src/migrations/v2.0.0-transform-class-refs.spec.ts index bbb04d44..f327a305 100644 --- a/packages/connect-migrate/src/migrations/v2.0.0-transform-class-refs.spec.ts +++ b/packages/connect-migrate/src/migrations/v2.0.0-transform-class-refs.spec.ts @@ -150,4 +150,42 @@ describe("v2.0.0 transform class references", () => { const result = updateSourceFileInMemory(transform, input, "foo.ts"); expect(result.source).toBe(output); }); + it("transforms static fromBinary call", () => { + const input = [ + `import {Foo} from "./foo_pb";`, + `Foo.fromBinary(x, y);`, + ].join("\n"); + const output = [ + `import { FooSchema } from "./foo_pb";`, + `import { fromBinary } from "@bufbuild/protobuf";`, + `fromBinary(FooSchema, x, y);`, + ].join("\n"); + const result = updateSourceFileInMemory(transform, input, "foo.ts"); + expect(result.source).toEqual(output); + }); + it("transforms static fromJson call", () => { + const input = [`import {Foo} from "./foo_pb";`, `Foo.fromJson(x, y);`].join( + "\n", + ); + const output = [ + `import { FooSchema } from "./foo_pb";`, + `import { fromJson } from "@bufbuild/protobuf";`, + `fromJson(FooSchema, x, y);`, + ].join("\n"); + const result = updateSourceFileInMemory(transform, input, "foo.ts"); + expect(result.source).toEqual(output); + }); + it("transforms static fromJsonString call", () => { + const input = [ + `import {Foo} from "./foo_pb";`, + `Foo.fromJsonString(x, y);`, + ].join("\n"); + const output = [ + `import { FooSchema } from "./foo_pb";`, + `import { fromJsonString } from "@bufbuild/protobuf";`, + `fromJsonString(FooSchema, x, y);`, + ].join("\n"); + const result = updateSourceFileInMemory(transform, input, "foo.ts"); + expect(result.source).toEqual(output); + }); }); diff --git a/packages/connect-migrate/src/migrations/v2.0.0-transform-class-refs.ts b/packages/connect-migrate/src/migrations/v2.0.0-transform-class-refs.ts index 390d9cec..4b2c6c3c 100644 --- a/packages/connect-migrate/src/migrations/v2.0.0-transform-class-refs.ts +++ b/packages/connect-migrate/src/migrations/v2.0.0-transform-class-refs.ts @@ -30,8 +30,8 @@ const transform: j.Transform = (file, { j }, options) => { // Identifiers imported from generated protos const pbNames = new Set(); - // Will we need to import "create"? - let importCreate = false; + // Identifiers we'll need to import from @bufbuild/protobuf + const needBufbuildProtobufImports = new Set(); // Replace wkt imports from @bufbuild/protobuf to @bufbuild/protobuf/wkt root @@ -92,7 +92,7 @@ const transform: j.Transform = (file, { j }, options) => { ]), ); pbNames.add(name); - importCreate = true; + needBufbuildProtobufImports.add("create"); }); // Replace `isMessage(foo, Foo)` -> `isMessage(foo, FooSchema)` @@ -127,6 +127,26 @@ const transform: j.Transform = (file, { j }, options) => { pbNames.add(ident.name); }); + // Replace `Foo.fromBinary(x, y)` -> `fromBinary(FooSchema, x, y)` + replaceStaticMethodCall( + "fromBinary", + pbNames, + needBufbuildProtobufImports, + root, + ); + replaceStaticMethodCall( + "fromJson", + pbNames, + needBufbuildProtobufImports, + root, + ); + replaceStaticMethodCall( + "fromJsonString", + pbNames, + needBufbuildProtobufImports, + root, + ); + // Replace `import {Foo}` -> `import {FooSchema}` for (const name of pbNames) { findPbImports(name, root).forEach((path) => { @@ -164,7 +184,10 @@ const transform: j.Transform = (file, { j }, options) => { // Add `import {create} from "@bufbuild/protobuf"` // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- linter is wrong - if (importCreate) { + if (needBufbuildProtobufImports.size > 0) { + const needSpecifiers = Array.from(needBufbuildProtobufImports).map((name) => + j.importSpecifier(j.identifier(name)), + ); // Find existing import const importBufbuildProtobuf = root.find(j.ImportDeclaration, { specifiers: [ @@ -184,9 +207,7 @@ const transform: j.Transform = (file, { j }, options) => { .at(0) .replaceWith((path) => j.importDeclaration( - (path.value.specifiers ?? []).concat( - j.importSpecifier(j.identifier("create")), - ), + [...(path.value.specifiers ?? []), ...needSpecifiers], path.value.source, path.value.importKind, ), @@ -204,7 +225,7 @@ const transform: j.Transform = (file, { j }, options) => { .at(0) .insertAfter( j.importDeclaration( - [j.importSpecifier(j.identifier("create"))], + needSpecifiers, j.stringLiteral(bufbuildProtobufPackage), ), ); @@ -256,6 +277,43 @@ function findPbImports(name: string, root: j.Collection) { }); } +function replaceStaticMethodCall( + methodName: string, + pbNames: Set, + needBufbuildProtobufImports: Set, + root: j.Collection, +): void { + root + .find(j.CallExpression, { + callee: { + type: "MemberExpression", + object: { + type: "Identifier", + }, + property: { + type: "Identifier", + name: methodName, + }, + }, + }) + .forEach((path) => { + const callee = path.value.callee as j.MemberExpression; + const object = callee.object as j.Identifier; + const pbImports = findPbImports(object.name, root); + if (pbImports.size() === 0) { + return; + } + path.replace( + j.callExpression(j.identifier(methodName), [ + j.identifier(object.name + "Schema"), + ...path.value.arguments, + ]), + ); + pbNames.add(object.name); + needBufbuildProtobufImports.add(methodName); + }); +} + function determineQuoteStyle( importPaths: j.Collection, ): "double" | "single" {