import { compress } from '../compress.js'; import type { GraphqlRequestData } from '../interfaces.js'; import type { GqlFragment } from './fragment.js'; import { isGqlVariable, isGqlFragment, isGqlTemplate, GQL_TEMPLATE, GQL_VARIABLE, GQL_FRAGMENT } from './symbols.js'; import { GqlTemplate, type GqlTemplateContent, type GqlTemplateValue } from './template.js'; import type { GqlVariable } from './variable.js'; const CHARS = [...'abcdefghijklmnopqrstuvwxyz']; /** 创建变量名 */ function createVariableName(index: number): string { if (index < CHARS.length) return `_` + CHARS[index]!; return `${createVariableName(Math.trunc(index / CHARS.length) - 1)}${CHARS[index % CHARS.length]!}`; } /** 构造模板 */ export function template(operation: string, query: TemplateStringsArray, args: GqlTemplateValue[]): GraphqlRequestData { if (args.length === 0) { return { query: `${operation}{${compress(query.raw[0]!).trim()}}` }; } const fragments = new Map(); const variables = new Map(); let queryBody = ''; const appendPart = (part: GqlTemplateContent): void => { if (isGqlVariable(part)) { let name = variables.get(part); if (!name) { name = createVariableName(variables.size); variables.set(part, name); } queryBody += `$${name}`; } else if (isGqlFragment(part)) { let name = fragments.get(part); if (!name) { name = createVariableName(fragments.size); fragments.set(part, name); } queryBody += name; } else if (isGqlTemplate(part)) { const args = part[GQL_TEMPLATE]; const { parts } = part; for (let i = 0; i < args.length; ++i) { queryBody += parts[i]!; appendPart(args[i]!); } queryBody += parts.at(-1); } else if (Array.isArray(part)) { for (const item of part) { appendPart(item); } } else { queryBody += part; } }; appendPart(GqlTemplate(query, args)); let queryVariables = ''; const map: NonNullable = {}; for (const [{ value, [GQL_VARIABLE]: type }, name] of variables) { map[name] = value ?? null; if (queryVariables) queryVariables += ','; queryVariables += `$${name}:${type}`; } if (queryVariables) queryVariables = `(${queryVariables})`; let queryFragments = ''; for (const [{ fragment, [GQL_FRAGMENT]: type }, name] of fragments) { queryFragments += `fragment ${name} on ${type}{${fragment}}`; } return { query: `${queryFragments}${operation}${queryVariables}{${queryBody.trim()}}`, variables: queryVariables ? map : undefined, }; }