Skip to content

Commit 84a24f9

Browse files
authored
Merge pull request #241 from proto-graphql/izumin5210/rewrite-protoc-gen-nexus-with-ts-poet
refactor(protoc-gen-nexus): rewrite printer with ts-poet
2 parents af45fdf + 5ecd725 commit 84a24f9

File tree

25 files changed

+17334
-20838
lines changed

25 files changed

+17334
-20838
lines changed

.changeset/plenty-days-remain.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
"@proto-graphql/codegen-core": minor
3+
"@proto-graphql/proto-descriptors": minor
4+
"protoc-gen-nexus": minor
5+
"protoc-gen-pothos": patch
6+
---
7+
8+
rewrite protoc-gen-nexus printer with ts-poet

.changeset/popular-seals-compete.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@
44
"protoc-gen-pothos": minor
55
---
66

7-
rewrite printer with ts-poet
7+
rewrite protoc-gen-pothos printer with ts-poet

packages/@proto-graphql/codegen-core/src/printer/util.ts

Lines changed: 119 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
1-
import { ProtoEnum, ProtoMessage } from "@proto-graphql/proto-descriptors";
1+
import { ProtoEnum, ProtoField, ProtoMessage, ProtoScalarType } from "@proto-graphql/proto-descriptors";
2+
import { camelCase } from "change-case";
23
import * as path from "path";
34
import { code, Code, imp } from "ts-poet";
45
import {
56
EnumType,
7+
InputObjectField,
68
InputObjectType,
79
InterfaceType,
10+
ObjectField,
11+
ObjectOneofField,
812
ObjectType,
913
OneofUnionType,
1014
PrinterOptions,
@@ -30,6 +34,24 @@ export function filename(
3034
}
3135
}
3236

37+
export function generatedGraphQLTypeImportPath(
38+
field:
39+
| ObjectField<ObjectType | EnumType | InterfaceType | SquashedOneofUnionType>
40+
| InputObjectField<InputObjectType | EnumType>
41+
| ObjectOneofField,
42+
opts: PrinterOptions
43+
): string | null {
44+
if (field instanceof ObjectOneofField) return null;
45+
const [fromPath, toPath] = [filename(field.parent, opts), filename(field.type, opts)].map((f) =>
46+
path.isAbsolute(f) ? `.${path.sep}${f}` : f
47+
);
48+
49+
if (fromPath === toPath) return null;
50+
51+
const importPath = path.relative(path.dirname(fromPath), toPath).replace(/\.ts$/, "");
52+
return importPath.match(/^[\.\/]/) ? importPath : `./${importPath}`;
53+
}
54+
3355
/** Remove nullish values recursively. */
3456
export function compact(v: any): any {
3557
if (typeof v !== "object") return v;
@@ -46,19 +68,69 @@ function compactObj<In extends Out, Out extends Record<string, unknown>>(obj: In
4668
}, {} as Out);
4769
}
4870

