UNPKG

@graphql-codegen/c-sharp-operations

Version:

GraphQL Code Generator plugin for generating ready-to-use Angular Components based on GraphQL operations

256 lines (245 loc) • 10 kB
import { visit, print, Kind, concatAST } from 'graphql'; import { ClientSideBaseVisitor, DocumentMode, indentMultiline } from '@graphql-codegen/visitor-plugin-common'; import autoBind from 'auto-bind'; import { camelCase } from 'camel-case'; import { extname } from 'path'; import gql from 'graphql-tag'; const R_NAME = /name:\s*"([^"]+)"/; function R_DEF(directive) { return new RegExp(`\\s+\\@${directive}\\([^)]+\\)`, 'gm'); } class CSharpOperationsVisitor extends ClientSideBaseVisitor { constructor(schema, fragments, rawConfig, documents) { super(schema, fragments, rawConfig, { namedClient: rawConfig.namedClient, serviceName: rawConfig.serviceName, querySuffix: rawConfig.querySuffix, mutationSuffix: rawConfig.mutationSuffix, subscriptionSuffix: rawConfig.subscriptionSuffix, }, documents); this._operationsToInclude = []; autoBind(this); } _operationHasDirective(operation, directive) { if (typeof operation === 'string') { return operation.includes(`${directive}`); } let found = false; visit(operation, { Directive(node) { if (node.name.value === directive) { found = true; } }, }); return found; } _extractDirective(operation, directive) { const directives = print(operation).match(R_DEF(directive)); if (directives.length > 1) { throw new Error(`The ${directive} directive used multiple times in '${operation.name}' operation`); } return directives[0]; } _namedClient(operation) { let name; if (this._operationHasDirective(operation, 'namedClient')) { name = this._extractNamedClient(operation); } else if (this.config.namedClient) { name = this.config.namedClient; } return name ? `client = '${name}';` : ''; } _extractNamedClient(operation) { const [, name] = this._extractDirective(operation, 'namedClient').match(R_NAME); return name; } _gql(node) { const fragments = this._transformFragments(node); let doc = this._prepareDocument(` ${print(node).split('\\').join('\\\\')} ${this._includeFragments(fragments)}`); doc = doc.replace(/"/g, '""'); if (this.config.documentMode === DocumentMode.string) { return '@"' + doc + '"'; } return '@"' + doc + '"'; } _getDocumentNodeVariable(node, documentVariableName) { return this.config.documentMode === DocumentMode.external ? `Operations.${node.name.value}` : documentVariableName; } _operationSuffix(operationType) { const defaultSuffix = 'GQL'; switch (operationType) { case 'Query': return this.config.querySuffix || defaultSuffix; case 'Mutation': return this.config.mutationSuffix || defaultSuffix; case 'Subscription': return this.config.subscriptionSuffix || defaultSuffix; default: return defaultSuffix; } } buildOperation(node, documentVariableName, operationType, operationResultType, operationVariablesTypes) { const serviceName = `${this.convertName(node)}${this._operationSuffix(operationType)}`; this._operationsToInclude.push({ node, documentVariableName, operationType, operationResultType, operationVariablesTypes, serviceName, }); const content = ` public class ${serviceName}{ public static GraphQLRequest get${serviceName}() { return new GraphQLRequest { Query = ${this._getDocumentNodeVariable(node, documentVariableName)}, OperationName = "${this.convertName(node)}" }; } ${this._namedClient(node)} } `; return content; } get sdkClass() { const actionType = operation => { switch (operation) { case 'Mutation': return 'mutate'; case 'Subscription': return 'subscribe'; default: return 'fetch'; } }; const allPossibleActions = this._operationsToInclude .map(o => { const optionalVariables = !o.node.variableDefinitions || o.node.variableDefinitions.length === 0 || o.node.variableDefinitions.every(v => v.type.kind !== Kind.NON_NULL_TYPE || !!v.defaultValue); const options = o.operationType === 'Mutation' ? `${o.operationType}OptionsAlone<${o.operationResultType}, ${o.operationVariablesTypes}>` : `${o.operationType}OptionsAlone<${o.operationVariablesTypes}>`; const method = ` ${camelCase(o.node.name.value)}(variables${optionalVariables ? '?' : ''}: ${o.operationVariablesTypes}, options?: ${options}) { return this.${camelCase(o.serviceName)}.${actionType(o.operationType)}(variables, options) }`; let watchMethod; if (o.operationType === 'Query') { watchMethod = ` ${camelCase(o.node.name.value)}Watch(variables${optionalVariables ? '?' : ''}: ${o.operationVariablesTypes}, options?: WatchQueryOptionsAlone<${o.operationVariablesTypes}>) { return this.${camelCase(o.serviceName)}.watch(variables, options) }`; } return [method, watchMethod].join(''); }) .map(s => indentMultiline(s, 2)); const injectString = (service) => `private ${camelCase(service)}: ${service}`; const injections = this._operationsToInclude .map(op => injectString(op.serviceName)) .map(s => indentMultiline(s, 3)) .join(',\n'); const serviceName = this.config.serviceName || 'GraphQLSDK'; return ` type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>; interface WatchQueryOptionsAlone<V> extends Omit<ApolloCore.WatchQueryOptions<V>, 'query' | 'variables'> {} interface QueryOptionsAlone<V> extends Omit<ApolloCore.QueryOptions<V>, 'query' | 'variables'> {} interface MutationOptionsAlone<T, V> extends Omit<ApolloCore.MutationOptions<T, V>, 'mutation' | 'variables'> {} interface SubscriptionOptionsAlone<V> extends Omit<ApolloCore.SubscriptionOptions<V>, 'query' | 'variables'> {} public class ${serviceName} { constructor( ${injections} ) {} ${allPossibleActions.join('\n')} }`; } OperationDefinition(node) { if (!node.name || !node.name.value) { return null; } this._collectedOperations.push(node); const documentVariableName = this.convertName(node, { suffix: this.config.documentVariableSuffix, prefix: this.config.documentVariablePrefix, useTypesPrefix: false, }); let documentString = ''; if (this.config.documentMode !== DocumentMode.external) { const isDocumentNode = this.config.documentMode === DocumentMode.documentNode; documentString = `${this.config.noExport ? '' : 'public'} static string ${documentVariableName}${isDocumentNode ? ': DocumentNode' : ''} = ${this._gql(node)};`; } const operationType = node.operation; const operationTypeSuffix = this.config.dedupeOperationSuffix && node.name.value.toLowerCase().endsWith(node.operation) ? '' : !operationType ? '' : operationType; const operationResultType = this.convertName(node, { suffix: operationTypeSuffix + this._parsedConfig.operationResultSuffix, }); const operationVariablesTypes = this.convertName(node, { suffix: operationTypeSuffix + 'Variables', }); const serviceName = `${this.convertName(node)}${this._operationSuffix(operationType)}`; this._operationsToInclude.push({ node, documentVariableName, operationType, operationResultType, operationVariablesTypes, serviceName, }); const content = ` public class ${serviceName}{ public static GraphQLRequest get${serviceName}() { return new GraphQLRequest { Query = ${this._getDocumentNodeVariable(node, documentVariableName)}, OperationName = "${this.convertName(node)}" }; } ${this._namedClient(node)} ${documentString} } `; return [content].filter(a => a).join('\n'); } } const plugin = (schema, documents, config) => { const openNameSpace = 'namespace GraphQLCodeGen {'; const allAst = concatAST(documents.map(v => v.document)); const allFragments = [ ...allAst.definitions.filter(d => d.kind === Kind.FRAGMENT_DEFINITION).map(fragmentDef => ({ node: fragmentDef, name: fragmentDef.name.value, onType: fragmentDef.typeCondition.name.value, isExternal: false, })), ...(config.externalFragments || []), ]; const visitor = new CSharpOperationsVisitor(schema, allFragments, config, documents); const visitorResult = visit(allAst, { leave: visitor }); return { prepend: [], content: [openNameSpace, visitor.fragments, ...visitorResult.definitions.filter(t => typeof t === 'string'), '}'] .filter(a => a) .join('\n'), }; }; const addToSchema = gql ` directive @namedClient(name: String!) on OBJECT | FIELD `; const validate = async (schema, documents, config, outputFile) => { if (extname(outputFile) !== '.cs') { throw new Error(`Plugin "c-sharp-operations" requires extension to be ".cs"!`); } }; export { CSharpOperationsVisitor, addToSchema, plugin, validate }; //# sourceMappingURL=index.esm.js.map