Skip to content

Commit 39660f1

Browse files
committed
feat: support marker interfaces for union types
1 parent 66b4a88 commit 39660f1

File tree

12 files changed

+105
-14
lines changed

12 files changed

+105
-14
lines changed

.eslintrc.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
},
77
"plugins": ["@typescript-eslint"],
88
"rules": {
9+
"no-console": "error",
910
"@typescript-eslint/no-non-null-assertion": "error",
1011
"@typescript-eslint/no-unsafe-argument": "error",
1112
"@typescript-eslint/no-unsafe-call": "error"

src/config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,4 +129,5 @@ export const configSchema = object({
129129
}),
130130
),
131131
),
132+
useMarkerInterfaces: optional(boolean()),
132133
});

src/definitions/object.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,10 @@ import { buildAnnotations } from "../helpers/build-annotations";
1616
import { indent } from "@graphql-codegen/visitor-plugin-common";
1717
import { buildTypeMetadata } from "../helpers/build-type-metadata";
1818
import { shouldIncludeTypeDefinition } from "../helpers/should-include-type-definition";
19-
import { getDependentInterfaceNames } from "../helpers/dependent-type-utils";
19+
import {
20+
getDependentInterfaceNames,
21+
getDependentUnionsForType,
22+
} from "../helpers/dependent-type-utils";
2023
import { isResolverType } from "../helpers/is-resolver-type";
2124
import { buildFieldDefinition } from "../helpers/build-field-definition";
2225
import { isExternalField } from "../helpers/is-external-field";
@@ -36,7 +39,11 @@ export function buildObjectTypeDefinition(
3639
definitionNode: node,
3740
});
3841
const name = node.name.value;
39-
const interfacesToInherit = getDependentInterfaceNames(node);
42+
const dependentInterfaces = getDependentInterfaceNames(node);
43+
const dependentUnions = getDependentUnionsForType(schema, node);
44+
const interfacesToInherit = config.useMarkerInterfaces
45+
? dependentInterfaces.concat(dependentUnions)
46+
: dependentInterfaces;
4047
const interfaceInheritance = `${interfacesToInherit.length ? ` : ${interfacesToInherit.join(", ")}` : ""}`;
4148

