UNPKG

@graphql-mesh/transform-rename

Version:
194 lines (187 loc) • 9.89 kB
'use strict'; const wrap = require('@graphql-tools/wrap'); const utils = require('@graphql-mesh/utils'); const graphqlScalars = require('graphql-scalars'); const graphql = require('graphql'); const utils$1 = require('@graphql-tools/utils'); const ignoreList = [ 'Int', 'Float', 'String', 'Boolean', 'ID', 'date', 'hostname', 'regex', 'json-pointer', 'relative-json-pointer', 'uri-reference', 'uri-template', ...Object.keys(graphqlScalars.resolvers), ]; class WrapRename { constructor({ config }) { this.transforms = []; for (const change of config.renames) { const { from: { type: fromTypeName, field: fromFieldName, argument: fromArgumentName }, to: { type: toTypeName, field: toFieldName, argument: toArgumentName }, useRegExpForTypes, useRegExpForFields, useRegExpForArguments, } = change; const regExpFlags = change.regExpFlags || undefined; if (fromTypeName !== toTypeName) { let replaceTypeNameFn; if (useRegExpForTypes) { const typeNameRegExp = new RegExp(fromTypeName, regExpFlags); replaceTypeNameFn = (t) => t.replace(typeNameRegExp, toTypeName); } else { replaceTypeNameFn = t => (t === fromTypeName ? toTypeName : t); } this.transforms.push(new wrap.RenameTypes(typeName => { if (ignoreList.includes(typeName)) { return typeName; } return replaceTypeNameFn(typeName); })); } if (fromFieldName && toFieldName && fromFieldName !== toFieldName) { let replaceFieldNameFn; if (useRegExpForFields) { const fieldNameRegExp = new RegExp(fromFieldName, regExpFlags); replaceFieldNameFn = (typeName, fieldName) => typeName === toTypeName ? fieldName.replace(fieldNameRegExp, toFieldName) : fieldName; } else { replaceFieldNameFn = (typeName, fieldName) => typeName === toTypeName && fieldName === fromFieldName ? toFieldName : fieldName; } this.transforms.push(new wrap.RenameObjectFields(replaceFieldNameFn)); this.transforms.push(new wrap.RenameInputObjectFields(replaceFieldNameFn)); } if (fromTypeName && (fromTypeName === toTypeName || useRegExpForTypes) && toFieldName && (fromFieldName === toFieldName || useRegExpForFields) && fromArgumentName && fromArgumentName !== toArgumentName) { let replaceArgNameFn; const fieldNameMatch = (fieldName) => fieldName === (useRegExpForFields ? fieldName.replace(new RegExp(fromFieldName, regExpFlags), toFieldName) : toFieldName); const typeNameMatch = (typeName) => typeName === (useRegExpForTypes ? typeName.replace(new RegExp(fromTypeName, regExpFlags), toTypeName) : toTypeName); if (useRegExpForArguments) { const argNameRegExp = new RegExp(fromArgumentName, regExpFlags); replaceArgNameFn = (typeName, fieldName, argName) => typeNameMatch(typeName) && fieldNameMatch(fieldName) ? argName.replace(argNameRegExp, toArgumentName) : argName; } else { replaceArgNameFn = (typeName, fieldName, argName) => typeNameMatch(typeName) && fieldNameMatch(fieldName) && argName === fromArgumentName ? toArgumentName : argName; } this.transforms.push(new wrap.RenameObjectFieldArguments(replaceArgNameFn)); } } } 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); } } // Resolver composer mapping renamed field and arguments const defaultResolverComposer = (resolveFn = graphql.defaultFieldResolver, originalFieldName, argsMap) => (root, args, context, info) => resolveFn(root, // map renamed arguments to their original value argsMap ? Object.keys(args).reduce((acc, key) => ({ ...acc, [argsMap[key] || key]: args[key] }), {}) : args, context, // map renamed field name to its original value originalFieldName ? { ...info, fieldName: originalFieldName } : info); class BareRename { constructor({ config }) { this.noWrap = true; this.typesMap = new Map(); this.fieldsMap = new Map(); this.argsMap = new Map(); for (const rename of config.renames) { const { from: { type: fromTypeName, field: fromFieldName, argument: fromArgName }, to: { type: toTypeName, field: toFieldName, argument: toArgName }, useRegExpForTypes, useRegExpForFields, } = rename; const regExpFlags = rename.regExpFlags || undefined; if (fromTypeName && !fromFieldName && toTypeName && !toFieldName && fromTypeName !== toTypeName) { this.typesMap.set(useRegExpForTypes ? new RegExp(fromTypeName, regExpFlags) : fromTypeName, toTypeName); } if (fromTypeName && fromFieldName && toTypeName && toFieldName && fromFieldName !== toFieldName) { const fromName = useRegExpForFields ? new RegExp(fromFieldName, regExpFlags) : fromFieldName; const typeMap = this.fieldsMap.get(fromTypeName) || new Map(); this.fieldsMap.set(fromTypeName, typeMap.set(fromName, toFieldName)); } if (fromTypeName && fromFieldName && fromArgName && toTypeName && toFieldName && toArgName && fromArgName !== toArgName) { const fromName = useRegExpForFields ? new RegExp(fromArgName, regExpFlags) : fromArgName; const key = `${fromTypeName}.${fromFieldName}`; const typeMap = this.argsMap.get(key) || new Map(); this.argsMap.set(key, typeMap.set(fromName, toArgName)); } } } matchInMap(map, toMatch) { const mapKeyIsString = map.has(toMatch); const mapKey = mapKeyIsString ? toMatch : [...map.keys()].find(key => typeof key !== 'string' && key.test(toMatch)); if (!mapKey) return null; const newName = mapKeyIsString ? map.get(mapKey) : toMatch.replace(mapKey, map.get(mapKey)); // avoid re-iterating over strings that have already been renamed if (mapKeyIsString) map.delete(mapKey); return newName; } renameType(type) { const newTypeName = ignoreList.includes(type.toString()) ? null : this.matchInMap(this.typesMap, type.toString()); return newTypeName ? utils$1.renameType(type, newTypeName) : undefined; } transformSchema(schema) { return utils$1.mapSchema(schema, { ...(this.typesMap.size && { [utils$1.MapperKind.TYPE]: type => this.renameType(type) }), ...(this.typesMap.size && { [utils$1.MapperKind.ROOT_OBJECT]: type => this.renameType(type) }), ...((this.fieldsMap.size || this.argsMap.size) && { [utils$1.MapperKind.COMPOSITE_FIELD]: (fieldConfig, fieldName, typeName) => { const typeRules = this.fieldsMap.get(typeName); const fieldRules = this.argsMap.get(`${typeName}.${fieldName}`); const newFieldName = typeRules && this.matchInMap(typeRules, fieldName); const argsMap = fieldRules && Array.from(fieldRules.entries()).reduce((acc, [orName, newName]) => ({ ...acc, [newName]: orName }), {}); if (!newFieldName && !fieldRules) return undefined; // Rename rules for type might have been emptied by matchInMap, in which case we can cleanup if (!(typeRules === null || typeRules === void 0 ? void 0 : typeRules.size)) this.fieldsMap.delete(typeName); if (fieldRules && fieldConfig.args) { fieldConfig.args = Object.entries(fieldConfig.args).reduce((args, [argName, argConfig]) => ({ ...args, [this.matchInMap(fieldRules, argName) || argName]: argConfig, }), {}); } // Wrap resolve fn to handle mapping renamed field name and/or renamed arguments fieldConfig.resolve = defaultResolverComposer(fieldConfig.resolve, fieldName, argsMap); return [newFieldName || fieldName, fieldConfig]; }, }), }); } } const RenameTransform = (function RenameTransform(options) { if (Array.isArray(options.config)) { return new WrapRename({ config: { mode: 'wrap', renames: options.config, }, }); } return options.config.mode === 'bare' ? new BareRename(options) : new WrapRename(options); }); module.exports = RenameTransform;