| | import { renderContent } from '@/content-render/index' |
| | import fs from 'fs/promises' |
| | import { |
| | isScalarType, |
| | isObjectType, |
| | isInterfaceType, |
| | isUnionType, |
| | isEnumType, |
| | isInputObjectType, |
| | GraphQLSchema, |
| | } from 'graphql' |
| | import type { ConstDirectiveNode } from 'graphql/language' |
| | import path from 'path' |
| |
|
| | interface GraphQLTypeInfo { |
| | type: string |
| | kind: string |
| | } |
| |
|
| | interface TypeInfo { |
| | name: string |
| | id: string |
| | kind: string |
| | href: string |
| | } |
| |
|
| | interface ArgumentInfo { |
| | name: string |
| | defaultValue?: any |
| | description: string |
| | type: TypeInfo |
| | } |
| |
|
| | interface FieldNode { |
| | name: { value: string } |
| | type: any |
| | } |
| |
|
| | interface ArgumentNode { |
| | name: { value: string } |
| | defaultValue?: { value: any } |
| | description: { value: string } |
| | type: any |
| | } |
| |
|
| | interface SchemaMember { |
| | name: string |
| | isDeprecated?: boolean |
| | } |
| |
|
| | interface PreviewInfo { |
| | toggled_by: string[] |
| | } |
| |
|
| | const graphqlTypes: GraphQLTypeInfo[] = JSON.parse( |
| | await fs.readFile(path.join(process.cwd(), './src/graphql/lib/types.json'), 'utf-8'), |
| | ) |
| |
|
| | const singleQuotesInsteadOfBackticks = / '(\S+?)' / |
| |
|
| | function addPeriod(string: string): string { |
| | return string.endsWith('.') ? string : `${string}.` |
| | } |
| |
|
| | async function getArguments( |
| | args: ArgumentNode[], |
| | schema: GraphQLSchema, |
| | ): Promise<ArgumentInfo[] | undefined> { |
| | if (!args.length) return |
| |
|
| | const newArgs: ArgumentInfo[] = [] |
| |
|
| | for (const arg of args) { |
| | const newArg: Partial<ArgumentInfo> = {} |
| | const type: Partial<TypeInfo> = {} |
| | newArg.name = arg.name.value |
| | newArg.defaultValue = arg.defaultValue ? arg.defaultValue.value : undefined |
| | newArg.description = await getDescription(arg.description.value) |
| | const typeName = getType(arg) |
| | if (!typeName) continue |
| | type.name = typeName |
| | type.id = getId(typeName) |
| | const typeKind = getTypeKind(typeName, schema) |
| | if (!typeKind) continue |
| | type.kind = typeKind |
| | type.href = getFullLink(typeKind, type.id) |
| | newArg.type = type as TypeInfo |
| | newArgs.push(newArg as ArgumentInfo) |
| | } |
| |
|
| | return newArgs |
| | } |
| |
|
| | async function getDeprecationReason( |
| | directives: readonly ConstDirectiveNode[], |
| | schemaMember: SchemaMember, |
| | ): Promise<string | undefined> { |
| | if (!schemaMember.isDeprecated) return |
| |
|
| | |
| | const deprecationDirective = directives.filter((dir) => dir.name.value === 'deprecated') |
| |
|
| | |
| | if (deprecationDirective.length > 1) |
| | console.log(`more than one deprecation found for ${schemaMember.name}`) |
| |
|
| | const arg = deprecationDirective[0]?.arguments?.[0] |
| | if (!arg) return |
| | |
| | const value = (arg as any).value?.value |
| | if (!value) return |
| | return renderContent(value) |
| | } |
| |
|
| | function getDeprecationStatus(directives: readonly ConstDirectiveNode[]): boolean | undefined { |
| | if (!directives.length) return |
| |
|
| | return directives[0].name.value === 'deprecated' |
| | } |
| |
|
| | async function getDescription(rawDescription: string): Promise<string> { |
| | rawDescription = rawDescription.replace(singleQuotesInsteadOfBackticks, '`$1`') |
| |
|
| | return renderContent(addPeriod(rawDescription)) |
| | } |
| |
|
| | function getFullLink(baseType: string, id: string): string { |
| | return `/graphql/reference/${baseType}#${id}` |
| | } |
| |
|
| | function getId(typeName: string): string { |
| | return removeMarkers(typeName).toLowerCase() |
| | } |
| |
|
| | |
| | function getKind(type: string): string { |
| | return graphqlTypes.find((graphqlType) => graphqlType.type === type)!.kind |
| | } |
| |
|
| | async function getPreview( |
| | directives: readonly ConstDirectiveNode[], |
| | schemaMember: SchemaMember, |
| | previewsPerVersion: PreviewInfo[], |
| | ): Promise<PreviewInfo | undefined> { |
| | if (!directives.length) return |
| |
|
| | |
| | const previewDirective = directives.filter((dir) => dir.name.value === 'preview') |
| | if (!previewDirective.length) return |
| |
|
| | |
| | if (previewDirective.length > 1) |
| | console.log(`more than one preview found for ${schemaMember.name}`) |
| |
|
| | |
| | const firstArg = previewDirective[0]?.arguments?.[0] |
| | if (!firstArg) return |
| | |
| | const argValue = (firstArg as any).value |
| | if (!argValue || argValue.kind !== 'StringValue') return |
| |
|
| | const previewName = argValue.value |
| |
|
| | const preview = previewsPerVersion.find((p) => p.toggled_by.includes(previewName)) |
| | if (!preview) console.error(`cannot find "${previewName}" in graphql_previews.yml`) |
| |
|
| | return preview |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | function getType(field: FieldNode): string | undefined { |
| | |
| | if (field.type.kind !== 'ListType') { |
| | |
| | if (field.type.kind === 'NamedType') { |
| | return field.type.name.value |
| | } |
| |
|
| | |
| | if (field.type.kind === 'NonNullType' && field.type.type.kind === 'NamedType') { |
| | return `${field.type.type.name.value}!` |
| | } |
| | } |
| | |
| | if (field.type.kind === 'ListType') { |
| | |
| | if (field.type.type.kind === 'NamedType') { |
| | return `[${field.type.type.name.value}]` |
| | } |
| |
|
| | |
| | if (field.type.type.kind === 'NonNullType' && field.type.type.type.kind === 'NamedType') { |
| | return `[${field.type.type.type.name.value}!]` |
| | } |
| | } |
| |
|
| | |
| | if (field.type.kind === 'NonNullType' && field.type.type.kind === 'ListType') { |
| | |
| | if (field.type.type.type.kind === 'NamedType') { |
| | return `[${field.type.type.type.name.value}]!` |
| | } |
| |
|
| | |
| | if ( |
| | field.type.type.type.kind === 'NonNullType' && |
| | field.type.type.type.type.kind === 'NamedType' |
| | ) { |
| | return `[${field.type.type.type.type.name.value}!]!` |
| | } |
| | } |
| |
|
| | console.error(`cannot get type of ${field.name.value}`) |
| | return undefined |
| | } |
| |
|
| | function getTypeKind(type: string, schema: GraphQLSchema): string | undefined { |
| | type = removeMarkers(type) |
| |
|
| | const typeFromSchema = schema.getType(type) |
| |
|
| | if (isScalarType(typeFromSchema)) { |
| | return 'scalars' |
| | } |
| | if (isObjectType(typeFromSchema)) { |
| | return 'objects' |
| | } |
| | if (isInterfaceType(typeFromSchema)) { |
| | return 'interfaces' |
| | } |
| | if (isUnionType(typeFromSchema)) { |
| | return 'unions' |
| | } |
| | if (isEnumType(typeFromSchema)) { |
| | return 'enums' |
| | } |
| | if (isInputObjectType(typeFromSchema)) { |
| | return 'input-objects' |
| | } |
| |
|
| | console.error(`cannot find type kind of ${type}`) |
| | return undefined |
| | } |
| |
|
| | function removeMarkers(str: string): string { |
| | return str.replace('[', '').replace(']', '').replace(/!/g, '') |
| | } |
| |
|
| | export default { |
| | getArguments, |
| | getDeprecationReason, |
| | getDeprecationStatus, |
| | getDescription, |
| | getFullLink, |
| | getId, |
| | getKind, |
| | getPreview, |
| | getType, |
| | getTypeKind, |
| | } |
| |
|