Skip to content

Commit a9fce5e

Browse files
committed
feat: add marker interface support for union types (#39)
BREAKING CHANGE: generate marker interfaces for union types by default
1 parent 52f6b49 commit a9fce5e

File tree

44 files changed

+404
-329
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+404
-329
lines changed

.releaserc.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ plugins:
77
- type: breaking
88
release: major
99
- type: docs
10-
release: patch
10+
release: false
1111
- type: refactor
1212
release: patch
1313
- scope: no-release

bun.lockb

1.29 KB
Binary file not shown.

docs/docs/configuration.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@ sidebar_position: 3
55
# Configuration
66

77
```ts reference title="Config Schema"
8-
https://github.com/ExpediaGroup/graphql-kotlin-codegen/blob/main/src/config.ts#L17-L100000
8+
https://github.com/ExpediaGroup/graphql-kotlin-codegen/blob/main/src/config.ts#L1-L100000
99
```

eslint.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export default [
1313
},
1414
},
1515
rules: {
16+
"no-console": "error",
1617
"@typescript-eslint/no-non-null-assertion": "error",
1718
"@typescript-eslint/no-unsafe-argument": "error",
1819
"@typescript-eslint/no-unsafe-call": "error",

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
"@graphql-codegen/cli": "5.0.2",
2929
"@total-typescript/ts-reset": "0.5.1",
3030
"bun-types": "1.1.4",
31-
"eslint": "9.1.0",
31+
"eslint": "9.1.1",
3232
"husky": "9.0.11",
3333
"prettier": "3.2.5",
3434
"tsup": "8.0.2",
@@ -39,7 +39,7 @@
3939
"build": "tsup src/plugin.ts --clean --dts --external graphql",
4040
"format": "prettier --write .",
4141
"format-check": "prettier --check .",
42-
"integration": "bun run build && graphql-codegen && ./gradlew compileTestKotlin",
42+
"integration": "bun run build && graphql-codegen && ./gradlew build",
4343
"lint": "eslint .",
4444
"prepack": "bun run build",
4545
"prepare": "husky",

src/config.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
array,
1616
boolean,
1717
enum_,
18+
literal,
1819
object,
1920
optional,
2021
string,
@@ -44,11 +45,11 @@ export const configSchema = object({
4445
dependentTypesInScope: optional(array(string())),
4546
/**
4647
* Denotes Kotlin classes representing union types to be treated as interfaces rather than annotation classes.
47-
* This should be used for types outside `dependentTypesInScope` that are not generated by the plugin.
48+
* @description This should be used for types outside `dependentTypesInScope` that are not generated by the plugin. Only use when unionGeneration is set to `ANNOTATION_CLASS`.
4849
*/
4950
externalUnionsAsInterfaces: optional(array(string())),
5051
/**
51-
* Additional imports to add to the generated file.
52+
* Additional imports to add to the generated file. GraphQL Kotlin annotations are always imported.
5253
* @example ["com.example.additional.import.*"]
5354
*/
5455
extraImports: optional(array(string())),
@@ -129,4 +130,12 @@ export const configSchema = object({
129130
}),
130131
),
131132
),
133+
/**
134+
* Denotes the generation strategy for union types. Defaults to `MARKER_INTERFACE`.
135+
* @description The `MARKER_INTERFACE` option is highly recommended, since it is more type-safe than using annotation classes.
136+
* @link https://opensource.expediagroup.com/graphql-kotlin/docs/schema-generator/writing-schemas/unions/
137+
*/
138+
unionGeneration: optional(
139+
union([literal("ANNOTATION_CLASS"), literal("MARKER_INTERFACE")]),
140+
),
132141
});

src/definitions/enum.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@ import { EnumTypeDefinitionNode, EnumValueDefinitionNode } from "graphql";
1515
import { indentMultiline } from "@graphql-codegen/visitor-plugin-common";
1616
import { buildAnnotations } from "../helpers/build-annotations";
1717
import { shouldIncludeTypeDefinition } from "../helpers/should-include-type-definition";
18-
import { CodegenConfig } from "../plugin";
18+
import { CodegenConfigWithDefaults } from "../helpers/build-config-with-defaults";
1919

