Skip to content

Commit 4cf1733

Browse files
committed
[typescript-operations] Add missing enum tests & fix enum generating logic for standalone approach (#10525)
* Add tests for enum * Baselin tests * Ensure correctness of enumValues import * Split enum tests * Abstract import printing functions * Add missing enum tests * Format to minimise conflict * Add changeset
1 parent fb38936 commit 4cf1733

7 files changed

Lines changed: 1487 additions & 74 deletions

File tree

.changeset/tidy-jobs-unite.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
'@graphql-codegen/visitor-plugin-common': patch
3+
'@graphql-codegen/typescript-operations': patch
4+
'@graphql-codegen/typescript': patch
5+
'@graphql-codegen/typescript-resolvers': patch
6+
'@graphql-codegen/client-preset': patch
7+
---
8+
9+
Abstract how enum imports are generated into visitor-plugin-common package

packages/plugins/other/visitor-plugin-common/src/base-types-visitor.ts

Lines changed: 23 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { BaseVisitor, ParsedConfig, RawConfig } from './base-visitor.js';
2020
import { buildEnumValuesBlock } from './convert-schema-enum-to-declaration-block-string.js';
2121
import { normalizeDeclarationKind } from './declaration-kinds.js';
2222
import { parseEnumValues } from './enum-values.js';
23+
import { buildTypeImport, getEnumsImports } from './imports.js';
2324
import { transformDirectiveArgumentAndInputFieldMappings } from './mappers.js';
2425
import { DEFAULT_SCALARS } from './scalars.js';
2526
import {
@@ -562,21 +563,23 @@ export class BaseTypesVisitor<
562563

563564
if (mappedValue.input.isExternal) {
564565
res.push(
565-
this._buildTypeImport(
566-
mappedValue.input.import,
567-
mappedValue.input.source,
568-
mappedValue.input.default,
569-
),
566+
buildTypeImport({
567+
identifier: mappedValue.input.import,
568+
source: mappedValue.input.source,
569+
asDefault: mappedValue.input.default,
570+
useTypeImports: this.config.useTypeImports,
571+
}),
570572
);
571573
}
572574

573575
if (mappedValue.output.isExternal) {
574576
res.push(
575-
this._buildTypeImport(
576-
mappedValue.output.import,
577-
mappedValue.output.source,
578-
mappedValue.output.default,
579-
),
577+
buildTypeImport({
578+
identifier: mappedValue.output.import,
579+
source: mappedValue.output.source,
580+
asDefault: mappedValue.output.default,
581+
useTypeImports: this.config.useTypeImports,
582+
}),
580583
);
581584
}
582585

@@ -590,7 +593,12 @@ export class BaseTypesVisitor<
590593
const mappedValue = this.config.directiveArgumentAndInputFieldMappings[directive];
591594

592595
if (mappedValue.isExternal) {
593-
return this._buildTypeImport(mappedValue.import, mappedValue.source, mappedValue.default);
596+
return buildTypeImport({
597+
identifier: mappedValue.import,
598+
source: mappedValue.source,
599+
asDefault: mappedValue.default,
600+
useTypeImports: this.config.useTypeImports,
601+
});
594602
}
595603

596604
return null;
@@ -856,60 +864,11 @@ export class BaseTypesVisitor<
856864
return '';
857865
}
858866

859-
protected _buildTypeImport(identifier: string, source: string, asDefault = false): string {
860-
const { useTypeImports } = this.config;
861-
if (asDefault) {
862-
if (useTypeImports) {
863-
return `import type { default as ${identifier} } from '${source}';`;
864-
}
865-
return `import ${identifier} from '${source}';`;
866-
}
867-
return `import${useTypeImports ? ' type' : ''} { ${identifier} } from '${source}';`;
868-
}
869-
870-
protected handleEnumValueMapper(
871-
typeIdentifier: string,
872-
importIdentifier: string | null,
873-
sourceIdentifier: string | null,
874-
sourceFile: string | null,
875-
): string[] {
876-
if (importIdentifier !== sourceIdentifier) {
877-
// use namespace import to dereference nested enum
878-
// { enumValues: { MyEnum: './my-file#NS.NestedEnum' } }
879-
return [
880-
this._buildTypeImport(importIdentifier || sourceIdentifier, sourceFile),
881-
`import ${typeIdentifier} = ${sourceIdentifier};`,
882-
];
883-
}
884-
if (sourceIdentifier !== typeIdentifier) {
885-
return [this._buildTypeImport(`${sourceIdentifier} as ${typeIdentifier}`, sourceFile)];
886-
}
887-
return [this._buildTypeImport(importIdentifier || sourceIdentifier, sourceFile)];
888-
}
889-
890867
public getEnumsImports(): string[] {
891-
return Object.keys(this.config.enumValues)
892-
.flatMap(enumName => {
893-
const mappedValue = this.config.enumValues[enumName];
894-
895-
if (mappedValue.sourceFile) {
896-
if (mappedValue.isDefault) {
897-
return [
898-
this._buildTypeImport(mappedValue.typeIdentifier, mappedValue.sourceFile, true),
899-
];
900-
}
901-
902-
return this.handleEnumValueMapper(
903-
mappedValue.typeIdentifier,
904-
mappedValue.importIdentifier,
905-
mappedValue.sourceIdentifier,
906-
mappedValue.sourceFile,
907-
);
908-
}
909-
910-
return [];
911-
})
912-
.filter(Boolean);
868+
return getEnumsImports({
869+
enumValues: this.config.enumValues,
870+
useTypeImports: this.config.useTypeImports,
871+
});
913872
}
914873

915874
EnumTypeDefinition(node: EnumTypeDefinitionNode): string {

packages/plugins/other/visitor-plugin-common/src/imports.ts

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { dirname, isAbsolute, join, relative, resolve } from 'path';
22
import parse from 'parse-filepath';
33
import { normalizeImportExtension } from '@graphql-codegen/plugin-helpers';
4+
import type { ParsedEnumValuesMap } from './types';
45

56
export type ImportDeclaration<T = string> = {
67
outputPath: string;
@@ -105,3 +106,88 @@ export function clearExtension(path: string): string {
105106
export function fixLocalFilePath(path: string): string {
106107
return path.startsWith('..') ? path : `./${path}`;
107108
}
109+
110+
export function getEnumsImports({
111+
enumValues,
112+
useTypeImports,
113+
}: {
114+
enumValues: ParsedEnumValuesMap;
115+
useTypeImports: boolean;
116+
}): string[] {
117+
function handleEnumValueMapper({
118+
typeIdentifier,
119+
importIdentifier,
120+
sourceIdentifier,
121+
sourceFile,
122+
useTypeImports,
123+
}: {
124+
typeIdentifier: string;
125+
importIdentifier: string | null;
126+
sourceIdentifier: string | null;
127+
sourceFile: string | null;
128+
useTypeImports: boolean;
129+
}): string[] {
130+
if (importIdentifier !== sourceIdentifier) {
131+
// use namespace import to dereference nested enum
132+
// { enumValues: { MyEnum: './my-file#NS.NestedEnum' } }
133+
return [
134+
buildTypeImport({ identifier: importIdentifier || sourceIdentifier, source: sourceFile, useTypeImports }),
135+
`import ${typeIdentifier} = ${sourceIdentifier};`,
136+
];
137+
}
138+
if (sourceIdentifier !== typeIdentifier) {
139+
return [
140+
buildTypeImport({ identifier: `${sourceIdentifier} as ${typeIdentifier}`, source: sourceFile, useTypeImports }),
141+
];
142+
}
143+
return [buildTypeImport({ identifier: importIdentifier || sourceIdentifier, source: sourceFile, useTypeImports })];
144+
}
145+
146+
return Object.keys(enumValues)
147+
.flatMap(enumName => {
148+
const mappedValue = enumValues[enumName];
149+
if (mappedValue.sourceFile) {
150+
if (mappedValue.isDefault) {
151+
return [
152+
buildTypeImport({
153+
identifier: mappedValue.typeIdentifier,
154+
source: mappedValue.sourceFile,
155+
asDefault: true,
156+
useTypeImports,
157+
}),
158+
];
159+
}
160+
161+
return handleEnumValueMapper({
162+
typeIdentifier: mappedValue.typeIdentifier,
163+
importIdentifier: mappedValue.importIdentifier,
164+
sourceIdentifier: mappedValue.sourceIdentifier,
165+
sourceFile: mappedValue.sourceFile,
166+
useTypeImports,
167+
});
168+
}
169+
170+
return [];
171+
})
172+
.filter(Boolean);
173+
}
174+
175+
export function buildTypeImport({
176+
identifier,
177+
source,
178+
useTypeImports,
179+
asDefault = false,
180+
}: {
181+
identifier: string;
182+
source: string;
183+
useTypeImports: boolean;
184+
asDefault?: boolean;
185+
}): string {
186+
if (asDefault) {
187+
if (useTypeImports) {
188+
return `import type { default as ${identifier} } from '${source}';`;
189+
}
190+
return `import ${identifier} from '${source}';`;
191+
}
192+
return `import${useTypeImports ? ' type' : ''} { ${identifier} } from '${source}';`;
193+
}

packages/plugins/typescript/operations/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ export const plugin: PluginFunction<
101101
return {
102102
prepend: [
103103
...visitor.getImports(),
104+
...visitor.getEnumsImports(),
104105
...visitor.getGlobalDeclarations(visitor.config.noExport),
105106
'type Exact<T extends { [key: string]: unknown }> = { [K in keyof T]: T[K] };',
106107
],

packages/plugins/typescript/operations/src/visitor.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
DeclarationKind,
2222
generateFragmentImportStatement,
2323
getConfigValue,
24+
getEnumsImports,
2425
LoadedFragment,
2526
normalizeAvoidOptionals,
2627
NormalizedAvoidOptionalsConfig,
@@ -181,6 +182,7 @@ export class TypeScriptDocumentsVisitor extends BaseDocumentsVisitor<
181182
);
182183
this._declarationBlockConfig = {
183184
ignoreExport: this.config.noExport,
185+
enumNameValueSeparator: ' =',
184186
};
185187
}
186188

@@ -263,4 +265,11 @@ export class TypeScriptDocumentsVisitor extends BaseDocumentsVisitor<
263265

264266
return usedInputTypes;
265267
}
268+
269+
public getEnumsImports(): string[] {
270+
return getEnumsImports({
271+
enumValues: this.config.enumValues,
272+
useTypeImports: this.config.useTypeImports,
273+
});
274+
}
266275
}

0 commit comments

Comments
 (0)