49-
export function protoType(origProto: ProtoMessage | ProtoEnum, opts: PrinterOptions): Code {
71+
export function protoType(origProto: ProtoMessage | ProtoEnum | ProtoField, opts: PrinterOptions): Code {
72+
const origProtoType = origProto.kind === "Field" ? origProto.type : origProto;
73+
if (origProtoType.kind === "Scalar") {
74+
throw new Error("cannot import protobuf primitive types");
75+
}
76+
let proto = origProtoType;
77+
const chunks = [proto.name];
78+
while (proto.parent.kind !== "File") {
79+
proto = proto.parent;
80+
chunks.unshift(proto.name);
81+
}
82+
switch (opts.protobuf) {
83+
case "google-protobuf": {
84+
return code`${imp(`${chunks[0]}@${protoImportPath(proto, opts)}`)}${chunks
85+
.slice(1)
86+
.map((c) => `.${c}`)
87+
.join("")}`;
88+
}
89+
case "protobufjs": {
90+
chunks.unshift(...proto.file.package.split("."));
91+
const importPath = protoImportPath(origProto.kind === "Field" ? origProto.parent : origProto, opts);
92+
return code`${imp(`${chunks[0]}@${importPath}`)}.${chunks.slice(1).join(".")}`;
93+
}
94+
case "ts-proto": {
95+
return code`${imp(`${chunks.join("_")}@${protoImportPath(proto, opts)}`)}`;
96+
}
97+
/* istanbul ignore next */
98+
default: {
99+
const _exhaustiveCheck: never = opts;
100+
throw "unreachable";
101+
}
102+
}
103+
}
104+
105+
export function createGetFieldValueCode(parent: Code, proto: ProtoField, opts: PrinterOptions): Code {
106+
switch (opts.protobuf) {
107+
case "google-protobuf": {
108+
return code`${parent}.${googleProtobufFieldAccessor("get", proto)}()`;
109+
}
110+
case "protobufjs": {
111+
return code`${parent}.${camelCase(proto.name)}`;
112+
}
113+
case "ts-proto": {
114+
return code`${parent}.${proto.jsonName}`;
115+
}
116+
/* istanbul ignore next */
117+
default: {
118+
const _exhaustiveCheck: never = opts;
119+
throw "unreachable";
120+
}
121+
}
122+
}
123+
124+
export function createSetFieldValueCode(parent: Code, value: Code, proto: ProtoField, opts: PrinterOptions): Code {
50125
switch (opts.protobuf) {
51-
case "google-protobuf":
52-
case "protobufjs":
53-
throw new Error(`not implemented: ${opts.protobuf}`);
126+
case "google-protobuf": {
127+
return code`${parent}.${googleProtobufFieldAccessor("set", proto)}(${value})`;
128+
}
129+
case "protobufjs": {
130+
return code`${parent}.${camelCase(proto.name)} = ${value}`;
131+
}
54132
case "ts-proto": {
55-
let proto = origProto;
56-
let name = proto.name;
57-
while (proto.parent.kind !== "File") {
58-
proto = proto.parent;
59-
name = `${proto.name}_${name}`;
60-
}
61-
return code`${imp(`${name}@${protoImportPath(proto, opts)}`)}`;
133+
return code`${parent}.${proto.jsonName} = ${value}`;
62134
}
63135
/* istanbul ignore next */
64136
default: {
@@ -67,3 +139,38 @@ export function protoType(origProto: ProtoMessage | ProtoEnum, opts: PrinterOpti
67139
}
68140
}
69141
}
142+
143+
function googleProtobufFieldAccessor(type: "get" | "set", proto: ProtoField) {
144+
return `${type}${upperCaseFirst(proto.jsonName)}${proto.list ? "List" : ""}`;
145+
}
146+
147+
function upperCaseFirst(s: string): string {
148+
return `${s.charAt(0).toUpperCase()}${s.slice(1)}`;
149+
}
150+
151+
const longScalarPrimitiveTypes: ReadonlySet<ProtoScalarType> = new Set([
152+
"int64",
153+
"uint64",
154+
"fixed64",
155+
"sfixed64",
156+
"sint64",
157+
]);
158+
const longScalarWrapperTypes: ReadonlySet<string> = new Set([
159+
"google.protobuf.Int64Value",
160+
"google.protobuf.UInt64Value",
161+
]);
162+
163+
export function isProtobufLong(proto: ProtoField): boolean {
164+
switch (proto.type.kind) {
165+
case "Scalar":
166+
return longScalarPrimitiveTypes.has(proto.type.type);
167+
case "Message":
168+
return longScalarWrapperTypes.has(proto.type.fullName.toString());
169+
default:
170+
return false;
171+
}
172+
}
173+
174+
export function isWellKnownType(proto: ProtoField["type"]): proto is ProtoMessage {
175+
return proto.kind === "Message" && proto.file.name.startsWith("google/protobuf/");
176+
}

packages/@proto-graphql/proto-descriptors/src/impls.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -330,11 +330,13 @@ export class ProtoFieldImpl implements ProtoField {
330330
}
331331

332332
@memo()
333-
get type(): ProtoMessage | ProtoEnum | ProtoScalar | null {
333+
get type(): ProtoMessage | ProtoEnum | ProtoScalar {
334334
const scalarType = getScalarTypeFromDescriptor(this.descriptor);
335335
if (scalarType !== undefined) return { kind: "Scalar", type: scalarType };
336336
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
337-
return this.registry.findTypeByFullName(this.descriptor.getTypeName()!.replace(/^\./, ""));
337+
const foundType = this.registry.findTypeByFullName(this.descriptor.getTypeName()!.replace(/^\./, ""));
338+
if (foundType === null) throw new Error(`Not found type for ${this.fullName.toString()}`);
339+
return foundType;
338340
}
339341

340342
@memo()

packages/@proto-graphql/proto-descriptors/src/interfaces.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ export interface ProtoField extends ProtoBase<"Field"> {
147147
readonly index: number;
148148
readonly number: number;
149149
readonly parent: ProtoMessage;
150-
readonly type: ProtoMessage | ProtoEnum | ProtoScalar | null;
150+
readonly type: ProtoMessage | ProtoEnum | ProtoScalar;
151151
readonly containingOneof: ProtoOneof | null;
152152
readonly list: boolean;
153153
readonly comments: CommentSet;

packages/protoc-gen-nexus/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@
2727
"@proto-graphql/proto-descriptors": "^0.2.0",
2828
"@proto-graphql/protoc-plugin-helpers": "^0.2.1",
2929
"change-case": "^4.1.2",
30-
"google-protobuf": "^3.20.1"
30+
"google-protobuf": "^3.20.1",
31+
"ts-poet": "^6.3.0"
3132
},
3233
"devDependencies": {
3334
"@proto-nexus/google-protobuf": "^0.5.1",

0 commit comments

Comments
 (0)