@xyd-js/gql 0.1.0-xyd.5 → 0.1.0-xyd.54
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +450 -0
- package/LICENSE +21 -0
- package/TODO.md +8 -0
- package/__fixtures__/-1.opendocs.docs-nested/input.graphql +66 -0
- package/__fixtures__/-1.opendocs.docs-nested/output.json +554 -0
- package/__fixtures__/-1.opendocs.flat/input.graphql +19 -0
- package/__fixtures__/-1.opendocs.flat/output.json +243 -0
- package/__fixtures__/-1.opendocs.scopes/input.graphql +33 -0
- package/__fixtures__/-1.opendocs.scopes/output.json +378 -0
- package/__fixtures__/-1.opendocs.sidebar/input.graphql +44 -0
- package/__fixtures__/-1.opendocs.sort/input.graphql +92 -0
- package/__fixtures__/-1.opendocs.sort/output.json +1078 -0
- package/__fixtures__/-1.opendocs.sort+group/input.graphql +111 -0
- package/__fixtures__/-1.opendocs.sort+group/output.json +1114 -0
- package/__fixtures__/-1.opendocs.sort+group+path/input.graphql +118 -0
- package/__fixtures__/-1.opendocs.sort+group+path/output.json +1114 -0
- package/__fixtures__/-2.complex.github/input.graphql +69424 -0
- package/__fixtures__/-2.complex.github/output.json +269874 -0
- package/__fixtures__/-2.complex.livesession/input.graphql +23 -0
- package/__fixtures__/-2.complex.livesession/output.json +302 -0
- package/__fixtures__/-2.complex.monday/input.graphql +6089 -0
- package/__fixtures__/-2.complex.monday/output.json +1 -0
- package/__fixtures__/-3.array-non-null-return/input.graphql +9 -0
- package/__fixtures__/-3.array-non-null-return/output.json +151 -0
- package/__fixtures__/1.basic/input.graphql +118 -0
- package/__fixtures__/1.basic/output.json +630 -0
- package/__fixtures__/2.circular/input.graphql +17 -0
- package/__fixtures__/2.circular/output.json +248 -0
- package/__fixtures__/3.opendocs/input.graphql +27 -0
- package/__fixtures__/3.opendocs/output.json +338 -0
- package/__fixtures__/4.union/input.graphql +19 -0
- package/__fixtures__/4.union/output.json +344 -0
- package/__fixtures__/5.flat/input.graphql +27 -0
- package/__fixtures__/5.flat/output.json +383 -0
- package/__fixtures__/6.default-values/input.graphql +47 -0
- package/__fixtures__/6.default-values/output.json +655 -0
- package/__fixtures__/7.type-args/input.graphql +19 -0
- package/__fixtures__/7.type-args/output.json +301 -0
- package/__fixtures__/8.default-sort/input.graphql +60 -0
- package/__fixtures__/8.default-sort/output.json +1078 -0
- package/__tests__/gqlSchemaToReferences.test.ts +109 -0
- package/__tests__/utils.ts +45 -0
- package/declarations.d.ts +4 -0
- package/dist/index.d.ts +17 -1
- package/dist/index.js +1334 -19871
- package/dist/index.js.map +1 -1
- package/dist/opendocs.graphql +56 -0
- package/package.json +7 -9
- package/src/context.ts +17 -0
- package/src/converters/gql-arg.ts +51 -0
- package/src/converters/gql-enum.ts +27 -0
- package/src/converters/gql-field.ts +164 -0
- package/src/converters/gql-input.ts +34 -0
- package/src/converters/gql-interface.ts +35 -0
- package/src/converters/gql-mutation.ts +36 -0
- package/src/converters/gql-object.ts +83 -0
- package/src/converters/gql-operation.ts +128 -0
- package/src/converters/gql-query.ts +36 -0
- package/src/converters/gql-sample.ts +159 -0
- package/src/converters/gql-scalar.ts +16 -0
- package/src/converters/gql-subscription.ts +36 -0
- package/src/converters/gql-types.ts +195 -0
- package/src/converters/gql-union.ts +40 -0
- package/src/gql-core.ts +362 -0
- package/src/opendocs.graphql +56 -0
- package/src/opendocs.ts +253 -0
- package/src/schema.ts +253 -67
- package/src/types.ts +103 -0
- package/src/utils.ts +21 -96
- package/tsconfig.json +1 -1
- package/tsup.config.ts +15 -1
- package/vitest.config.ts +15 -1
- package/examples/basic/index.ts +0 -12
- package/examples/basic/schema.graphqls +0 -89
- package/examples/basic/todo-app.graphqls +0 -184
- package/examples/graphql-types/graphql-types.0.basic.graphqls +0 -28
- package/examples/nested/nested-arg.0.not-required.graphqls +0 -8
- package/examples/nested/nested-arg.0.required.graphqls +0 -8
- package/examples/nested/nested-arg.1.deep.graphqls +0 -12
- package/src/hydration/README.md +0 -1
- package/src/hydration/gql-arg.ts +0 -53
- package/src/hydration/gql-field.ts +0 -206
- package/src/hydration/gql-input.ts +0 -67
- package/src/hydration/gql-object.ts +0 -35
- package/src/hydration/gql-types.ts +0 -50
- package/src/samples/index.ts +0 -95
- package/test/graphql-types.0.test.ts +0 -125
- package/test/nested-arg.0.test.ts +0 -208
- package/test/nested-arg.1.test.ts +0 -19
package/src/gql-core.ts
ADDED
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
import {
|
|
2
|
+
GraphQLArgument,
|
|
3
|
+
GraphQLEnumType,
|
|
4
|
+
GraphQLEnumValue,
|
|
5
|
+
GraphQLInputObjectType,
|
|
6
|
+
GraphQLInterfaceType,
|
|
7
|
+
GraphQLObjectType,
|
|
8
|
+
GraphQLScalarType,
|
|
9
|
+
GraphQLUnionType,
|
|
10
|
+
GraphQLField,
|
|
11
|
+
GraphQLInputField,
|
|
12
|
+
GraphQLNamedType,
|
|
13
|
+
|
|
14
|
+
isIntrospectionType,
|
|
15
|
+
isSpecifiedScalarType, GraphQLNonNull,
|
|
16
|
+
GraphQLList
|
|
17
|
+
} from "graphql";
|
|
18
|
+
import GithubSlugger from 'github-slugger';
|
|
19
|
+
|
|
20
|
+
import {
|
|
21
|
+
Definition,
|
|
22
|
+
DefinitionProperty,
|
|
23
|
+
DefinitionPropertyMeta,
|
|
24
|
+
ExampleGroup,
|
|
25
|
+
Reference,
|
|
26
|
+
ReferenceCategory,
|
|
27
|
+
ReferenceType
|
|
28
|
+
} from "@xyd-js/uniform";
|
|
29
|
+
|
|
30
|
+
import {GQLTypeInfo, GQLOperation} from "./types";
|
|
31
|
+
import {gqlFieldToUniformDefinitionProperty} from "./converters/gql-field";
|
|
32
|
+
import {Context} from "./context";
|
|
33
|
+
import {openDocsCanonical, openDocsToGroup} from "./opendocs";
|
|
34
|
+
|
|
35
|
+
export type GraphqlUniformReferenceType =
|
|
36
|
+
| GraphQLScalarType
|
|
37
|
+
| GraphQLObjectType
|
|
38
|
+
| GraphQLInterfaceType
|
|
39
|
+
| GraphQLUnionType
|
|
40
|
+
| GraphQLEnumType
|
|
41
|
+
| GraphQLInputObjectType
|
|
42
|
+
| GQLOperation
|
|
43
|
+
| GraphQLField<any, any> | GraphQLInputField | GraphQLArgument
|
|
44
|
+
|
|
45
|
+
export function extractScopesFromDocDirective(
|
|
46
|
+
ctx: Context,
|
|
47
|
+
gqlType: GraphqlUniformReferenceType,
|
|
48
|
+
): string[] {
|
|
49
|
+
const scopes: string[] = []
|
|
50
|
+
|
|
51
|
+
const schema = ctx?.schema
|
|
52
|
+
|
|
53
|
+
if (gqlType.astNode?.directives) {
|
|
54
|
+
for (const directive of gqlType.astNode.directives) {
|
|
55
|
+
if (directive.name.value === "doc") {
|
|
56
|
+
const scopesArg = directive.arguments?.find(arg => arg.name.value === 'scopes')
|
|
57
|
+
if (scopesArg?.value.kind === 'ListValue') {
|
|
58
|
+
for (const scopeValue of scopesArg.value.values) {
|
|
59
|
+
if (scopeValue.kind === 'EnumValue') {
|
|
60
|
+
// For enum values, we need to find the enum type and its value with @scope directive
|
|
61
|
+
let enumType: GraphQLEnumType | undefined
|
|
62
|
+
|
|
63
|
+
// First check if current type is an enum
|
|
64
|
+
if (gqlType instanceof GraphQLEnumType) {
|
|
65
|
+
enumType = gqlType
|
|
66
|
+
}
|
|
67
|
+
// If not and we have a schema, try to find the enum type
|
|
68
|
+
else if (schema) {
|
|
69
|
+
const type = schema.getType('OpenDocsScope')
|
|
70
|
+
if (type instanceof GraphQLEnumType) {
|
|
71
|
+
enumType = type
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (enumType) {
|
|
76
|
+
const enumValue = enumType.getValue(scopeValue.value)
|
|
77
|
+
if (enumValue?.astNode?.directives) {
|
|
78
|
+
for (const enumDirective of enumValue.astNode.directives) {
|
|
79
|
+
if (enumDirective.name.value === 'scope') {
|
|
80
|
+
const valueArg = enumDirective.arguments?.find(arg => arg.name.value === 'value')
|
|
81
|
+
if (valueArg?.value.kind === 'StringValue') {
|
|
82
|
+
scopes.push(valueArg.value.value)
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
} else if (scopeValue.kind === 'StringValue') {
|
|
89
|
+
// Handle string literal (e.g. "user:write")
|
|
90
|
+
scopes.push(scopeValue.value)
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return scopes
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function uniformify(
|
|
102
|
+
ctx: Context,
|
|
103
|
+
gqlType: GraphqlUniformReferenceType,
|
|
104
|
+
definitions: Definition[],
|
|
105
|
+
examples: ExampleGroup[],
|
|
106
|
+
): Reference {
|
|
107
|
+
let canonicalPrefix = ""
|
|
108
|
+
let graphqlTypeShort = ""
|
|
109
|
+
let refType: ReferenceType | undefined = undefined
|
|
110
|
+
|
|
111
|
+
if (gqlType instanceof GraphQLScalarType) {
|
|
112
|
+
canonicalPrefix = "scalars"
|
|
113
|
+
graphqlTypeShort = "scalar"
|
|
114
|
+
refType = ReferenceType.GRAPHQL_SCALAR
|
|
115
|
+
} else if (gqlType instanceof GraphQLObjectType) {
|
|
116
|
+
canonicalPrefix = "objects"
|
|
117
|
+
graphqlTypeShort = "object"
|
|
118
|
+
refType = ReferenceType.GRAPHQL_OBJECT
|
|
119
|
+
} else if (gqlType instanceof GraphQLInterfaceType) {
|
|
120
|
+
canonicalPrefix = "interfaces"
|
|
121
|
+
graphqlTypeShort = "interface"
|
|
122
|
+
refType = ReferenceType.GRAPHQL_INTERFACE
|
|
123
|
+
} else if (gqlType instanceof GraphQLUnionType) {
|
|
124
|
+
canonicalPrefix = "unions"
|
|
125
|
+
graphqlTypeShort = "union"
|
|
126
|
+
refType = ReferenceType.GRAPHQL_UNION
|
|
127
|
+
} else if (gqlType instanceof GraphQLEnumType) {
|
|
128
|
+
canonicalPrefix = "enums"
|
|
129
|
+
graphqlTypeShort = "enum"
|
|
130
|
+
refType = ReferenceType.GRAPHQL_ENUM
|
|
131
|
+
} else if (gqlType instanceof GraphQLInputObjectType) {
|
|
132
|
+
canonicalPrefix = "inputs"
|
|
133
|
+
graphqlTypeShort = "input"
|
|
134
|
+
refType = ReferenceType.GRAPHQL_INPUT
|
|
135
|
+
} else if (gqlType instanceof GQLOperation) {
|
|
136
|
+
switch (gqlType._operationType) {
|
|
137
|
+
case "query": {
|
|
138
|
+
canonicalPrefix = "queries"
|
|
139
|
+
graphqlTypeShort = "query"
|
|
140
|
+
refType = ReferenceType.GRAPHQL_QUERY
|
|
141
|
+
break;
|
|
142
|
+
}
|
|
143
|
+
case "mutation": {
|
|
144
|
+
canonicalPrefix = "mutations"
|
|
145
|
+
graphqlTypeShort = "mutation"
|
|
146
|
+
refType = ReferenceType.GRAPHQL_MUTATION
|
|
147
|
+
break;
|
|
148
|
+
}
|
|
149
|
+
case "subscription": {
|
|
150
|
+
canonicalPrefix = "subscriptions"
|
|
151
|
+
graphqlTypeShort = "subscription"
|
|
152
|
+
refType = ReferenceType.GRAPHQL_SUBSCRIPTION
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
} else {
|
|
157
|
+
const info = gqlFieldTypeInfo(gqlType)
|
|
158
|
+
if (info?.typeFlat && !isBuiltInType(info?.typeFlat)) {
|
|
159
|
+
if (info?.typeFlat) {
|
|
160
|
+
return uniformify(ctx, info.typeFlat, definitions, examples)
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const slugger = new GithubSlugger();
|
|
166
|
+
// const slug = slugger.slug(gqlType.name);
|
|
167
|
+
const slug = gqlType.name;
|
|
168
|
+
|
|
169
|
+
const odCanonical = openDocsCanonical(ctx, gqlType);
|
|
170
|
+
|
|
171
|
+
let canonical = ""
|
|
172
|
+
if (odCanonical) {
|
|
173
|
+
canonical = odCanonical;
|
|
174
|
+
} else if (canonicalPrefix) {
|
|
175
|
+
canonical = [canonicalPrefix, slug].join("/")
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const scopes = extractScopesFromDocDirective(ctx, gqlType) || []
|
|
179
|
+
return {
|
|
180
|
+
title: gqlType.name,
|
|
181
|
+
description: gqlType.description || "",
|
|
182
|
+
canonical,
|
|
183
|
+
|
|
184
|
+
category: ReferenceCategory.GRAPHQL,
|
|
185
|
+
type: refType,
|
|
186
|
+
|
|
187
|
+
context: {
|
|
188
|
+
graphqlTypeShort: graphqlTypeShort,
|
|
189
|
+
graphqlName: gqlType.name,
|
|
190
|
+
group: openDocsToGroup(ctx, gqlType),
|
|
191
|
+
scopes
|
|
192
|
+
},
|
|
193
|
+
|
|
194
|
+
examples: {
|
|
195
|
+
groups: examples || [],
|
|
196
|
+
},
|
|
197
|
+
|
|
198
|
+
definitions,
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export function propsUniformify(
|
|
203
|
+
ctx: Context,
|
|
204
|
+
field: GraphQLField<any, any> | GraphQLInputField | GraphQLArgument,
|
|
205
|
+
properties?: DefinitionProperty[],
|
|
206
|
+
meta?: DefinitionPropertyMeta[]
|
|
207
|
+
): DefinitionProperty {
|
|
208
|
+
const fieldInfo = gqlFieldTypeInfo(field);
|
|
209
|
+
const objRef = uniformify(ctx, field, [], []);
|
|
210
|
+
|
|
211
|
+
const builtInType = fieldInfo?.typeFlat ? isBuiltInType(fieldInfo?.typeFlat) : undefined
|
|
212
|
+
|
|
213
|
+
return {
|
|
214
|
+
name: field.name,
|
|
215
|
+
type: field.type.toJSON(),
|
|
216
|
+
description: field.description || "",
|
|
217
|
+
context: {
|
|
218
|
+
graphqlName: field.name,
|
|
219
|
+
graphqlTypeFlat: fieldInfo.typeFlat?.toJSON() || "",
|
|
220
|
+
graphqlBuiltInType: builtInType,
|
|
221
|
+
},
|
|
222
|
+
properties: properties || [],
|
|
223
|
+
meta: [
|
|
224
|
+
...gqlFieldToUniformMeta(field),
|
|
225
|
+
...(meta || []),
|
|
226
|
+
],
|
|
227
|
+
symbolDef: {
|
|
228
|
+
canonical: objRef?.canonical,
|
|
229
|
+
},
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export function gqlObjectPropsUniformify(
|
|
234
|
+
ctx: Context,
|
|
235
|
+
obj: GraphQLInputObjectType | GraphQLObjectType,
|
|
236
|
+
meta?: DefinitionPropertyMeta[]
|
|
237
|
+
): DefinitionProperty {
|
|
238
|
+
const objRef = uniformify(ctx, obj, [], []);
|
|
239
|
+
const inputFields = obj.getFields?.()
|
|
240
|
+
const nestedProps: DefinitionProperty[] = []
|
|
241
|
+
|
|
242
|
+
const nestedDefinitionProperty: DefinitionProperty = {
|
|
243
|
+
name: obj.name,
|
|
244
|
+
type: obj.toJSON(),
|
|
245
|
+
description: obj.description || "",
|
|
246
|
+
context: objRef.context,
|
|
247
|
+
properties: nestedProps,
|
|
248
|
+
meta: [
|
|
249
|
+
...(meta || []),
|
|
250
|
+
],
|
|
251
|
+
symbolDef: {
|
|
252
|
+
canonical: objRef.canonical,
|
|
253
|
+
},
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
if (!ctx?.config?.flatArg) {
|
|
257
|
+
for (const [name, inputField] of Object.entries(inputFields)) {
|
|
258
|
+
const prop = gqlFieldToUniformDefinitionProperty(
|
|
259
|
+
ctx,
|
|
260
|
+
inputField,
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
if (prop) {
|
|
264
|
+
nestedProps.push(prop)
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
return nestedDefinitionProperty
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
export function gqlFieldToUniformMeta(
|
|
273
|
+
field: GraphQLField<any, any> | GraphQLInputField | GraphQLArgument
|
|
274
|
+
): DefinitionPropertyMeta[] {
|
|
275
|
+
const meta: DefinitionPropertyMeta[] = []
|
|
276
|
+
|
|
277
|
+
// Check if field is required (non-null)
|
|
278
|
+
if (isNonNullField(field.type) || isListOfNonNullItems(field.type)) {
|
|
279
|
+
meta.push({
|
|
280
|
+
name: "required",
|
|
281
|
+
value: "true"
|
|
282
|
+
})
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Handle directives
|
|
286
|
+
if (field.astNode?.directives) {
|
|
287
|
+
for (const directive of field.astNode.directives) {
|
|
288
|
+
// Handle @deprecated directive
|
|
289
|
+
if (directive.name.value === "deprecated") {
|
|
290
|
+
let foundDeprecatedReason = false
|
|
291
|
+
for (const arg of directive.arguments || []) {
|
|
292
|
+
if (arg.name.value === "reason") {
|
|
293
|
+
foundDeprecatedReason = true
|
|
294
|
+
meta.push({
|
|
295
|
+
name: "deprecated",
|
|
296
|
+
value: arg.value.kind === 'StringValue' ? arg.value.value : "true"
|
|
297
|
+
})
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
if (!foundDeprecatedReason) {
|
|
302
|
+
meta.push({
|
|
303
|
+
name: "deprecated",
|
|
304
|
+
value: "true"
|
|
305
|
+
})
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
if ("defaultValue" in field && field.defaultValue !== undefined) {
|
|
312
|
+
meta.push({
|
|
313
|
+
name: "defaults",
|
|
314
|
+
value: field.defaultValue
|
|
315
|
+
})
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
return meta
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
export function gqlFieldTypeInfo(
|
|
322
|
+
field: GraphQLField<any, any> | GraphQLInputField
|
|
323
|
+
): GQLTypeInfo {
|
|
324
|
+
const getTypeInfo = (type: any): GraphQLNamedType | undefined => {
|
|
325
|
+
if (!type) return undefined;
|
|
326
|
+
|
|
327
|
+
// Handle non-null types
|
|
328
|
+
if (type instanceof GraphQLNonNull) {
|
|
329
|
+
return getTypeInfo(type.ofType);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
if (type instanceof GraphQLList) {
|
|
334
|
+
return getTypeInfo(type.ofType);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// Return the actual type
|
|
338
|
+
return type;
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
const graphqlTypeFlat = getTypeInfo(field.type);
|
|
342
|
+
|
|
343
|
+
return {
|
|
344
|
+
typeFlat: graphqlTypeFlat
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
export function isBuiltInType(gqlType: GraphQLNamedType): boolean {
|
|
349
|
+
return isSpecifiedScalarType(gqlType) || isIntrospectionType(gqlType)
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// TODO: fix any
|
|
353
|
+
function isNonNullField(type: any): boolean {
|
|
354
|
+
return type.constructor.name === "GraphQLNonNull"
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// TODO: fix any
|
|
358
|
+
function isListOfNonNullItems(type: any): boolean {
|
|
359
|
+
return "ofType" in type &&
|
|
360
|
+
type.constructor.name === "GraphQLList" &&
|
|
361
|
+
type.ofType.constructor.name === "GraphQLNonNull"
|
|
362
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
directive @scope(value: String!) on ENUM_VALUE
|
|
2
|
+
|
|
3
|
+
enum OpenDocsScope {
|
|
4
|
+
_PLACEHOLDER_
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
directive @docs(
|
|
8
|
+
route: String
|
|
9
|
+
sort: [OpenDocsSortInput!]
|
|
10
|
+
sortStack: [[String!]!]
|
|
11
|
+
flattenTypes: Boolean
|
|
12
|
+
sidebar: [OpenDocsPage]
|
|
13
|
+
group: [String]
|
|
14
|
+
) repeatable on SCHEMA
|
|
15
|
+
|
|
16
|
+
input OpenDocsPage {
|
|
17
|
+
type: OpenDocsSidebarItemType!
|
|
18
|
+
key: String!
|
|
19
|
+
group: String
|
|
20
|
+
path: String
|
|
21
|
+
pages: [OpenDocsPage]
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
enum OpenDocsSidebarItemType {
|
|
25
|
+
OPERATION
|
|
26
|
+
TYPE
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
directive @doc(
|
|
30
|
+
group: [String!],
|
|
31
|
+
path: String,
|
|
32
|
+
example: OpenDocsExampleInput,
|
|
33
|
+
scopes: [OpenDocsScope],
|
|
34
|
+
) on QUERY |
|
|
35
|
+
FIELD_DEFINITION |
|
|
36
|
+
MUTATION |
|
|
37
|
+
SUBSCRIPTION |
|
|
38
|
+
OBJECT |
|
|
39
|
+
INPUT_OBJECT |
|
|
40
|
+
ENUM |
|
|
41
|
+
SCALAR |
|
|
42
|
+
UNION
|
|
43
|
+
|
|
44
|
+
# TODO: finish
|
|
45
|
+
input OpenDocsExampleInput {
|
|
46
|
+
"""
|
|
47
|
+
The name of the code
|
|
48
|
+
"""
|
|
49
|
+
code: String!
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
input OpenDocsSortInput {
|
|
53
|
+
node: String
|
|
54
|
+
group: [String!]
|
|
55
|
+
stack: Int
|
|
56
|
+
}
|
package/src/opendocs.ts
ADDED
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import {
|
|
2
|
+
GraphQLSchema,
|
|
3
|
+
GraphQLArgument,
|
|
4
|
+
GraphQLEnumType,
|
|
5
|
+
GraphQLEnumValue,
|
|
6
|
+
GraphQLField,
|
|
7
|
+
GraphQLInputObjectType,
|
|
8
|
+
GraphQLInterfaceType,
|
|
9
|
+
GraphQLObjectType,
|
|
10
|
+
GraphQLScalarType,
|
|
11
|
+
GraphQLUnionType,
|
|
12
|
+
ConstValueNode,
|
|
13
|
+
StringValueNode,
|
|
14
|
+
} from "graphql";
|
|
15
|
+
|
|
16
|
+
import {GQLOperation, GQLSchemaToReferencesOptions, SortItem} from "./types";
|
|
17
|
+
import {GraphqlUniformReferenceType} from "./gql-core";
|
|
18
|
+
import {Context} from "./context";
|
|
19
|
+
|
|
20
|
+
export const OPEN_DOCS_SCHEMA_DIRECTIVE_NAME = "docs";
|
|
21
|
+
export const OPEN_DOCS_DIRECTIVE_NAME = "doc";
|
|
22
|
+
|
|
23
|
+
export function openDocsExtensionsToOptions(
|
|
24
|
+
schema: GraphQLSchema,
|
|
25
|
+
) {
|
|
26
|
+
const options: GQLSchemaToReferencesOptions = {}
|
|
27
|
+
|
|
28
|
+
// Check for @docs directive in schema extensions
|
|
29
|
+
for (const extension of schema.extensionASTNodes || []) {
|
|
30
|
+
if (extension.kind === 'SchemaExtension') {
|
|
31
|
+
for (const directive of extension.directives || []) {
|
|
32
|
+
if (directive.name.value === OPEN_DOCS_SCHEMA_DIRECTIVE_NAME) {
|
|
33
|
+
for (const arg of directive.arguments || []) {
|
|
34
|
+
if (arg.name.value === 'flattenTypes' && arg.value.kind === 'BooleanValue') {
|
|
35
|
+
if (arg.value.value === true) {
|
|
36
|
+
options.flat = true
|
|
37
|
+
} else if (arg.value.value === false) {
|
|
38
|
+
options.flat = false
|
|
39
|
+
}
|
|
40
|
+
} else if (arg.name.value === 'sort' && arg.value.kind === 'ListValue') {
|
|
41
|
+
const sortItems: SortItem[] = [];
|
|
42
|
+
for (const item of arg.value.values) {
|
|
43
|
+
if (item.kind === 'ObjectValue') {
|
|
44
|
+
const sortItem: SortItem = {};
|
|
45
|
+
for (const field of item.fields) {
|
|
46
|
+
if (field.name.value === 'node' && field.value.kind === 'StringValue') {
|
|
47
|
+
sortItem.node = field.value.value;
|
|
48
|
+
} else if (field.name.value === 'group' && field.value.kind === 'ListValue') {
|
|
49
|
+
sortItem.group = field.value.values
|
|
50
|
+
.filter((v): v is StringValueNode => v.kind === 'StringValue')
|
|
51
|
+
.map(v => v.value);
|
|
52
|
+
} else if (field.name.value === 'stack' && field.value.kind === 'IntValue') {
|
|
53
|
+
sortItem.stack = parseInt(field.value.value);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
sortItems.push(sortItem);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
if (!options.sort) {
|
|
60
|
+
options.sort = {};
|
|
61
|
+
}
|
|
62
|
+
options.sort.sort = sortItems;
|
|
63
|
+
} else if (arg.name.value === 'sortStack' && arg.value.kind === 'ListValue') {
|
|
64
|
+
const sortStacks: string[][] = [];
|
|
65
|
+
for (const item of arg.value.values) {
|
|
66
|
+
if (item.kind === 'ListValue') {
|
|
67
|
+
const stack = item.values
|
|
68
|
+
.filter((v): v is StringValueNode => v.kind === 'StringValue')
|
|
69
|
+
.map(v => v.value);
|
|
70
|
+
sortStacks.push(stack);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (!options.sort) {
|
|
74
|
+
options.sort = {};
|
|
75
|
+
}
|
|
76
|
+
options.sort.sortStack = sortStacks;
|
|
77
|
+
} else if (arg.name.value === 'route' && arg.value.kind === 'StringValue') {
|
|
78
|
+
options.route = arg.value.value;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return options;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
type OpenDocsGQLNode =
|
|
90
|
+
| GQLOperation
|
|
91
|
+
| GraphQLScalarType
|
|
92
|
+
| GraphQLObjectType
|
|
93
|
+
| GraphQLField<any, any>
|
|
94
|
+
| GraphQLArgument
|
|
95
|
+
| GraphQLInterfaceType
|
|
96
|
+
| GraphQLUnionType
|
|
97
|
+
| GraphQLEnumType
|
|
98
|
+
| GraphQLEnumValue
|
|
99
|
+
| GraphQLInputObjectType
|
|
100
|
+
|
|
101
|
+
export function openDocsToGroup(
|
|
102
|
+
ctx: Context | undefined,
|
|
103
|
+
odGqlNode: OpenDocsGQLNode,
|
|
104
|
+
): string[] {
|
|
105
|
+
let groups: string[] = []
|
|
106
|
+
|
|
107
|
+
const metadata = (ctx?.schema as any).__metadata;
|
|
108
|
+
|
|
109
|
+
if (metadata?.rootGroups) {
|
|
110
|
+
groups = [...metadata.rootGroups];
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
let directiveGroups = false
|
|
114
|
+
|
|
115
|
+
// Check schema metadata for field-specific groups (for operations)
|
|
116
|
+
if (ctx?.schema && 'name' in odGqlNode) {
|
|
117
|
+
const metadata = (ctx.schema as any).__metadata;
|
|
118
|
+
if (metadata?.fields) {
|
|
119
|
+
if ("_operationType" in odGqlNode) {
|
|
120
|
+
let fieldKey = ""
|
|
121
|
+
|
|
122
|
+
switch (odGqlNode._operationType) {
|
|
123
|
+
case "query": {
|
|
124
|
+
fieldKey = `Query.${odGqlNode.name}`;
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
127
|
+
case "mutation": {
|
|
128
|
+
fieldKey = `Mutation.${odGqlNode.name}`;
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
131
|
+
case "subscription": {
|
|
132
|
+
fieldKey = `Subscription.${odGqlNode.name}`;
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const fieldMetadata = metadata.fields.get(fieldKey);
|
|
138
|
+
if (fieldMetadata?.groups) {
|
|
139
|
+
directiveGroups = true
|
|
140
|
+
groups.push(...fieldMetadata.groups);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// If no groups from metadata, try getting groups from the node itself
|
|
147
|
+
if (!directiveGroups && odGqlNode.astNode?.directives) {
|
|
148
|
+
for (const directive of odGqlNode.astNode.directives) {
|
|
149
|
+
switch (directive.name.value) {
|
|
150
|
+
case OPEN_DOCS_DIRECTIVE_NAME: {
|
|
151
|
+
const groupArg = directive.arguments?.find((arg: {
|
|
152
|
+
name: { value: string }
|
|
153
|
+
}) => arg.name.value === 'group')
|
|
154
|
+
if (groupArg?.value.kind === 'ListValue') {
|
|
155
|
+
directiveGroups = true
|
|
156
|
+
groups.push(...groupArg.value.values
|
|
157
|
+
.filter((v: ConstValueNode): v is StringValueNode => v.kind === 'StringValue')
|
|
158
|
+
.map(v => v.value)
|
|
159
|
+
)
|
|
160
|
+
}
|
|
161
|
+
break
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// If still no groups, use default based on type
|
|
168
|
+
if (!directiveGroups) {
|
|
169
|
+
if (odGqlNode instanceof GraphQLObjectType) {
|
|
170
|
+
groups.push("Objects")
|
|
171
|
+
} else if (odGqlNode instanceof GraphQLInterfaceType) {
|
|
172
|
+
groups.push("Interfaces")
|
|
173
|
+
} else if (odGqlNode instanceof GraphQLUnionType) {
|
|
174
|
+
groups.push("Unions")
|
|
175
|
+
} else if (odGqlNode instanceof GraphQLEnumType) {
|
|
176
|
+
groups.push("Enums")
|
|
177
|
+
} else if (odGqlNode instanceof GraphQLInputObjectType) {
|
|
178
|
+
groups.push("Inputs")
|
|
179
|
+
} else if (odGqlNode instanceof GraphQLScalarType) {
|
|
180
|
+
groups.push("Scalars")
|
|
181
|
+
} else if (odGqlNode instanceof GQLOperation) {
|
|
182
|
+
switch (odGqlNode._operationType) {
|
|
183
|
+
case "query": {
|
|
184
|
+
groups.push("Queries")
|
|
185
|
+
break;
|
|
186
|
+
}
|
|
187
|
+
case "mutation": {
|
|
188
|
+
groups.push("Mutations")
|
|
189
|
+
break;
|
|
190
|
+
}
|
|
191
|
+
case "subscription": {
|
|
192
|
+
groups.push("Subscriptions")
|
|
193
|
+
break;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return groups
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export function openDocsCanonical(
|
|
203
|
+
ctx: Context,
|
|
204
|
+
gqlType: GraphqlUniformReferenceType,
|
|
205
|
+
) {
|
|
206
|
+
let path = ""
|
|
207
|
+
|
|
208
|
+
// Get parent path if this is a field
|
|
209
|
+
if ('astNode' in gqlType && gqlType.astNode?.kind === 'FieldDefinition' && ctx?.schema) {
|
|
210
|
+
// Check schema metadata for field-specific path
|
|
211
|
+
const metadata = (ctx.schema as any).__metadata;
|
|
212
|
+
if (metadata?.fields && 'name' in gqlType) {
|
|
213
|
+
if ("_operationType" in gqlType) {
|
|
214
|
+
let fieldKey = ""
|
|
215
|
+
|
|
216
|
+
switch (gqlType._operationType) {
|
|
217
|
+
case "query": {
|
|
218
|
+
fieldKey = `Query.${gqlType.name}`;
|
|
219
|
+
break;
|
|
220
|
+
}
|
|
221
|
+
case "mutation": {
|
|
222
|
+
fieldKey = `Mutation.${gqlType.name}`;
|
|
223
|
+
break;
|
|
224
|
+
}
|
|
225
|
+
case "subscription": {
|
|
226
|
+
fieldKey = `Subscription.${gqlType.name}`;
|
|
227
|
+
break;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
const fieldMetadata = metadata.fields.get(fieldKey);
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
if (fieldMetadata?.path) {
|
|
234
|
+
path = fieldMetadata.path;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Extract path from @doc directive if present
|
|
241
|
+
if (!path && gqlType.astNode?.directives) {
|
|
242
|
+
for (const directive of gqlType.astNode.directives) {
|
|
243
|
+
if (directive.name.value === OPEN_DOCS_DIRECTIVE_NAME) {
|
|
244
|
+
const pathArg = directive.arguments?.find(arg => arg.name.value === 'path')
|
|
245
|
+
if (pathArg?.value.kind === 'StringValue') {
|
|
246
|
+
path = pathArg.value.value
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return path
|
|
253
|
+
}
|