UNPKG

@graphql-codegen/visitor-plugin-common

Version:
1,266 lines (1,255 loc) • 148 kB
import { Kind, isScalarType, isListType, isNonNullType, isObjectType, isAbstractType, isEnumType, isInterfaceType, GraphQLObjectType, isUnionType, print, isTypeSubTypeOf, SchemaMetaFieldDef, TypeMetaFieldDef } from 'graphql'; import { resolveExternalModuleAndFn, DetailedError, ApolloFederation, getBaseType, oldVisit, removeNonNullWrapper } from '@graphql-codegen/plugin-helpers'; import { pascalCase } from 'change-case-all'; import { resolve, relative, dirname, join, isAbsolute, extname, basename } from 'path'; import parse from 'parse-filepath'; import autoBind from 'auto-bind'; import { getRootTypeNames as getRootTypeNames$1, getRootTypes } from '@graphql-tools/utils'; import { DepGraph } from 'dependency-graph'; import gqlTag from 'graphql-tag'; import { optimizeDocumentNode } from '@graphql-tools/optimize'; import { createHash } from 'crypto'; import { optimizeDocuments } from '@graphql-tools/relay-operation-optimizer'; const DEFAULT_SCALARS = { ID: 'string', String: 'string', Boolean: 'boolean', Int: 'number', Float: 'number', }; function isExternalMapperType(m) { return !!m.import; } var MapperKind; (function (MapperKind) { MapperKind[MapperKind["Namespace"] = 0] = "Namespace"; MapperKind[MapperKind["Default"] = 1] = "Default"; MapperKind[MapperKind["Regular"] = 2] = "Regular"; })(MapperKind || (MapperKind = {})); function prepareLegacy(mapper) { const items = mapper.split('#'); const isNamespace = items.length === 3; const isDefault = items[1].trim() === 'default' || items[1].startsWith('default '); const hasAlias = items[1].includes(' as '); return { items, isDefault, isNamespace, hasAlias, }; } function prepare(mapper) { const [source, path] = mapper.split('#'); const isNamespace = path.includes('.'); const isDefault = path.trim() === 'default' || path.startsWith('default '); const hasAlias = path.includes(' as '); return { items: isNamespace ? [source, ...path.split('.')] : [source, path], isDefault, isNamespace, hasAlias, }; } function isLegacyMode(mapper) { return mapper.split('#').length === 3; } function parseMapper(mapper, gqlTypeName = null, suffix) { if (isExternalMapper(mapper)) { const { isNamespace, isDefault, hasAlias, items } = isLegacyMode(mapper) ? prepareLegacy(mapper) : prepare(mapper); const mapperKind = isNamespace ? MapperKind.Namespace : isDefault ? MapperKind.Default : MapperKind.Regular; function handleAlias(isDefault = false) { const [importedType, aliasType] = items[1].split(/\s+as\s+/); const type = maybeSuffix(aliasType); return { importElement: isDefault ? type : `${importedType} as ${type}`, type, }; } function maybeSuffix(type) { if (suffix) { return addSuffix(type, suffix); } return type; } function handle() { switch (mapperKind) { // ./my/module#Namespace#Identifier case MapperKind.Namespace: { const [, ns, identifier] = items; return { type: `${ns}.${identifier}`, importElement: ns, }; } case MapperKind.Default: { // ./my/module#default as alias if (hasAlias) { return handleAlias(true); } const type = maybeSuffix(`${gqlTypeName}`); // ./my/module#default return { importElement: type, type, }; } case MapperKind.Regular: { // ./my/module#Identifier as alias if (hasAlias) { return handleAlias(); } const identifier = items[1]; const type = maybeSuffix(identifier); // ./my/module#Identifier return { type, importElement: suffix ? `${identifier} as ${type}` : type, }; } } } const { type, importElement } = handle(); return { default: isDefault, isExternal: true, source: items[0], type, import: importElement.replace(/<(.*?)>/g, ''), }; } return { isExternal: false, type: mapper, }; } function addSuffix(element, suffix) { const generic = element.indexOf('<'); if (generic === -1) { return `${element}${suffix}`; } return `${element.slice(0, generic)}${suffix}${element.slice(generic)}`; } function isExternalMapper(value) { return value.includes('#'); } function transformMappers(rawMappers, mapperTypeSuffix) { const result = {}; Object.keys(rawMappers).forEach(gqlTypeName => { const mapperDef = rawMappers[gqlTypeName]; const parsedMapper = parseMapper(mapperDef, gqlTypeName, mapperTypeSuffix); result[gqlTypeName] = parsedMapper; }); return result; } function transformDirectiveArgumentAndInputFieldMappings(rawDirectiveArgumentAndInputFieldMappings, directiveArgumentAndInputFieldMappingTypeSuffix) { const result = {}; Object.keys(rawDirectiveArgumentAndInputFieldMappings).forEach(directive => { const mapperDef = rawDirectiveArgumentAndInputFieldMappings[directive]; const parsedMapper = parseMapper(mapperDef, directive, directiveArgumentAndInputFieldMappingTypeSuffix); result[directive] = parsedMapper; }); return result; } function buildMapperImport(source, types, useTypeImports) { if (!types || types.length === 0) { return null; } const defaultType = types.find(t => t.asDefault === true); let namedTypes = types.filter(t => !t.asDefault); if (useTypeImports) { if (defaultType) { // default as Baz namedTypes = [{ identifier: `default as ${defaultType.identifier}` }, ...namedTypes]; } // { Foo, Bar as BarModel } const namedImports = namedTypes.length ? `{ ${namedTypes.map(t => t.identifier).join(', ')} }` : ''; // { default as Baz, Foo, Bar as BarModel } return `import type ${[namedImports].filter(Boolean).join(', ')} from '${source}';`; } // { Foo, Bar as BarModel } const namedImports = namedTypes.length ? `{ ${namedTypes.map(t => t.identifier).join(', ')} }` : ''; // Baz const defaultImport = defaultType ? defaultType.identifier : ''; // Baz, { Foo, Bar as BarModel } return `import ${[defaultImport, namedImports].filter(Boolean).join(', ')} from '${source}';`; } const getConfigValue = (value, defaultValue) => { if (value === null || value === undefined) { return defaultValue; } return value; }; function quoteIfNeeded(array, joinWith = ' & ') { if (array.length === 0) { return ''; } else if (array.length === 1) { return array[0]; } else { return `(${array.join(joinWith)})`; } } function block(array) { return array && array.length !== 0 ? '{\n' + array.join('\n') + '\n}' : ''; } function wrapWithSingleQuotes(value, skipNumericCheck = false) { if (skipNumericCheck) { if (typeof value === 'number') { return `${value}`; } else { return `'${value}'`; } } if (typeof value === 'number' || (typeof value === 'string' && !isNaN(parseInt(value)) && parseFloat(value).toString() === value)) { return `${value}`; } return `'${value}'`; } function breakLine(str) { return str + '\n'; } function indent(str, count = 1) { return new Array(count).fill(' ').join('') + str; } function indentMultiline(str, count = 1) { const indentation = new Array(count).fill(' ').join(''); const replaceWith = '\n' + indentation; return indentation + str.replace(/\n/g, replaceWith); } function transformComment(comment, indentLevel = 0, disabled = false) { if (!comment || comment === '' || disabled) { return ''; } if (isStringValueNode(comment)) { comment = comment.value; } comment = comment.split('*/').join('*\\/'); let lines = comment.split('\n'); if (lines.length === 1) { return indent(`/** ${lines[0]} */\n`, indentLevel); } lines = ['/**', ...lines.map(line => ` * ${line}`), ' */\n']; return stripTrailingSpaces(lines.map(line => indent(line, indentLevel)).join('\n')); } class DeclarationBlock { constructor(_config) { this._config = _config; this._decorator = null; this._export = false; this._name = null; this._kind = null; this._methodName = null; this._content = null; this._block = null; this._nameGenerics = null; this._comment = null; this._ignoreBlockWrapper = false; this._config = { blockWrapper: '', blockTransformer: block => block, enumNameValueSeparator: ':', ...this._config, }; } withDecorator(decorator) { this._decorator = decorator; return this; } export(exp = true) { if (!this._config.ignoreExport) { this._export = exp; } return this; } asKind(kind) { this._kind = kind; return this; } withComment(comment, disabled = false) { const nonEmptyComment = isStringValueNode(comment) ? !!comment.value : !!comment; if (nonEmptyComment && !disabled) { this._comment = transformComment(comment, 0); } return this; } withMethodCall(methodName, ignoreBlockWrapper = false) { this._methodName = methodName; this._ignoreBlockWrapper = ignoreBlockWrapper; return this; } withBlock(block) { this._block = block; return this; } withContent(content) { this._content = content; return this; } withName(name, generics = null) { this._name = name; this._nameGenerics = generics; return this; } get string() { let result = ''; if (this._decorator) { result += this._decorator + '\n'; } if (this._export) { result += 'export '; } if (this._kind) { let extra = ''; let name = ''; if (['type', 'const', 'var', 'let'].includes(this._kind)) { extra = '= '; } if (this._name) { name = this._name + (this._nameGenerics || '') + ' '; } result += this._kind + ' ' + name + extra; } if (this._block) { if (this._content) { result += this._content; } const blockWrapper = this._ignoreBlockWrapper ? '' : this._config.blockWrapper; const before = '{' + blockWrapper; const after = blockWrapper + '}'; const block = [before, this._block, after].filter(val => !!val).join('\n'); if (this._methodName) { result += `${this._methodName}(${this._config.blockTransformer(block)})`; } else { result += this._config.blockTransformer(block); } } else if (this._content) { result += this._content; } else if (this._kind) { result += this._config.blockTransformer('{}'); } return stripTrailingSpaces((this._comment ? this._comment : '') + result + (this._kind === 'interface' || this._kind === 'enum' || this._kind === 'namespace' || this._kind === 'function' ? '' : ';') + '\n'); } } function getBaseTypeNode(typeNode) { if (typeNode.kind === Kind.LIST_TYPE || typeNode.kind === Kind.NON_NULL_TYPE) { return getBaseTypeNode(typeNode.type); } return typeNode; } function convertNameParts(str, func, removeUnderscore = false) { if (removeUnderscore) { return func(str); } return str .split('_') .map(s => func(s)) .join('_'); } function buildScalarsFromConfig(schema, config, defaultScalarsMapping = DEFAULT_SCALARS, defaultScalarType = 'any') { return buildScalars(schema, config.scalars, defaultScalarsMapping, config.strictScalars ? null : config.defaultScalarType || defaultScalarType); } function buildScalars(schema, scalarsMapping, defaultScalarsMapping = DEFAULT_SCALARS, defaultScalarType = 'any') { const result = {}; Object.keys(defaultScalarsMapping).forEach(name => { result[name] = parseMapper(defaultScalarsMapping[name]); }); if (schema) { const typeMap = schema.getTypeMap(); Object.keys(typeMap) .map(typeName => typeMap[typeName]) .filter(type => isScalarType(type)) .map((scalarType) => { var _a; const { name } = scalarType; if (typeof scalarsMapping === 'string') { const value = parseMapper(scalarsMapping + '#' + name, name); result[name] = value; } else if (scalarsMapping && typeof scalarsMapping[name] === 'string') { const value = parseMapper(scalarsMapping[name], name); result[name] = value; } else if (scalarsMapping && scalarsMapping[name]) { result[name] = { isExternal: false, type: JSON.stringify(scalarsMapping[name]), }; } else if ((_a = scalarType.extensions) === null || _a === void 0 ? void 0 : _a.codegenScalarType) { result[name] = { isExternal: false, type: scalarType.extensions.codegenScalarType, }; } else if (!defaultScalarsMapping[name]) { if (defaultScalarType === null) { throw new Error(`Unknown scalar type ${name}. Please override it using the "scalars" configuration field!`); } result[name] = { isExternal: false, type: defaultScalarType, }; } }); } else if (scalarsMapping) { if (typeof scalarsMapping === 'string') { throw new Error('Cannot use string scalars mapping when building without a schema'); } Object.keys(scalarsMapping).forEach(name => { if (typeof scalarsMapping[name] === 'string') { const value = parseMapper(scalarsMapping[name], name); result[name] = value; } else { result[name] = { isExternal: false, type: JSON.stringify(scalarsMapping[name]), }; } }); } return result; } function isStringValueNode(node) { return node && typeof node === 'object' && node.kind === Kind.STRING; } // will be removed on next release because tools already has it function getRootTypeNames(schema) { return [schema.getQueryType(), schema.getMutationType(), schema.getSubscriptionType()] .filter(t => t) .map(t => t.name); } function stripMapperTypeInterpolation(identifier) { return identifier.trim().replace(/<{.*}>/, ''); } const OMIT_TYPE = 'export type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;'; const REQUIRE_FIELDS_TYPE = `export type RequireFields<T, K extends keyof T> = Omit<T, K> & { [P in K]-?: NonNullable<T[P]> };`; /** * merge selection sets into a new selection set without mutating the inputs. */ function mergeSelectionSets(selectionSet1, selectionSet2) { const newSelections = [...selectionSet1.selections]; for (let selection2 of selectionSet2.selections) { if (selection2.kind === 'FragmentSpread' || selection2.kind === 'InlineFragment') { newSelections.push(selection2); continue; } if (selection2.kind !== 'Field') { throw new TypeError('Invalid state.'); } const match = newSelections.find(selection1 => selection1.kind === 'Field' && getFieldNodeNameValue(selection1) === getFieldNodeNameValue(selection2)); if (match) { // recursively merge all selection sets if (match.kind === 'Field' && match.selectionSet && selection2.selectionSet) { selection2 = { ...selection2, selectionSet: mergeSelectionSets(match.selectionSet, selection2.selectionSet), }; } } newSelections.push(selection2); } return { kind: Kind.SELECTION_SET, selections: newSelections, }; } const getFieldNodeNameValue = (node) => { return (node.alias || node.name).value; }; function separateSelectionSet(selections) { return { fields: selections.filter(s => s.kind === Kind.FIELD), inlines: selections.filter(s => s.kind === Kind.INLINE_FRAGMENT), spreads: selections.filter(s => s.kind === Kind.FRAGMENT_SPREAD), }; } function getPossibleTypes(schema, type) { if (isListType(type) || isNonNullType(type)) { return getPossibleTypes(schema, type.ofType); } else if (isObjectType(type)) { return [type]; } else if (isAbstractType(type)) { return schema.getPossibleTypes(type); } return []; } function hasConditionalDirectives(field) { var _a; const CONDITIONAL_DIRECTIVES = ['skip', 'include']; return (_a = field.directives) === null || _a === void 0 ? void 0 : _a.some(directive => CONDITIONAL_DIRECTIVES.includes(directive.name.value)); } function wrapTypeWithModifiers(baseType, type, options) { let currentType = type; const modifiers = []; while (currentType) { if (isNonNullType(currentType)) { currentType = currentType.ofType; } else { modifiers.push(options.wrapOptional); } if (isListType(currentType)) { modifiers.push(options.wrapArray); currentType = currentType.ofType; } else { break; } } return modifiers.reduceRight((result, modifier) => modifier(result), baseType); } function removeDescription(nodes) { return nodes.map(node => ({ ...node, description: undefined })); } function wrapTypeNodeWithModifiers(baseType, typeNode) { switch (typeNode.kind) { case Kind.NAMED_TYPE: { return `Maybe<${baseType}>`; } case Kind.NON_NULL_TYPE: { const innerType = wrapTypeNodeWithModifiers(baseType, typeNode.type); return clearOptional(innerType); } case Kind.LIST_TYPE: { const innerType = wrapTypeNodeWithModifiers(baseType, typeNode.type); return `Maybe<Array<${innerType}>>`; } } } function clearOptional(str) { const rgx = new RegExp(`^Maybe<(.*?)>$`, 'i'); if (str.startsWith(`Maybe`)) { return str.replace(rgx, '$1'); } return str; } function stripTrailingSpaces(str) { return str.replace(/ +\n/g, '\n'); } function getKind(node) { if (typeof node === 'string') { return 'typeNames'; } if (['EnumValueDefinition', 'EnumValue'].includes(node.kind)) { return 'enumValues'; } return 'typeNames'; } function getName(node) { if (node == null) { return undefined; } if (typeof node === 'string') { return node; } switch (node.kind) { case 'OperationDefinition': case 'Variable': case 'Argument': case 'FragmentSpread': case 'FragmentDefinition': case 'ObjectField': case 'Directive': case 'NamedType': case 'ScalarTypeDefinition': case 'ObjectTypeDefinition': case 'FieldDefinition': case 'InputValueDefinition': case 'InterfaceTypeDefinition': case 'UnionTypeDefinition': case 'EnumTypeDefinition': case 'EnumValueDefinition': case 'InputObjectTypeDefinition': case 'DirectiveDefinition': { return getName(node.name); } case 'Name': { return node.value; } case 'Field': { return getName(node.alias || node.name); } case 'VariableDefinition': { return getName(node.variable); } } return undefined; } function convertFactory(config) { function resolveConventionName(type) { if (!config.namingConvention) { return (str, opts = {}) => { return convertNameParts(str, pascalCase, getConfigValue((opts || {}).transformUnderscore, false)); }; } if (typeof config.namingConvention === 'string') { if (config.namingConvention === 'keep') { return str => str; } return (str, opts = {}) => { return convertNameParts(str, resolveExternalModuleAndFn(config.namingConvention), getConfigValue((opts || {}).transformUnderscore, false)); }; } if (typeof config.namingConvention === 'function') { return (str, opts = {}) => { return convertNameParts(str, config.namingConvention, getConfigValue((opts || {}).transformUnderscore, false)); }; } if (typeof config.namingConvention === 'object' && config.namingConvention[type] === 'keep') { return str => str; } if (typeof config.namingConvention === 'object') { if (!config.namingConvention[type]) { return (str, opts = {}) => { const transformUnderscore = config.namingConvention.transformUnderscore || (opts || {}).transformUnderscore; return convertNameParts(str, pascalCase, getConfigValue(transformUnderscore, false)); }; } return (str, opts = {}) => { return convertNameParts(str, resolveExternalModuleAndFn(config.namingConvention[type]), getConfigValue((opts || {}).transformUnderscore, true)); }; } return config.namingConvention[type]; } return (node, opts) => { const prefix = opts && opts.prefix; const suffix = opts && opts.suffix; const kind = getKind(node); const str = [prefix || '', getName(node), suffix || ''].join(''); return resolveConventionName(kind)(str, opts); }; } function escapeString(str) { return str.replace(/\\/g, '\\\\').replace(/\n/g, '\\n').replace(/'/g, "\\'"); } function parseEnumValues({ schema, mapOrStr = {}, ignoreEnumValuesFromSchema, }) { const allTypes = schema.getTypeMap(); const allEnums = Object.keys(allTypes).filter(t => isEnumType(allTypes[t])); if (typeof mapOrStr === 'object') { if (!ignoreEnumValuesFromSchema) { for (const enumTypeName of allEnums) { const enumType = schema.getType(enumTypeName); for (const { name, value } of enumType.getValues()) { if (value && value !== name) { mapOrStr[enumTypeName] = mapOrStr[enumTypeName] || {}; if (typeof mapOrStr[enumTypeName] !== 'string' && !mapOrStr[enumTypeName][name]) { mapOrStr[enumTypeName][name] = typeof value === 'string' ? escapeString(value) : value; } } } } } const invalidMappings = Object.keys(mapOrStr).filter(gqlName => !allEnums.includes(gqlName)); if (invalidMappings.length > 0) { throw new DetailedError(`Invalid 'enumValues' mapping!`, `The following types does not exist in your GraphQL schema: ${invalidMappings.join(', ')}`); } return Object.keys(mapOrStr).reduce((prev, gqlIdentifier) => { const pointer = mapOrStr[gqlIdentifier]; if (typeof pointer === 'string') { const mapper = parseMapper(pointer, gqlIdentifier); return { ...prev, [gqlIdentifier]: { isDefault: mapper.isExternal && mapper.default, typeIdentifier: gqlIdentifier, sourceFile: mapper.isExternal ? mapper.source : null, sourceIdentifier: mapper.type, importIdentifier: mapper.isExternal ? mapper.import : null, mappedValues: null, }, }; } else if (typeof pointer === 'object') { return { ...prev, [gqlIdentifier]: { isDefault: false, typeIdentifier: gqlIdentifier, sourceFile: null, sourceIdentifier: null, importIdentifier: null, mappedValues: pointer, }, }; } else { throw new DetailedError(`Invalid "enumValues" configuration`, `Enum "${gqlIdentifier}": expected string or object (with enum values mapping)`); } }, {}); } else if (typeof mapOrStr === 'string') { return allEnums .filter(enumName => !enumName.startsWith('__')) .reduce((prev, enumName) => { return { ...prev, [enumName]: { isDefault: false, typeIdentifier: enumName, sourceFile: mapOrStr, sourceIdentifier: enumName, importIdentifier: enumName, mappedValues: null, }, }; }, {}); } return {}; } const DEFAULT_DECLARATION_KINDS = { directive: 'type', scalar: 'type', input: 'type', type: 'type', interface: 'type', arguments: 'type', }; function normalizeDeclarationKind(declarationKind) { if (typeof declarationKind === 'string') { return { directive: declarationKind, scalar: declarationKind, input: declarationKind, type: declarationKind, interface: declarationKind, arguments: declarationKind, }; } return { ...DEFAULT_DECLARATION_KINDS, ...declarationKind, }; } const DEFAULT_AVOID_OPTIONALS = { object: false, inputValue: false, field: false, defaultValue: false, resolvers: false, }; function normalizeAvoidOptionals(avoidOptionals) { if (typeof avoidOptionals === 'boolean') { return { object: avoidOptionals, inputValue: avoidOptionals, field: avoidOptionals, defaultValue: avoidOptionals, resolvers: avoidOptionals, }; } return { ...DEFAULT_AVOID_OPTIONALS, ...avoidOptionals, }; } function generateFragmentImportStatement(statement, kind) { const { importSource: fragmentImportSource, ...rest } = statement; const { identifiers, path, namespace } = fragmentImportSource; const importSource = { identifiers: identifiers .filter(fragmentImport => kind === 'both' || kind === fragmentImport.kind) .map(({ name }) => name), path, namespace, }; return generateImportStatement({ importSource, ...rest, typesImport: kind === 'type' ? statement.typesImport : false, }); } function generateImportStatement(statement) { const { baseDir, importSource, outputPath, typesImport } = statement; const importPath = resolveImportPath(baseDir, outputPath, importSource.path); const importNames = importSource.identifiers && importSource.identifiers.length ? `{ ${Array.from(new Set(importSource.identifiers)).join(', ')} }` : '*'; const importAlias = importSource.namespace ? ` as ${importSource.namespace}` : ''; const importStatement = typesImport ? 'import type' : 'import'; return `${importStatement} ${importNames}${importAlias} from '${importPath}';${importAlias ? '\n' : ''}`; } function resolveImportPath(baseDir, outputPath, sourcePath) { const shouldAbsolute = !sourcePath.startsWith('~'); if (shouldAbsolute) { const absGeneratedFilePath = resolve(baseDir, outputPath); const absImportFilePath = resolve(baseDir, sourcePath); return resolveRelativeImport(absGeneratedFilePath, absImportFilePath); } else { return sourcePath.replace(`~`, ''); } } function resolveRelativeImport(from, to) { if (!isAbsolute(from)) { throw new Error(`Argument 'from' must be an absolute path, '${from}' given.`); } if (!isAbsolute(to)) { throw new Error(`Argument 'to' must be an absolute path, '${to}' given.`); } return fixLocalFilePath(clearExtension(relative(dirname(from), to))); } function resolveImportSource(source) { return typeof source === 'string' ? { path: source } : source; } function clearExtension(path) { const parsedPath = parse(path); return join(parsedPath.dir, parsedPath.name).replace(/\\/g, '/'); } function fixLocalFilePath(path) { return !path.startsWith('..') ? `./${path}` : path; } class BaseVisitor { constructor(rawConfig, additionalConfig) { var _a; this._declarationBlockConfig = {}; this._parsedConfig = { convert: convertFactory(rawConfig), typesPrefix: rawConfig.typesPrefix || '', typesSuffix: rawConfig.typesSuffix || '', externalFragments: rawConfig.externalFragments || [], fragmentImports: rawConfig.fragmentImports || [], addTypename: !rawConfig.skipTypename, nonOptionalTypename: !!rawConfig.nonOptionalTypename, useTypeImports: !!rawConfig.useTypeImports, dedupeFragments: !!rawConfig.dedupeFragments, allowEnumStringTypes: !!rawConfig.allowEnumStringTypes, inlineFragmentTypes: (_a = rawConfig.inlineFragmentTypes) !== null && _a !== void 0 ? _a : 'inline', ...(additionalConfig || {}), }; this.scalars = {}; Object.keys(this.config.scalars || {}).forEach(key => { this.scalars[key] = this.config.scalars[key].type; }); autoBind(this); } getVisitorKindContextFromAncestors(ancestors) { if (!ancestors) { return []; } return ancestors.map(t => t.kind).filter(Boolean); } get config() { return this._parsedConfig; } convertName(node, options) { const useTypesPrefix = typeof (options && options.useTypesPrefix) === 'boolean' ? options.useTypesPrefix : true; const useTypesSuffix = typeof (options && options.useTypesSuffix) === 'boolean' ? options.useTypesSuffix : true; let convertedName = ''; if (useTypesPrefix) { convertedName += this.config.typesPrefix; } convertedName += this.config.convert(node, options); if (useTypesSuffix) { convertedName += this.config.typesSuffix; } return convertedName; } getOperationSuffix(node, operationType) { const { omitOperationSuffix = false, dedupeOperationSuffix = false } = this.config; const operationName = typeof node === 'string' ? node : node.name ? node.name.value : ''; return omitOperationSuffix ? '' : dedupeOperationSuffix && operationName.toLowerCase().endsWith(operationType.toLowerCase()) ? '' : operationType; } getFragmentSuffix(node) { return this.getOperationSuffix(node, 'Fragment'); } getFragmentName(node) { return this.convertName(node, { suffix: this.getFragmentSuffix(node), useTypesPrefix: false, }); } getFragmentVariableName(node) { const { omitOperationSuffix = false, dedupeOperationSuffix = false, fragmentVariableSuffix = 'FragmentDoc', fragmentVariablePrefix = '', } = this.config; const fragmentName = typeof node === 'string' ? node : node.name.value; const suffix = omitOperationSuffix ? '' : dedupeOperationSuffix && fragmentName.toLowerCase().endsWith('fragment') && fragmentVariableSuffix.toLowerCase().startsWith('fragment') ? fragmentVariableSuffix.substring('fragment'.length) : fragmentVariableSuffix; return this.convertName(node, { prefix: fragmentVariablePrefix, suffix, useTypesPrefix: false, }); } getPunctuation(_declarationKind) { return ''; } } class OperationVariablesToObject { constructor(_scalars, _convertName, _namespacedImportName = null, _enumNames = [], _enumPrefix = true, _enumValues = {}, _applyCoercion = false, _directiveArgumentAndInputFieldMappings = {}) { this._scalars = _scalars; this._convertName = _convertName; this._namespacedImportName = _namespacedImportName; this._enumNames = _enumNames; this._enumPrefix = _enumPrefix; this._enumValues = _enumValues; this._applyCoercion = _applyCoercion; this._directiveArgumentAndInputFieldMappings = _directiveArgumentAndInputFieldMappings; autoBind(this); } getName(node) { if (node.name) { if (typeof node.name === 'string') { return node.name; } return node.name.value; } else if (node.variable) { return node.variable.name.value; } return null; } transform(variablesNode) { if (!variablesNode || variablesNode.length === 0) { return null; } return (variablesNode.map(variable => indent(this.transformVariable(variable))).join(`${this.getPunctuation()}\n`) + this.getPunctuation()); } getScalar(name) { const prefix = this._namespacedImportName ? `${this._namespacedImportName}.` : ''; return `${prefix}Scalars['${name}']`; } getDirectiveMapping(name) { return `DirectiveArgumentAndInputFieldMappings['${name}']`; } getDirectiveOverrideType(directives) { if (!this._directiveArgumentAndInputFieldMappings) return null; const type = directives .map(directive => { const directiveName = directive.name.value; if (this._directiveArgumentAndInputFieldMappings[directiveName]) { return this.getDirectiveMapping(directiveName); } return null; }) .reverse() .find(a => !!a); return type || null; } transformVariable(variable) { let typeValue = null; const prefix = this._namespacedImportName ? `${this._namespacedImportName}.` : ''; if (typeof variable.type === 'string') { typeValue = variable.type; } else { const baseType = getBaseTypeNode(variable.type); const overrideType = variable.directives ? this.getDirectiveOverrideType(variable.directives) : null; const typeName = baseType.name.value; if (overrideType) { typeValue = overrideType; } else if (this._scalars[typeName]) { typeValue = this.getScalar(typeName); } else if (this._enumValues[typeName] && this._enumValues[typeName].sourceFile) { typeValue = this._enumValues[typeName].typeIdentifier || this._enumValues[typeName].sourceIdentifier; } else { typeValue = `${prefix}${this._convertName(baseType, { useTypesPrefix: this._enumNames.includes(typeName) ? this._enumPrefix : true, })}`; } } const fieldName = this.getName(variable); const fieldType = this.wrapAstTypeWithModifiers(typeValue, variable.type, this._applyCoercion); const hasDefaultValue = variable.defaultValue != null && typeof variable.defaultValue !== 'undefined'; const isNonNullType = variable.type.kind === Kind.NON_NULL_TYPE; const formattedFieldString = this.formatFieldString(fieldName, isNonNullType, hasDefaultValue); const formattedTypeString = this.formatTypeString(fieldType, isNonNullType, hasDefaultValue); return `${formattedFieldString}: ${formattedTypeString}`; } wrapAstTypeWithModifiers(_baseType, _typeNode, _applyCoercion) { throw new Error(`You must override "wrapAstTypeWithModifiers" of OperationVariablesToObject!`); } formatFieldString(fieldName, isNonNullType, _hasDefaultValue) { return fieldName; } formatTypeString(fieldType, isNonNullType, hasDefaultValue) { const prefix = this._namespacedImportName ? `${this._namespacedImportName}.` : ''; if (hasDefaultValue) { return `${prefix}Maybe<${fieldType}>`; } return fieldType; } getPunctuation() { return ','; } } class BaseTypesVisitor extends BaseVisitor { constructor(_schema, rawConfig, additionalConfig, defaultScalars = DEFAULT_SCALARS) { var _a; super(rawConfig, { enumPrefix: getConfigValue(rawConfig.enumPrefix, true), onlyEnums: getConfigValue(rawConfig.onlyEnums, false), onlyOperationTypes: getConfigValue(rawConfig.onlyOperationTypes, false), addUnderscoreToArgsType: getConfigValue(rawConfig.addUnderscoreToArgsType, false), enumValues: parseEnumValues({ schema: _schema, mapOrStr: rawConfig.enumValues, ignoreEnumValuesFromSchema: rawConfig.ignoreEnumValuesFromSchema, }), declarationKind: normalizeDeclarationKind(rawConfig.declarationKind), scalars: buildScalarsFromConfig(_schema, rawConfig, defaultScalars), fieldWrapperValue: getConfigValue(rawConfig.fieldWrapperValue, 'T'), wrapFieldDefinitions: getConfigValue(rawConfig.wrapFieldDefinitions, false), entireFieldWrapperValue: getConfigValue(rawConfig.entireFieldWrapperValue, 'T'), wrapEntireDefinitions: getConfigValue(rawConfig.wrapEntireFieldDefinitions, false), ignoreEnumValuesFromSchema: getConfigValue(rawConfig.ignoreEnumValuesFromSchema, false), directiveArgumentAndInputFieldMappings: transformDirectiveArgumentAndInputFieldMappings((_a = rawConfig.directiveArgumentAndInputFieldMappings) !== null && _a !== void 0 ? _a : {}, rawConfig.directiveArgumentAndInputFieldMappingTypeSuffix), ...additionalConfig, }); this._schema = _schema; // Note: Missing directive mappers but not a problem since always overriden by implementors this._argumentsTransformer = new OperationVariablesToObject(this.scalars, this.convertName); } getExportPrefix() { return 'export '; } getFieldWrapperValue() { if (this.config.fieldWrapperValue) { return `${this.getExportPrefix()}type FieldWrapper<T> = ${this.config.fieldWrapperValue};`; } return ''; } getEntireFieldWrapperValue() { if (this.config.entireFieldWrapperValue) { return `${this.getExportPrefix()}type EntireFieldWrapper<T> = ${this.config.entireFieldWrapperValue};`; } return ''; } getScalarsImports() { return Object.keys(this.config.scalars) .map(enumName => { const mappedValue = this.config.scalars[enumName]; if (mappedValue.isExternal) { return this._buildTypeImport(mappedValue.import, mappedValue.source, mappedValue.default); } return null; }) .filter(a => a); } getDirectiveArgumentAndInputFieldMappingsImports() { return Object.keys(this.config.directiveArgumentAndInputFieldMappings) .map(directive => { const mappedValue = this.config.directiveArgumentAndInputFieldMappings[directive]; if (mappedValue.isExternal) { return this._buildTypeImport(mappedValue.import, mappedValue.source, mappedValue.default); } return null; }) .filter(a => a); } get scalarsDefinition() { if (this.config.onlyEnums) return ''; const allScalars = Object.keys(this.config.scalars).map(scalarName => { const scalarValue = this.config.scalars[scalarName].type; const scalarType = this._schema.getType(scalarName); const comment = scalarType && scalarType.astNode && scalarType.description ? transformComment(scalarType.description, 1) : ''; const { scalar } = this._parsedConfig.declarationKind; return comment + indent(`${scalarName}: ${scalarValue}${this.getPunctuation(scalar)}`); }); return new DeclarationBlock(this._declarationBlockConfig) .export() .asKind(this._parsedConfig.declarationKind.scalar) .withName('Scalars') .withComment('All built-in and custom scalars, mapped to their actual values') .withBlock(allScalars.join('\n')).string; } get directiveArgumentAndInputFieldMappingsDefinition() { const directiveEntries = Object.entries(this.config.directiveArgumentAndInputFieldMappings); if (directiveEntries.length === 0) { return ''; } const allDirectives = []; for (const [directiveName, parsedMapper] of directiveEntries) { const directiveType = this._schema.getDirective(directiveName); const comment = (directiveType === null || directiveType === void 0 ? void 0 : directiveType.astNode) && directiveType.description ? transformComment(directiveType.description, 1) : ''; const { directive } = this._parsedConfig.declarationKind; allDirectives.push(comment + indent(`${directiveName}: ${parsedMapper.type}${this.getPunctuation(directive)}`)); } return new DeclarationBlock(this._declarationBlockConfig) .export() .asKind(this._parsedConfig.declarationKind.directive) .withName('DirectiveArgumentAndInputFieldMappings') .withComment('Type overrides using directives') .withBlock(allDirectives.join('\n')).string; } setDeclarationBlockConfig(config) { this._declarationBlockConfig = config; } setArgumentsTransformer(argumentsTransfomer) { this._argumentsTransformer = argumentsTransfomer; } NonNullType(node) { const asString = node.type; return asString; } getInputObjectDeclarationBlock(node) { return new DeclarationBlock(this._declarationBlockConfig) .export() .asKind(this._parsedConfig.declarationKind.input) .withName(this.convertName(node)) .withComment(node.description) .withBlock(node.fields.join('\n')); } InputObjectTypeDefinition(node) { if (this.config.onlyEnums) return ''; return this.getInputObjectDeclarationBlock(node).string; } InputValueDefinition(node) { if (this.config.onlyEnums) return ''; const comment = transformComment(node.description, 1); const { input } = this._parsedConfig.declarationKind; let type = node.type; if (node.directives && this.config.directiveArgumentAndInputFieldMappings) { type = this._getDirectiveOverrideType(node.directives) || type; } return comment + indent(`${node.name}: ${type}${this.getPunctuation(input)}`); } Name(node) { return node.value; } FieldDefinition(node) { if (this.config.onlyEnums) return ''; const typeString = node.type; const { type } = this._parsedConfig.declarationKind; const comment = this.getNodeComment(node); return comment + indent(`${node.name}: ${typeString}${this.getPunctuation(type)}`); } UnionTypeDefinition(node, key, parent) { if (this.config.onlyOperationTypes || this.config.onlyEnums) return ''; const originalNode = parent[key]; const possibleTypes = originalNode.types .map(t => (this.scalars[t.name.value] ? this._getScalar(t.name.value) : this.convertName(t))) .join(' | '); return new DeclarationBlock(this._declarationBlockConfig) .export() .asKind('type') .withName(this.convertName(node)) .withComment(node.description) .withContent(possibleTypes).string; } mergeInterfaces(interfaces, hasOtherFields) { return interfaces.join(' & ') + (interfaces.length && hasOtherFields ? ' & ' : ''); } appendInterfacesAndFieldsToBlock(block, interfaces, fields) { block.withContent(this.mergeInterfaces(interfaces, fields.length > 0)); block.withBlock(this.mergeAllFields(fields, interfaces.length > 0)); } getObjectTypeDeclarationBlock(node, originalNode) { const optionalTypename = this.config.nonOptionalTypename ? '__typename' : '__typename?'; const { type, interface: interfacesType } = this._parsedConfig.declarationKind; const allFields = [ ...(this.config.addTypename ? [ indent(`${this.config.immutableTypes ? 'readonly ' : ''}${optionalTypename}: '${node.name}'${this.getPunctuation(type)}`), ] : []), ...node.fields, ]; const interfacesNames = originalNode.interfaces ? originalNode.interfaces.map(i => this.convertName(i)) : []; const declarationBlock = new DeclarationBlock(this._declarationBlockConfig) .export() .asKind(type) .withName(this.convertName(node)) .withComment(node.description); if (type === 'interface' || type === 'class') { if (interfacesNames.length > 0) { const keyword = interfacesType === 'interface' && type === 'class' ? 'implements' : 'extends'; declarationBlock.withContent(`${keyword} ` + interfacesNames.join(', ') + (allFields.length > 0 ? ' ' : ' {}')); } declarationBlock.withBlock(this.mergeAllFields(allFields, false)); } else { this.appendInterfacesAndFieldsToBlock(declarationBlock, interfacesNames, allFields); } return declarationBlock; } mergeAllFields(allFields, _hasInterfaces) { return allFields.join('\n'); } ObjectTypeDefinition(node, key, parent) { if (this.config.onlyOperationTypes || this.config.onlyEnums) return ''; const originalNode = parent[key]; return [this.getObjectTypeDeclarationBlock(node, originalNode).string, this.buildArgumentsBlock(originalNode)] .filter(f => f) .join('\n\n'); } getInterfaceTypeDeclarationBlock(node, _originalNode) { const declarationBlock = new DeclarationBlock(this._declarationBlockConfig) .export() .asKind(this._parsedConfig.declarationKind.interface) .withName(this.convertName(node)) .withComment(node.description); return declarationBlock.withBlock(node.fields.join('\n')); } InterfaceTypeDefinition(node, key, parent) { if (this.config.onlyOperationTypes || this.config.onlyEnums) return ''; const originalNode = parent[key]; return [this.getInterfaceTypeDeclarationBlock(node, originalNode).string, this.buildArgumentsBlock(originalNode)] .filter(f => f) .join('\n\n'); } ScalarTypeDefinition(_node) { // We empty this because we handle scalars in a different way, see constructor. return ''; } _buildTypeImport(identifier, source, asDefault = false) { const { useTypeImports } = this.config; if (asDefault) { if (useTypeImports) { return `import type { default as ${identifier} } from '${source}';`; } return `import ${identifier} from '${source}';`; } return `import${useTypeImports ? ' type' : ''} { ${identifier} } from '${source}';`; } handleEnumValueMapper(typeIdentifier, importIdentifier, sourceIdentifier, sourceFile) { const importStatement = this._buildTypeImport(importIdentifier || sourceIdentifier, sourceFile); if (importIdentifier !== sourceIdentifier || sourceIdentifier !== typeIdentifier) { return [importStatement, `import ${typeIdentifier} = ${sourceIdentifier};`]; } return [importStatement]; } getEnumsImports() { return Object.keys(this.config.enumValues) .flatMap(enumName => { const mappedValue = this.config.enumValues[enumName]; if (mappedValu