From 7f5a239e170dc7cd20dce2aed94770f4708b4eb9 Mon Sep 17 00:00:00 2001 From: izumin5210 Date: Mon, 28 Dec 2020 07:56:42 +0000 Subject: [PATCH] fix: support referencing other packages --- .../multipkgs/subpkg1/descriptor_set.pb | Bin 0 -> 434 bytes .../testapis/multipkgs/subpkg1/types.proto | 13 ++ .../multipkgs/subpkg2/descriptor_set.pb | Bin 0 -> 880 bytes .../testapis/multipkgs/subpkg2/types.proto | 10 + .../__helpers__/process.test.helper.ts | 67 +++--- .../__snapshots__/multipkgs.test.ts.snap | 95 +++++++++ .../__integration__/multipkgs.test.ts | 7 + .../__snapshots__/process.test.ts.snap | 192 ++++++++++++++++++ .../src/__tests__/process.test.ts | 9 + .../protoc-gen-nexus/src/dslgen/import.ts | 16 +- scripts/compile-testapis-proto | 14 +- 11 files changed, 391 insertions(+), 32 deletions(-) create mode 100644 packages/@testapis/proto/src/testapis/multipkgs/subpkg1/descriptor_set.pb create mode 100644 packages/@testapis/proto/src/testapis/multipkgs/subpkg1/types.proto create mode 100644 packages/@testapis/proto/src/testapis/multipkgs/subpkg2/descriptor_set.pb create mode 100644 packages/@testapis/proto/src/testapis/multipkgs/subpkg2/types.proto create mode 100644 packages/protoc-gen-nexus/src/__tests__/__integration__/__snapshots__/multipkgs.test.ts.snap create mode 100644 packages/protoc-gen-nexus/src/__tests__/__integration__/multipkgs.test.ts diff --git a/packages/@testapis/proto/src/testapis/multipkgs/subpkg1/descriptor_set.pb b/packages/@testapis/proto/src/testapis/multipkgs/subpkg1/descriptor_set.pb new file mode 100644 index 0000000000000000000000000000000000000000..17ecee129931d3fba785c6b438fc85a842e1c1f3 GIT binary patch literal 434 zcmX|;%Sr<=6ozxH$(a_%1L9r8f{2SL;?kAXsZ{BOmaa>SLn*e-V5W;cm2W7Xj5A&3 zeEGlp2?Ty5+~kSNXW7!V)|*wn%sv;caevaAPWt0v{M_wy~sM5OP1XpGKj0`{htW#aNw#0bWPR82U+|6s!pVLNQc|)i}*kF^*E1LkNKO1VgC> zLLIaR?a5R`qPC5SBvE+a>onCFe;w6nUuX2buhT`H6_@^q2a2pXsfgZV&o4@mA`nnU dydYf>iC}Cg5(GO@kQj(c6h1R{UMqg#;SWV7J&ynY literal 0 HcmV?d00001 diff --git a/packages/@testapis/proto/src/testapis/multipkgs/subpkg1/types.proto b/packages/@testapis/proto/src/testapis/multipkgs/subpkg1/types.proto new file mode 100644 index 00000000..f32d9ff5 --- /dev/null +++ b/packages/@testapis/proto/src/testapis/multipkgs/subpkg1/types.proto @@ -0,0 +1,13 @@ +syntax = "proto3"; + +package testapis.multipkgs.subpkg1; + +message SubpkgMessage { + string body = 1; +} + +enum SubpkgEnum { + SUBPKG_ENUM_UNSPECIFIED = 0; + FOO = 1; + BAR = 2; +} diff --git a/packages/@testapis/proto/src/testapis/multipkgs/subpkg2/descriptor_set.pb b/packages/@testapis/proto/src/testapis/multipkgs/subpkg2/descriptor_set.pb new file mode 100644 index 0000000000000000000000000000000000000000..8c13aaf0233059c4840a256d53616e7d3b4e7e28 GIT binary patch literal 880 zcma)4-AWrl7@hfMW_RXGv~fVx^an{GT3a@nVhin6O=C%lQ4)feV(4N+8x!2kh40~O zNFSi@R{UnOqX_-U)qZEs&n}#UKLys(I7!c^qoh8b{YXdC?-xlunGNZ=S5L2}aS~0h zCh5eMy)L@fMY*n0#if2`If|3y{35m%<#6)#dYy0bZPUx{oezj}QR{Lxwoh@bKR7t~ z^7*XQ9UPqvy8V+@vwhfZeJU{Vn zi#AqXu=UCf$CbR)(^2|uUZ8DaIG#71HC$-CjQ+lk=+QKK-f7>XjA;q$KpRLZu_-IWYhIs-`*Xc%%p+< literal 0 HcmV?d00001 diff --git a/packages/@testapis/proto/src/testapis/multipkgs/subpkg2/types.proto b/packages/@testapis/proto/src/testapis/multipkgs/subpkg2/types.proto new file mode 100644 index 00000000..fa3a4ff3 --- /dev/null +++ b/packages/@testapis/proto/src/testapis/multipkgs/subpkg2/types.proto @@ -0,0 +1,10 @@ +syntax = "proto3"; + +package testapis.multipkgs.subpkg1; + +import "testapis/multipkgs/subpkg1/types.proto"; + +message MessageWithSubpkg { + testapis.multipkgs.subpkg1.SubpkgMessage message = 1; + testapis.multipkgs.subpkg1.SubpkgEnum enum = 2; +} diff --git a/packages/protoc-gen-nexus/src/__tests__/__helpers__/process.test.helper.ts b/packages/protoc-gen-nexus/src/__tests__/__helpers__/process.test.helper.ts index 8c73c4ea..63b770fa 100644 --- a/packages/protoc-gen-nexus/src/__tests__/__helpers__/process.test.helper.ts +++ b/packages/protoc-gen-nexus/src/__tests__/__helpers__/process.test.helper.ts @@ -50,7 +50,7 @@ export function itGeneratesNexusDSLsToMatchSnapshtos( } export function testSchemaGeneration( - name: string, + name: string | string[], target: GenerationTarget, opts: { types?: Record; @@ -62,13 +62,21 @@ export function testSchemaGeneration( } = {} ) { describe(`schema generation with ${target}`, () => { - let resp: CodeGeneratorResponse; + let files: CodeGeneratorResponse.File[]; beforeEach(async () => { - resp = await generateDSLs(name, target, { withPrefix: true }); + files = []; + + for (const n of Array.isArray(name) ? name : [name]) { + files.push( + ...( + await generateDSLs(n, target, { withPrefix: true }) + ).getFileList() + ); + } }); it("can make GraphQL schema from generated DSLs", async () => { - await withGeneratedSchema(resp, opts?.types ?? {}, async (dir) => { + await withGeneratedSchema(files, opts?.types ?? {}, async (dir) => { try { await validateGeneratedFiles(dir); const gqlSchema = await fs.readFile( @@ -88,7 +96,7 @@ export function testSchemaGeneration( it.each(opts.schemaTests)("%s", async (_name, types, f) => { try { await withGeneratedSchema( - resp, + files, { ...(opts.types ?? {}), ...types }, async (_dir, schema) => { await f(schema); @@ -175,7 +183,7 @@ function getFileMap(resp: CodeGeneratorResponse): Record { } async function withGeneratedResults( - resp: CodeGeneratorResponse, + files: CodeGeneratorResponse.File[], cb: (dir: string) => Promise ) { const suffix = Math.random().toString(36).substring(7); @@ -185,19 +193,15 @@ async function withGeneratedResults( .then(() => Promise.all( [ - ...new Set( - resp.getFileList().map((f) => join(dir, dirname(f.getName()!))) - ), + ...new Set(files.map((f) => join(dir, dirname(f.getName()!)))), ].map((p) => fs.mkdir(p, { recursive: true })) ).then(() => dir) ) .then((dir) => Promise.all( - resp - .getFileList() - .map((f) => - fs.writeFile(join(dir, f.getName()!), f.getContent()!, "utf-8") - ) + files.map((f) => + fs.writeFile(join(dir, f.getName()!), f.getContent()!, "utf-8") + ) ).then(() => dir) ) .then(cb) @@ -205,22 +209,31 @@ async function withGeneratedResults( } async function withGeneratedSchema( - resp: CodeGeneratorResponse, + files: CodeGeneratorResponse.File[], queries: Record>, cb: (dir: string, schema: NexusGraphQLSchema) => Promise ) { - await withGeneratedResults(resp, async (dir) => { - const paths = resp.getFileList().map((f) => join(dir, f.getName()!)); - const types = paths.reduce((o, p) => ({ ...o, ...require(p) }), {} as any); - const schema = makeSchema({ - types: { ...types, ...queries }, - outputs: { - schema: join(dir, "schema.graphql"), - typegen: join(dir, "typings.ts"), - }, - shouldGenerateArtifacts: true, - }); - await cb(dir, schema); + await withGeneratedResults(files, async (dir) => { + const paths = files.map((f) => join(dir, f.getName()!)); + try { + const types = paths.reduce( + (o, p) => ({ ...o, ...require(p) }), + {} as any + ); + const schema = makeSchema({ + types: { ...types, ...queries }, + outputs: { + schema: join(dir, "schema.graphql"), + typegen: join(dir, "typings.ts"), + }, + shouldGenerateArtifacts: true, + }); + await cb(dir, schema); + } catch (e) { + // eslint-disable-next-line no-console + console.error(e); + throw e; + } }); } diff --git a/packages/protoc-gen-nexus/src/__tests__/__integration__/__snapshots__/multipkgs.test.ts.snap b/packages/protoc-gen-nexus/src/__tests__/__integration__/__snapshots__/multipkgs.test.ts.snap new file mode 100644 index 00000000..fbb0d163 --- /dev/null +++ b/packages/protoc-gen-nexus/src/__tests__/__integration__/__snapshots__/multipkgs.test.ts.snap @@ -0,0 +1,95 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`schema generation with native protobuf can make GraphQL schema from generated DSLs: schema.graphql 1`] = ` +"### This file was generated by Nexus Schema +### Do not make changes to this file directly + + +\\"\\"\\"\\"\\"\\" +type MessageWithSubpkg { + \\"\\"\\"\\"\\"\\" + enum: SubpkgEnum + + \\"\\"\\"\\"\\"\\" + message: SubpkgMessage +} + +\\"\\"\\"\\"\\"\\" +input MessageWithSubpkgInput { + \\"\\"\\"\\"\\"\\" + enum: SubpkgEnum + + \\"\\"\\"\\"\\"\\" + message: SubpkgMessageInput +} + +type Query { + ok: Boolean! +} + +\\"\\"\\"\\"\\"\\" +enum SubpkgEnum { + BAR + FOO +} + +\\"\\"\\"\\"\\"\\" +type SubpkgMessage { + \\"\\"\\"\\"\\"\\" + body: String! +} + +\\"\\"\\"\\"\\"\\" +input SubpkgMessageInput { + \\"\\"\\"\\"\\"\\" + body: String! +} +" +`; + +exports[`schema generation with protobufjs can make GraphQL schema from generated DSLs: schema.graphql 1`] = ` +"### This file was generated by Nexus Schema +### Do not make changes to this file directly + + +\\"\\"\\"\\"\\"\\" +type MessageWithSubpkg { + \\"\\"\\"\\"\\"\\" + enum: SubpkgEnum + + \\"\\"\\"\\"\\"\\" + message: SubpkgMessage +} + +\\"\\"\\"\\"\\"\\" +input MessageWithSubpkgInput { + \\"\\"\\"\\"\\"\\" + enum: SubpkgEnum + + \\"\\"\\"\\"\\"\\" + message: SubpkgMessageInput +} + +type Query { + ok: Boolean! +} + +\\"\\"\\"\\"\\"\\" +enum SubpkgEnum { + BAR + FOO +} + +\\"\\"\\"\\"\\"\\" +type SubpkgMessage { + \\"\\"\\"\\"\\"\\" + body: String! +} + +\\"\\"\\"\\"\\"\\" +input SubpkgMessageInput { + \\"\\"\\"\\"\\"\\" + body: String! +} +" +`; diff --git a/packages/protoc-gen-nexus/src/__tests__/__integration__/multipkgs.test.ts b/packages/protoc-gen-nexus/src/__tests__/__integration__/multipkgs.test.ts new file mode 100644 index 00000000..bdc8860e --- /dev/null +++ b/packages/protoc-gen-nexus/src/__tests__/__integration__/multipkgs.test.ts @@ -0,0 +1,7 @@ +import { testSchemaGeneration } from "../__helpers__/process.test.helper"; + +testSchemaGeneration(["multipkgs/subpkg1", "multipkgs/subpkg2"], "protobufjs"); +testSchemaGeneration( + ["multipkgs/subpkg1", "multipkgs/subpkg2"], + "native protobuf" +); diff --git a/packages/protoc-gen-nexus/src/__tests__/__snapshots__/process.test.ts.snap b/packages/protoc-gen-nexus/src/__tests__/__snapshots__/process.test.ts.snap index 81b7f5a0..2e1ab9a0 100644 --- a/packages/protoc-gen-nexus/src/__tests__/__snapshots__/process.test.ts.snap +++ b/packages/protoc-gen-nexus/src/__tests__/__snapshots__/process.test.ts.snap @@ -932,6 +932,198 @@ export const FieldBehaviorComentsMessagePostInput = inputObjectType({ " `; +exports[`multipkgs with native protobuf generates nexus DSLs: multipkgs/subpkg1/types_pb_nexus.ts 1`] = ` +"// Code generated by protoc-gen-nexus. DO NOT EDIT. +// source: testapis/multipkgs/subpkg1/types.proto + +import { objectType, inputObjectType, enumType, nonNull } from \\"nexus\\"; +import * as _$testapis$multipkgs$subpkg1$types_pb from \\"./testapis/multipkgs/subpkg1/types_pb\\"; +export type _$testapis$multipkgs$subpkg1$types_pb$SubpkgMessage = _$testapis$multipkgs$subpkg1$types_pb.SubpkgMessage; +export const SubpkgMessage = objectType({ + name: \\"SubpkgMessage\\", + description: \\"\\", + definition(t) { + t.field(\\"body\\", { + type: nonNull(\\"String\\"), + description: \\"\\", + resolve(root) { return root.getBody(); } + }); + }, + sourceType: { module: __filename, export: \\"_$testapis$multipkgs$subpkg1$types_pb$SubpkgMessage\\" } +}); +export const SubpkgMessageInput = inputObjectType({ + name: \\"SubpkgMessageInput\\", + description: \\"\\", + definition(t) { + t.field(\\"body\\", { + type: nonNull(\\"String\\"), + description: \\"\\" + }); + } +}); +export const SubpkgEnum = enumType({ + name: \\"SubpkgEnum\\", + description: \\"\\", + members: [ + { + name: \\"FOO\\", + value: 1 + }, + { + name: \\"BAR\\", + value: 2 + } + ] +}); +" +`; + +exports[`multipkgs with native protobuf generates nexus DSLs: multipkgs/subpkg2/types_pb_nexus.ts 1`] = ` +"// Code generated by protoc-gen-nexus. DO NOT EDIT. +// source: testapis/multipkgs/subpkg2/types.proto + +import { objectType, inputObjectType, nullable } from \\"nexus\\"; +import * as _$testapis$multipkgs$subpkg2$types_pb from \\"./testapis/multipkgs/subpkg2/types_pb\\"; +import * as _$testapis$multipkgs$subpkg1$types_pb from \\"./testapis/multipkgs/subpkg1/types_pb\\"; +export type _$testapis$multipkgs$subpkg2$types_pb$MessageWithSubpkg = _$testapis$multipkgs$subpkg2$types_pb.MessageWithSubpkg; +export const MessageWithSubpkg = objectType({ + name: \\"MessageWithSubpkg\\", + description: \\"\\", + definition(t) { + t.field(\\"message\\", { + type: nullable(\\"SubpkgMessage\\"), + description: \\"\\", + resolve(root) { return root.getMessage() ?? null; } + }); + t.field(\\"enum\\", { + type: nullable(\\"SubpkgEnum\\"), + description: \\"\\", + resolve(root) { + if (root.getEnum == null) { + return null; + } + if (root.getEnum === _$testapis$multipkgs$subpkg1$types_pb.SubpkgEnum.SUBPKG_ENUM_UNSPECIFIED) { + return null; + } + return root.getEnum; + } + }); + }, + sourceType: { module: __filename, export: \\"_$testapis$multipkgs$subpkg2$types_pb$MessageWithSubpkg\\" } +}); +export const MessageWithSubpkgInput = inputObjectType({ + name: \\"MessageWithSubpkgInput\\", + description: \\"\\", + definition(t) { + t.field(\\"message\\", { + type: nullable(\\"SubpkgMessageInput\\"), + description: \\"\\" + }); + t.field(\\"enum\\", { + type: nullable(\\"SubpkgEnum\\"), + description: \\"\\" + }); + } +}); +" +`; + +exports[`multipkgs with protobufjs generates nexus DSLs: multipkgs/subpkg1/types_pb_nexus.ts 1`] = ` +"// Code generated by protoc-gen-nexus. DO NOT EDIT. +// source: testapis/multipkgs/subpkg1/types.proto + +import { objectType, inputObjectType, enumType, nonNull } from \\"nexus\\"; +import * as _$testapis$multipkgs$subpkg1 from \\"./testapis/multipkgs/subpkg1\\"; +export type _$testapis$multipkgs$subpkg1$testapis$multipkgs$subpkg1$SubpkgMessage = _$testapis$multipkgs$subpkg1.testapis.multipkgs.subpkg1.ISubpkgMessage; +export const SubpkgMessage = objectType({ + name: \\"SubpkgMessage\\", + description: \\"\\", + definition(t) { + t.field(\\"body\\", { + type: nonNull(\\"String\\"), + description: \\"\\", + resolve(root) { return root.body!; } + }); + }, + sourceType: { module: __filename, export: \\"_$testapis$multipkgs$subpkg1$testapis$multipkgs$subpkg1$SubpkgMessage\\" } +}); +export const SubpkgMessageInput = inputObjectType({ + name: \\"SubpkgMessageInput\\", + description: \\"\\", + definition(t) { + t.field(\\"body\\", { + type: nonNull(\\"String\\"), + description: \\"\\" + }); + } +}); +export const SubpkgEnum = enumType({ + name: \\"SubpkgEnum\\", + description: \\"\\", + members: [ + { + name: \\"FOO\\", + value: 1 + }, + { + name: \\"BAR\\", + value: 2 + } + ] +}); +" +`; + +exports[`multipkgs with protobufjs generates nexus DSLs: multipkgs/subpkg2/types_pb_nexus.ts 1`] = ` +"// Code generated by protoc-gen-nexus. DO NOT EDIT. +// source: testapis/multipkgs/subpkg2/types.proto + +import { objectType, inputObjectType, nullable } from \\"nexus\\"; +import * as _$testapis$multipkgs$subpkg2 from \\"./testapis/multipkgs/subpkg2\\"; +import * as _$testapis$multipkgs$subpkg1 from \\"./testapis/multipkgs/subpkg1\\"; +export type _$testapis$multipkgs$subpkg2$testapis$multipkgs$subpkg1$MessageWithSubpkg = _$testapis$multipkgs$subpkg2.testapis.multipkgs.subpkg1.IMessageWithSubpkg; +export const MessageWithSubpkg = objectType({ + name: \\"MessageWithSubpkg\\", + description: \\"\\", + definition(t) { + t.field(\\"message\\", { + type: nullable(\\"SubpkgMessage\\"), + description: \\"\\", + resolve(root) { return root.message ?? null; } + }); + t.field(\\"enum\\", { + type: nullable(\\"SubpkgEnum\\"), + description: \\"\\", + resolve(root) { + if (root.enum == null) { + return null; + } + if (root.enum === _$testapis$multipkgs$subpkg1.testapis.multipkgs.subpkg1.SubpkgEnum.SUBPKG_ENUM_UNSPECIFIED) { + return null; + } + return root.enum; + } + }); + }, + sourceType: { module: __filename, export: \\"_$testapis$multipkgs$subpkg2$testapis$multipkgs$subpkg1$MessageWithSubpkg\\" } +}); +export const MessageWithSubpkgInput = inputObjectType({ + name: \\"MessageWithSubpkgInput\\", + description: \\"\\", + definition(t) { + t.field(\\"message\\", { + type: nullable(\\"SubpkgMessageInput\\"), + description: \\"\\" + }); + t.field(\\"enum\\", { + type: nullable(\\"SubpkgEnum\\"), + description: \\"\\" + }); + } +}); +" +`; + exports[`nested protobuf types with native protobuf generates nexus DSLs: nested/nested_pb_nexus.ts 1`] = ` "// Code generated by protoc-gen-nexus. DO NOT EDIT. // source: testapis/nested/nested.proto diff --git a/packages/protoc-gen-nexus/src/__tests__/process.test.ts b/packages/protoc-gen-nexus/src/__tests__/process.test.ts index 82c38810..9b1486c8 100644 --- a/packages/protoc-gen-nexus/src/__tests__/process.test.ts +++ b/packages/protoc-gen-nexus/src/__tests__/process.test.ts @@ -67,6 +67,15 @@ describe("protobuf oneof", () => { itGeneratesNexusDSLsToMatchSnapshtos("oneof", ["oneof/oneof_pb_nexus.ts"]); }); +describe("multipkgs", () => { + itGeneratesNexusDSLsToMatchSnapshtos("multipkgs/subpkg1", [ + "multipkgs/subpkg1/types_pb_nexus.ts", + ]); + itGeneratesNexusDSLsToMatchSnapshtos("multipkgs/subpkg2", [ + "multipkgs/subpkg2/types_pb_nexus.ts", + ]); +}); + describe("deprecation", () => { itGeneratesNexusDSLsToMatchSnapshtos("deprecation", [ "deprecation/deprecation_pb_nexus.ts", diff --git a/packages/protoc-gen-nexus/src/dslgen/import.ts b/packages/protoc-gen-nexus/src/dslgen/import.ts index 74712a03..da916ced 100644 --- a/packages/protoc-gen-nexus/src/dslgen/import.ts +++ b/packages/protoc-gen-nexus/src/dslgen/import.ts @@ -4,6 +4,7 @@ import { GenerationParams } from "./types"; import { getUnwrapFunc } from "./unwrap"; import { createImportAllWithAliastDecl, + getEnumValueForUnspecified, isRequiredField, onlyNonNull, onlyUnique, @@ -92,8 +93,19 @@ export function createImportProtoDecls( msgs: ReadonlyArray, opts: GenerationParams ): ts.ImportDeclaration[] { - return msgs - .map((m) => protoImportPath(m, opts)) + return [ + ...msgs.map((m) => protoImportPath(m, opts)), + ...msgs + .flatMap((m) => + m.fields + .map((f) => f.type) + .filter( + (t): t is ProtoEnum => + t instanceof ProtoEnum && getEnumValueForUnspecified(t) != null + ) + ) + .map((e) => protoImportPath(e, opts)), + ] .filter(onlyUnique()) .map(createImportAllWithAliastDecl); } diff --git a/scripts/compile-testapis-proto b/scripts/compile-testapis-proto index 5b736ab9..53af4792 100755 --- a/scripts/compile-testapis-proto +++ b/scripts/compile-testapis-proto @@ -25,6 +25,10 @@ protocw() { "$@" } +listPackages() { + find $TESTAPIS_ROOT/proto/src/testapis -name '*.proto' | xargs dirname | sort | uniq +} + execProtoc() { protocw \ --plugin=protoc-gen-ts=`yarn bin protoc-gen-ts` \ @@ -32,7 +36,7 @@ execProtoc() { --ts_out="$TESTAPIS_ROOT/node-native/lib" \ ${PLUGIN_ROOT}/include/graphql/*.proto - for protoDir in $TESTAPIS_ROOT/proto/src/testapis/*; do + for protoDir in $(listPackages); do protocw --include_source_info --include_imports --descriptor_set_out=${protoDir}/descriptor_set.pb ${protoDir}/*.proto protocw \ @@ -43,9 +47,13 @@ execProtoc() { nodeOutDir="${protoDir/"/proto/src/"/"/node/lib/"}" mkdir -p $nodeOutDir - yarn pbjs --target static-module --wrap commonjs --out "$nodeOutDir/index.js" ${protoDir}/*.proto + yarn pbjs \ + --target static-module \ + --wrap commonjs \ + --path $TESTAPIS_ROOT/proto/src \ + --out "$nodeOutDir/index.js" \ + ${protoDir}/*.proto yarn pbts --out "$nodeOutDir/index.d.ts" "$nodeOutDir/index.js" - done }