Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .changeset/plenty-days-remain.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@proto-graphql/codegen-core": minor
"@proto-graphql/proto-descriptors": minor
"protoc-gen-nexus": minor
"protoc-gen-pothos": patch
---

rewrite protoc-gen-nexus printer with ts-poet
2 changes: 1 addition & 1 deletion .changeset/popular-seals-compete.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@
"protoc-gen-pothos": minor
---

rewrite printer with ts-poet
rewrite protoc-gen-pothos printer with ts-poet
131 changes: 119 additions & 12 deletions packages/@proto-graphql/codegen-core/src/printer/util.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import { ProtoEnum, ProtoMessage } from "@proto-graphql/proto-descriptors";
import { ProtoEnum, ProtoField, ProtoMessage, ProtoScalarType } from "@proto-graphql/proto-descriptors";
import { camelCase } from "change-case";
import * as path from "path";
import { code, Code, imp } from "ts-poet";
import {
EnumType,
InputObjectField,
InputObjectType,
InterfaceType,
ObjectField,
ObjectOneofField,
ObjectType,
OneofUnionType,
PrinterOptions,
Expand All @@ -30,6 +34,24 @@ export function filename(
}
}

export function generatedGraphQLTypeImportPath(
field:
| ObjectField<ObjectType | EnumType | InterfaceType | SquashedOneofUnionType>
| InputObjectField<InputObjectType | EnumType>
| ObjectOneofField,
opts: PrinterOptions
): string | null {
if (field instanceof ObjectOneofField) return null;
const [fromPath, toPath] = [filename(field.parent, opts), filename(field.type, opts)].map((f) =>
path.isAbsolute(f) ? `.${path.sep}${f}` : f
);

if (fromPath === toPath) return null;

const importPath = path.relative(path.dirname(fromPath), toPath).replace(/\.ts$/, "");
return importPath.match(/^[\.\/]/) ? importPath : `./${importPath}`;
}

/** Remove nullish values recursively. */
export function compact(v: any): any {
if (typeof v !== "object") return v;
Expand All @@ -46,19 +68,69 @@ function compactObj<In extends Out, Out extends Record<string, unknown>>(obj: In
}, {} as Out);
}

export function protoType(origProto: ProtoMessage | ProtoEnum, opts: PrinterOptions): Code {
export function protoType(origProto: ProtoMessage | ProtoEnum | ProtoField, opts: PrinterOptions): Code {
const origProtoType = origProto.kind === "Field" ? origProto.type : origProto;
if (origProtoType.kind === "Scalar") {
throw new Error("cannot import protobuf primitive types");
}
let proto = origProtoType;
const chunks = [proto.name];
while (proto.parent.kind !== "File") {
proto = proto.parent;
chunks.unshift(proto.name);
}
switch (opts.protobuf) {
case "google-protobuf": {
return code`${imp(`${chunks[0]}@${protoImportPath(proto, opts)}`)}${chunks
.slice(1)
.map((c) => `.${c}`)
.join("")}`;
}
case "protobufjs": {
chunks.unshift(...proto.file.package.split("."));
const importPath = protoImportPath(origProto.kind === "Field" ? origProto.parent : origProto, opts);
return code`${imp(`${chunks[0]}@${importPath}`)}.${chunks.slice(1).join(".")}`;
}
case "ts-proto": {
return code`${imp(`${chunks.join("_")}@${protoImportPath(proto, opts)}`)}`;
}
/* istanbul ignore next */
default: {
const _exhaustiveCheck: never = opts;
throw "unreachable";
}
}
}

export function createGetFieldValueCode(parent: Code, proto: ProtoField, opts: PrinterOptions): Code {
switch (opts.protobuf) {
case "google-protobuf": {
return code`${parent}.${googleProtobufFieldAccessor("get", proto)}()`;
}
case "protobufjs": {
return code`${parent}.${camelCase(proto.name)}`;
}
case "ts-proto": {
return code`${parent}.${proto.jsonName}`;
}
/* istanbul ignore next */
default: {
const _exhaustiveCheck: never = opts;
throw "unreachable";
}
}
}

export function createSetFieldValueCode(parent: Code, value: Code, proto: ProtoField, opts: PrinterOptions): Code {
switch (opts.protobuf) {
case "google-protobuf":
case "protobufjs":
throw new Error(`not implemented: ${opts.protobuf}`);
case "google-protobuf": {
return code`${parent}.${googleProtobufFieldAccessor("set", proto)}(${value})`;
}
case "protobufjs": {
return code`${parent}.${camelCase(proto.name)} = ${value}`;
}
case "ts-proto": {
let proto = origProto;
let name = proto.name;
while (proto.parent.kind !== "File") {
proto = proto.parent;
name = `${proto.name}_${name}`;
}
return code`${imp(`${name}@${protoImportPath(proto, opts)}`)}`;
return code`${parent}.${proto.jsonName} = ${value}`;
}
/* istanbul ignore next */
default: {
Expand All @@ -67,3 +139,38 @@ export function protoType(origProto: ProtoMessage | ProtoEnum, opts: PrinterOpti
}
}
}

function googleProtobufFieldAccessor(type: "get" | "set", proto: ProtoField) {
return `${type}${upperCaseFirst(proto.jsonName)}${proto.list ? "List" : ""}`;
}

function upperCaseFirst(s: string): string {
return `${s.charAt(0).toUpperCase()}${s.slice(1)}`;
}

const longScalarPrimitiveTypes: ReadonlySet<ProtoScalarType> = new Set([
"int64",
"uint64",
"fixed64",
"sfixed64",
"sint64",
]);
const longScalarWrapperTypes: ReadonlySet<string> = new Set([
"google.protobuf.Int64Value",
"google.protobuf.UInt64Value",
]);

export function isProtobufLong(proto: ProtoField): boolean {
switch (proto.type.kind) {
case "Scalar":
return longScalarPrimitiveTypes.has(proto.type.type);
case "Message":
return longScalarWrapperTypes.has(proto.type.fullName.toString());
default:
return false;
}
}

export function isWellKnownType(proto: ProtoField["type"]): proto is ProtoMessage {
return proto.kind === "Message" && proto.file.name.startsWith("google/protobuf/");
}
6 changes: 4 additions & 2 deletions packages/@proto-graphql/proto-descriptors/src/impls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -330,11 +330,13 @@ export class ProtoFieldImpl implements ProtoField {
}

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

@memo()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ export interface ProtoField extends ProtoBase<"Field"> {
readonly index: number;
readonly number: number;
readonly parent: ProtoMessage;
readonly type: ProtoMessage | ProtoEnum | ProtoScalar | null;
readonly type: ProtoMessage | ProtoEnum | ProtoScalar;
readonly containingOneof: ProtoOneof | null;
readonly list: boolean;
readonly comments: CommentSet;
Expand Down
3 changes: 2 additions & 1 deletion packages/protoc-gen-nexus/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@
"@proto-graphql/proto-descriptors": "^0.2.0",
"@proto-graphql/protoc-plugin-helpers": "^0.2.1",
"change-case": "^4.1.2",
"google-protobuf": "^3.20.1"
"google-protobuf": "^3.20.1",
"ts-poet": "^6.3.0"
},
"devDependencies": {
"@proto-nexus/google-protobuf": "^0.5.1",
Expand Down
Loading