@graphql-tools/utils
Version:
Common package containing utils and types for GraphQL tools
110 lines (109 loc) • 4.31 kB
JavaScript
import { versionInfo } from 'graphql';
import { asArray } from './helpers.js';
import { MapperKind } from './Interfaces.js';
import { mapSchema } from './mapSchema.js';
function handleDirectiveExtensions(extensions, removeDirectives) {
extensions = extensions || {};
const { directives: existingDirectives, ...rest } = extensions;
const finalExtensions = {
...rest,
};
// GraphQL.js v17+ may put symbol keys on extensions (toObjMapWithSymbols). Object
// rest/spread already copies enumerable own symbols; this loop also picks up
// non-enumerable ones (assignment makes them enumerable on the result). Gated to
// v17+ to avoid surprising behavior on older runtimes.
if (versionInfo?.major >= 17) {
for (const sym of Object.getOwnPropertySymbols(extensions)) {
finalExtensions[sym] = extensions[sym];
}
}
if (!removeDirectives) {
if (existingDirectives != null) {
const directives = {};
for (const directiveName in existingDirectives) {
directives[directiveName] = [...asArray(existingDirectives[directiveName])];
}
finalExtensions.directives = directives;
}
}
return finalExtensions;
}
export function extractExtensionsFromSchema(schema, removeDirectives = false) {
const result = {
schemaExtensions: handleDirectiveExtensions(schema.extensions, removeDirectives),
types: {},
};
mapSchema(schema, {
[MapperKind.OBJECT_TYPE]: type => {
result.types[type.name] = {
fields: {},
type: 'object',
extensions: handleDirectiveExtensions(type.extensions, removeDirectives),
};
return type;
},
[MapperKind.INTERFACE_TYPE]: type => {
result.types[type.name] = {
fields: {},
type: 'interface',
extensions: handleDirectiveExtensions(type.extensions, removeDirectives),
};
return type;
},
[MapperKind.FIELD]: (field, fieldName, typeName) => {
result.types[typeName].fields[fieldName] = {
arguments: {},
extensions: handleDirectiveExtensions(field.extensions, removeDirectives),
};
const args = field.args;
if (args != null) {
for (const argName in args) {
result.types[typeName].fields[fieldName].arguments[argName] =
handleDirectiveExtensions(args[argName].extensions, removeDirectives);
}
}
return field;
},
[MapperKind.ENUM_TYPE]: type => {
result.types[type.name] = {
values: {},
type: 'enum',
extensions: handleDirectiveExtensions(type.extensions, removeDirectives),
};
return type;
},
[MapperKind.ENUM_VALUE]: (value, typeName, _schema, valueName) => {
result.types[typeName].values[valueName] = handleDirectiveExtensions(value.extensions, removeDirectives);
return value;
},
[MapperKind.SCALAR_TYPE]: type => {
result.types[type.name] = {
type: 'scalar',
extensions: handleDirectiveExtensions(type.extensions, removeDirectives),
};
return type;
},
[MapperKind.UNION_TYPE]: type => {
result.types[type.name] = {
type: 'union',
extensions: handleDirectiveExtensions(type.extensions, removeDirectives),
};
return type;
},
[MapperKind.INPUT_OBJECT_TYPE]: type => {
result.types[type.name] = {
fields: {},
type: 'input',
extensions: handleDirectiveExtensions(type.extensions, removeDirectives),
};
return type;
},
[MapperKind.INPUT_OBJECT_FIELD]: (field, fieldName, typeName) => {
result.types[typeName].fields[fieldName] = {
extensions: handleDirectiveExtensions(field.extensions, removeDirectives),
};
return field;
},
});
return result;
}