Skip to content

Commit 7a68fd9

Browse files
committed
Extract convertSchemaEnumToDeclarationBlockString and getNodeComment to visitor-plugin-common to re-use in next major version
1 parent 408e042 commit 7a68fd9

File tree

6 files changed

+320
-131
lines changed

6 files changed

+320
-131
lines changed

.changeset/silly-kiwis-sip.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@graphql-codegen/visitor-plugin-common': patch
3+
'@graphql-codegen/typescript': patch
4+
---
5+
6+
Extract utilities from base-type-visitor to be shared with other plugins later: convertSchemaEnumToDeclarationBlockString, getNodeComment

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

Lines changed: 3 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ import {
4343
wrapWithSingleQuotes,
4444
} from './utils.js';
4545
import { OperationVariablesToObject } from './variables-to-object.js';
46+
import { getNodeComment } from './get-node-comment.js';
4647

4748
export interface ParsedTypesConfig extends ParsedConfig {
4849
enumValues: ParsedEnumValuesMap;
@@ -703,7 +704,7 @@ export class BaseTypesVisitor<
703704

704705
const typeString = node.type as any as string;
705706
const { type } = this._parsedConfig.declarationKind;
706-
const comment = this.getNodeComment(node);
707+
const comment = getNodeComment(node);
707708

708709
return comment + indent(`${node.name.value}: ${typeString}${this.getPunctuation(type)}`);
709710
}
@@ -910,7 +911,7 @@ export class BaseTypesVisitor<
910911
transformUnderscore: !onlyUnderscoresPattern.test(enumOption.name.value),
911912
})
912913
);
913-
const comment = this.getNodeComment(enumOption);
914+
const comment = getNodeComment(enumOption);
914915
const schemaEnumValue =
915916
schemaEnumType && !this.config.ignoreEnumValuesFromSchema
916917
? schemaEnumType.getValue(enumOption.name.value).value
@@ -1050,28 +1051,6 @@ export class BaseTypesVisitor<
10501051
return null;
10511052
}
10521053

