@graphql-codegen/typescript-msw
Version:
GraphQL Code Generator plugin for generating MSW mock handlers based on GraphQL operations
83 lines (82 loc) • 3.95 kB
JavaScript
import autoBind from 'auto-bind';
import { pascalCase } from 'change-case-all';
import { print } from 'graphql';
import { ClientSideBaseVisitor, getConfigValue, } from '@graphql-codegen/visitor-plugin-common';
export class MSWVisitor extends ClientSideBaseVisitor {
constructor(schema, fragments, rawConfig) {
super(schema, fragments, rawConfig, { link: getConfigValue(rawConfig.link, undefined) });
this._operationsToInclude = [];
autoBind(this);
this._externalImportPrefix = this.config.importOperationTypesFrom
? `${this.config.importOperationTypesFrom}.`
: '';
}
getImports() {
const hasOperations = this._collectedOperations.length > 0;
if (!hasOperations) {
return [];
}
return [
`import { graphql, type GraphQLResponseResolver, type RequestHandlerOptions } from 'msw'`,
];
}
getContent() {
var _a;
const { link } = this.config;
let endpoint;
if (link) {
endpoint = `const ${link.name} = graphql.link('${link.endpoint}')\n`;
}
const suffix = pascalCase((link === null || link === void 0 ? void 0 : link.name) || '');
const withSuffix = (_a = link === null || link === void 0 ? void 0 : link.withSuffix) !== null && _a !== void 0 ? _a : true;
const operations = this._operationsToInclude.map(({ node, operationType, operationResultType, operationVariablesTypes }) => {
if (operationType === 'Query' || operationType === 'Mutation') {
const handlerName = `mock${pascalCase(node.name.value)}${operationType}${withSuffix ? suffix : ''}`;
/** @ts-expect-error name DOES exist on @type{import('graphql').SelectionNode} */
const selections = node.selectionSet.selections.map(sel => sel.name.value).join(', ');
const variables = node.variableDefinitions.map(def => def.variable.name.value).join(', ');
return `/**
* @param resolver A function that accepts [resolver arguments](https://mswjs.io/docs/api/graphql#resolver-argument) and must always return the instruction on what to do with the intercepted request. ([see more](https://mswjs.io/docs/concepts/response-resolver#resolver-instructions))
* @param options Options object to customize the behavior of the mock. ([see more](https://mswjs.io/docs/api/graphql#handler-options))
* @see https://mswjs.io/docs/basics/response-resolver
* @example
* ${handlerName}(
* ({ query, variables }) => {${variables &&
`
* const { ${variables} } = variables;`}
* return HttpResponse.json({
* data: { ${selections} }
* })
* },
* requestOptions
* )
*/
export const ${handlerName} = (resolver: GraphQLResponseResolver<${operationResultType}, ${operationVariablesTypes}>, options?: RequestHandlerOptions) =>
${(link === null || link === void 0 ? void 0 : link.name) || 'graphql'}.${operationType.toLowerCase()}<${operationResultType}, ${operationVariablesTypes}>(
'${node.name.value}',
resolver,
options
)\n`;
}
return '';
});
return [endpoint, ...operations].join('\n');
}
buildOperation(node, documentVariableName, operationType, operationResultType, operationVariablesTypes) {
operationResultType = this._externalImportPrefix + operationResultType;
operationVariablesTypes = this._externalImportPrefix + operationVariablesTypes;
if (node.name == null) {
throw new Error("Plugin 'msw' cannot generate mocks for unnamed operation.\n\n" + print(node));
}
else {
this._operationsToInclude.push({
node,
documentVariableName,
operationType,
operationResultType,
operationVariablesTypes,
});
}
return null;
}
}