import { compress } from '../compress.js'; import type { GqlEnum } from './enum.js'; import { GQL_FRAGMENT, GQL_TEMPLATE, isGqlFragment, isGqlTemplate, isGqlVariable } from './symbols.js'; import { GqlTemplate } from './template.js'; import { formatType, type GqlType } from './type.js'; /** 表示一个 GQL Fragment */ export interface GqlFragment { /** Fragment 的 GQL 类型 */ [GQL_FRAGMENT]: GqlType; /** Fragment 内容 */ fragment: string; } /** GQL Fragment 模板可填充的类型 */ export type GqlFragmentTemplateValue = null | string | number | boolean | bigint | GqlEnum | GqlFragment | GqlTemplate | GqlTemplate[]; /** 创建一个 GQL Fragment */ export type GqlFragmentBuilder = ( template: TemplateStringsArray, ...args: GqlFragmentTemplateValue[] ) => GqlFragment; /** 构建模板 */ function buildTemplate(template: GqlTemplate): string { if (!isGqlTemplate(template)) throw new TypeError(`Expected a GqlTemplate`); let ret = ''; for (let i = 0; i < template[GQL_TEMPLATE].length; i++) { ret += template.parts[i]!; const temp = template[GQL_TEMPLATE][i]!; if (typeof temp == 'string') { ret += temp; } else if (isGqlVariable(temp)) { throw new Error('fragment cannot contain variables'); } else if (isGqlFragment(temp)) { ret += `on ${temp[GQL_FRAGMENT]}{${temp.fragment}}`; } else if (Array.isArray(temp)) { for (const t of temp) { ret += buildTemplate(t); } } else { ret += buildTemplate(temp); } } ret += template.parts[template[GQL_TEMPLATE].length]; return ret; } /** * 创建一个 GQL Fragment,使用模板字符串表示内容 * @example GqlFragment('Model')`rid name access(user: ${'Maxwell'})` */ export function GqlFragment(type: T): GqlFragmentBuilder; /** * 创建一个 GQL Fragment,使用字符串表示内容 * @example GqlFragment('Model','rid name access(user: "Maxwell")') */ export function GqlFragment(type: T, fragment: string): GqlFragment; /** * 创建一个 GQL Fragment,使用字符串序列表示内容 * @example GqlFragment('Model',['rid', 'name', 'access(user: "Maxwell")']) */ export function GqlFragment(type: T, fields: Iterable): GqlFragment; /** 创建一个 GQL Fragment */ export function GqlFragment( type: T, fragment?: string | Iterable, ): GqlFragment | GqlFragmentBuilder { const fragmentType = formatType(type, true); if (fragment != null) { const isFragment = typeof fragment == 'string'; const isIterable = !isFragment && fragment != null && typeof fragment[Symbol.iterator] == 'function'; if (!isFragment && !isIterable) throw new TypeError('fragment must be a string or iterable'); let frag: string; if (isFragment) frag = compress(fragment).trim(); else { const fragmentSet = new Set(); for (const field of fragment) { const compressed = compress(field).trim(); if (compressed) fragmentSet.add(compressed); } frag = [...fragmentSet].join(' '); } if (!frag) throw new Error(`${isFragment ? 'fragment' : 'fields'} must not be empty`); return { [GQL_FRAGMENT]: fragmentType, fragment: frag, }; } return function GqlFragmentBuilder(template, ...args) { const fragment = buildTemplate(GqlTemplate(template, args)).trim(); if (!fragment) throw new Error('fragment must not be empty'); return { [GQL_FRAGMENT]: fragmentType, fragment, }; }; }