UNPKG

21.5 kBJavaScriptView Raw
1'use strict';
2
3Object.defineProperty(exports, '__esModule', { value: true });
4
5function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
6
7const pluginHelpers = require('@graphql-codegen/plugin-helpers');
8const graphql = require('graphql');
9const visitorPluginCommon = require('@graphql-codegen/visitor-plugin-common');
10const autoBind = _interopDefault(require('auto-bind'));
11const schemaAst = require('@graphql-codegen/schema-ast');
12
13class TypeScriptOperationVariablesToObject extends visitorPluginCommon.OperationVariablesToObject {
14 constructor(_scalars, _convertName, _avoidOptionals, _immutableTypes, _namespacedImportName = null, _enumNames = [], _enumPrefix = true, _enumValues = {}, _applyCoercion = false, _directiveArgumentAndInputFieldMappings = {}, _maybeType = 'Maybe') {
15 super(_scalars, _convertName, _namespacedImportName, _enumNames, _enumPrefix, _enumValues, _applyCoercion, _directiveArgumentAndInputFieldMappings);
16 this._avoidOptionals = _avoidOptionals;
17 this._immutableTypes = _immutableTypes;
18 this._maybeType = _maybeType;
19 }
20 clearOptional(str) {
21 const prefix = this._namespacedImportName ? `${this._namespacedImportName}.` : '';
22 const rgx = new RegExp(`^${this.wrapMaybe(`(.*?)`)}$`, 'i');
23 if (str.startsWith(`${prefix}${this._maybeType}`)) {
24 return str.replace(rgx, '$1');
25 }
26 return str;
27 }
28 wrapAstTypeWithModifiers(baseType, typeNode, applyCoercion = false) {
29 if (typeNode.kind === graphql.Kind.NON_NULL_TYPE) {
30 const type = this.wrapAstTypeWithModifiers(baseType, typeNode.type, applyCoercion);
31 return this.clearOptional(type);
32 }
33 else if (typeNode.kind === graphql.Kind.LIST_TYPE) {
34 const innerType = this.wrapAstTypeWithModifiers(baseType, typeNode.type, applyCoercion);
35 const listInputCoercionExtension = applyCoercion ? ` | ${innerType}` : '';
36 return this.wrapMaybe(`${this._immutableTypes ? 'ReadonlyArray' : 'Array'}<${innerType}>${listInputCoercionExtension}`);
37 }
38 else {
39 return this.wrapMaybe(baseType);
40 }
41 }
42 formatFieldString(fieldName, isNonNullType, hasDefaultValue) {
43 return `${fieldName}${this.getAvoidOption(isNonNullType, hasDefaultValue) ? '?' : ''}`;
44 }
45 formatTypeString(fieldType, isNonNullType, hasDefaultValue) {
46 if (!hasDefaultValue && isNonNullType) {
47 return this.clearOptional(fieldType);
48 }
49 return fieldType;
50 }
51 wrapMaybe(type) {
52 const prefix = this._namespacedImportName ? `${this._namespacedImportName}.` : '';
53 return `${prefix}${this._maybeType}${type ? `<${type}>` : ''}`;
54 }
55 getAvoidOption(isNonNullType, hasDefaultValue) {
56 const options = visitorPluginCommon.normalizeAvoidOptionals(this._avoidOptionals);
57 return ((options.object || !options.defaultValue) && hasDefaultValue) || (!options.object && !isNonNullType);
58 }
59 getPunctuation() {
60 return ';';
61 }
62}
63
64const EXACT_SIGNATURE = `type Exact<T extends { [key: string]: unknown }> = { [K in keyof T]: T[K] };`;
65const MAKE_OPTIONAL_SIGNATURE = `type MakeOptional<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]?: Maybe<T[SubKey]> };`;
66const MAKE_MAYBE_SIGNATURE = `type MakeMaybe<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]: Maybe<T[SubKey]> };`;
67class TsVisitor extends visitorPluginCommon.BaseTypesVisitor {
68 constructor(schema, pluginConfig, additionalConfig = {}) {
69 super(schema, pluginConfig, {
70 noExport: visitorPluginCommon.getConfigValue(pluginConfig.noExport, false),
71 avoidOptionals: visitorPluginCommon.normalizeAvoidOptionals(visitorPluginCommon.getConfigValue(pluginConfig.avoidOptionals, false)),
72 maybeValue: visitorPluginCommon.getConfigValue(pluginConfig.maybeValue, 'T | null'),
73 inputMaybeValue: visitorPluginCommon.getConfigValue(pluginConfig.inputMaybeValue, visitorPluginCommon.getConfigValue(pluginConfig.maybeValue, 'Maybe<T>')),
74 constEnums: visitorPluginCommon.getConfigValue(pluginConfig.constEnums, false),
75 enumsAsTypes: visitorPluginCommon.getConfigValue(pluginConfig.enumsAsTypes, false),
76 futureProofEnums: visitorPluginCommon.getConfigValue(pluginConfig.futureProofEnums, false),
77 futureProofUnions: visitorPluginCommon.getConfigValue(pluginConfig.futureProofUnions, false),
78 enumsAsConst: visitorPluginCommon.getConfigValue(pluginConfig.enumsAsConst, false),
79 numericEnums: visitorPluginCommon.getConfigValue(pluginConfig.numericEnums, false),
80 onlyEnums: visitorPluginCommon.getConfigValue(pluginConfig.onlyEnums, false),
81 onlyOperationTypes: visitorPluginCommon.getConfigValue(pluginConfig.onlyOperationTypes, false),
82 immutableTypes: visitorPluginCommon.getConfigValue(pluginConfig.immutableTypes, false),
83 useImplementingTypes: visitorPluginCommon.getConfigValue(pluginConfig.useImplementingTypes, false),
84 entireFieldWrapperValue: visitorPluginCommon.getConfigValue(pluginConfig.entireFieldWrapperValue, 'T'),
85 wrapEntireDefinitions: visitorPluginCommon.getConfigValue(pluginConfig.wrapEntireFieldDefinitions, false),
86 ...additionalConfig,
87 });
88 autoBind(this);
89 const enumNames = Object.values(schema.getTypeMap())
90 .filter(graphql.isEnumType)
91 .map(type => type.name);
92 this.setArgumentsTransformer(new TypeScriptOperationVariablesToObject(this.scalars, this.convertName, this.config.avoidOptionals, this.config.immutableTypes, null, enumNames, pluginConfig.enumPrefix, this.config.enumValues, false, this.config.directiveArgumentAndInputFieldMappings, 'InputMaybe'));
93 this.setDeclarationBlockConfig({
94 enumNameValueSeparator: ' =',
95 ignoreExport: this.config.noExport,
96 });
97 }
98 _getTypeForNode(node) {
99 const typeAsString = node.name;
100 if (this.config.useImplementingTypes) {
101 const allTypesMap = this._schema.getTypeMap();
102 const implementingTypes = [];
103 // TODO: Move this to a better place, since we are using this logic in some other places as well.
104 for (const graphqlType of Object.values(allTypesMap)) {
105 if (graphqlType instanceof graphql.GraphQLObjectType) {
106 const allInterfaces = graphqlType.getInterfaces();
107 if (allInterfaces.some(int => typeAsString === int.name)) {
108 implementingTypes.push(this.convertName(graphqlType.name));
109 }
110 }
111 }
112 if (implementingTypes.length > 0) {
113 return implementingTypes.join(' | ');
114 }
115 }
116 const typeString = super._getTypeForNode(node);
117 const schemaType = this._schema.getType(node.name);
118 if (graphql.isEnumType(schemaType)) {
119 // futureProofEnums + enumsAsTypes combination adds the future value to the enum type itself
120 // so it's not necessary to repeat it in the usage
121 const futureProofEnumUsageEnabled = this.config.futureProofEnums === true && this.config.enumsAsTypes !== true;
122 if (futureProofEnumUsageEnabled && this.config.allowEnumStringTypes === true) {
123 return `${typeString} | '%future added value' | ` + '`${' + typeString + '}`';
124 }
125 if (futureProofEnumUsageEnabled) {
126 return `${typeString} | '%future added value'`;
127 }
128 if (this.config.allowEnumStringTypes === true) {
129 return `${typeString} | ` + '`${' + typeString + '}`';
130 }
131 }
132 return typeString;
133 }
134 getWrapperDefinitions() {
135 if (this.config.onlyEnums)
136 return [];
137 const definitions = [
138 this.getMaybeValue(),
139 this.getInputMaybeValue(),
140 this.getExactDefinition(),
141 this.getMakeOptionalDefinition(),
142 this.getMakeMaybeDefinition(),
143 ];
144 if (this.config.wrapFieldDefinitions) {
145 definitions.push(this.getFieldWrapperValue());
146 }
147 if (this.config.wrapEntireDefinitions) {
148 definitions.push(this.getEntireFieldWrapperValue());
149 }
150 return definitions;
151 }
152 getExactDefinition() {
153 if (this.config.onlyEnums)
154 return '';
155 return `${this.getExportPrefix()}${EXACT_SIGNATURE}`;
156 }
157 getMakeOptionalDefinition() {
158 return `${this.getExportPrefix()}${MAKE_OPTIONAL_SIGNATURE}`;
159 }
160 getMakeMaybeDefinition() {
161 if (this.config.onlyEnums)
162 return '';
163 return `${this.getExportPrefix()}${MAKE_MAYBE_SIGNATURE}`;
164 }
165 getMaybeValue() {
166 return `${this.getExportPrefix()}type Maybe<T> = ${this.config.maybeValue};`;
167 }
168 getInputMaybeValue() {
169 return `${this.getExportPrefix()}type InputMaybe<T> = ${this.config.inputMaybeValue};`;
170 }
171 clearOptional(str) {
172 if (str.startsWith('Maybe')) {
173 return str.replace(/Maybe<(.*?)>$/, '$1');
174 }
175 if (str.startsWith('InputMaybe')) {
176 return str.replace(/InputMaybe<(.*?)>$/, '$1');
177 }
178 return str;
179 }
180 getExportPrefix() {
181 if (this.config.noExport) {
182 return '';
183 }
184 return super.getExportPrefix();
185 }
186 getMaybeWrapper(ancestors) {
187 const currentVisitContext = this.getVisitorKindContextFromAncestors(ancestors);
188 const isInputContext = currentVisitContext.includes(graphql.Kind.INPUT_OBJECT_TYPE_DEFINITION);
189 return isInputContext ? 'InputMaybe' : 'Maybe';
190 }
191 NamedType(node, key, parent, path, ancestors) {
192 return `${this.getMaybeWrapper(ancestors)}<${super.NamedType(node, key, parent, path, ancestors)}>`;
193 }
194 ListType(node, key, parent, path, ancestors) {
195 return `${this.getMaybeWrapper(ancestors)}<${super.ListType(node, key, parent, path, ancestors)}>`;
196 }
197 UnionTypeDefinition(node, key, parent) {
198 if (this.config.onlyOperationTypes || this.config.onlyEnums)
199 return '';
200 let withFutureAddedValue = [];
201 if (this.config.futureProofUnions) {
202 withFutureAddedValue = [
203 this.config.immutableTypes ? `{ readonly __typename?: "%other" }` : `{ __typename?: "%other" }`,
204 ];
205 }
206 const originalNode = parent[key];
207 const possibleTypes = originalNode.types
208 .map(t => (this.scalars[t.name.value] ? this._getScalar(t.name.value) : this.convertName(t)))
209 .concat(...withFutureAddedValue)
210 .join(' | ');
211 return new visitorPluginCommon.DeclarationBlock(this._declarationBlockConfig)
212 .export()
213 .asKind('type')
214 .withName(this.convertName(node))
215 .withComment(node.description)
216 .withContent(possibleTypes).string;
217 // return super.UnionTypeDefinition(node, key, parent).concat(withFutureAddedValue).join("");
218 }
219 wrapWithListType(str) {
220 return `${this.config.immutableTypes ? 'ReadonlyArray' : 'Array'}<${str}>`;
221 }
222 NonNullType(node) {
223 const baseValue = super.NonNullType(node);
224 return this.clearOptional(baseValue);
225 }
226 FieldDefinition(node, key, parent) {
227 const typeString = this.config.wrapEntireDefinitions
228 ? `EntireFieldWrapper<${node.type}>`
229 : node.type;
230 const originalFieldNode = parent[key];
231 const addOptionalSign = !this.config.avoidOptionals.field && originalFieldNode.type.kind !== graphql.Kind.NON_NULL_TYPE;
232 const comment = this.getNodeComment(node);
233 const { type } = this.config.declarationKind;
234 return (comment +
235 visitorPluginCommon.indent(`${this.config.immutableTypes ? 'readonly ' : ''}${node.name}${addOptionalSign ? '?' : ''}: ${typeString}${this.getPunctuation(type)}`));
236 }
237 InputValueDefinition(node, key, parent) {
238 const originalFieldNode = parent[key];
239 const addOptionalSign = !this.config.avoidOptionals.inputValue &&
240 (originalFieldNode.type.kind !== graphql.Kind.NON_NULL_TYPE ||
241 (!this.config.avoidOptionals.defaultValue && node.defaultValue !== undefined));
242 const comment = visitorPluginCommon.transformComment(node.description, 1);
243 const declarationKind = this.config.declarationKind.type;
244 let type = node.type;
245 if (node.directives && this.config.directiveArgumentAndInputFieldMappings) {
246 type = this._getDirectiveOverrideType(node.directives) || type;
247 }
248 return (comment +
249 visitorPluginCommon.indent(`${this.config.immutableTypes ? 'readonly ' : ''}${node.name}${addOptionalSign ? '?' : ''}: ${type}${this.getPunctuation(declarationKind)}`));
250 }
251 EnumTypeDefinition(node) {
252 const enumName = node.name;
253 // In case of mapped external enum string
254 if (this.config.enumValues[enumName] && this.config.enumValues[enumName].sourceFile) {
255 return `export { ${this.config.enumValues[enumName].typeIdentifier} };\n`;
256 }
257 const getValueFromConfig = (enumValue) => {
258 if (this.config.enumValues[enumName] &&
259 this.config.enumValues[enumName].mappedValues &&
260 typeof this.config.enumValues[enumName].mappedValues[enumValue] !== 'undefined') {
261 return this.config.enumValues[enumName].mappedValues[enumValue];
262 }
263 return null;
264 };
265 const withFutureAddedValue = [
266 this.config.futureProofEnums ? [visitorPluginCommon.indent('| ' + visitorPluginCommon.wrapWithSingleQuotes('%future added value'))] : [],
267 ];
268 const enumTypeName = this.convertName(node, {
269 useTypesPrefix: this.config.enumPrefix,
270 });
271 if (this.config.enumsAsTypes) {
272 return new visitorPluginCommon.DeclarationBlock(this._declarationBlockConfig)
273 .export()
274 .asKind('type')
275 .withComment(node.description)
276 .withName(enumTypeName)
277 .withContent('\n' +
278 node.values
279 .map(enumOption => {
280 var _a;
281 const name = enumOption.name;
282 const enumValue = (_a = getValueFromConfig(name)) !== null && _a !== void 0 ? _a : name;
283 const comment = visitorPluginCommon.transformComment(enumOption.description, 1);
284 return comment + visitorPluginCommon.indent('| ' + visitorPluginCommon.wrapWithSingleQuotes(enumValue));
285 })
286 .concat(...withFutureAddedValue)
287 .join('\n')).string;
288 }
289 if (this.config.numericEnums) {
290 const block = new visitorPluginCommon.DeclarationBlock(this._declarationBlockConfig)
291 .export()
292 .withComment(node.description)
293 .withName(enumTypeName)
294 .asKind('enum')
295 .withBlock(node.values
296 .map((enumOption, i) => {
297 const valueFromConfig = getValueFromConfig(enumOption.name);
298 const enumValue = valueFromConfig !== null && valueFromConfig !== void 0 ? valueFromConfig : i;
299 const comment = visitorPluginCommon.transformComment(enumOption.description, 1);
300 const optionName = this.makeValidEnumIdentifier(this.convertName(enumOption, {
301 useTypesPrefix: false,
302 transformUnderscore: true,
303 }));
304 return comment + visitorPluginCommon.indent(optionName) + ` = ${enumValue}`;
305 })
306 .concat(...withFutureAddedValue)
307 .join(',\n')).string;
308 return block;
309 }
310 if (this.config.enumsAsConst) {
311 const typeName = `export type ${enumTypeName} = typeof ${enumTypeName}[keyof typeof ${enumTypeName}];`;
312 const enumAsConst = new visitorPluginCommon.DeclarationBlock({
313 ...this._declarationBlockConfig,
314 blockTransformer: block => {
315 return block + ' as const';
316 },
317 })
318 .export()
319 .asKind('const')
320 .withName(enumTypeName)
321 .withComment(node.description)
322 .withBlock(node.values
323 .map(enumOption => {
324 var _a;
325 const optionName = this.convertName(enumOption, {
326 useTypesPrefix: false,
327 transformUnderscore: true,
328 });
329 const comment = visitorPluginCommon.transformComment(enumOption.description, 1);
330 const name = enumOption.name;
331 const enumValue = (_a = getValueFromConfig(name)) !== null && _a !== void 0 ? _a : name;
332 return comment + visitorPluginCommon.indent(`${optionName}: ${visitorPluginCommon.wrapWithSingleQuotes(enumValue)}`);
333 })
334 .join(',\n')).string;
335 return [enumAsConst, typeName].join('\n');
336 }
337 return new visitorPluginCommon.DeclarationBlock(this._declarationBlockConfig)
338 .export()
339 .asKind(this.config.constEnums ? 'const enum' : 'enum')
340 .withName(enumTypeName)
341 .withComment(node.description)
342 .withBlock(this.buildEnumValuesBlock(enumName, node.values)).string;
343 }
344 getPunctuation(_declarationKind) {
345 return ';';
346 }
347}
348
349class TsIntrospectionVisitor extends TsVisitor {
350 constructor(schema, pluginConfig = {}, typesToInclude) {
351 super(schema, pluginConfig);
352 this.typesToInclude = [];
353 this.typesToInclude = typesToInclude;
354 autoBind(this);
355 }
356 DirectiveDefinition() {
357 return null;
358 }
359 ObjectTypeDefinition(node, key, parent) {
360 const name = node.name;
361 if (this.typesToInclude.some(type => type.name === name)) {
362 return super.ObjectTypeDefinition(node, key, parent);
363 }
364 return null;
365 }
366 EnumTypeDefinition(node) {
367 const name = node.name;
368 if (this.typesToInclude.some(type => type.name === name)) {
369 return super.EnumTypeDefinition(node);
370 }
371 return null;
372 }
373}
374
375const plugin = (schema, documents, config) => {
376 const { schema: _schema, ast } = schemaAst.transformSchemaAST(schema, config);
377 const visitor = new TsVisitor(_schema, config);
378 const visitorResult = pluginHelpers.oldVisit(ast, { leave: visitor });
379 const introspectionDefinitions = includeIntrospectionTypesDefinitions(_schema, documents, config);
380 const scalars = visitor.scalarsDefinition;
381 const directiveArgumentAndInputFieldMappings = visitor.directiveArgumentAndInputFieldMappingsDefinition;
382 return {
383 prepend: [
384 ...visitor.getEnumsImports(),
385 ...visitor.getDirectiveArgumentAndInputFieldMappingsImports(),
386 ...visitor.getScalarsImports(),
387 ...visitor.getWrapperDefinitions(),
388 ].filter(Boolean),
389 content: [
390 scalars,
391 directiveArgumentAndInputFieldMappings,
392 ...visitorResult.definitions,
393 ...introspectionDefinitions,
394 ]
395 .filter(Boolean)
396 .join('\n'),
397 };
398};
399function includeIntrospectionTypesDefinitions(schema, documents, config) {
400 const typeInfo = new graphql.TypeInfo(schema);
401 const usedTypes = [];
402 const documentsVisitor = graphql.visitWithTypeInfo(typeInfo, {
403 Field() {
404 const type = graphql.getNamedType(typeInfo.getType());
405 if (graphql.isIntrospectionType(type) && !usedTypes.includes(type)) {
406 usedTypes.push(type);
407 }
408 },
409 });
410 documents.forEach(doc => graphql.visit(doc.document, documentsVisitor));
411 const typesToInclude = [];
412 usedTypes.forEach(type => {
413 collectTypes(type);
414 });
415 const visitor = new TsIntrospectionVisitor(schema, config, typesToInclude);
416 const result = pluginHelpers.oldVisit(graphql.parse(graphql.printIntrospectionSchema(schema)), { leave: visitor });
417 // recursively go through each `usedTypes` and their children and collect all used types
418 // we don't care about Interfaces, Unions and others, but Objects and Enums
419 function collectTypes(type) {
420 if (typesToInclude.includes(type)) {
421 return;
422 }
423 typesToInclude.push(type);
424 if (graphql.isObjectType(type)) {
425 const fields = type.getFields();
426 Object.keys(fields).forEach(key => {
427 const field = fields[key];
428 const type = graphql.getNamedType(field.type);
429 collectTypes(type);
430 });
431 }
432 }
433 return result.definitions;
434}
435
436exports.EXACT_SIGNATURE = EXACT_SIGNATURE;
437exports.MAKE_MAYBE_SIGNATURE = MAKE_MAYBE_SIGNATURE;
438exports.MAKE_OPTIONAL_SIGNATURE = MAKE_OPTIONAL_SIGNATURE;
439exports.TsIntrospectionVisitor = TsIntrospectionVisitor;
440exports.TsVisitor = TsVisitor;
441exports.TypeScriptOperationVariablesToObject = TypeScriptOperationVariablesToObject;
442exports.includeIntrospectionTypesDefinitions = includeIntrospectionTypesDefinitions;
443exports.plugin = plugin;