@xyd-js/gql 0.1.0-xyd.13 → 0.1.0-xyd.15
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 +18 -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 -6
- 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/schema.ts
CHANGED
|
@@ -1,107 +1,293 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import {fileURLToPath} from "node:url";
|
|
4
|
+
|
|
1
5
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
6
|
+
buildSchema,
|
|
7
|
+
DirectiveNode,
|
|
8
|
+
print,
|
|
9
|
+
visit,
|
|
10
|
+
parse,
|
|
11
|
+
ObjectTypeExtensionNode,
|
|
12
|
+
GraphQLSchema,
|
|
13
|
+
StringValueNode
|
|
7
14
|
} from "graphql";
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
import {getDocumentLoaders, loadSchema} from "@graphql-markdown/graphql";
|
|
11
|
-
import {GraphQLScalarType} from "@graphql-markdown/types";
|
|
15
|
+
import {mergeTypeDefs} from '@graphql-tools/merge';
|
|
12
16
|
|
|
13
17
|
import type {Reference} from "@xyd-js/uniform"
|
|
14
|
-
import {ReferenceType} from "@xyd-js/uniform"
|
|
15
|
-
|
|
16
|
-
import {
|
|
17
|
-
|
|
18
|
-
|
|
18
|
+
import {ReferenceType} from "@xyd-js/uniform";
|
|
19
|
+
|
|
20
|
+
import type {
|
|
21
|
+
GQLSchemaToReferencesOptions,
|
|
22
|
+
OpenDocsSortConfig,
|
|
23
|
+
FieldMetadata,
|
|
24
|
+
GQLSchemaMetadata,
|
|
25
|
+
SortItem
|
|
26
|
+
} from "./types";
|
|
27
|
+
import {DEFAULT_SORT_ORDER} from "./types";
|
|
28
|
+
import {graphqlTypesToUniformReferences} from "./converters/gql-types";
|
|
29
|
+
import {graphqlQueriesToUniformReferences} from "./converters/gql-query";
|
|
30
|
+
import {graphqlMutationsToUniformReferences} from "./converters/gql-mutation";
|
|
31
|
+
import {graphqlSubscriptionsToUniformReferences} from "./converters/gql-subscription";
|
|
32
|
+
import {OPEN_DOCS_DIRECTIVE_NAME, OPEN_DOCS_SCHEMA_DIRECTIVE_NAME, openDocsExtensionsToOptions} from "./opendocs";
|
|
33
|
+
import openDocsSchemaRaw from './opendocs.graphql'
|
|
19
34
|
|
|
20
35
|
// TODO: support multi graphql files
|
|
21
|
-
// TODO: !!! CIRCULAR_DEPENDENCY !!!
|
|
22
36
|
// TODO: sort by tag??
|
|
23
|
-
|
|
24
37
|
export async function gqlSchemaToReferences(
|
|
25
|
-
schemaLocation: string
|
|
38
|
+
schemaLocation: string | string[],
|
|
39
|
+
options?: GQLSchemaToReferencesOptions
|
|
26
40
|
): Promise<Reference[]> {
|
|
27
|
-
|
|
28
|
-
|
|
41
|
+
// 1. Convert schemaLocation to array
|
|
42
|
+
const schemaLocations = Array.isArray(schemaLocation) ? schemaLocation : [schemaLocation];
|
|
43
|
+
|
|
44
|
+
// Add opendocs.graphql to schema locations (first)
|
|
45
|
+
schemaLocations.unshift(openDocsSchemaRaw);
|
|
46
|
+
|
|
47
|
+
// 2. Read all schema contents
|
|
48
|
+
const schemaContents = await Promise.all(schemaLocations.map(async location => {
|
|
49
|
+
if (location.startsWith('http://') || location.startsWith('https://')) {
|
|
50
|
+
const response = await fetch(location);
|
|
51
|
+
if (!response.ok) {
|
|
52
|
+
throw new Error(`Failed to fetch schema from URL: ${location}`);
|
|
53
|
+
}
|
|
54
|
+
return response.text();
|
|
55
|
+
}
|
|
56
|
+
if (fs.existsSync(location)) {
|
|
57
|
+
return fs.readFileSync(location, 'utf-8');
|
|
58
|
+
}
|
|
59
|
+
return location;
|
|
60
|
+
}));
|
|
61
|
+
|
|
62
|
+
// 3. Merge all schema contents
|
|
63
|
+
const mergedTypeDefs = mergeTypeDefs(schemaContents);
|
|
64
|
+
const schemaString = print(mergedTypeDefs);
|
|
65
|
+
|
|
66
|
+
// 4. Build the schema
|
|
67
|
+
const schema = buildSchema(schemaString, {
|
|
68
|
+
assumeValid: true
|
|
69
|
+
});
|
|
70
|
+
if (schemaContents.length > 2) {
|
|
71
|
+
console.warn(`Warning: More than 2 schema files provided - no all featues will be supported!`);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
docDirectiveChain(schemaContents[1], schema);
|
|
75
|
+
// TODO: fix schemaContents[1]
|
|
76
|
+
|
|
77
|
+
if (!options) {
|
|
78
|
+
options = {}
|
|
29
79
|
}
|
|
30
|
-
const loaders = await getDocumentLoaders(loadersList);
|
|
31
80
|
|
|
32
|
-
|
|
33
|
-
|
|
81
|
+
if (!options.hasOwnProperty('flat')) {
|
|
82
|
+
options.flat = true; // Default flat is true
|
|
83
|
+
}
|
|
84
|
+
options = {
|
|
85
|
+
...options,
|
|
86
|
+
...openDocsExtensionsToOptions(schema)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// 5. Generate uniform references from the schema
|
|
90
|
+
const references = [
|
|
91
|
+
// types
|
|
92
|
+
...graphqlTypesToUniformReferences(schema, options),
|
|
93
|
+
|
|
94
|
+
// queries
|
|
95
|
+
...graphqlQueriesToUniformReferences(schema, options),
|
|
96
|
+
|
|
97
|
+
// mutations
|
|
98
|
+
...graphqlMutationsToUniformReferences(schema, options),
|
|
99
|
+
|
|
100
|
+
// subscriptions
|
|
101
|
+
...graphqlSubscriptionsToUniformReferences(schema, options),
|
|
102
|
+
]
|
|
103
|
+
|
|
104
|
+
// Sort references using provided sort config or defaults
|
|
105
|
+
const sortConfig = options.sort ?? {sort: DEFAULT_SORT_ORDER};
|
|
106
|
+
references.sort((a, b) => {
|
|
107
|
+
const aOrder = getSortOrder(a, sortConfig);
|
|
108
|
+
const bOrder = getSortOrder(b, sortConfig);
|
|
109
|
+
return aOrder - bOrder;
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
if (options.route) {
|
|
113
|
+
// TODO: types or better solution!!!
|
|
114
|
+
// @ts-ignore
|
|
115
|
+
references.__UNSAFE_route = () => options.route
|
|
116
|
+
}
|
|
117
|
+
return references
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function getSortOrder(reference: Reference, sortConfig: OpenDocsSortConfig): number {
|
|
121
|
+
const sortItems = sortConfig.sort ?? DEFAULT_SORT_ORDER;
|
|
122
|
+
const sortStacks = sortConfig.sortStack ?? [];
|
|
123
|
+
const referenceGroups = getReferenceGroups(reference);
|
|
124
|
+
|
|
125
|
+
// First, find which primary group this reference belongs to
|
|
126
|
+
for (let groupIndex = 0; groupIndex < sortItems.length; groupIndex++) {
|
|
127
|
+
const sortItem = sortItems[groupIndex];
|
|
34
128
|
|
|
35
|
-
|
|
129
|
+
// Check if this reference matches the primary group
|
|
130
|
+
if (matchesPrimaryGroup(reference, sortItem)) {
|
|
131
|
+
// Determine which stack to use (default to 0 if not specified)
|
|
132
|
+
const stackIndex = sortItem.stack !== undefined ? sortItem.stack : 0;
|
|
36
133
|
|
|
37
|
-
|
|
38
|
-
|
|
134
|
+
// Calculate position within this group using the stack
|
|
135
|
+
const positionInGroup = calculatePositionInGroup(reference, stackIndex, sortStacks);
|
|
39
136
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
137
|
+
const result = (groupIndex * 1000) + positionInGroup;
|
|
138
|
+
|
|
139
|
+
// Return order: groupIndex * 1000 + positionInGroup
|
|
140
|
+
// This ensures all items in group 0 come before all items in group 1, etc.
|
|
141
|
+
return result;
|
|
142
|
+
}
|
|
45
143
|
}
|
|
46
144
|
|
|
47
|
-
|
|
48
|
-
|
|
145
|
+
return Number.MAX_SAFE_INTEGER;
|
|
146
|
+
}
|
|
49
147
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
)
|
|
148
|
+
function matchesPrimaryGroup(reference: Reference, sortItem: SortItem): boolean {
|
|
149
|
+
// Check node match first
|
|
150
|
+
if (sortItem.node) {
|
|
151
|
+
const context = reference.context as any;
|
|
152
|
+
if (context?.graphqlTypeShort === sortItem.node) {
|
|
153
|
+
return true;
|
|
154
|
+
}
|
|
155
|
+
// If node is specified but doesn't match, return false (don't fall through)
|
|
156
|
+
return false;
|
|
55
157
|
}
|
|
56
158
|
|
|
57
|
-
|
|
159
|
+
// Check group match
|
|
160
|
+
if (sortItem.group && sortItem.group.length > 0) {
|
|
161
|
+
const referenceGroups = getReferenceGroups(reference);
|
|
162
|
+
const match = sortItem.group.some(group => referenceGroups.includes(group));
|
|
163
|
+
return match;
|
|
164
|
+
}
|
|
58
165
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
166
|
+
return true;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function calculatePositionInGroup(reference: Reference, stackIndex: number, sortStacks: string[][]): number {
|
|
170
|
+
if (stackIndex < 0 || stackIndex >= sortStacks.length) {
|
|
171
|
+
return 0;
|
|
172
|
+
}
|
|
63
173
|
|
|
64
|
-
|
|
65
|
-
|
|
174
|
+
const stackGroups = sortStacks[stackIndex];
|
|
175
|
+
const referenceGroups = getReferenceGroups(reference);
|
|
176
|
+
|
|
177
|
+
// Find the position of the reference's type in the stack
|
|
178
|
+
for (let i = 0; i < stackGroups.length; i++) {
|
|
179
|
+
const stackGroup = stackGroups[i];
|
|
180
|
+
if (referenceGroups.includes(stackGroup)) {
|
|
181
|
+
return i;
|
|
66
182
|
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return 999; // If not found in stack, put at the end of the group
|
|
186
|
+
}
|
|
67
187
|
|
|
68
|
-
switch (gqlType.constructor.name) {
|
|
69
|
-
case 'GraphQLObjectType': {
|
|
70
|
-
const type = gqlType as GraphQLObjectType;
|
|
71
188
|
|
|
72
|
-
|
|
189
|
+
function getReferenceGroups(reference: Reference): string[] {
|
|
190
|
+
// Extract groups from reference context
|
|
191
|
+
const context = reference.context as any;
|
|
192
|
+
if (context?.group && Array.isArray(context.group)) {
|
|
193
|
+
return context.group;
|
|
194
|
+
}
|
|
73
195
|
|
|
74
|
-
|
|
196
|
+
// Fallback: try to get groups from metadata if available
|
|
197
|
+
if (reference.__UNSAFE_selector) {
|
|
198
|
+
try {
|
|
199
|
+
const selector = reference.__UNSAFE_selector;
|
|
200
|
+
const metadata = selector("[metadata]");
|
|
201
|
+
if (metadata?.groups) {
|
|
202
|
+
return metadata.groups;
|
|
75
203
|
}
|
|
204
|
+
} catch (e) {
|
|
205
|
+
// Ignore errors from selector
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return [];
|
|
210
|
+
}
|
|
76
211
|
|
|
77
|
-
|
|
78
|
-
|
|
212
|
+
function docDirectiveChain(
|
|
213
|
+
rawSDL: string,
|
|
214
|
+
schema: GraphQLSchema
|
|
215
|
+
) {
|
|
216
|
+
const ast = parse(rawSDL);
|
|
217
|
+
const metadata: GQLSchemaMetadata = {
|
|
218
|
+
fields: new Map<string, FieldMetadata>()
|
|
219
|
+
};
|
|
79
220
|
|
|
80
|
-
|
|
81
|
-
|
|
221
|
+
// First, find root groups from @docs directive
|
|
222
|
+
visit(ast, {
|
|
223
|
+
SchemaExtension(node) {
|
|
224
|
+
for (const directive of node.directives || []) {
|
|
225
|
+
if (directive.name.value === OPEN_DOCS_SCHEMA_DIRECTIVE_NAME) {
|
|
226
|
+
const groupArg = directive.arguments?.find(arg => arg.name.value === 'group');
|
|
227
|
+
if (groupArg?.value.kind === 'ListValue') {
|
|
228
|
+
metadata.rootGroups = groupArg.value.values
|
|
229
|
+
.filter((v): v is StringValueNode => v.kind === 'StringValue')
|
|
230
|
+
.map(v => v.value);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
82
233
|
}
|
|
234
|
+
}
|
|
235
|
+
});
|
|
83
236
|
|
|
84
|
-
|
|
85
|
-
|
|
237
|
+
// Then process type extensions and fields
|
|
238
|
+
visit(ast, {
|
|
239
|
+
ObjectTypeExtension(node: ObjectTypeExtensionNode) {
|
|
240
|
+
const validNodeTypes = ['Query', 'Mutation', 'Subscription'];
|
|
241
|
+
if (!validNodeTypes.includes(node.name.value)) return;
|
|
86
242
|
|
|
87
|
-
|
|
243
|
+
const typeLevelDoc = node.directives?.find(d => d.name.value === OPEN_DOCS_DIRECTIVE_NAME);
|
|
244
|
+
const typeLevelDocArgs = typeLevelDoc ? extractDocArgs(typeLevelDoc) : {};
|
|
88
245
|
|
|
89
|
-
|
|
90
|
-
|
|
246
|
+
for (const field of node.fields ?? []) {
|
|
247
|
+
const fieldName = field.name.value;
|
|
91
248
|
|
|
92
|
-
|
|
93
|
-
const
|
|
249
|
+
const fieldDoc = field.directives?.find(d => d.name.value === OPEN_DOCS_DIRECTIVE_NAME);
|
|
250
|
+
const fieldDocArgs = fieldDoc ? extractDocArgs(fieldDoc) : {};
|
|
94
251
|
|
|
95
|
-
|
|
252
|
+
// Merge paths: if both type and field have paths, join them
|
|
253
|
+
let path = fieldDocArgs.path;
|
|
254
|
+
if (typeLevelDocArgs.path && fieldDocArgs.path) {
|
|
255
|
+
path = `${typeLevelDocArgs.path}/${fieldDocArgs.path}`;
|
|
256
|
+
} else if (typeLevelDocArgs.path) {
|
|
257
|
+
path = typeLevelDocArgs.path;
|
|
258
|
+
}
|
|
96
259
|
|
|
97
|
-
|
|
98
|
-
|
|
260
|
+
if (!fieldDocArgs.path && path) {
|
|
261
|
+
path += "/" + fieldName
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Merge groups: combine root groups with type/field groups
|
|
265
|
+
const groups = fieldDocArgs.groups ?? typeLevelDocArgs.groups ?? [];
|
|
99
266
|
|
|
100
|
-
|
|
101
|
-
|
|
267
|
+
const effectiveDoc: FieldMetadata = {
|
|
268
|
+
groups,
|
|
269
|
+
path
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
metadata.fields.set(`${node.name.value}.${fieldName}`, effectiveDoc);
|
|
102
273
|
}
|
|
103
274
|
}
|
|
104
|
-
}
|
|
275
|
+
});
|
|
105
276
|
|
|
106
|
-
|
|
277
|
+
// Attach metadata to schema
|
|
278
|
+
(schema as any).__metadata = metadata;
|
|
107
279
|
}
|
|
280
|
+
|
|
281
|
+
function extractDocArgs(directive: DirectiveNode): FieldMetadata {
|
|
282
|
+
const info: FieldMetadata = {};
|
|
283
|
+
for (const arg of directive.arguments ?? []) {
|
|
284
|
+
if (arg.name.value === 'group' && arg.value.kind === 'ListValue') {
|
|
285
|
+
info.groups = arg.value.values
|
|
286
|
+
.filter((v): v is StringValueNode => v.kind === 'StringValue')
|
|
287
|
+
.map(v => v.value);
|
|
288
|
+
} else if (arg.name.value === 'path' && arg.value.kind === 'StringValue') {
|
|
289
|
+
info.path = arg.value.value;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
return info;
|
|
293
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import type {GraphQLInputObjectType, GraphQLInterfaceType, GraphQLNamedType, GraphQLObjectType} from "graphql";
|
|
2
|
+
import {GraphQLField, OperationTypeNode} from "graphql";
|
|
3
|
+
|
|
4
|
+
import type {DefinitionProperty} from "@xyd-js/uniform";
|
|
5
|
+
|
|
6
|
+
// New sorting types based on the documentation
|
|
7
|
+
export interface SortItem {
|
|
8
|
+
node?: string;
|
|
9
|
+
group?: string[];
|
|
10
|
+
stack?: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface SortStack {
|
|
14
|
+
sortStack?: string[][];
|
|
15
|
+
sort?: SortItem[];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface OpenDocsSortConfig {
|
|
19
|
+
sortStack?: string[][];
|
|
20
|
+
sort?: SortItem[];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const DEFAULT_SORT_ORDER: SortItem[] = [
|
|
24
|
+
{ node: "query" },
|
|
25
|
+
{ node: "mutation" },
|
|
26
|
+
{ node: "subscription" },
|
|
27
|
+
{ node: "object" },
|
|
28
|
+
{ node: "interface" },
|
|
29
|
+
{ node: "union" },
|
|
30
|
+
{ node: "input" },
|
|
31
|
+
{ node: "enum" },
|
|
32
|
+
{ node: "scalar" },
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
export interface GQLSchemaToReferencesOptions {
|
|
36
|
+
// TODO: support line ranged in the future?
|
|
37
|
+
regions?: string[] // TODO: BETTER API - UNIFY FOR REST API / GRAPHQL ETC
|
|
38
|
+
|
|
39
|
+
flat?: boolean;
|
|
40
|
+
sort?: OpenDocsSortConfig;
|
|
41
|
+
route?: string
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export type NestedGraphqlType = {
|
|
45
|
+
__definitionProperties?: DefinitionProperty[];
|
|
46
|
+
} & (GraphQLObjectType | GraphQLInputObjectType | GraphQLInterfaceType)
|
|
47
|
+
|
|
48
|
+
// needed cuz GraphQLField does not have operation info?
|
|
49
|
+
export class GQLOperation implements GraphQLField<any, any> {
|
|
50
|
+
public _operationType!: OperationTypeNode
|
|
51
|
+
field: GraphQLField<any, any>;
|
|
52
|
+
|
|
53
|
+
constructor(field: GraphQLField<any, any>) {
|
|
54
|
+
this.field = field;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
get name() {
|
|
58
|
+
return this.field.name;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
get description() {
|
|
62
|
+
return this.field.description;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
get type() {
|
|
66
|
+
return this.field.type;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
get args() {
|
|
70
|
+
return this.field.args;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
get deprecationReason() {
|
|
74
|
+
return this.field.deprecationReason;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
get extensions() {
|
|
78
|
+
return this.field.extensions;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
get astNode() {
|
|
82
|
+
return this.field.astNode;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
set __operationType(operationType: OperationTypeNode) {
|
|
86
|
+
this._operationType = operationType;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export interface GQLTypeInfo {
|
|
91
|
+
typeFlat?: GraphQLNamedType
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export interface FieldMetadata {
|
|
95
|
+
path?: string;
|
|
96
|
+
groups?: string[];
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export interface GQLSchemaMetadata {
|
|
100
|
+
fields: Map<string, FieldMetadata>;
|
|
101
|
+
rootGroups?: string[];
|
|
102
|
+
}
|
|
103
|
+
|
package/src/utils.ts
CHANGED
|
@@ -1,100 +1,25 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
// gqlOperationsToUniformRef is a helper function to create a list of xyd reference for a GraphQL query or mutation.
|
|
16
|
-
export function gqlOperationsToUniformRef(
|
|
17
|
-
operationType: ReferenceType.GRAPHQL_MUTATION | ReferenceType.GRAPHQL_QUERY,
|
|
18
|
-
fieldsMap: GraphQLFieldMap<any, any>
|
|
19
|
-
) {
|
|
20
|
-
const references: Reference[] = []
|
|
21
|
-
|
|
22
|
-
for (const [operationName, operationField] of Object.entries(fieldsMap)) {
|
|
23
|
-
const definitions: Definition[] = []
|
|
24
|
-
|
|
25
|
-
const args = gqlArgToUniformDefinitionProperty(operationField.args)
|
|
26
|
-
const returns = gqlFieldToUniformDefinitionProperty(operationName, operationField)
|
|
27
|
-
const returnProperties = returns.properties || []
|
|
28
|
-
|
|
29
|
-
definitions.push({
|
|
30
|
-
title: "Arguments",
|
|
31
|
-
properties: args,
|
|
32
|
-
})
|
|
33
|
-
definitions.push({
|
|
34
|
-
title: "Returns",
|
|
35
|
-
properties: returnProperties
|
|
36
|
-
})
|
|
37
|
-
|
|
38
|
-
const exampleQuery = simpleGraphqlExample(
|
|
39
|
-
operationType,
|
|
40
|
-
operationName,
|
|
41
|
-
args,
|
|
42
|
-
returnProperties
|
|
43
|
-
)
|
|
44
|
-
const examples: Example[] = [
|
|
45
|
-
{
|
|
46
|
-
codeblock: {
|
|
47
|
-
tabs: [
|
|
48
|
-
{
|
|
49
|
-
title: "graphql",
|
|
50
|
-
language: "graphql",
|
|
51
|
-
code: exampleQuery,
|
|
52
|
-
}
|
|
53
|
-
]
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
]
|
|
57
|
-
|
|
58
|
-
const exampleGroup = {
|
|
59
|
-
description: "Example request",
|
|
60
|
-
examples,
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
let description = operationField.description || ""
|
|
64
|
-
|
|
65
|
-
references.push(graphqlReference(
|
|
66
|
-
operationType,
|
|
67
|
-
operationName,
|
|
68
|
-
operationName,
|
|
69
|
-
description,
|
|
70
|
-
[exampleGroup],
|
|
71
|
-
definitions,
|
|
72
|
-
))
|
|
1
|
+
/**
|
|
2
|
+
* Helper function to filter fields based on region patterns
|
|
3
|
+
* @param fields - The fields to filter
|
|
4
|
+
* @param prefix - The prefix for the region key (e.g., "Query" or "Mutation")
|
|
5
|
+
* @param regions - The regions to filter by
|
|
6
|
+
* @returns Filtered fields object
|
|
7
|
+
*/
|
|
8
|
+
export function filterFieldsByRegions<T>(
|
|
9
|
+
fields: Record<string, T>,
|
|
10
|
+
prefix: string,
|
|
11
|
+
regions?: string[]
|
|
12
|
+
): Record<string, T> {
|
|
13
|
+
if (!regions || regions.length === 0) {
|
|
14
|
+
return fields;
|
|
73
15
|
}
|
|
74
16
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
title: string,
|
|
82
|
-
canonical: string,
|
|
83
|
-
description: string,
|
|
84
|
-
examples: ExampleGroup[],
|
|
85
|
-
definitions: Definition[],
|
|
86
|
-
): Reference {
|
|
87
|
-
return {
|
|
88
|
-
title,
|
|
89
|
-
canonical,
|
|
90
|
-
description,
|
|
91
|
-
|
|
92
|
-
category: ReferenceCategory.GRAPHQL,
|
|
93
|
-
type: operationType,
|
|
94
|
-
|
|
95
|
-
examples: {
|
|
96
|
-
groups: examples,
|
|
97
|
-
},
|
|
98
|
-
definitions,
|
|
17
|
+
const filteredFields: Record<string, T> = {};
|
|
18
|
+
for (const [fieldName, field] of Object.entries(fields)) {
|
|
19
|
+
const regionKey = `${prefix}.${fieldName}`;
|
|
20
|
+
if (regions.some(region => region === regionKey)) {
|
|
21
|
+
filteredFields[fieldName] = field;
|
|
22
|
+
}
|
|
99
23
|
}
|
|
24
|
+
return filteredFields;
|
|
100
25
|
}
|
package/tsconfig.json
CHANGED
|
@@ -13,6 +13,6 @@
|
|
|
13
13
|
"incremental": true,
|
|
14
14
|
"tsBuildInfoFile": "./dist/tsconfig.tsbuildinfo"
|
|
15
15
|
},
|
|
16
|
-
"include": ["examples/basic-example/**/*.ts", "src/**/*.ts"],
|
|
16
|
+
"include": ["examples/basic-example/**/*.ts", "src/**/*.ts", "declarations.d.ts"],
|
|
17
17
|
"exclude": ["node_modules", "dist"]
|
|
18
18
|
}
|
package/tsup.config.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { copyFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
|
|
1
4
|
import {defineConfig} from 'tsup';
|
|
2
5
|
|
|
3
6
|
export default defineConfig({
|
|
@@ -14,6 +17,17 @@ export default defineConfig({
|
|
|
14
17
|
esbuildOptions: (options) => {
|
|
15
18
|
options.platform = 'node'; // Ensure the platform is set to Node.js
|
|
16
19
|
options.external = ['node:fs/promises']; // Mark 'node:fs/promises' as external
|
|
17
|
-
options.loader = {
|
|
20
|
+
options.loader = {
|
|
21
|
+
'.js': 'jsx', // Ensure proper handling of .js files
|
|
22
|
+
'.graphql': 'text' // Load .graphql files as text
|
|
23
|
+
};
|
|
18
24
|
},
|
|
25
|
+
onSuccess: async () => {
|
|
26
|
+
// Copy opendocs.graphql to dist
|
|
27
|
+
copyFileSync(
|
|
28
|
+
join('src', 'opendocs.graphql'),
|
|
29
|
+
join('dist', 'opendocs.graphql')
|
|
30
|
+
);
|
|
31
|
+
return Promise.resolve();
|
|
32
|
+
}
|
|
19
33
|
});
|
package/vitest.config.ts
CHANGED
|
@@ -2,6 +2,20 @@ import { defineConfig } from 'vitest/config'
|
|
|
2
2
|
|
|
3
3
|
export default defineConfig({
|
|
4
4
|
test: {
|
|
5
|
-
|
|
5
|
+
environment: 'node',
|
|
6
|
+
globals: true,
|
|
6
7
|
},
|
|
8
|
+
plugins: [
|
|
9
|
+
{
|
|
10
|
+
name: 'graphql-raw',
|
|
11
|
+
transform(code, id) {
|
|
12
|
+
if (id.endsWith('.graphql')) {
|
|
13
|
+
return {
|
|
14
|
+
code: `export default ${JSON.stringify(code)};`,
|
|
15
|
+
map: null
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
]
|
|
7
21
|
})
|
package/examples/basic/index.ts
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import path from 'path';
|
|
2
|
-
|
|
3
|
-
import {gqlSchemaToReferences} from "../../src";
|
|
4
|
-
|
|
5
|
-
// TODO: support multi graphql files
|
|
6
|
-
// TODO: !!! CIRCULAR_DEPENDENCY !!!
|
|
7
|
-
// TODO: sort by tag??
|
|
8
|
-
(async () => {
|
|
9
|
-
const schemaLocation = path.join(process.cwd(), "./examples/basic/todo-app.graphqls")
|
|
10
|
-
|
|
11
|
-
const references = await gqlSchemaToReferences(schemaLocation)
|
|
12
|
-
})()
|