1
1
import { getValueWithoutLoc } from '@/GqlParser/valueNode' ;
2
- import { OperationType , ParserField } from '@/Models' ;
2
+ import { OperationType , ParserField , TypeDefinition } from '@/Models' ;
3
3
import { GqlParserTree , VariableDefinitionWithoutLoc } from '@/Models/GqlParserTree' ;
4
4
import { Parser } from '@/Parser' ;
5
5
import { TypeResolver } from '@/Parser/typeResolver' ;
6
- import { compileType } from '@/shared' ;
6
+ import { compileType , getTypeName } from '@/shared' ;
7
7
import {
8
8
DefinitionNode ,
9
9
parse ,
@@ -13,10 +13,14 @@ import {
13
13
DirectiveNode ,
14
14
FieldNode ,
15
15
VariableDefinitionNode ,
16
+ FragmentDefinitionNode ,
17
+ FragmentSpreadNode ,
18
+ InlineFragmentNode ,
16
19
} from 'graphql' ;
17
- export const GqlParser = ( gql : string , schema : string ) => {
20
+ export const parseGql = ( gql : string , schema : string ) => {
18
21
const { definitions } = parse ( schema + '\n' + gql ) ;
19
22
const ops = definitions . filter ( onlyOperations ) ;
23
+ const frags = definitions . filter ( onlyFragments ) ;
20
24
const { nodes } = Parser . parse ( schema ) ;
21
25
22
26
const composeDefinition = ( d : OperationDefinitionNode ) : GqlParserTree => {
@@ -42,7 +46,21 @@ export const GqlParser = (gql: string, schema: string) => {
42
46
variableDefinitions : d . variableDefinitions . map ( ( vd ) => composeVariableDefinition ( vd ) ) ,
43
47
}
44
48
: { } ) ,
45
- children : d . selectionSet . selections . filter ( onlyFieldNodes ) . map ( ( s ) => composeSelectionNode ( s , node ) ) ,
49
+ children : d . selectionSet . selections . map ( ( s ) => composeSelectionNode ( s , node ) ) ,
50
+ } ;
51
+ } ;
52
+
53
+ const composeFragment = ( f : FragmentDefinitionNode ) : GqlParserTree => {
54
+ const node = nodes . find ( ( n ) => n . name == f . typeCondition . name . value ) ;
55
+ if ( ! node ) {
56
+ throw new Error ( `Type ${ f . typeCondition . name . value } does not exist in schema` ) ;
57
+ }
58
+ return {
59
+ node,
60
+ fragment : true ,
61
+ name : f . name . value ,
62
+ ...( f . directives ?. length ? { directives : f . directives . map ( ( a ) => composeDirectiveNode ( a , node ) ) } : { } ) ,
63
+ children : f . selectionSet . selections . map ( ( s ) => composeSelectionNode ( s , node ) ) ,
46
64
} ;
47
65
} ;
48
66
@@ -74,29 +92,66 @@ export const GqlParser = (gql: string, schema: string) => {
74
92
return {
75
93
node,
76
94
name : a . name . value ,
77
- arguments : a . arguments ?. map ( ( a ) => composeArgumentNode ( a , node ) ) ,
95
+ ... ( a . arguments ?. length ? { arguments : a . arguments ?. map ( ( ar ) => composeArgumentNode ( ar , node ) ) } : { } ) ,
78
96
} ;
79
97
} ;
80
98
81
- const composeSelectionNode = ( s : FieldNode , parentNode : ParserField ) : GqlParserTree => {
99
+ const composeFragmentSpread = ( f : FragmentSpreadNode , node : ParserField ) : GqlParserTree => {
100
+ return {
101
+ node,
102
+ name : f . name . value ,
103
+ ...( f . directives ?. length ? { directives : f . directives ?. map ( ( a ) => composeDirectiveNode ( a , node ) ) } : { } ) ,
104
+ fragmentSpread : true ,
105
+ } ;
106
+ } ;
107
+ const composeInlineFragment = ( f : InlineFragmentNode , node : ParserField ) : GqlParserTree => {
108
+ const chosenNode = nodes . find ( ( n ) => n . name === f . typeCondition ?. name . value ) ;
109
+ const rightNode = chosenNode || node ;
110
+ return {
111
+ node : rightNode ,
112
+ name : rightNode . name ,
113
+ ...( f . directives ?. length ? { directives : f . directives ?. map ( ( a ) => composeDirectiveNode ( a , rightNode ) ) } : { } ) ,
114
+ inlineFragment : true ,
115
+ children : f . selectionSet . selections . map ( ( s ) => composeSelectionNode ( s , rightNode ) ) ,
116
+ } ;
117
+ } ;
118
+
119
+ const composeSelectionNode = ( s : SelectionNode , node : ParserField ) : GqlParserTree => {
120
+ if ( s . kind === 'Field' ) return composeFieldNode ( s , node ) ;
121
+ if ( s . kind === 'FragmentSpread' ) return composeFragmentSpread ( s , node ) ;
122
+ return composeInlineFragment ( s , node ) ;
123
+ } ;
124
+
125
+ const composeFieldNode = ( s : FieldNode , parentNode : ParserField ) : GqlParserTree => {
82
126
const fieldNode = parentNode . args . find ( ( a ) => a . name === s . name . value ) ;
83
127
if ( ! fieldNode ) {
84
- throw new Error ( ' Field does not exist in schema' ) ;
128
+ throw new Error ( ` Field " ${ s . name . value } " does not exist in " ${ parentNode . name } " node` ) ;
85
129
}
130
+ const passParentDown = getTypeName ( fieldNode . type . fieldType ) ;
131
+ const isParentObjectNode = nodes . find (
132
+ ( n ) =>
133
+ n . name === passParentDown &&
134
+ ( n . data . type === TypeDefinition . ObjectTypeDefinition ||
135
+ n . data . type === TypeDefinition . UnionTypeDefinition ||
136
+ n . data . type === TypeDefinition . InterfaceTypeDefinition ) ,
137
+ ) ;
138
+
86
139
return {
87
140
node : fieldNode ,
88
141
name : s . name . value ,
89
- arguments : s . arguments ?. map ( ( a ) => composeArgumentNode ( a , fieldNode ) ) ,
90
- directives : s . directives ?. map ( ( a ) => composeDirectiveNode ( a , fieldNode ) ) ,
91
- children : s . selectionSet ?. selections . filter ( onlyFieldNodes ) . map ( ( a ) => composeSelectionNode ( a , fieldNode ) ) ,
142
+ ...( s . arguments ?. length ? { arguments : s . arguments ?. map ( ( a ) => composeArgumentNode ( a , fieldNode ) ) } : { } ) ,
143
+ ...( s . directives ?. length ? { directives : s . directives ?. map ( ( a ) => composeDirectiveNode ( a , fieldNode ) ) } : { } ) ,
144
+ ...( s . selectionSet ?. selections . length
145
+ ? { children : s . selectionSet . selections . map ( ( s ) => composeSelectionNode ( s , isParentObjectNode || fieldNode ) ) }
146
+ : { } ) ,
92
147
} as GqlParserTree ;
93
148
} ;
94
- return ops . map ( ( o ) => composeDefinition ( o ) ) ;
149
+ return [ ... frags . map ( ( f ) => composeFragment ( f ) ) , ... ops . map ( ( o ) => composeDefinition ( o ) ) ] ;
95
150
} ;
96
151
97
152
const onlyOperations = ( definition : DefinitionNode ) : definition is OperationDefinitionNode => {
98
153
return definition . kind === 'OperationDefinition' ;
99
154
} ;
100
- const onlyFieldNodes = ( definition : SelectionNode ) : definition is FieldNode => {
101
- return definition . kind === 'Field ' ;
155
+ const onlyFragments = ( definition : DefinitionNode ) : definition is FragmentDefinitionNode => {
156
+ return definition . kind === 'FragmentDefinition ' ;
102
157
} ;
0 commit comments