UNPKG

@backland/schema

Version:

TypeScript schema declaration and validation library with static type inference

142 lines (138 loc) 7.59 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.generateClientUtils = generateClientUtils; exports.saveGraphQLClientUtils = saveGraphQLClientUtils; var fs = _interopRequireWildcard(require("fs-extra")); var _CircularDeps = require("../CircularDeps"); var _ObjectType = require("../ObjectType"); var _createGraphQLSchema = require("../createGraphQLSchema"); var _getQueryTemplates = require("./getQueryTemplates"); function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } async function generateClientUtils(schema, options) { const [tsParts, queryTemplates] = await Promise.all([(0, _createGraphQLSchema.resolversTypescriptParts)({ name: 'GraphQLInterfaces', options: {}, resolvers: schema.utils.resolvers }), (0, _getQueryTemplates.getSchemaQueryTemplates)(schema, options)]); const header = ['// Autogenerated, do not edit by hand', '/* istanbul ignore file */', '/* eslint-disable */', // "import { GraphType } from '@backland/schema';\n\n", '\nexport type GraphQLClientError = { message: string, path: string[] };\n', // '\nexport type ID = number | string;\n', // `\nexport type GraphQLClientResponse<Result> = {data: Result, errors: null} | {data: null, errors: GraphQLClientError[]}\n`]; let clientInterface = ``; clientInterface += `\nexport interface ExpectedGraphQLClient {\n`; let helpersText = `\n\n`; helpersText += `\nexport const graphqlClientHelpers = {\n`; const commonTypings = new Map(); // adding the input (args) and payload types for each resolver tsParts.lines.forEach(({ payloadName, args, payload, inputName }) => { commonTypings.set(payloadName, `export type ${payloadName} = ${payload.code}`); commonTypings.set(inputName, `export type ${inputName} = ${args.code}`); }); // adding the GraphqlClient interface for each resolver tsParts.lines.forEach(({ payloadName, inputName, resolver: { description, deprecationReason, name, args: resolverArgs } }) => { clientInterface += description ? `/**\n${description}\n**/` : ''; clientInterface += deprecationReason ? `/**\n@deprecated\n${deprecationReason}\n**/` : ''; const argsText = resolverArgs ? `args: ${inputName}` : `args?: ${inputName}`; clientInterface += `\n${name}: {${argsText}, payload: GraphQLClientResponse<${payloadName}>},`; clientInterface += `\n`; }); // adding the query and fragment texts for each resolver tsParts.lines.forEach(({ payloadName, inputName, resolver: { name, typeDef, kind, argsDef } }) => { helpersText += `\n${name}: {\n name: "${name}", \n`; helpersText += `kind: '${kind}',payload: ${rehydrateType(payloadName, typeDef)},\n`; helpersText += `\ninput: ${rehydrateType(inputName, argsDef ? { object: argsDef } : { record: { keyType: 'string', type: 'unknown' } })},\n`; const resolverQueries = queryTemplates.queryByResolver[kind][name]; helpersText += `\noperation: ${JSON.stringify({ query: resolverQueries.fullQuery, varNames: resolverQueries.argsParsed.vars.reduce((acc, next) => { return { ...acc, [next.name]: { ...next, // example -> "limit": "cashbackIntervals_limit" varName: next.varName.replace(/^\$/, '') } }; }, {}) }, null, 2)} as const,\n`; helpersText += `\n},\n`; }); clientInterface += `\n}\n`; helpersText += `\n} as const;\n`; helpersText += genClientBody(); const result = [header.join('\n\n'), // [...commonTypings.values()].join('\n\n'), clientInterface, helpersText].join('\n'); return _CircularDeps.CircularDeps.prettier.format(result, { parser: 'typescript', singleQuote: true }); } let creating = false; async function saveGraphQLClientUtils(schema, DEST) { if (creating) return; creating = true; const exists = fs.existsSync(DEST); const now = Date.now(); const mtime = exists ? fs.statSync(DEST).mtimeMs : 0; const diff = now - mtime; if (diff < 3000) return; console.info(`saveGraphQLTypescript in progress.`); const ts = await generateClientUtils(schema); if (exists) { await fs.remove(DEST); } await fs.ensureFile(DEST); await fs.writeFile(DEST, ts); creating = false; console.info(`generated in ${DEST}`); } function rehydrateType(name, field) { const parsed = (0, _ObjectType.parseFieldDefinitionConfig)(field, { deep: { omitMeta: true } }); const json = JSON.stringify(parsed); return `GraphType.getOrSet("${name}", ${json} as const)`; } function genClientBody() { return ['', 'export type GraphqlClientHelpers = typeof graphqlClientHelpers;', 'export type GraphQLEntry = GraphqlClientHelpers[keyof GraphqlClientHelpers];', '', "export type GraphQLFetchParams<K extends GraphQLEntry['name']> = {", ' operationInfo: GraphqlClientHelpers[K];', ' operationName: K;', " getBody: (args: ExpectedGraphQLClient[K]['args']) => {", ' query: string;', ' variables: Record<string, any>;', ' operationName: K;', ' };', " parseArgs: (args: ExpectedGraphQLClient[K]['args']) => Record<string, any>;", " mountBodyString(args: ExpectedGraphQLClient[K]['args']): string;", '};', '', "export function getGraphQLFetchHelpers<MethodName extends GraphQLEntry['name']>(", ' methodName: MethodName,', '): GraphQLFetchParams<MethodName> {', ' const helpers = graphqlClientHelpers[methodName];', '', ' function parseArgs(args: any) {', ' const vars: Record<string, any> = {};', ' const parsedArgs: any = helpers.input.parse(args || {}, (_, error) => {', ' return `\\nGraphQLClientArgumentsError: method ${methodName}: \\n${error.message}`;', ' });', '', ' Object.entries(helpers.operation.varNames).forEach(([inputVarName, { varName }]) => {', ' vars[varName] = parsedArgs[inputVarName];', ' });', '', ' return vars;', ' }', '', ' function getBody(args: any) {', ' const variables = parseArgs(args);', ' return {', ' query: helpers.operation.query,', ' variables,', ' operationName: methodName,', ' };', ' }', '', ' function mountBodyString(args: any) {', ' const body = getBody(args);', ' return JSON.stringify(body);', ' }', '', ' return {', ' operationInfo: helpers,', ' operationName: methodName,', ' parseArgs,', ' getBody,', ' mountBodyString,', ' };', '}', ''].join('\n'); } //# sourceMappingURL=generateClientUtils.js.map