|
1 |
| -import { DefinitionNode, DocumentNode, GraphQLSchema, parse, Source, Kind, isSchema } from 'graphql'; |
2 |
| -import { isSourceTypes, isStringTypes, isSchemaDefinition } from './utils'; |
3 |
| -import { MergedResultMap, mergeGraphQLNodes } from './merge-nodes'; |
| 1 | +import { |
| 2 | + DefinitionNode, |
| 3 | + DocumentNode, |
| 4 | + GraphQLSchema, |
| 5 | + parse, |
| 6 | + Source, |
| 7 | + Kind, |
| 8 | + isSchema, |
| 9 | + OperationTypeDefinitionNode, |
| 10 | + OperationTypeNode, |
| 11 | + isDefinitionNode, |
| 12 | +} from 'graphql'; |
| 13 | +import { CompareFn, defaultStringComparator, isSourceTypes, isStringTypes } from './utils'; |
| 14 | +import { MergedResultMap, mergeGraphQLNodes, schemaDefSymbol } from './merge-nodes'; |
4 | 15 | import { resetComments, printWithComments } from './comments';
|
5 |
| -import { createSchemaDefinition, getDocumentNodeFromSchema } from '@graphql-tools/utils'; |
| 16 | +import { getDocumentNodeFromSchema } from '@graphql-tools/utils'; |
| 17 | +import { operationTypeDefinitionNodeTypeRootTypeMap } from './schema-def'; |
6 | 18 |
|
7 | 19 | type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
|
8 |
| -type CompareFn<T> = (a: T, b: T) => number; |
9 | 20 |
|
10 | 21 | export interface Config {
|
11 | 22 | /**
|
@@ -104,75 +115,95 @@ export function mergeTypeDefs(
|
104 | 115 | return result;
|
105 | 116 | }
|
106 | 117 |
|
| 118 | +function visitTypeSources( |
| 119 | + types: Array<string | Source | DocumentNode | GraphQLSchema | DefinitionNode>, |
| 120 | + allNodes: DefinitionNode[] |
| 121 | +) { |
| 122 | + for (const type of types) { |
| 123 | + if (type) { |
| 124 | + if (Array.isArray(type)) { |
| 125 | + visitTypeSources(types, allNodes); |
| 126 | + } else if (isSchema(type)) { |
| 127 | + const documentNode = getDocumentNodeFromSchema(type); |
| 128 | + visitTypeSources(documentNode.definitions as DefinitionNode[], allNodes); |
| 129 | + } else if (isStringTypes(type) || isSourceTypes(type)) { |
| 130 | + const documentNode = parse(type); |
| 131 | + visitTypeSources(documentNode.definitions as DefinitionNode[], allNodes); |
| 132 | + } else if (isDefinitionNode(type)) { |
| 133 | + allNodes.push(type); |
| 134 | + } else { |
| 135 | + visitTypeSources(type.definitions as DefinitionNode[], allNodes); |
| 136 | + } |
| 137 | + } |
| 138 | + } |
| 139 | +} |
| 140 | + |
107 | 141 | export function mergeGraphQLTypes(
|
108 | 142 | types: Array<string | Source | DocumentNode | GraphQLSchema>,
|
109 | 143 | config: Config
|
110 | 144 | ): DefinitionNode[] {
|
111 | 145 | resetComments();
|
112 | 146 |
|
113 |
| - const allNodes: ReadonlyArray<DefinitionNode> = types |
114 |
| - .map<DocumentNode>(type => { |
115 |
| - if (Array.isArray(type)) { |
116 |
| - type = mergeTypeDefs(type); |
117 |
| - } |
118 |
| - if (isSchema(type)) { |
119 |
| - return getDocumentNodeFromSchema(type); |
120 |
| - } else if (isStringTypes(type) || isSourceTypes(type)) { |
121 |
| - return parse(type); |
122 |
| - } |
| 147 | + const allNodes: DefinitionNode[] = []; |
| 148 | + visitTypeSources(types, allNodes); |
123 | 149 |
|
124 |
| - return type; |
125 |
| - }) |
126 |
| - .map(ast => ast.definitions) |
127 |
| - .reduce((defs, newDef = []) => [...defs, ...newDef], []); |
| 150 | + const mergedNodes: MergedResultMap = mergeGraphQLNodes(allNodes, config); |
128 | 151 |
|
129 | 152 | // XXX: right now we don't handle multiple schema definitions
|
130 |
| - let schemaDef: { |
131 |
| - query: string | null; |
132 |
| - mutation: string | null; |
133 |
| - subscription: string | null; |
134 |
| - } = allNodes.filter(isSchemaDefinition).reduce<any>( |
135 |
| - (def, node) => { |
136 |
| - node.operationTypes |
137 |
| - .filter(op => op.type.name.value) |
138 |
| - .forEach(op => { |
139 |
| - def[op.operation] = op.type.name.value; |
140 |
| - }); |
141 |
| - |
142 |
| - return def; |
143 |
| - }, |
144 |
| - { |
145 |
| - query: null, |
146 |
| - mutation: null, |
147 |
| - subscription: null, |
148 |
| - } |
149 |
| - ); |
150 |
| - |
151 |
| - const mergedNodes: MergedResultMap = mergeGraphQLNodes(allNodes, config); |
152 |
| - const allTypes = Object.keys(mergedNodes); |
| 153 | + let schemaDef = mergedNodes[schemaDefSymbol] || { |
| 154 | + kind: Kind.SCHEMA_DEFINITION, |
| 155 | + operationTypes: [], |
| 156 | + }; |
153 | 157 |
|
154 |
| - if (config && config.sort) { |
155 |
| - allTypes.sort(typeof config.sort === 'function' ? config.sort : undefined); |
| 158 | + if (config?.useSchemaDefinition) { |
| 159 | + const operationTypes = schemaDef.operationTypes as OperationTypeDefinitionNode[]; |
| 160 | + for (const opTypeDefNodeType in operationTypeDefinitionNodeTypeRootTypeMap) { |
| 161 | + const opTypeDefNode = operationTypes.find(operationType => operationType.operation === opTypeDefNodeType); |
| 162 | + if (!opTypeDefNode) { |
| 163 | + const existingPossibleRootType = mergedNodes[operationTypeDefinitionNodeTypeRootTypeMap[opTypeDefNodeType]]; |
| 164 | + if (existingPossibleRootType) { |
| 165 | + operationTypes.push({ |
| 166 | + kind: Kind.OPERATION_TYPE_DEFINITION, |
| 167 | + type: { |
| 168 | + kind: Kind.NAMED_TYPE, |
| 169 | + name: existingPossibleRootType.name, |
| 170 | + }, |
| 171 | + operation: opTypeDefNodeType as OperationTypeNode, |
| 172 | + }); |
| 173 | + } |
| 174 | + } |
| 175 | + } |
156 | 176 | }
|
157 | 177 |
|
158 |
| - if (config && config.useSchemaDefinition) { |
159 |
| - const queryType = schemaDef.query ? schemaDef.query : allTypes.find(t => t === 'Query'); |
160 |
| - const mutationType = schemaDef.mutation ? schemaDef.mutation : allTypes.find(t => t === 'Mutation'); |
161 |
| - const subscriptionType = schemaDef.subscription ? schemaDef.subscription : allTypes.find(t => t === 'Subscription'); |
| 178 | + if (config?.forceSchemaDefinition && !schemaDef?.operationTypes?.length) { |
162 | 179 | schemaDef = {
|
163 |
| - query: queryType, |
164 |
| - mutation: mutationType, |
165 |
| - subscription: subscriptionType, |
| 180 | + kind: Kind.SCHEMA_DEFINITION, |
| 181 | + operationTypes: [ |
| 182 | + { |
| 183 | + kind: Kind.OPERATION_TYPE_DEFINITION, |
| 184 | + operation: 'query', |
| 185 | + type: { |
| 186 | + kind: Kind.NAMED_TYPE, |
| 187 | + name: { |
| 188 | + kind: Kind.NAME, |
| 189 | + value: 'Query', |
| 190 | + }, |
| 191 | + }, |
| 192 | + }, |
| 193 | + ], |
166 | 194 | };
|
167 | 195 | }
|
168 | 196 |
|
169 |
| - const schemaDefinition = createSchemaDefinition(schemaDef, { |
170 |
| - force: config.forceSchemaDefinition, |
171 |
| - }); |
| 197 | + const mergedNodeDefinitions = Object.values(mergedNodes); |
| 198 | + |
| 199 | + if (schemaDef.operationTypes?.length) { |
| 200 | + mergedNodeDefinitions.push(schemaDef); |
| 201 | + } |
172 | 202 |
|
173 |
| - if (!schemaDefinition) { |
174 |
| - return Object.values(mergedNodes); |
| 203 | + if (config?.sort) { |
| 204 | + const sortFn = typeof config.sort === 'function' ? config.sort : defaultStringComparator; |
| 205 | + mergedNodeDefinitions.sort((a, b) => sortFn(a.name?.value, b.name?.value)); |
175 | 206 | }
|
176 | 207 |
|
177 |
| - return [...Object.values(mergedNodes), parse(schemaDefinition).definitions[0]]; |
| 208 | + return mergedNodeDefinitions; |
178 | 209 | }
|
0 commit comments