Skip to content

Commit 863eced

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 d636a7d commit 863eced

File tree

7 files changed

+1489
-64
lines changed

7 files changed

+1489
-64
lines changed

.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,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
@@ -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)