Skip to content

Instantly share code, notes, and snippets.

@bhoriuchi
Created May 10, 2019 20:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bhoriuchi/6de5a4fa8d7eadbbd602b4392888a537 to your computer and use it in GitHub Desktop.
Save bhoriuchi/6de5a4fa8d7eadbbd602b4392888a537 to your computer and use it in GitHub Desktop.
Merge multiple GraphQL typeDefs into a single typeDef
import { parse, print } from 'graphql'
/**
* Merges multiple graphql definitions into a single one
* with the ability to merge fields and perform conflict
* resolution on types and fields with custom functions
*/
export function mergeDefinitions(documents, options) {
const types = {};
const opts = Object.assign({
mergeFields: ['Query', 'Mutation', 'Subscription'],
onTypeConflict(original) {
return original
},
onFieldConflict(original) {
return original
},
parsed: true,
}, options)
// expand documents and parse any strings
const docs = documents
.reduce((accum, def) => accum.concat(def), [])
.map(def => typeof def === 'string' ? parse(def) : def)
.filter(def => Object.assign({}, def).kind === 'Document')
// validate there is at least 2 documents
if (docs.length < 2) {
throw new Error('merge requires at least two documents')
}
// attempt to merge defs
for (let doc of docs) {
for (let def of doc.definitions) {
const typeName = def.name.value
const oType = types[typeName]
// if the type doesnt exist in the type hash add it
if (!oType) {
types[typeName] = def
continue;
}
// if the type has fields and is in the field merge list
if (Array.isArray(def.fields) && opts.mergeFields.indexOf(typeName) !== -1) {
const fields = oType.fields.reduce((accum, field) => {
accum[field.name.value] = field
return accum;
}, {})
for (let field of def.fields) {
const fieldName = field.name.value
const oField = fields[fieldName]
// if the field doesnt exist add it
if (!oField) {
fields[fieldName] = field
continue;
}
// otherwise perform a conflict resolution
fields[fieldName] = opts.onFieldConflict(oField, field)
}
// convert the keys back into a field array
oType.fields = Object.keys(fields).map(fieldName => fields[fieldName])
continue;
}
// otherwise perform the type conflict
types[typeName] = opts.onTypeConflict(oType, def)
}
}
const mergedDefinitions = print({
kind: 'Document',
definitions: Object.keys(types).map(typeName => types[typeName])
})
return opts.parsed ? parse(mergedDefinitions) : mergedDefinitions
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment