Skip to content

Commit

Permalink
Transform static fromBinary, fromJson, fromJsonString
Browse files Browse the repository at this point in the history
Signed-off-by: Timo Stamm <ts@timostamm.de>
  • Loading branch information
timostamm committed Nov 27, 2024
1 parent 6f0565c commit cd9fe6c
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ const transform: j.Transform = (file, { j }, options) => {
// Identifiers imported from generated protos
const pbNames = new Set<string>();

// Will we need to import "create"?
let importCreate = false;
// Identifiers we'll need to import from @bufbuild/protobuf
const needBufbuildProtobufImports = new Set<string>();

// Replace wkt imports from @bufbuild/protobuf to @bufbuild/protobuf/wkt
root
Expand Down Expand Up @@ -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)`
Expand Down Expand Up @@ -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) => {
Expand Down Expand Up @@ -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: [
Expand All @@ -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,
),
Expand All @@ -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),
),
);
Expand Down Expand Up @@ -256,6 +277,43 @@ function findPbImports(name: string, root: j.Collection) {
});
}

function replaceStaticMethodCall(
methodName: string,
pbNames: Set<string>,
needBufbuildProtobufImports: Set<string>,
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<j.ImportDeclaration>,
): "double" | "single" {
Expand Down

0 comments on commit cd9fe6c

Please sign in to comment.