UNPKG

@graphql-codegen/typescript

Version:

GraphQL Code Generator plugin for generating TypeScript types

301 lines (292 loc) • 13.9 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } const graphql = require('graphql'); const visitorPluginCommon = require('@graphql-codegen/visitor-plugin-common'); const autoBind = _interopDefault(require('auto-bind')); class TypeScriptOperationVariablesToObject extends visitorPluginCommon.OperationVariablesToObject { constructor(_scalars, _convertName, _avoidOptionals, _immutableTypes, _namespacedImportName = null, _enumNames = [], _enumPrefix = true, _enumValues = {}) { super(_scalars, _convertName, _namespacedImportName, _enumNames, _enumPrefix, _enumValues); this._avoidOptionals = _avoidOptionals; this._immutableTypes = _immutableTypes; } clearOptional(str) { const prefix = this._namespacedImportName ? `${this._namespacedImportName}.` : ''; const rgx = new RegExp(`^${prefix}Maybe<(.*?)>$`, 'i'); if (str.startsWith(`${this._namespacedImportName ? `${this._namespacedImportName}.` : ''}Maybe`)) { return str.replace(rgx, '$1'); } return str; } wrapAstTypeWithModifiers(baseType, typeNode) { const prefix = this._namespacedImportName ? `${this._namespacedImportName}.` : ''; if (typeNode.kind === graphql.Kind.NON_NULL_TYPE) { const type = this.wrapAstTypeWithModifiers(baseType, typeNode.type); return this.clearOptional(type); } else if (typeNode.kind === graphql.Kind.LIST_TYPE) { const innerType = this.wrapAstTypeWithModifiers(baseType, typeNode.type); return `${prefix}Maybe<${this._immutableTypes ? 'ReadonlyArray' : 'Array'}<${innerType}>>`; } else { return `${prefix}Maybe<${baseType}>`; } } formatFieldString(fieldName, isNonNullType, hasDefaultValue) { if (!hasDefaultValue && (this._avoidOptionals || isNonNullType)) { return fieldName; } return `${fieldName}?`; } formatTypeString(fieldType, isNonNullType, hasDefaultValue) { if (!hasDefaultValue && isNonNullType) { return this.clearOptional(fieldType); } return fieldType; } getPunctuation() { return ';'; } } class TsVisitor extends visitorPluginCommon.BaseTypesVisitor { constructor(schema, pluginConfig, additionalConfig = {}) { super(schema, pluginConfig, { noExport: visitorPluginCommon.getConfigValue(pluginConfig.noExport, false), avoidOptionals: visitorPluginCommon.normalizeAvoidOptionals(visitorPluginCommon.getConfigValue(pluginConfig.avoidOptionals, false)), maybeValue: visitorPluginCommon.getConfigValue(pluginConfig.maybeValue, 'T | null'), constEnums: visitorPluginCommon.getConfigValue(pluginConfig.constEnums, false), enumsAsTypes: visitorPluginCommon.getConfigValue(pluginConfig.enumsAsTypes, false), futureProofEnums: visitorPluginCommon.getConfigValue(pluginConfig.futureProofEnums, false), enumsAsConst: visitorPluginCommon.getConfigValue(pluginConfig.enumsAsConst, false), numericEnums: visitorPluginCommon.getConfigValue(pluginConfig.numericEnums, false), onlyOperationTypes: visitorPluginCommon.getConfigValue(pluginConfig.onlyOperationTypes, false), immutableTypes: visitorPluginCommon.getConfigValue(pluginConfig.immutableTypes, false), ...(additionalConfig || {}), }); autoBind(this); const enumNames = Object.values(schema.getTypeMap()) .filter(graphql.isEnumType) .map(type => type.name); this.setArgumentsTransformer(new TypeScriptOperationVariablesToObject(this.scalars, this.convertName, this.config.avoidOptionals.object, this.config.immutableTypes, null, enumNames, pluginConfig.enumPrefix, this.config.enumValues)); this.setDeclarationBlockConfig({ enumNameValueSeparator: ' =', ignoreExport: this.config.noExport, }); } getWrapperDefinitions() { const definitions = [this.getMaybeValue()]; if (this.config.wrapFieldDefinitions) { definitions.push(this.getFieldWrapperValue()); } return definitions; } getMaybeValue() { return `${this.getExportPrefix()}type Maybe<T> = ${this.config.maybeValue};`; } clearOptional(str) { if (str.startsWith('Maybe')) { return str.replace(/Maybe<(.*?)>$/, '$1'); } return str; } getExportPrefix() { if (this.config.noExport) { return ''; } return super.getExportPrefix(); } NamedType(node, key, parent, path, ancestors) { return `Maybe<${super.NamedType(node, key, parent, path, ancestors)}>`; } ListType(node) { return `Maybe<${super.ListType(node)}>`; } wrapWithListType(str) { return `${this.config.immutableTypes ? 'ReadonlyArray' : 'Array'}<${str}>`; } NonNullType(node) { const baseValue = super.NonNullType(node); return this.clearOptional(baseValue); } FieldDefinition(node, key, parent) { const typeString = node.type; const originalFieldNode = parent[key]; const addOptionalSign = !this.config.avoidOptionals.field && originalFieldNode.type.kind !== graphql.Kind.NON_NULL_TYPE; const comment = this.getFieldComment(node); const { type } = this.config.declarationKind; return (comment + visitorPluginCommon.indent(`${this.config.immutableTypes ? 'readonly ' : ''}${node.name}${addOptionalSign ? '?' : ''}: ${typeString}${this.getPunctuation(type)}`)); } InputValueDefinition(node, key, parent) { const originalFieldNode = parent[key]; const addOptionalSign = !this.config.avoidOptionals.inputValue && originalFieldNode.type.kind !== graphql.Kind.NON_NULL_TYPE; const comment = visitorPluginCommon.transformComment(node.description, 1); const { type } = this.config.declarationKind; return (comment + visitorPluginCommon.indent(`${this.config.immutableTypes ? 'readonly ' : ''}${node.name}${addOptionalSign ? '?' : ''}: ${node.type}${this.getPunctuation(type)}`)); } EnumTypeDefinition(node) { const enumName = node.name; // In case of mapped external enum string if (this.config.enumValues[enumName] && this.config.enumValues[enumName].sourceFile) { return `export { ${this.config.enumValues[enumName].typeIdentifier} };\n`; } const getValueFromConfig = (enumValue) => { if (this.config.enumValues[enumName] && this.config.enumValues[enumName].mappedValues && typeof this.config.enumValues[enumName].mappedValues[enumValue] !== 'undefined') { return this.config.enumValues[enumName].mappedValues[enumValue]; } return null; }; const withFutureAddedValue = [ this.config.futureProofEnums ? [visitorPluginCommon.indent('| ' + visitorPluginCommon.wrapWithSingleQuotes('%future added value'))] : [], ]; const enumTypeName = this.convertName(node, { useTypesPrefix: this.config.enumPrefix }); if (this.config.enumsAsTypes) { return new visitorPluginCommon.DeclarationBlock(this._declarationBlockConfig) .export() .asKind('type') .withComment(node.description) .withName(enumTypeName) .withContent('\n' + node.values .map(enumOption => { const name = enumOption.name; const enumValue = getValueFromConfig(name) || name; const comment = visitorPluginCommon.transformComment(enumOption.description, 1); return comment + visitorPluginCommon.indent('| ' + visitorPluginCommon.wrapWithSingleQuotes(enumValue)); }) .concat(...withFutureAddedValue) .join('\n')).string; } if (this.config.numericEnums) { const block = new visitorPluginCommon.DeclarationBlock(this._declarationBlockConfig) .export() .withComment(node.description) .withName(enumTypeName) .asKind('enum') .withBlock(node.values .map((enumOption, i) => { const valueFromConfig = getValueFromConfig(enumOption.name); const enumValue = valueFromConfig || i; const comment = visitorPluginCommon.transformComment(enumOption.description, 1); return comment + visitorPluginCommon.indent(enumOption.name) + ` = ${enumValue}`; }) .concat(...withFutureAddedValue) .join(',\n')).string; return block; } if (this.config.enumsAsConst) { const typeName = `export type ${enumTypeName} = typeof ${enumTypeName}[keyof typeof ${enumTypeName}];`; const enumAsConst = new visitorPluginCommon.DeclarationBlock({ ...this._declarationBlockConfig, blockTransformer: block => { return block + ' as const'; }, }) .export() .asKind('const') .withName(enumTypeName) .withComment(node.description) .withBlock(node.values .map(enumOption => { const optionName = this.convertName(enumOption, { useTypesPrefix: false, transformUnderscore: true }); const comment = visitorPluginCommon.transformComment(enumOption.description, 1); const name = enumOption.name; const enumValue = getValueFromConfig(name) || name; return comment + visitorPluginCommon.indent(`${optionName}: ${visitorPluginCommon.wrapWithSingleQuotes(enumValue)}`); }) .join(',\n')).string; return [enumAsConst, typeName].join('\n'); } return new visitorPluginCommon.DeclarationBlock(this._declarationBlockConfig) .export() .asKind(this.config.constEnums ? 'const enum' : 'enum') .withName(enumTypeName) .withComment(node.description) .withBlock(this.buildEnumValuesBlock(enumName, node.values)).string; } getPunctuation(declarationKind) { return ';'; } } class TsIntrospectionVisitor extends TsVisitor { constructor(schema, pluginConfig = {}, typesToInclude) { super(schema, pluginConfig); this.typesToInclude = []; this.typesToInclude = typesToInclude; autoBind(this); } DirectiveDefinition() { return null; } ObjectTypeDefinition(node, key, parent) { const name = node.name; if (this.typesToInclude.some(type => type.name === name)) { return super.ObjectTypeDefinition(node, key, parent); } return null; } EnumTypeDefinition(node) { const name = node.name; if (this.typesToInclude.some(type => type.name === name)) { return super.EnumTypeDefinition(node); } return null; } } const plugin = (schema, documents, config) => { const visitor = new TsVisitor(schema, config); const printedSchema = graphql.printSchema(schema); const astNode = graphql.parse(printedSchema); const visitorResult = graphql.visit(astNode, { leave: visitor }); const introspectionDefinitions = includeIntrospectionDefinitions(schema, documents, config); const scalars = visitor.scalarsDefinition; return { prepend: [...visitor.getEnumsImports(), ...visitor.getScalarsImports(), ...visitor.getWrapperDefinitions()], content: [scalars, ...visitorResult.definitions, ...introspectionDefinitions].join('\n'), }; }; function includeIntrospectionDefinitions(schema, documents, config) { const typeInfo = new graphql.TypeInfo(schema); const usedTypes = []; const documentsVisitor = graphql.visitWithTypeInfo(typeInfo, { Field() { const type = graphql.getNamedType(typeInfo.getType()); if (graphql.isIntrospectionType(type) && !usedTypes.includes(type)) { usedTypes.push(type); } }, }); documents.forEach(doc => graphql.visit(doc.document, documentsVisitor)); const typesToInclude = []; usedTypes.forEach(type => { collectTypes(type); }); const visitor = new TsIntrospectionVisitor(schema, config, typesToInclude); const result = graphql.visit(graphql.parse(graphql.printIntrospectionSchema(schema)), { leave: visitor }); // recursively go through each `usedTypes` and their children and collect all used types // we don't care about Interfaces, Unions and others, but Objects and Enums function collectTypes(type) { if (typesToInclude.includes(type)) { return; } typesToInclude.push(type); if (graphql.isObjectType(type)) { const fields = type.getFields(); Object.keys(fields).forEach(key => { const field = fields[key]; const type = graphql.getNamedType(field.type); collectTypes(type); }); } } return result.definitions; } exports.TsIntrospectionVisitor = TsIntrospectionVisitor; exports.TsVisitor = TsVisitor; exports.TypeScriptOperationVariablesToObject = TypeScriptOperationVariablesToObject; exports.includeIntrospectionDefinitions = includeIntrospectionDefinitions; exports.plugin = plugin; //# sourceMappingURL=index.cjs.js.map