1053-
getNodeComment(node: FieldDefinitionNode | EnumValueDefinitionNode | InputValueDefinitionNode): string {
1054-
let commentText = node.description?.value;
1055-
const deprecationDirective = node.directives.find(v => v.name.value === 'deprecated');
1056-
if (deprecationDirective) {
1057-
const deprecationReason = this.getDeprecationReason(deprecationDirective);
1058-
commentText = `${commentText ? `${commentText}\n` : ''}@deprecated ${deprecationReason}`;
1059-
}
1060-
const comment = transformComment(commentText, 1);
1061-
return comment;
1062-
}
1063-
1064-
protected getDeprecationReason(directive: DirectiveNode): string | void {
1065-
if (directive.name.value === 'deprecated') {
1066-
let reason = 'Field no longer supported';
1067-
const deprecatedReason = directive.arguments[0];
1068-
if (deprecatedReason && deprecatedReason.value.kind === Kind.STRING) {
1069-
reason = deprecatedReason.value.value;
1070-
}
1071-
return reason;
1072-
}
1073-
}
1074-
10751054
protected wrapWithListType(str: string): string {
10761055
return `Array<${str}>`;
10771056
}
Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
import type { EnumTypeDefinitionNode, EnumValueDefinitionNode, GraphQLEnumType, GraphQLSchema } from 'graphql';
2+
import type { ConvertFn, ParsedEnumValuesMap } from './types';
3+
import { DeclarationBlock, type DeclarationBlockConfig, indent, transformComment, wrapWithSingleQuotes } from './utils';
4+
import { getNodeComment } from './get-node-comment';
5+
6+
export const convertSchemaEnumToDeclarationBlockString = ({
7+
schema,
8+
node,
9+
enumName,
10+
enumValues,
11+
futureProofEnums,
12+
ignoreEnumValuesFromSchema,
13+
outputType,
14+
declarationBlockConfig,
15+
naming,
16+
}: {
17+
schema: GraphQLSchema;
18+
node: EnumTypeDefinitionNode;
19+
enumName: string;
20+
enumValues: ParsedEnumValuesMap;
21+
futureProofEnums: boolean;
22+
ignoreEnumValuesFromSchema: boolean;
23+
naming: {
24+
convert: ConvertFn;
25+
typesPrefix: string;
26+
typesSuffix: string;
27+
useTypesPrefix?: boolean;
28+
useTypesSuffix?: boolean;
29+
};
30+
31+
outputType: 'string-literal' | 'numeric' | 'const' | 'native-const' | 'native';
32+
declarationBlockConfig: DeclarationBlockConfig;
33+
}): string => {
34+
if (enumValues[enumName]?.sourceFile) {
35+
return `export { ${enumValues[enumName].typeIdentifier} };\n`;
36+
}
37+
38+
const getValueFromConfig = (enumValue: string | number) => {
39+
if (typeof enumValues[enumName]?.mappedValues?.[enumValue] !== 'undefined') {
40+
return enumValues[enumName].mappedValues[enumValue];
41+
}
42+
return null;
43+
};
44+
45+
const withFutureAddedValue = [futureProofEnums ? [indent('| ' + wrapWithSingleQuotes('%future added value'))] : []];
46+
47+
const enumTypeName = convertName({
48+
options: {
49+
typesPrefix: naming.typesPrefix,
50+
typesSuffix: naming.typesSuffix,
51+
useTypesPrefix: naming.useTypesPrefix,
52+
useTypesSuffix: naming.useTypesSuffix,
53+
},
54+
convert: () => naming.convert(node),
55+
});
56+
57+
if (outputType === 'string-literal') {
58+
return new DeclarationBlock(declarationBlockConfig)
59+
.export()
60+
.asKind('type')
61+
.withComment(node.description?.value)
62+
.withName(enumTypeName)
63+
.withContent(
64+
'\n' +
65+
node.values
66+
.map(enumOption => {
67+
const name = enumOption.name.value;
68+
const enumValue: string | number = getValueFromConfig(name) ?? name;
69+
const comment = transformComment(enumOption.description?.value, 1);
70+
71+
return comment + indent('| ' + wrapWithSingleQuotes(enumValue));
72+
})
73+
.concat(...withFutureAddedValue)
74+
.join('\n')
75+
).string;
76+
}
77+
78+
if (outputType === 'numeric') {
79+
return new DeclarationBlock(declarationBlockConfig)
80+
.export()
81+
.withComment(node.description?.value)
82+
.withName(enumTypeName)
83+
.asKind('enum')
84+
.withBlock(
85+
node.values
86+
.map((enumOption, i) => {
87+
const valueFromConfig = getValueFromConfig(enumOption.name.value);
88+
const enumValue: string | number = valueFromConfig ?? i;
89+
const comment = transformComment(enumOption.description?.value, 1);
90+
const optionName = makeValidEnumIdentifier(
91+
convertName({
92+
options: {
93+
typesPrefix: naming.typesPrefix,
94+
typesSuffix: naming.typesSuffix,
95+
useTypesPrefix: false,
96+
},
97+
convert: () => naming.convert(enumOption, { transformUnderscore: true }),
98+
})
99+
);
100+
return comment + indent(optionName) + ` = ${enumValue}`;
101+
})
102+
.concat(...withFutureAddedValue)
103+
.join(',\n')
104+
).string;
105+
}
106+
107+
if (outputType === 'const') {
108+
const typeName = `export type ${enumTypeName} = typeof ${enumTypeName}[keyof typeof ${enumTypeName}];`;
109+
const enumAsConst = new DeclarationBlock({
110+
...declarationBlockConfig,
111+
blockTransformer: block => {
112+
return block + ' as const';
113+
},
114+
})
115+
.export()
116+
.asKind('const')
117+
.withName(enumTypeName)
118+
.withComment(node.description?.value)
119+
.withBlock(
120+
node.values
121+
.map(enumOption => {
122+
const optionName = makeValidEnumIdentifier(
123+
convertName({
124+
options: {
125+
typesPrefix: naming.typesPrefix,
126+
typesSuffix: naming.typesPrefix,
127+
},
128+
convert: () =>
129+
naming.convert(enumOption, {
130+
transformUnderscore: true,
131+
}),
132+
})
133+
);
134+
const comment = transformComment(enumOption.description?.value, 1);
135+
const name = enumOption.name.value;
136+
const enumValue: string | number = getValueFromConfig(name) ?? name;
137+
138+
return comment + indent(`${optionName}: ${wrapWithSingleQuotes(enumValue)}`);
139+
})
140+
.join(',\n')
141+
).string;
142+
143+
return [enumAsConst, typeName].join('\n');
144+
}
145+
146+
const buildEnumValuesBlock = ({
147+
typeName,
148+
values,
149+
schema,
150+
}: {
151+
typeName: string;
152+
values: ReadonlyArray<EnumValueDefinitionNode>;
153+
schema: GraphQLSchema;
154+
}): string => {
155+
const schemaEnumType: GraphQLEnumType | undefined = schema
156+
? (schema.getType(typeName) as GraphQLEnumType)
157+
: undefined;
158+
159+
return values
160+
.map(enumOption => {
161+
const onlyUnderscoresPattern = /^_+$/;
162+
const optionName = makeValidEnumIdentifier(
163+
convertName({
164+
options: {
165+
useTypesPrefix: false,
166+
typesPrefix: naming.typesPrefix,
167+
typesSuffix: naming.typesSuffix,
168+
},
169+
convert: () =>
170+
naming.convert(enumOption, {
171+
// We can only strip out the underscores if the value contains other
172+
// characters. Otherwise we'll generate syntactically invalid code.
173+
transformUnderscore: !onlyUnderscoresPattern.test(enumOption.name.value),
174+
}),
175+
})
176+
);
177+
const comment = getNodeComment(enumOption);
178+
const schemaEnumValue =
179+
schemaEnumType && !ignoreEnumValuesFromSchema
180+
? schemaEnumType.getValue(enumOption.name.value).value
181+
: undefined;
182+
let enumValue: string | number =
183+
typeof schemaEnumValue === 'undefined' ? enumOption.name.value : schemaEnumValue;
184+
185+
if (typeof enumValues[typeName]?.mappedValues?.[enumValue] !== 'undefined') {
186+
enumValue = enumValues[typeName].mappedValues[enumValue];
187+
}
188+
189+
return (
190+
comment +
191+
indent(
192+
`${optionName}${declarationBlockConfig.enumNameValueSeparator} ${wrapWithSingleQuotes(
193+
enumValue,
194+
typeof schemaEnumValue !== 'undefined'
195+
)}`
196+
)
197+
);
198+
})
199+
.join(',\n');
200+
};
201+
202+
return new DeclarationBlock(declarationBlockConfig)
203+
.export()
204+
.asKind(outputType === 'native-const' ? 'const enum' : 'enum')
205+
.withName(enumTypeName)
206+
.withComment(node.description?.value)
207+
.withBlock(buildEnumValuesBlock({ typeName: enumName, values: node.values, schema })).string;
208+
};
209+
210+
const makeValidEnumIdentifier = (identifier: string): string => {
211+
if (/^[0-9]/.exec(identifier)) {
212+
return wrapWithSingleQuotes(identifier, true);
213+
}
214+
return identifier;
215+
};
216+
217+
const convertName = ({
218+
convert,
219+
options,
220+
}: {
221+
options: {
222+
typesPrefix: string;
223+
useTypesPrefix?: boolean;
224+
typesSuffix: string;
225+
useTypesSuffix?: boolean;
226+
};
227+
convert: () => string;
228+
}): string => {
229+
const useTypesPrefix = typeof options.useTypesPrefix === 'boolean' ? options.useTypesPrefix : true;
230+
const useTypesSuffix = typeof options.useTypesSuffix === 'boolean' ? options.useTypesSuffix : true;
231+
232+
let convertedName = '';
233+
234+
if (useTypesPrefix) {
235+
convertedName += options.typesPrefix;
236+
}
237+
238+
convertedName += convert();
239+
240+
if (useTypesSuffix) {
241+
convertedName += options.typesSuffix;
242+
}
243+
244+
return convertedName;
245+
};
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import {
2+
Kind,
3+
type DirectiveNode,
4+
type EnumValueDefinitionNode,
5+
type FieldDefinitionNode,
6+
type InputValueDefinitionNode,
7+
} from 'graphql';
8+
import { transformComment } from './utils';
9+
10+
export const getNodeComment = (
11+
node: FieldDefinitionNode | EnumValueDefinitionNode | InputValueDefinitionNode
12+
): string => {
13+
let commentText = node.description?.value;
14+
const deprecationDirective = node.directives.find(v => v.name.value === 'deprecated');
15+
if (deprecationDirective) {
16+
const deprecationReason = getDeprecationReason(deprecationDirective);
17+
commentText = `${commentText ? `${commentText}\n` : ''}@deprecated ${deprecationReason}`;
18+
}
19+
const comment = transformComment(commentText, 1);
20+
return comment;
21+
};
22+
23+
const getDeprecationReason = (directive: DirectiveNode): string | void => {
24+
if (directive.name.value === 'deprecated') {
25+
let reason = 'Field no longer supported';
26+
const deprecatedReason = directive.arguments[0];
27+
if (deprecatedReason && deprecatedReason.value.kind === Kind.STRING) {
28+
reason = deprecatedReason.value.value;
29+
}
30+
return reason;
31+
}
32+
};

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,5 @@ export * from './selection-set-to-object.js';
1717
export * from './types.js';
1818
export * from './utils.js';
1919
export * from './variables-to-object.js';
20+
export * from './convert-schema-enum-to-declaration-block-string.js';
21+
export * from './get-node-comment.js';

0 commit comments

Comments
 (0)