Skip to content

Commit ad00ac8

Browse files
authored
[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 0461021 commit ad00ac8

7 files changed

Lines changed: 1489 additions & 64 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: 25 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ import {
4343
} from './utils.js';
4444
import { OperationVariablesToObject } from './variables-to-object.js';
4545
import { buildEnumValuesBlock } from './convert-schema-enum-to-declaration-block-string.js';
46+
import { buildTypeImport, getEnumsImports } from './imports.js';
4647

4748
export interface ParsedTypesConfig extends ParsedConfig {
4849
enumValues: ParsedEnumValuesMap;
@@ -559,12 +560,24 @@ export class BaseTypesVisitor<
559560
const mappedValue = this.config.scalars[enumName];
560561

561562
if (mappedValue.input.isExternal) {
562-
res.push(this._buildTypeImport(mappedValue.input.import, mappedValue.input.source, mappedValue.input.default));
563+
res.push(
564+
buildTypeImport({
565+
identifier: mappedValue.input.import,
566+
source: mappedValue.input.source,
567+
asDefault: mappedValue.input.default,
568+
useTypeImports: this.config.useTypeImports,
569+
})
570+
);
563571
}
564572

565573
if (mappedValue.output.isExternal) {
566574
res.push(
567-
this._buildTypeImport(mappedValue.output.import, mappedValue.output.source, mappedValue.output.default)
575+
buildTypeImport({
576+
identifier: mappedValue.output.import,
577+
source: mappedValue.output.source,
578+
asDefault: mappedValue.output.default,
579+
useTypeImports: this.config.useTypeImports,
580+
})
568581
);
569582
}
570583

@@ -578,7 +591,12 @@ export class BaseTypesVisitor<
578591
const mappedValue = this.config.directiveArgumentAndInputFieldMappings[directive];
579592

580593
if (mappedValue.isExternal) {
581-
return this._buildTypeImport(mappedValue.import, mappedValue.source, mappedValue.default);
594+
return buildTypeImport({
595+
identifier: mappedValue.import,
596+
source: mappedValue.source,
597+
asDefault: mappedValue.default,
598+
useTypeImports: this.config.useTypeImports,
599+
});
582600
}
583601

584602
return null;
@@ -811,58 +829,11 @@ export class BaseTypesVisitor<
811829
return '';
812830
}
813831

814-
protected _buildTypeImport(identifier: string, source: string, asDefault = false): string {
815-
const { useTypeImports } = this.config;
816-
if (asDefault) {
817-
if (useTypeImports) {
818-
return `import type { default as ${identifier} } from '${source}';`;
819-
}
820-
return `import ${identifier} from '${source}';`;
821-
}
822-
return `import${useTypeImports ? ' type' : ''} { ${identifier} } from '${source}';`;
823-
}
824-
825-
protected handleEnumValueMapper(
826-
typeIdentifier: string,
827-
importIdentifier: string | null,
828-
sourceIdentifier: string | null,
829-
sourceFile: string | null
830-
): string[] {
831-
if (importIdentifier !== sourceIdentifier) {
832-
// use namespace import to dereference nested enum
833-
// { enumValues: { MyEnum: './my-file#NS.NestedEnum' } }
834-
return [
835-
this._buildTypeImport(importIdentifier || sourceIdentifier, sourceFile),
836-
`import ${typeIdentifier} = ${sourceIdentifier};`,
837-
];
838-
}
839-
if (sourceIdentifier !== typeIdentifier) {
840-
return [this._buildTypeImport(`${sourceIdentifier} as ${typeIdentifier}`, sourceFile)];
841-
}
842-
return [this._buildTypeImport(importIdentifier || sourceIdentifier, sourceFile)];
843-
}
844-
845832
public getEnumsImports(): string[] {
846-
return Object.keys(this.config.enumValues)
847-
.flatMap(enumName => {
848-
const mappedValue = this.config.enumValues[enumName];
849-
850-
if (mappedValue.sourceFile) {
851-
if (mappedValue.isDefault) {
852-
return [this._buildTypeImport(mappedValue.typeIdentifier, mappedValue.sourceFile, true)];
853-
}
854-
855-
return this.handleEnumValueMapper(
856-
mappedValue.typeIdentifier,
857-
mappedValue.importIdentifier,
858-
mappedValue.sourceIdentifier,
859-
mappedValue.sourceFile
860-
);
861-
}
862-
863-
return [];
864-
})
865-
.filter(Boolean);
833+
return getEnumsImports({
834+
enumValues: this.config.enumValues,
835+
useTypeImports: this.config.useTypeImports,
836+
});
866837
}
867838

868839
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,5 +1,6 @@
11
import { dirname, isAbsolute, join, relative, resolve } from 'path';
22
import parse from 'parse-filepath';
3+
import type { ParsedEnumValuesMap } from './types';
34

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

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ export const plugin: PluginFunction<TypeScriptDocumentsPluginConfig, Types.Compl
6363
return {
6464
prepend: [
6565
...visitor.getImports(),
66+
...visitor.getEnumsImports(),
6667
...visitor.getGlobalDeclarations(visitor.config.noExport),
6768
'type Exact<T extends { [key: string]: unknown }> = { [K in keyof T]: T[K] };',
6869
],

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
DeclarationKind,
66
generateFragmentImportStatement,
77
getConfigValue,
8+
getEnumsImports,
89
LoadedFragment,
910
normalizeAvoidOptionals,
1011
NormalizedAvoidOptionalsConfig,
@@ -168,6 +169,7 @@ export class TypeScriptDocumentsVisitor extends BaseDocumentsVisitor<
168169
);
169170
this._declarationBlockConfig = {
170171
ignoreExport: this.config.noExport,
172+
enumNameValueSeparator: ' =',
171173
};
172174
}
173175

@@ -245,4 +247,11 @@ export class TypeScriptDocumentsVisitor extends BaseDocumentsVisitor<
245247

246248
return usedInputTypes;
247249
}
250+
251+
public getEnumsImports(): string[] {
252+
return getEnumsImports({
253+
enumValues: this.config.enumValues,
254+
useTypeImports: this.config.useTypeImports,
255+
});
256+
}
248257
}

0 commit comments

Comments
 (0)