@xyd-js/gql 0.1.0-xyd.0
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/README.md +3 -0
- package/examples/basic/index.ts +12 -0
- package/examples/basic/schema.graphqls +89 -0
- package/examples/basic/todo-app.graphqls +184 -0
- package/index.ts +3 -0
- package/package.json +26 -0
- package/src/arguments.ts +52 -0
- package/src/examples.ts +96 -0
- package/src/fields.ts +156 -0
- package/src/index.ts +3 -0
- package/src/schema.ts +47 -0
- package/src/utils.ts +101 -0
- package/tsconfig.json +18 -0
- package/tsup.config.ts +19 -0
- package/tsup.examples-config.ts +30 -0
package/README.md
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
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
|
+
})()
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# TODO: !!! case with circular dependencies !!!
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
A book type
|
|
5
|
+
"""
|
|
6
|
+
type Book {
|
|
7
|
+
"""
|
|
8
|
+
The title of the book
|
|
9
|
+
"""
|
|
10
|
+
title: String
|
|
11
|
+
|
|
12
|
+
"""
|
|
13
|
+
The author of the book
|
|
14
|
+
"""
|
|
15
|
+
author_name: String
|
|
16
|
+
# author: Author
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
"""
|
|
20
|
+
An author type
|
|
21
|
+
"""
|
|
22
|
+
type Author {
|
|
23
|
+
"""
|
|
24
|
+
The name of the author
|
|
25
|
+
"""
|
|
26
|
+
name: String
|
|
27
|
+
|
|
28
|
+
"""
|
|
29
|
+
The books of the author
|
|
30
|
+
"""
|
|
31
|
+
books: [String]
|
|
32
|
+
# books: [Book]
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
"""
|
|
36
|
+
A nested book type
|
|
37
|
+
"""
|
|
38
|
+
input NestedBook {
|
|
39
|
+
"""
|
|
40
|
+
The date of the book
|
|
41
|
+
"""
|
|
42
|
+
date: String
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
"""
|
|
46
|
+
The input object for the getBook query
|
|
47
|
+
"""
|
|
48
|
+
input BookInput {
|
|
49
|
+
"""
|
|
50
|
+
The title of the book
|
|
51
|
+
"""
|
|
52
|
+
title: String
|
|
53
|
+
|
|
54
|
+
"""
|
|
55
|
+
The author of the book
|
|
56
|
+
"""
|
|
57
|
+
nestedBook: NestedBook
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
type Query {
|
|
61
|
+
# books: [Book]
|
|
62
|
+
# authors: [Author]
|
|
63
|
+
"""
|
|
64
|
+
Get a book by title
|
|
65
|
+
"""
|
|
66
|
+
getBook(
|
|
67
|
+
"""
|
|
68
|
+
The title of the book
|
|
69
|
+
"""
|
|
70
|
+
title: String
|
|
71
|
+
|
|
72
|
+
"""
|
|
73
|
+
The author of the book
|
|
74
|
+
"""
|
|
75
|
+
input: BookInput
|
|
76
|
+
): Book
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
type Mutation {
|
|
80
|
+
"""
|
|
81
|
+
Add a book
|
|
82
|
+
"""
|
|
83
|
+
addBook(title: String, author: String): Book
|
|
84
|
+
|
|
85
|
+
"""
|
|
86
|
+
Add an author
|
|
87
|
+
"""
|
|
88
|
+
addAuthor(name: String): Author
|
|
89
|
+
}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
# Define custom scalars for Date and ID
|
|
2
|
+
scalar Date
|
|
3
|
+
scalar ID
|
|
4
|
+
|
|
5
|
+
# Define the User type
|
|
6
|
+
"""
|
|
7
|
+
A user type representing a user in the system
|
|
8
|
+
"""
|
|
9
|
+
type User {
|
|
10
|
+
"""
|
|
11
|
+
The unique ID of the user
|
|
12
|
+
"""
|
|
13
|
+
id: ID!
|
|
14
|
+
|
|
15
|
+
"""
|
|
16
|
+
The name of the user
|
|
17
|
+
"""
|
|
18
|
+
name: String!
|
|
19
|
+
|
|
20
|
+
"""
|
|
21
|
+
The email of the user
|
|
22
|
+
"""
|
|
23
|
+
email: String!
|
|
24
|
+
|
|
25
|
+
"""
|
|
26
|
+
The list of todos created by the user
|
|
27
|
+
"""
|
|
28
|
+
todos: [Todo!]!
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
# Define the Todo type
|
|
32
|
+
"""
|
|
33
|
+
A todo type representing a task to be done
|
|
34
|
+
"""
|
|
35
|
+
type Todo {
|
|
36
|
+
"""
|
|
37
|
+
The unique ID of the todo
|
|
38
|
+
"""
|
|
39
|
+
id: ID!
|
|
40
|
+
|
|
41
|
+
"""
|
|
42
|
+
The title of the todo
|
|
43
|
+
"""
|
|
44
|
+
title: String!
|
|
45
|
+
|
|
46
|
+
"""
|
|
47
|
+
The description of the todo
|
|
48
|
+
"""
|
|
49
|
+
description: String
|
|
50
|
+
|
|
51
|
+
"""
|
|
52
|
+
The status of the todo (completed or not)
|
|
53
|
+
"""
|
|
54
|
+
completed: Boolean!
|
|
55
|
+
|
|
56
|
+
"""
|
|
57
|
+
The due date of the todo
|
|
58
|
+
"""
|
|
59
|
+
dueDate: Date
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
# Define input types for creating and updating todos
|
|
63
|
+
"""
|
|
64
|
+
Input type for creating a new todo
|
|
65
|
+
"""
|
|
66
|
+
input CreateTodoInput {
|
|
67
|
+
title: String!
|
|
68
|
+
description: String
|
|
69
|
+
dueDate: Date
|
|
70
|
+
userId: ID!
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
"""
|
|
74
|
+
Input type for updating an existing todo
|
|
75
|
+
"""
|
|
76
|
+
input UpdateTodoInput {
|
|
77
|
+
id: ID!
|
|
78
|
+
title: String
|
|
79
|
+
description: String
|
|
80
|
+
completed: Boolean
|
|
81
|
+
dueDate: Date
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
# Define the Query type
|
|
85
|
+
"""
|
|
86
|
+
---
|
|
87
|
+
title: Query
|
|
88
|
+
group: [Root Types]
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
The root query type
|
|
92
|
+
"""
|
|
93
|
+
type Query {
|
|
94
|
+
"""
|
|
95
|
+
---
|
|
96
|
+
title: Users
|
|
97
|
+
group: [GraphQL, Users]
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
Get a list of all users
|
|
101
|
+
"""
|
|
102
|
+
users: [User!]!
|
|
103
|
+
|
|
104
|
+
"""
|
|
105
|
+
---
|
|
106
|
+
title: User
|
|
107
|
+
group: [GraphQL, Users]
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
Get a user by ID
|
|
111
|
+
"""
|
|
112
|
+
user(id: ID!): User
|
|
113
|
+
|
|
114
|
+
"""
|
|
115
|
+
---
|
|
116
|
+
title: Todos
|
|
117
|
+
group: [GraphQL, Todos]
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
Get a list of all todos
|
|
121
|
+
"""
|
|
122
|
+
todos: [Todo!]!
|
|
123
|
+
|
|
124
|
+
"""
|
|
125
|
+
---
|
|
126
|
+
title: Todo
|
|
127
|
+
group: [GraphQL, Todos]
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
Get a todo by ID
|
|
131
|
+
"""
|
|
132
|
+
todo(id: ID!): Todo
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
# Define the Mutation type
|
|
136
|
+
"""
|
|
137
|
+
---
|
|
138
|
+
title: Mutation
|
|
139
|
+
group: [Root Types]
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
The root mutation type
|
|
143
|
+
"""
|
|
144
|
+
type Mutation {
|
|
145
|
+
"""
|
|
146
|
+
---
|
|
147
|
+
title: Create User
|
|
148
|
+
group: [GraphQL, Users]
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
Create a new user
|
|
152
|
+
"""
|
|
153
|
+
createUser(name: String!, email: String!): User!
|
|
154
|
+
|
|
155
|
+
"""
|
|
156
|
+
---
|
|
157
|
+
title: Create Todo
|
|
158
|
+
group: [GraphQL, Todos]
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
Create a new todo
|
|
162
|
+
"""
|
|
163
|
+
createTodo(input: CreateTodoInput!): Todo!
|
|
164
|
+
|
|
165
|
+
"""
|
|
166
|
+
---
|
|
167
|
+
title: Update Todo
|
|
168
|
+
group: [GraphQL, Todos]
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
Update an existing todo
|
|
172
|
+
"""
|
|
173
|
+
updateTodo(input: UpdateTodoInput!): Todo!
|
|
174
|
+
|
|
175
|
+
"""
|
|
176
|
+
---
|
|
177
|
+
title: Delete Todo
|
|
178
|
+
group: [GraphQL, Todos]
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
Delete a todo by ID
|
|
182
|
+
"""
|
|
183
|
+
deleteTodo(id: ID!): Boolean!
|
|
184
|
+
}
|
package/index.ts
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@xyd-js/gql",
|
|
3
|
+
"version": "0.1.0-xyd.0",
|
|
4
|
+
"description": "",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"dependencies": {
|
|
8
|
+
"@graphql-markdown/core": "^1.12.0",
|
|
9
|
+
"@graphql-markdown/graphql": "^1.1.4",
|
|
10
|
+
"@graphql-markdown/types": "^1.4.0",
|
|
11
|
+
"graphql-config": "^5.1.2",
|
|
12
|
+
"gray-matter": "^4.0.3",
|
|
13
|
+
"json-to-graphql-query": "^2.3.0",
|
|
14
|
+
"@xyd-js/core": "0.1.0-xyd.0",
|
|
15
|
+
"@xyd-js/uniform": "0.1.0-xyd.2"
|
|
16
|
+
},
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"tsup": "^8.3.0"
|
|
19
|
+
},
|
|
20
|
+
"scripts": {
|
|
21
|
+
"clean": "rimraf build",
|
|
22
|
+
"prebuild": "pnpm clean",
|
|
23
|
+
"build": "tsup",
|
|
24
|
+
"build:examples": "tsup --config tsup.examples-config.ts"
|
|
25
|
+
}
|
|
26
|
+
}
|
package/src/arguments.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import {GraphQLArgument} from "graphql/type/definition";
|
|
2
|
+
import {GraphQLInputObjectType} from "graphql/type";
|
|
3
|
+
import {DefinitionProperty} from "@xyd-js/uniform";
|
|
4
|
+
|
|
5
|
+
import {fieldIntoDefinitionProperty} from "./fields";
|
|
6
|
+
|
|
7
|
+
// argumentsIntoDefinitionProperty converts GraphQL arguments into xyd 'uniform' definition properties
|
|
8
|
+
export function argumentsIntoDefinitionProperty(
|
|
9
|
+
args: readonly GraphQLArgument[]
|
|
10
|
+
): DefinitionProperty[] {
|
|
11
|
+
const resp: DefinitionProperty[] = []
|
|
12
|
+
|
|
13
|
+
args.forEach(arg => {
|
|
14
|
+
if (arg.type.constructor.name === "GraphQLInputObjectType") {
|
|
15
|
+
const inputObjectType = arg.type as GraphQLInputObjectType
|
|
16
|
+
|
|
17
|
+
const inputFields = inputObjectType.getFields?.()
|
|
18
|
+
|
|
19
|
+
const nestedProps: DefinitionProperty[] = []
|
|
20
|
+
const nestedDefinitionProperty: DefinitionProperty = {
|
|
21
|
+
name: arg.name,
|
|
22
|
+
type: arg.type.toJSON(),
|
|
23
|
+
description: arg.description || "",
|
|
24
|
+
properties: nestedProps,
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
for (const [name, inputField] of Object.entries(inputFields)) {
|
|
28
|
+
const prop = fieldIntoDefinitionProperty(
|
|
29
|
+
name,
|
|
30
|
+
inputField,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
if (prop) {
|
|
34
|
+
nestedProps.push(prop)
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
resp.push(nestedDefinitionProperty)
|
|
39
|
+
} else {
|
|
40
|
+
const prop: DefinitionProperty = {
|
|
41
|
+
name: arg.name,
|
|
42
|
+
type: arg.type.toJSON(),
|
|
43
|
+
description: arg.description || "",
|
|
44
|
+
properties: [],
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
resp.push(prop)
|
|
48
|
+
}
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
return resp
|
|
52
|
+
}
|
package/src/examples.ts
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import {jsonToGraphQLQuery, VariableType} from "json-to-graphql-query";
|
|
2
|
+
import {
|
|
3
|
+
DefinitionProperty,
|
|
4
|
+
ReferenceType
|
|
5
|
+
} from "@xyd-js/uniform";
|
|
6
|
+
|
|
7
|
+
// TODO: support args
|
|
8
|
+
// simpleGraphqlExample is a helper function to create a simple GraphQL example query or mutation.
|
|
9
|
+
export function simpleGraphqlExample(
|
|
10
|
+
operationType: ReferenceType.GRAPHQL_QUERY | ReferenceType.GRAPHQL_MUTATION,
|
|
11
|
+
operationName: string,
|
|
12
|
+
args: DefinitionProperty[],
|
|
13
|
+
returns: DefinitionProperty[],
|
|
14
|
+
) {
|
|
15
|
+
let obj: any = {}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
switch (operationType) {
|
|
19
|
+
case ReferenceType.GRAPHQL_QUERY: {
|
|
20
|
+
const exampleReturnProps = exampleReturns(returns)
|
|
21
|
+
|
|
22
|
+
obj = {
|
|
23
|
+
query: {
|
|
24
|
+
__name: operationName,
|
|
25
|
+
[operationName]: exampleReturnProps
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
break
|
|
30
|
+
}
|
|
31
|
+
case ReferenceType.GRAPHQL_MUTATION: {
|
|
32
|
+
const exampleReturnProps = exampleReturns(returns)
|
|
33
|
+
const vars = exampleVariables(args)
|
|
34
|
+
const argumen = exampleArguments(args)
|
|
35
|
+
|
|
36
|
+
obj = {
|
|
37
|
+
mutation: {
|
|
38
|
+
__name: operationName,
|
|
39
|
+
__variables: vars,
|
|
40
|
+
[operationName]: {
|
|
41
|
+
...exampleReturnProps,
|
|
42
|
+
__args: argumen,
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return jsonToGraphQLQuery(obj, {pretty: true});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// exampleReturns return an example of return GraphQL object
|
|
55
|
+
function exampleReturns(
|
|
56
|
+
properties: DefinitionProperty[],
|
|
57
|
+
obj: any = {}
|
|
58
|
+
) {
|
|
59
|
+
properties.forEach((property) => {
|
|
60
|
+
// TODO: only if required?
|
|
61
|
+
obj[property.name] = true
|
|
62
|
+
|
|
63
|
+
if (property?.properties?.length) {
|
|
64
|
+
obj[property.name] = {}
|
|
65
|
+
exampleReturns(property.properties, obj[property.name])
|
|
66
|
+
}
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
return obj
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// exampleArguments return an example of GraphQL arguments
|
|
73
|
+
function exampleArguments(
|
|
74
|
+
properties: DefinitionProperty[],
|
|
75
|
+
obj: any = {}
|
|
76
|
+
) {
|
|
77
|
+
|
|
78
|
+
properties.forEach((property) => {
|
|
79
|
+
obj[property.name] = new VariableType(property.name)
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
return obj
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// exampleVariables return an example of GraphQL variables
|
|
86
|
+
function exampleVariables(
|
|
87
|
+
properties: DefinitionProperty[],
|
|
88
|
+
obj: any = {}
|
|
89
|
+
) {
|
|
90
|
+
|
|
91
|
+
properties.forEach((property) => {
|
|
92
|
+
obj[property.name] = property.type
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
return obj
|
|
96
|
+
}
|
package/src/fields.ts
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
// overFields iterates over fields of object or input object types
|
|
2
|
+
import {
|
|
3
|
+
GraphQLField,
|
|
4
|
+
GraphQLFieldMap,
|
|
5
|
+
GraphQLInputField,
|
|
6
|
+
GraphQLInputFieldMap,
|
|
7
|
+
GraphQLInputObjectType
|
|
8
|
+
} from "graphql/type";
|
|
9
|
+
import {GraphQLObjectType} from "graphql";
|
|
10
|
+
import {DefinitionProperty} from "@xyd-js/uniform";
|
|
11
|
+
|
|
12
|
+
// fieldIntoDefinitionProperty converts GraphQL fields (field or input field) into xyd 'uniform' definition property
|
|
13
|
+
export function fieldIntoDefinitionProperty(
|
|
14
|
+
fieldName: string,
|
|
15
|
+
field: GraphQLField<any, any> | GraphQLInputField,
|
|
16
|
+
): DefinitionProperty {
|
|
17
|
+
let properties;
|
|
18
|
+
|
|
19
|
+
// if 'ofType' types (non-null values e.g '!<type>')
|
|
20
|
+
if ("ofType" in field.type) {
|
|
21
|
+
switch (field.type.constructor.name) {
|
|
22
|
+
case "GraphQLList": {
|
|
23
|
+
switch (field.type.ofType.constructor.name) {
|
|
24
|
+
case "GraphQLObjectType": {
|
|
25
|
+
const objectType = field.type.ofType as GraphQLObjectType
|
|
26
|
+
|
|
27
|
+
properties = nestedProperties(objectType)
|
|
28
|
+
|
|
29
|
+
break
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
case "GraphQLInputObjectType": {
|
|
33
|
+
const inputObjectType = field.type.ofType as GraphQLInputObjectType
|
|
34
|
+
|
|
35
|
+
properties = nestedProperties(inputObjectType)
|
|
36
|
+
|
|
37
|
+
break
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
case "GraphQLScalarType" : {
|
|
41
|
+
properties = definitionPropsFromNestedObj(field) || []
|
|
42
|
+
|
|
43
|
+
break
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
case "GraphQLNonNull": {
|
|
47
|
+
properties = definitionPropsFromNestedObj(field) || []
|
|
48
|
+
|
|
49
|
+
break
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
default: {
|
|
53
|
+
console.error("unsupported ofType list", field.type.ofType.constructor.name)
|
|
54
|
+
break
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
break
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
case "GraphQLNonNull": {
|
|
63
|
+
properties = definitionPropsFromNestedObj(field) || []
|
|
64
|
+
|
|
65
|
+
break
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
default: {
|
|
69
|
+
console.error("unsupported ofType", field.type.constructor.name)
|
|
70
|
+
|
|
71
|
+
break
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// if regular object type
|
|
78
|
+
else if (field.type.constructor.name === "GraphQLObjectType") {
|
|
79
|
+
const objectType = field.type as GraphQLObjectType
|
|
80
|
+
|
|
81
|
+
// TODO: support nested & circular references - ITS JUST A FAST SOLUTION FOR TESTING PURPOSES
|
|
82
|
+
// properties = [
|
|
83
|
+
// {
|
|
84
|
+
// name: fieldName,
|
|
85
|
+
// type: "object",
|
|
86
|
+
// description: objectType.description || "",
|
|
87
|
+
// }
|
|
88
|
+
// ]
|
|
89
|
+
|
|
90
|
+
// TODO: comment if bug with circular references
|
|
91
|
+
properties = nestedProperties(objectType)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// if input object type
|
|
95
|
+
else if (field.type.constructor.name === "GraphQLInputObjectType") {
|
|
96
|
+
const objectType = field.type as GraphQLInputObjectType
|
|
97
|
+
|
|
98
|
+
properties = nestedProperties(objectType)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
name: fieldName,
|
|
103
|
+
type: field.type.toJSON(),
|
|
104
|
+
description: field.description || "",
|
|
105
|
+
properties,
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// TODO: fix any + another more safety solution?
|
|
110
|
+
// definitionPropsFromNestedObj converts graphql nested obj into xyd 'uniform' definition properties
|
|
111
|
+
function definitionPropsFromNestedObj(obj: any): DefinitionProperty[] | null {
|
|
112
|
+
if (!obj) {
|
|
113
|
+
return null
|
|
114
|
+
}
|
|
115
|
+
if (obj.getFields) {
|
|
116
|
+
return nestedProperties(obj)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (obj.ofType) {
|
|
120
|
+
return definitionPropsFromNestedObj(obj.ofType)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (obj.type) {
|
|
124
|
+
return definitionPropsFromNestedObj(obj.type)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return null
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// deepFieldMap iterates over GraphQL field (field or input fields) maps
|
|
131
|
+
function deepFieldMap(
|
|
132
|
+
fieldsMap: GraphQLFieldMap<any, any> | GraphQLInputFieldMap,
|
|
133
|
+
) {
|
|
134
|
+
const properties: DefinitionProperty[] = []
|
|
135
|
+
|
|
136
|
+
for (const [name, field] of Object.entries(fieldsMap)) {
|
|
137
|
+
const prop = fieldIntoDefinitionProperty(
|
|
138
|
+
name,
|
|
139
|
+
field,
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
if (prop) {
|
|
143
|
+
properties.push(prop)
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return properties
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// nestedProperties get fields from object or input object types and iterates over them
|
|
151
|
+
function nestedProperties(objectType: GraphQLObjectType | GraphQLInputObjectType) {
|
|
152
|
+
const nestedFields = objectType?.getFields?.()
|
|
153
|
+
|
|
154
|
+
return deepFieldMap(nestedFields)
|
|
155
|
+
}
|
|
156
|
+
|
package/src/index.ts
ADDED
package/src/schema.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import {getDocumentLoaders, loadSchema} from "@graphql-markdown/graphql";
|
|
2
|
+
import {OperationTypeNode} from "graphql/language/ast";
|
|
3
|
+
import {Reference, ReferenceType} from "@xyd-js/uniform"
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
graphqlOperationReferences
|
|
7
|
+
} from "./utils";
|
|
8
|
+
|
|
9
|
+
// TODO: support multi graphql files
|
|
10
|
+
// TODO: !!! CIRCULAR_DEPENDENCY !!!
|
|
11
|
+
// TODO: sort by tag??
|
|
12
|
+
|
|
13
|
+
export async function gqlSchemaToReferences(
|
|
14
|
+
schemaLocation: string
|
|
15
|
+
): Promise<Reference[]> {
|
|
16
|
+
const loadersList = {
|
|
17
|
+
["GraphQLFileLoader"]: "@graphql-tools/graphql-file-loader",
|
|
18
|
+
}
|
|
19
|
+
const loaders = await getDocumentLoaders(loadersList);
|
|
20
|
+
|
|
21
|
+
// @ts-ignore TODO: but ts works in @graphql-markdown/core
|
|
22
|
+
const schema = await loadSchema(schemaLocation as string, loaders);
|
|
23
|
+
|
|
24
|
+
const references: Reference[] = []
|
|
25
|
+
|
|
26
|
+
const queries = schema.getRootType(OperationTypeNode.QUERY)
|
|
27
|
+
const queryFields = queries?.getFields?.()
|
|
28
|
+
|
|
29
|
+
if (queryFields) {
|
|
30
|
+
references.push(...graphqlOperationReferences(
|
|
31
|
+
ReferenceType.GRAPHQL_QUERY,
|
|
32
|
+
queryFields
|
|
33
|
+
))
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const mutations = schema.getRootType(OperationTypeNode.MUTATION)
|
|
37
|
+
const mutationFields = mutations?.getFields?.()
|
|
38
|
+
|
|
39
|
+
if (mutationFields) {
|
|
40
|
+
references.push(...graphqlOperationReferences(
|
|
41
|
+
ReferenceType.GRAPHQL_MUTATION,
|
|
42
|
+
mutationFields
|
|
43
|
+
))
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return references
|
|
47
|
+
}
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import {GraphQLFieldMap} from "graphql/type";
|
|
2
|
+
import {
|
|
3
|
+
Reference,
|
|
4
|
+
ReferenceCategory,
|
|
5
|
+
ReferenceType,
|
|
6
|
+
ExampleGroup,
|
|
7
|
+
Definition,
|
|
8
|
+
Example,
|
|
9
|
+
} from "@xyd-js/uniform";
|
|
10
|
+
|
|
11
|
+
import {argumentsIntoDefinitionProperty} from "./arguments";
|
|
12
|
+
import {fieldIntoDefinitionProperty} from "./fields";
|
|
13
|
+
import {simpleGraphqlExample} from "./examples";
|
|
14
|
+
|
|
15
|
+
// TODO: examples
|
|
16
|
+
// graphqlOperationReferences is a helper function to create a list of xyd reference for a GraphQL query or mutation.
|
|
17
|
+
export function graphqlOperationReferences(
|
|
18
|
+
operationType: ReferenceType.GRAPHQL_MUTATION | ReferenceType.GRAPHQL_QUERY,
|
|
19
|
+
fieldsMap: GraphQLFieldMap<any, any>
|
|
20
|
+
) {
|
|
21
|
+
const references: Reference[] = []
|
|
22
|
+
|
|
23
|
+
for (const [operationName, operationField] of Object.entries(fieldsMap)) {
|
|
24
|
+
const definitions: Definition[] = []
|
|
25
|
+
|
|
26
|
+
const args = argumentsIntoDefinitionProperty(operationField.args)
|
|
27
|
+
const returns = fieldIntoDefinitionProperty(operationName, operationField)
|
|
28
|
+
const returnProperties = returns.properties || []
|
|
29
|
+
|
|
30
|
+
definitions.push({
|
|
31
|
+
title: "Arguments",
|
|
32
|
+
properties: args,
|
|
33
|
+
})
|
|
34
|
+
definitions.push({
|
|
35
|
+
title: "Returns",
|
|
36
|
+
properties: returnProperties
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
const exampleQuery = simpleGraphqlExample(
|
|
40
|
+
operationType,
|
|
41
|
+
operationName,
|
|
42
|
+
args,
|
|
43
|
+
returnProperties
|
|
44
|
+
)
|
|
45
|
+
const examples: Example[] = [
|
|
46
|
+
{
|
|
47
|
+
codeblock: {
|
|
48
|
+
tabs: [
|
|
49
|
+
{
|
|
50
|
+
title: "graphql",
|
|
51
|
+
language: "graphql",
|
|
52
|
+
code: exampleQuery,
|
|
53
|
+
}
|
|
54
|
+
]
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
]
|
|
58
|
+
|
|
59
|
+
const exampleGroup = {
|
|
60
|
+
description: "Example request",
|
|
61
|
+
examples,
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
let description = operationField.description || ""
|
|
65
|
+
|
|
66
|
+
references.push(graphqlReference(
|
|
67
|
+
operationType,
|
|
68
|
+
operationName,
|
|
69
|
+
operationName,
|
|
70
|
+
description,
|
|
71
|
+
[exampleGroup],
|
|
72
|
+
definitions,
|
|
73
|
+
))
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return references
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// graphqlReference is a helper function to create a Reference object for a GraphQL query or mutation.
|
|
80
|
+
function graphqlReference(
|
|
81
|
+
operationType: ReferenceType.GRAPHQL_QUERY | ReferenceType.GRAPHQL_MUTATION,
|
|
82
|
+
title: string,
|
|
83
|
+
canonical: string,
|
|
84
|
+
description: string,
|
|
85
|
+
examples: ExampleGroup[],
|
|
86
|
+
definitions: Definition[],
|
|
87
|
+
): Reference {
|
|
88
|
+
return {
|
|
89
|
+
title,
|
|
90
|
+
canonical,
|
|
91
|
+
description,
|
|
92
|
+
|
|
93
|
+
category: ReferenceCategory.GRAPHQL,
|
|
94
|
+
type: operationType,
|
|
95
|
+
|
|
96
|
+
examples: {
|
|
97
|
+
groups: examples,
|
|
98
|
+
},
|
|
99
|
+
definitions,
|
|
100
|
+
}
|
|
101
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "node",
|
|
6
|
+
"strict": true,
|
|
7
|
+
"esModuleInterop": true,
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
"forceConsistentCasingInFileNames": true,
|
|
10
|
+
"outDir": "./dist",
|
|
11
|
+
"declaration": true,
|
|
12
|
+
"declarationMap": true,
|
|
13
|
+
"incremental": true,
|
|
14
|
+
"tsBuildInfoFile": "./dist/tsconfig.tsbuildinfo"
|
|
15
|
+
},
|
|
16
|
+
"include": ["examples/basic-example/**/*.ts", "src/**/*.ts"],
|
|
17
|
+
"exclude": ["node_modules", "dist"]
|
|
18
|
+
}
|
package/tsup.config.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import {defineConfig} from 'tsup';
|
|
2
|
+
|
|
3
|
+
export default defineConfig({
|
|
4
|
+
entry: ['index.ts'],
|
|
5
|
+
format: ['esm'], // Output both ESM and CJS formats
|
|
6
|
+
target: 'node16', // Ensure compatibility with Node.js 16
|
|
7
|
+
dts: {
|
|
8
|
+
entry: 'index.ts', // Specify the entry for DTS
|
|
9
|
+
resolve: true, // Resolve external types
|
|
10
|
+
},
|
|
11
|
+
splitting: false, // Disable code splitting
|
|
12
|
+
sourcemap: true, // Generate source maps
|
|
13
|
+
clean: true, // Clean the output directory before each build
|
|
14
|
+
esbuildOptions: (options) => {
|
|
15
|
+
options.platform = 'node'; // Ensure the platform is set to Node.js
|
|
16
|
+
options.external = ['node:fs/promises']; // Mark 'node:fs/promises' as external
|
|
17
|
+
options.loader = {'.js': 'jsx'}; // Ensure proper handling of .js files
|
|
18
|
+
},
|
|
19
|
+
});
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { defineConfig } from 'tsup';
|
|
2
|
+
|
|
3
|
+
// TODO: multiple entry points + outputs
|
|
4
|
+
|
|
5
|
+
const isDebugMode = !!process.env.DEBUG
|
|
6
|
+
|
|
7
|
+
export default defineConfig({
|
|
8
|
+
entry: [
|
|
9
|
+
"./examples/basic/index.ts"
|
|
10
|
+
],
|
|
11
|
+
format: ['esm', 'cjs'], // Output both ESM and CJS formats
|
|
12
|
+
target: 'node16', // Ensure compatibility with Node.js 16
|
|
13
|
+
dts: {
|
|
14
|
+
entry: "./examples/basic/index.ts",
|
|
15
|
+
resolve: true, // Resolve external types
|
|
16
|
+
},
|
|
17
|
+
splitting: false, // Disable code splitting
|
|
18
|
+
sourcemap: true, // Generate source maps
|
|
19
|
+
clean: true, // Clean the output directory before each build
|
|
20
|
+
outDir: './examples/dist', // Output directory
|
|
21
|
+
esbuildOptions(options) {
|
|
22
|
+
options.minify = true;
|
|
23
|
+
if (isDebugMode) {
|
|
24
|
+
options.drop = [];
|
|
25
|
+
} else {
|
|
26
|
+
options.drop = [];
|
|
27
|
+
options.pure = ['console.debug'];
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
});
|