@graphql-codegen/typescript-react-query
Version:
GraphQL Code Generator plugin for generating a ready-to-use React-Query Hooks based on GraphQL operations
107 lines (106 loc) • 5.81 kB
JavaScript
import { buildMapperImport, parseMapper } from '@graphql-codegen/visitor-plugin-common';
import { generateInfiniteQueryKey, generateMutationKey, generateQueryKey } from './variables-generator.js';
export class CustomMapperFetcher {
constructor(visitor, customFetcher) {
this.visitor = visitor;
if (typeof customFetcher === 'string') {
customFetcher = { func: customFetcher };
}
this._mapper = parseMapper(customFetcher.func);
this._isReactHook = customFetcher.isReactHook;
}
getFetcherFnName(operationResultType, operationVariablesTypes) {
return `${this._mapper.type}<${operationResultType}, ${operationVariablesTypes}>`;
}
generateFetcherImplementaion() {
if (this._mapper.isExternal) {
return buildMapperImport(this._mapper.source, [
{
identifier: this._mapper.type,
asDefault: this._mapper.default,
},
], this.visitor.config.useTypeImports);
}
return null;
}
generateInfiniteQueryHook(node, documentVariableName, operationName, operationResultType, operationVariablesTypes, hasRequiredVariables) {
const pageParamKey = `pageParamKey: keyof ${operationVariablesTypes}`;
const variables = `variables${hasRequiredVariables ? '' : '?'}: ${operationVariablesTypes}`;
const hookConfig = this.visitor.queryMethodMap;
this.visitor.reactQueryHookIdentifiersInUse.add(hookConfig.infiniteQuery.hook);
this.visitor.reactQueryOptionsIdentifiersInUse.add(hookConfig.infiniteQuery.options);
const options = `options?: ${hookConfig.infiniteQuery.options}<${operationResultType}, TError, TData>`;
const typedFetcher = this.getFetcherFnName(operationResultType, operationVariablesTypes);
const implHookOuter = this._isReactHook ? `const query = ${typedFetcher}(${documentVariableName})` : '';
const impl = this._isReactHook
? `(metaData) => query({...variables, [pageParamKey]: metaData.pageParam })`
: `(metaData) => ${typedFetcher}(${documentVariableName}, {...variables, [pageParamKey]: metaData.pageParam })()`;
return `export const useInfinite${operationName} = <
TData = ${operationResultType},
TError = ${this.visitor.config.errorType}
>(
${pageParamKey},
${variables},
${options}
) =>{
${implHookOuter}
return ${hookConfig.infiniteQuery.hook}<${operationResultType}, TError, TData>(
${generateInfiniteQueryKey(node, hasRequiredVariables)},
${impl},
options
)};`;
}
generateQueryHook(node, documentVariableName, operationName, operationResultType, operationVariablesTypes, hasRequiredVariables) {
const variables = `variables${hasRequiredVariables ? '' : '?'}: ${operationVariablesTypes}`;
const hookConfig = this.visitor.queryMethodMap;
this.visitor.reactQueryHookIdentifiersInUse.add(hookConfig.query.hook);
this.visitor.reactQueryOptionsIdentifiersInUse.add(hookConfig.query.options);
const options = `options?: ${hookConfig.query.options}<${operationResultType}, TError, TData>`;
const typedFetcher = this.getFetcherFnName(operationResultType, operationVariablesTypes);
const impl = this._isReactHook
? `${typedFetcher}(${documentVariableName}).bind(null, variables)`
: `${typedFetcher}(${documentVariableName}, variables)`;
return `export const use${operationName} = <
TData = ${operationResultType},
TError = ${this.visitor.config.errorType}
>(
${variables},
${options}
) =>
${hookConfig.query.hook}<${operationResultType}, TError, TData>(
${generateQueryKey(node, hasRequiredVariables)},
${impl},
options
);`;
}
generateMutationHook(node, documentVariableName, operationName, operationResultType, operationVariablesTypes, hasRequiredVariables) {
const variables = `variables?: ${operationVariablesTypes}`;
const hookConfig = this.visitor.queryMethodMap;
this.visitor.reactQueryHookIdentifiersInUse.add(hookConfig.mutation.hook);
this.visitor.reactQueryOptionsIdentifiersInUse.add(hookConfig.mutation.options);
const options = `options?: ${hookConfig.mutation.options}<${operationResultType}, TError, ${operationVariablesTypes}, TContext>`;
const typedFetcher = this.getFetcherFnName(operationResultType, operationVariablesTypes);
const impl = this._isReactHook
? `${typedFetcher}(${documentVariableName})`
: `(${variables}) => ${typedFetcher}(${documentVariableName}, variables)()`;
return `export const use${operationName} = <
TError = ${this.visitor.config.errorType},
TContext = unknown
>(${options}) =>
${hookConfig.mutation.hook}<${operationResultType}, TError, ${operationVariablesTypes}, TContext>(
${generateMutationKey(node)},
${impl},
options
);`;
}
generateFetcherFetch(node, documentVariableName, operationName, operationResultType, operationVariablesTypes, hasRequiredVariables) {
// We can't generate a fetcher field since we can't call react hooks outside of a React Fucntion Component
// Related: https://reactjs.org/docs/hooks-rules.html
if (this._isReactHook)
return '';
const variables = `variables${hasRequiredVariables ? '' : '?'}: ${operationVariablesTypes}`;
const typedFetcher = this.getFetcherFnName(operationResultType, operationVariablesTypes);
const impl = `${typedFetcher}(${documentVariableName}, variables, options)`;
return `\nuse${operationName}.fetcher = (${variables}, options?: RequestInit['headers']) => ${impl};`;
}
}