@graphql-codegen/typescript-urql-graphcache
Version:
GraphQL Code Generator plugin for generating a generic to be used in graphcache cache config
199 lines (198 loc) • 9.05 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.plugin = void 0;
const graphql_1 = require("graphql");
const visitor_plugin_common_1 = require("@graphql-codegen/visitor-plugin-common");
const unwrapType = (type) => (0, graphql_1.isWrappingType)(type) ? unwrapType(type.ofType) : type || null;
const getObjectTypes = (schema) => {
const typeMap = schema.getTypeMap();
const queryType = schema.getQueryType();
const mutationType = schema.getMutationType();
const subscriptionType = schema.getSubscriptionType();
const objectTypes = [];
for (const key in typeMap) {
if (!typeMap[key] || !typeMap[key].name)
continue;
const type = typeMap[key];
switch (type.name) {
case '__Directive':
case '__DirectiveLocation':
case '__EnumValue':
case '__InputValue':
case '__Field':
case '__Type':
case '__TypeKind':
case '__Schema':
continue;
default:
if (!(type instanceof graphql_1.GraphQLObjectType))
continue;
}
if (type !== queryType && type !== mutationType && type !== subscriptionType) {
objectTypes.push(type);
}
}
return objectTypes;
};
function constructType(typeNode, schema, convertName, config, nullable = true, allowString = false) {
if ((0, graphql_1.isListType)(typeNode)) {
return nullable
? `Maybe<Array<${constructType(typeNode.ofType, schema, convertName, config, false, allowString)}>>`
: `Array<${constructType(typeNode.ofType, schema, convertName, config, false, allowString)}>`;
}
if ((0, graphql_1.isNonNullType)(typeNode)) {
return constructType(typeNode.ofType, schema, convertName, config, false, allowString);
}
const type = schema.getType(typeNode.name);
if ((0, graphql_1.isScalarType)(type)) {
return nullable
? `Maybe<Scalars['${type.name}']${allowString ? ' | string' : ''}>`
: `Scalars['${type.name}']${allowString ? ' | string' : ''}`;
}
const tsTypeName = convertName(typeNode.name, {
prefix: config.typesPrefix,
suffix: config.typesSuffix,
});
if ((0, graphql_1.isUnionType)(type) || (0, graphql_1.isInputObjectType)(type) || (0, graphql_1.isObjectType)(type)) {
const finalType = `WithTypename<${tsTypeName}>${allowString ? ' | string' : ''}`;
return nullable ? `Maybe<${finalType}>` : finalType;
}
if ((0, graphql_1.isEnumType)(type)) {
const finalType = `${tsTypeName}${allowString ? ' | string' : ''}`;
return nullable ? `Maybe<${finalType}>` : finalType;
}
if ((0, graphql_1.isInterfaceType)(type)) {
const possibleTypes = schema.getPossibleTypes(type).map(possibleType => {
const tsPossibleTypeName = convertName(possibleType.name, {
prefix: config.typesPrefix,
suffix: config.typesSuffix,
});
return `WithTypename<${tsPossibleTypeName}>`;
});
const finalType = allowString
? possibleTypes.join(' | ') + ' | string'
: possibleTypes.join(' | ');
return nullable ? `Maybe<${finalType}>` : finalType;
}
throw new Error(`Unhandled type ${type}`);
}
const capitalize = (s) => s.charAt(0).toUpperCase() + s.slice(1);
function getKeysConfig(schema, convertName, config) {
const keys = getObjectTypes(schema).reduce((keys, type) => {
keys.push(`${type.name}?: (data: WithTypename<${convertName(type.name, {
prefix: config.typesPrefix,
suffix: config.typesSuffix,
})}>) => null | string`);
return keys;
}, []);
return 'export type GraphCacheKeysConfig = {\n ' + keys.join(',\n ') + '\n}';
}
function getResolversConfig(schema, convertName, config) {
const objectTypes = [schema.getQueryType(), ...getObjectTypes(schema)];
const resolvers = objectTypes.reduce((resolvers, parentType) => {
const fields = Object.entries(parentType.getFields()).reduce((fields, [fieldName, field]) => {
const args = Object.entries(field.args);
const argsName = args.length
? convertName(`${parentType.name}${capitalize(fieldName)}Args`, {
prefix: config.typesPrefix,
suffix: config.typesSuffix,
})
: 'Record<string, never>';
fields.push(`${fieldName}?: GraphCacheResolver<WithTypename<` +
`${convertName(parentType.name, {
prefix: config.typesPrefix,
suffix: config.typesSuffix,
})}>, ${argsName}, ` +
`${constructType(field.type, schema, convertName, config, false, true)}>`);
return fields;
}, []);
resolvers.push(` ${parentType.name}?: {\n ` + fields.join(',\n ') + '\n }');
return resolvers;
}, []);
return resolvers;
}
function getRootUpdatersConfig(schema, convertName, config) {
const [mutationUpdaters, subscriptionUpdaters] = [
schema.getMutationType(),
schema.getSubscriptionType(),
].map(rootType => {
if (rootType) {
const updaters = [];
Object.values(rootType.getFields()).forEach(field => {
const argsName = field.args.length
? convertName(`${rootType.name}${capitalize(field.name)}Args`, {
prefix: config.typesPrefix,
suffix: config.typesSuffix,
})
: 'Record<string, never>';
updaters.push(`${field.name}?: GraphCacheUpdateResolver<{ ${field.name}: ${constructType(field.type, schema, convertName, config)} }, ${argsName}>`);
});
return updaters;
}
return null;
});
return {
mutationUpdaters,
subscriptionUpdaters,
};
}
function getOptimisticUpdatersConfig(schema, convertName, config) {
const mutationType = schema.getMutationType();
if (mutationType) {
const optimistic = [];
Object.values(mutationType.getFields()).forEach(field => {
const argsName = field.args.length
? convertName(`${capitalize(mutationType.name)}${capitalize(field.name)}Args`, {
prefix: config.typesPrefix,
suffix: config.typesSuffix,
})
: 'Record<string, never>';
const outputType = constructType(field.type, schema, convertName, config);
optimistic.push(`${field.name}?: GraphCacheOptimisticMutationResolver<` +
`${argsName}, ` +
`${outputType}>`);
});
return optimistic;
}
return null;
}
function getImports(config) {
return [
"import { offlineExchange } from '@urql/exchange-graphcache';",
`${config.useTypeImports ? 'import type' : 'import'} { Resolver as GraphCacheResolver, UpdateResolver as GraphCacheUpdateResolver, OptimisticMutationResolver as GraphCacheOptimisticMutationResolver } from '@urql/exchange-graphcache';\n`,
].join('\n');
}
const plugin = (schema, _documents, config) => {
const convertName = (0, visitor_plugin_common_1.convertFactory)(config);
const imports = getImports(config);
const keys = getKeysConfig(schema, convertName, config);
const resolvers = getResolversConfig(schema, convertName, config);
const { mutationUpdaters, subscriptionUpdaters } = getRootUpdatersConfig(schema, convertName, config);
const optimisticUpdaters = getOptimisticUpdatersConfig(schema, convertName, config);
return {
prepend: [imports],
content: [
`export type WithTypename<T extends { __typename?: any }> = Partial<T> & { __typename: NonNullable<T['__typename']> };`,
keys,
'export type GraphCacheResolvers = {\n' + resolvers.join(',\n') + '\n};',
'export type GraphCacheOptimisticUpdaters = ' +
(optimisticUpdaters ? '{\n ' + optimisticUpdaters.join(',\n ') + '\n};' : '{};'),
'export type GraphCacheUpdaters = {\n' +
' Mutation?: ' +
(mutationUpdaters ? `{\n ${mutationUpdaters.join(',\n ')}\n }` : '{}') +
',\n' +
' Subscription?: ' +
(subscriptionUpdaters ? `{\n ${subscriptionUpdaters.join(',\n ')}\n }` : '{}') +
',\n};',
'export type GraphCacheConfig = Parameters<typeof offlineExchange>[0] & {\n' +
' updates?: GraphCacheUpdaters,\n' +
' keys?: GraphCacheKeysConfig,\n' +
' optimistic?: GraphCacheOptimisticUpdaters,\n' +
' resolvers?: GraphCacheResolvers,\n' +
'};',
]
.filter(Boolean)
.join('\n\n'),
};
};
exports.plugin = plugin;
;