@graphql-mesh/transform-naming-convention
Version:
244 lines (237 loc) • 12.1 kB
JavaScript
;
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;