UNPKG

@graphql-mesh/transform-naming-convention

Version:
244 lines (237 loc) • 12.1 kB
'use strict'; const wrap = require('@graphql-tools/wrap'); const utils = require('@graphql-mesh/utils'); const changeCase = require('change-case'); const upperCase = require('upper-case'); const lowerCase = require('lower-case'); const graphqlScalars = require('graphql-scalars'); const graphql = require('graphql'); const utils$1 = require('@graphql-tools/utils'); const NAMING_CONVENTIONS = { camelCase: changeCase.camelCase, capitalCase: changeCase.capitalCase, constantCase: changeCase.constantCase, dotCase: changeCase.dotCase, headerCase: changeCase.headerCase, noCase: changeCase.noCase, paramCase: changeCase.paramCase, pascalCase: changeCase.pascalCase, pathCase: changeCase.pathCase, sentenceCase: changeCase.sentenceCase, snakeCase: changeCase.snakeCase, upperCase: upperCase.upperCase, lowerCase: lowerCase.lowerCase, }; // Ignore fields needed by Federation spec const IGNORED_ROOT_FIELD_NAMES = ['_service', '_entities']; const IGNORED_TYPE_NAMES = [ 'Int', 'Float', 'String', 'Boolean', 'ID', 'date', 'hostname', 'regex', 'json-pointer', 'relative-json-pointer', 'uri-reference', 'uri-template', ...Object.keys(graphqlScalars.resolvers), ]; class NamingConventionTransform { constructor(options) { this.transforms = []; if (options.config.typeNames) { const namingConventionFn = NAMING_CONVENTIONS[options.config.typeNames]; this.transforms.push(new wrap.RenameTypes(typeName => IGNORED_TYPE_NAMES.includes(typeName) ? typeName : namingConventionFn(typeName) || typeName)); } if (options.config.fieldNames) { const fieldNamingConventionFn = options.config.fieldNames ? NAMING_CONVENTIONS[options.config.fieldNames] : (s) => s; this.transforms.push(new wrap.RenameInputObjectFields((_, fieldName) => fieldNamingConventionFn(fieldName) || fieldName), new wrap.TransformObjectFields((_, fieldName, fieldConfig) => [ IGNORED_ROOT_FIELD_NAMES.includes(fieldName) ? fieldName : fieldNamingConventionFn(fieldName) || fieldName, fieldConfig, ]), new wrap.RenameInterfaceFields((_, fieldName) => fieldNamingConventionFn(fieldName) || fieldName)); } if (options.config.fieldArgumentNames) { const fieldArgNamingConventionFn = options.config.fieldArgumentNames ? NAMING_CONVENTIONS[options.config.fieldArgumentNames] : (s) => s; this.transforms.push(new wrap.RenameObjectFieldArguments((_typeName, _fieldName, argName) => fieldArgNamingConventionFn(argName))); } if (options.config.enumValues) { const namingConventionFn = NAMING_CONVENTIONS[options.config.enumValues]; this.transforms.push(new wrap.TransformEnumValues((typeName, externalValue, enumValueConfig) => { const newEnumValue = namingConventionFn(externalValue) || externalValue; return [ newEnumValue, { ...enumValueConfig, value: newEnumValue, }, ]; })); } } transformSchema(originalWrappingSchema, subschemaConfig, transformedSchema) { return utils.applySchemaTransforms(originalWrappingSchema, subschemaConfig, transformedSchema, this.transforms); } transformRequest(originalRequest, delegationContext, transformationContext) { return utils.applyRequestTransforms(originalRequest, delegationContext, transformationContext, this.transforms); } transformResult(originalResult, delegationContext, transformationContext) { return utils.applyResultTransforms(originalResult, delegationContext, transformationContext, this.transforms); } } const isObject = (input) => typeof input === 'object' && input !== null && !Array.isArray(input) && true; const getUnderlyingType = (type) => type.ofType ? getUnderlyingType(type.ofType) : type; // Resolver composer mapping renamed field and arguments const defaultResolverComposer = (resolveFn = graphql.defaultFieldResolver, originalFieldName, argsMap, resultMap) => (root, args, context, info) => { const originalResult = resolveFn(root, // map renamed arguments to their original value argsMap ? Object.keys(args).reduce((acc, key) => { if (!argsMap[key]) { return { ...acc, [key]: args[key] }; } const argKey = argsMap[key]; const mappedArgKeyIsObject = isObject(argKey); const newArgName = Object.keys(argKey)[0]; return { ...acc, [mappedArgKeyIsObject ? newArgName : argKey]: mappedArgKeyIsObject ? Object.entries(args[key]).reduce((acc, [key, value]) => { const oldInputFieldName = argKey[newArgName][key]; return { ...acc, [oldInputFieldName || key]: value }; }, {}) : args[key], }; }, {}) : args, context, // map renamed field name to its original value originalFieldName ? { ...info, fieldName: originalFieldName } : info); // map result values from original value to new renamed value return resultMap ? Array.isArray(originalResult) ? originalResult.map(result => resultMap[result] || originalResult) : resultMap[originalResult] || originalResult : originalResult; }; class NamingConventionTransform$1 { constructor(options) { this.noWrap = true; this.config = { ...options.config }; } transformSchema(schema) { return utils$1.mapSchema(schema, { ...(this.config.typeNames && { [utils$1.MapperKind.TYPE]: type => { const oldName = type.name; const namingConventionFn = NAMING_CONVENTIONS[this.config.typeNames]; const newName = IGNORED_TYPE_NAMES.includes(oldName) ? oldName : namingConventionFn(oldName); if (newName !== undefined && newName !== oldName) { return utils$1.renameType(type, newName); } return undefined; }, }), ...(this.config.enumValues && { [utils$1.MapperKind.ENUM_VALUE]: (valueConfig, _typeName, _schema, externalValue) => { const namingConventionFn = NAMING_CONVENTIONS[this.config.enumValues]; const newEnumValue = namingConventionFn(externalValue); if (newEnumValue === externalValue) { return undefined; } return [ newEnumValue, { ...valueConfig, value: newEnumValue, astNode: { ...valueConfig.astNode, name: { ...valueConfig.astNode.name, value: newEnumValue, }, }, }, ]; }, }), ...((this.config.fieldNames || this.config.fieldArgumentNames) && { [utils$1.MapperKind.COMPOSITE_FIELD]: (fieldConfig, fieldName) => { const enumNamingConventionFn = NAMING_CONVENTIONS[this.config.enumValues]; const fieldNamingConventionFn = this.config.fieldNames && NAMING_CONVENTIONS[this.config.fieldNames]; const argNamingConventionFn = this.config.fieldArgumentNames && NAMING_CONVENTIONS[this.config.fieldArgumentNames]; const argsMap = fieldConfig.args && {}; const newFieldName = this.config.fieldNames && !IGNORED_ROOT_FIELD_NAMES.includes(fieldName) && fieldNamingConventionFn(fieldName); const fieldActualType = getUnderlyingType(fieldConfig.type); const resultMap = this.config.enumValues && graphql.isEnumType(fieldActualType) && Object.keys(fieldActualType.toConfig().values).reduce((map, value) => { if (Number.isFinite(value)) { return map; } const newValue = enumNamingConventionFn(value); return newValue === value ? map : { ...map, [value]: newValue, }; }, {}); if (fieldConfig.args) { fieldConfig.args = Object.entries(fieldConfig.args).reduce((args, [argName, argConfig]) => { const newArgName = this.config.fieldArgumentNames && argNamingConventionFn(argName); const useArgName = newArgName || argName; const argIsInputObjectType = graphql.isInputObjectType(argConfig.type); if (argName !== useArgName || argIsInputObjectType) { // take advantage of the loop to map arg name from Old to New argsMap[useArgName] = !argIsInputObjectType ? argName : { [argName]: Object.keys(argConfig.type.toConfig().fields).reduce((inputFields, inputFieldName) => { if (Number.isFinite(inputFieldName)) return inputFields; const newInputFieldName = fieldNamingConventionFn(inputFieldName); return newInputFieldName === inputFieldName ? inputFields : { ...inputFields, [fieldNamingConventionFn(inputFieldName)]: inputFieldName, }; }, {}), }; } return { ...args, [useArgName]: argConfig, }; }, {}); } // Wrap resolve fn to handle mapping renamed field and argument names as well as results (for enums) fieldConfig.resolve = defaultResolverComposer(fieldConfig.resolve, fieldName, argsMap, resultMap); return [newFieldName || fieldName, fieldConfig]; }, }), ...(this.config.fieldNames && { [utils$1.MapperKind.INPUT_OBJECT_FIELD]: (inputFieldConfig, fieldName) => { const namingConventionFn = this.config.fieldNames && NAMING_CONVENTIONS[this.config.fieldNames]; const newName = namingConventionFn(fieldName); if (newName === fieldName) { return undefined; } return [newName, inputFieldConfig]; }, }), }); } } const NamingConventionTransform$2 = (function NamingConventionTransform$2(options) { return options.config.mode === 'bare' ? new NamingConventionTransform$1(options) : new NamingConventionTransform(options); }); module.exports = NamingConventionTransform$2;