2020
export function buildEnumTypeDefinition(
2121
node: EnumTypeDefinitionNode,
22-
config: CodegenConfig,
22+
config: CodegenConfigWithDefaults,
2323
) {
2424
if (!shouldIncludeTypeDefinition(node, config)) {
2525
return "";
@@ -46,11 +46,11 @@ ${indentMultiline(enumValues.join(",\n") + ";", 2)}
4646

4747
function buildEnumValueDefinition(
4848
node: EnumValueDefinitionNode,
49-
config: CodegenConfig,
49+
config: CodegenConfigWithDefaults,
5050
) {
5151
const annotations = buildAnnotations({
5252
config,
5353
definitionNode: node,
5454
});
55-
return `${annotations}${config.convert(node)}`;
55+
return `${annotations}${config.convert?.(node)}`;
5656
}

src/definitions/input.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,12 @@ import { shouldIncludeTypeDefinition } from "../helpers/should-include-type-defi
1616
import { buildTypeMetadata } from "../helpers/build-type-metadata";
1717
import { buildAnnotations } from "../helpers/build-annotations";
1818
import { indent } from "@graphql-codegen/visitor-plugin-common";
19-
import { CodegenConfig } from "../plugin";
19+
import { CodegenConfigWithDefaults } from "../helpers/build-config-with-defaults";
2020

2121
export function buildInputObjectDefinition(
2222
node: InputObjectTypeDefinitionNode,
2323
schema: GraphQLSchema,
24-
config: CodegenConfig,
24+
config: CodegenConfigWithDefaults,
2525
) {
2626
if (!shouldIncludeTypeDefinition(node, config)) {
2727
return "";
@@ -47,7 +47,6 @@ export function buildInputObjectDefinition(
4747

4848
const annotations = buildAnnotations({
4949
config,
50-
inputDescription: node.description?.value,
5150
definitionNode: node,
5251
});
5352

src/definitions/interface.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,12 @@ 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";
1919
import { buildFieldDefinition } from "../helpers/build-field-definition";
20-
import { CodegenConfig } from "../plugin";
20+
import { CodegenConfigWithDefaults } from "../helpers/build-config-with-defaults";
2121

2222
export function buildInterfaceDefinition(
2323
node: InterfaceTypeDefinitionNode,
2424
schema: GraphQLSchema,
25-
config: CodegenConfig,
25+
config: CodegenConfigWithDefaults,
2626
) {
2727
if (!shouldIncludeTypeDefinition(node, config)) {
2828
return "";

src/definitions/object.ts

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,19 @@ 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";
23-
import { CodegenConfig } from "../plugin";
26+
import { CodegenConfigWithDefaults } from "../helpers/build-config-with-defaults";
2427

2528
export function buildObjectTypeDefinition(
2629
node: ObjectTypeDefinitionNode,
2730
schema: GraphQLSchema,
28-
config: CodegenConfig,
31+
config: CodegenConfigWithDefaults,
2932
) {
3033
if (!shouldIncludeTypeDefinition(node, config)) {
3134
return "";
@@ -36,33 +39,38 @@ 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 =
45+
config.unionGeneration === "MARKER_INTERFACE"
46+
? dependentInterfaces.concat(dependentUnions)
47+
: dependentInterfaces;
4048
const interfaceInheritance = `${interfacesToInherit.length ? ` : ${interfacesToInherit.join(", ")}` : ""}`;
4149

4250
if (isResolverType(node, config)) {
4351
return `${annotations}@GraphQLIgnore\ninterface ${name}${interfaceInheritance} {
44-
${getClassMembers({ node, schema, config })}
52+
${getDataClassMembers({ node, schema, config })}
4553
}
4654
4755
${annotations}@GraphQLIgnore\ninterface ${name}CompletableFuture {
48-
${getClassMembers({ node, schema, config, completableFuture: true })}
56+
${getDataClassMembers({ node, schema, config, completableFuture: true })}
4957
}`;
5058
}
5159

5260
return `${annotations}data class ${name}(
53-
${getClassMembers({ node, schema, config })}
61+
${getDataClassMembers({ node, schema, config })}
5462
)${interfaceInheritance}`;
5563
}
5664

57-
function getClassMembers({
65+
function getDataClassMembers({
5866
node,
5967
schema,
6068
config,
6169
completableFuture,
6270
}: {
6371
node: ObjectTypeDefinitionNode;
6472
schema: GraphQLSchema;
65-
config: CodegenConfig;
73+
config: CodegenConfigWithDefaults;
6674
completableFuture?: boolean;
6775
}) {
6876
const resolverType = isResolverType(node, config);

0 commit comments

Comments
 (0)