4249
if (isResolverType(node, config)) {

src/definitions/union.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@ import { UnionTypeDefinitionNode } from "graphql";
1515
import { shouldIncludeTypeDefinition } from "../helpers/should-include-type-definition";
1616
import { buildDirectiveAnnotations } from "../helpers/build-directive-annotations";
1717
import { CodegenConfig } from "../plugin";
18-
import { trimDescription } from "../helpers/build-annotations";
18+
import {
19+
buildAnnotations,
20+
trimDescription,
21+
} from "../helpers/build-annotations";
1922

2023
export function buildUnionTypeDefinition(
2124
node: UnionTypeDefinitionNode,
@@ -24,14 +27,21 @@ export function buildUnionTypeDefinition(
2427
if (!shouldIncludeTypeDefinition(node, config)) {
2528
return "";
2629
}
30+
const annotations = buildAnnotations({
31+
config,
32+
definitionNode: node,
33+
});
34+
if (config.useMarkerInterfaces) {
35+
return `${annotations}interface ${node.name.value}`;
36+
}
2737

2838
const directiveAnnotations = buildDirectiveAnnotations(node, config);
2939
const possibleTypes =
3040
node.types?.map((type) => `${type.name.value}::class`).join(", ") || "";
3141
return `${directiveAnnotations}@GraphQLUnion(
3242
name = "${node.name.value}",
3343
possibleTypes = [${possibleTypes}],
34-
description = "${node.description?.value ? trimDescription(node.description.value) : ""}"
44+
description = "${trimDescription(node.description?.value)}"
3545
)
3646
annotation class ${node.name.value}`;
3747
}

src/helpers/add-dependent-types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export function addDependentTypes(
2525
.map((typeName) => schema.getType(typeName)?.astNode)
2626
.filter(Boolean);
2727
const dependentTypeNames = onlyTypesNodes.flatMap((node) =>
28-
getDependentTypeNames(schema, node, config.dependentTypesInScope),
28+
getDependentTypeNames(schema, node, config),
2929
);
3030
const dependentTypesInScope = dependentTypeNames.filter((typeName) =>
3131
dependentTypeIsInScope(typeName, config),

src/helpers/build-annotations.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,10 +95,10 @@ export function isDeprecatedDescription(
9595
);
9696
}
9797

98-
export function trimDescription(description: string) {
98+
export function trimDescription(description?: string) {
9999
return (
100100
description
101-
.split("\n")
101+
?.split("\n")
102102
.map((str) => str.trim().replaceAll('"', "").replaceAll("\\", ""))
103103
.find((str) => str.match(/^[a-zA-Z]/)) ?? ""
104104
);

src/helpers/build-type-metadata.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ export function buildTypeMetadata(
6060
};
6161
} else if (isUnionType(schemaType)) {
6262
const shouldTreatUnionAsInterface =
63+
config.useMarkerInterfaces ||
6364
config.externalUnionsAsInterfaces?.includes(schemaType.name);
6465
return {
6566
...commonMetadata,

src/helpers/dependent-type-utils.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,13 @@ See the License for the specific language governing permissions and
1111
limitations under the License.
1212
*/
1313

14-
import { Kind, TypeDefinitionNode, TypeNode } from "graphql";
14+
import {
15+
GraphQLSchema,
16+
GraphQLUnionType,
17+
Kind,
18+
TypeDefinitionNode,
19+
TypeNode,
20+
} from "graphql";
1521
import { CodegenConfig } from "../plugin";
1622

1723
export function getDependentFieldTypeNames(
@@ -55,3 +61,20 @@ export function getDependentUnionNames(node: TypeDefinitionNode) {
5561
? node.types?.map((type) => type.name.value) ?? []
5662
: [];
5763
}
64+
65+
export function getDependentUnionsForType(
66+
schema: GraphQLSchema,
67+
node: TypeDefinitionNode,
68+
) {
69+
const typeMap = schema.getTypeMap();
70+
const unions = Object.keys(typeMap)
71+
.filter(
72+
(type) => typeMap[type]?.astNode?.kind === Kind.UNION_TYPE_DEFINITION,
73+
)
74+
.map((type) => typeMap[type] as GraphQLUnionType);
75+
return unions
76+
.filter((union) =>
77+
union.getTypes().some((type) => type.name === node.name.value),
78+
)
79+
.map((union) => union.name);
80+
}

src/helpers/get-dependent-type-names.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,21 +18,22 @@ import {
1818
getDependentInterfaceNames,
1919
getDependentUnionNames,
2020
} from "./dependent-type-utils";
21-
import { CodegenConfig } from "../plugin";
21+
import { GraphQLKotlinCodegenConfig } from "../plugin";
2222

2323
export function getDependentTypeNames(
2424
schema: GraphQLSchema,
2525
node: TypeDefinitionNode,
26-
dependentTypesInScope: CodegenConfig["dependentTypesInScope"],
26+
config: GraphQLKotlinCodegenConfig,
2727
): string[] {
28-
const namedTypes = getDependentFieldTypeNames(node, dependentTypesInScope)
28+
const namedTypes = getDependentFieldTypeNames(
29+
node,
30+
config.dependentTypesInScope,
31+
)
2932
.concat(getDependentUnionNames(node))
3033
.concat(getDependentInterfaceNames(node));
3134
const recursivelyFoundTypes = namedTypes
3235
.map((typeName) => schema.getType(typeName)?.astNode)
3336
.filter(Boolean)
34-
.flatMap((node) =>
35-
getDependentTypeNames(schema, node, dependentTypesInScope),
36-
);
37+
.flatMap((node) => getDependentTypeNames(schema, node, config));
3738
return namedTypes.concat(recursivelyFoundTypes);
3839
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { GraphQLKotlinCodegenConfig } from "../../../src/plugin";
2+
3+
export default {
4+
useMarkerInterfaces: true,
5+
} satisfies GraphQLKotlinCodegenConfig;

0 commit comments

Comments
 (0)