1
1
import "package:built_collection/built_collection.dart" ;
2
2
import "package:code_builder/code_builder.dart" ;
3
3
import "package:gql/ast.dart" ;
4
+ import "package:gql_code_builder/src/common.dart" ;
4
5
import "package:gql_code_builder/src/config/when_extension_config.dart" ;
5
6
6
7
import "./source.dart" ;
@@ -19,6 +20,9 @@ Library buildDataLibrary(
19
20
generateMaybeWhenExtensionMethod: false ,
20
21
),
21
22
]) {
23
+ final fragmentMap = _fragmentMap (docSource);
24
+ final dataClassAliasMap = _dataClassAliasMap (docSource, fragmentMap);
25
+
22
26
final operationDataClasses = docSource.document.definitions
23
27
.whereType <OperationDefinitionNode >()
24
28
.expand (
@@ -28,6 +32,8 @@ Library buildDataLibrary(
28
32
schemaSource,
29
33
typeOverrides,
30
34
whenExtensionConfig,
35
+ fragmentMap,
36
+ dataClassAliasMap,
31
37
),
32
38
)
33
39
.toList ();
@@ -41,6 +47,8 @@ Library buildDataLibrary(
41
47
schemaSource,
42
48
typeOverrides,
43
49
whenExtensionConfig,
50
+ fragmentMap,
51
+ dataClassAliasMap,
44
52
),
45
53
)
46
54
.toList ();
@@ -54,3 +62,127 @@ Library buildDataLibrary(
54
62
]),
55
63
);
56
64
}
65
+
66
+
67
+ Map <String , SourceSelections > _fragmentMap (SourceNode source) => {
68
+ for (var def
69
+ in source.document.definitions.whereType <FragmentDefinitionNode >())
70
+ def.name.value: SourceSelections (
71
+ url: source.url,
72
+ selections: def.selectionSet.selections,
73
+ ),
74
+ for (var import in source.imports) ..._fragmentMap (import)
75
+ };
76
+
77
+ Map <String , Reference > _dataClassAliasMap (SourceNode source, Map <String , SourceSelections > fragmentMap, [Map <String , Reference >? aliasMap, Set <String >? visitedSource]) {
78
+ aliasMap ?? = {};
79
+ visitedSource ?? = {};
80
+
81
+ source.imports.forEach ((s) {
82
+ if (! visitedSource! .contains (source.url)) {
83
+ visitedSource.add (source.url);
84
+ _dataClassAliasMap (s, fragmentMap, aliasMap);
85
+ }
86
+ });
87
+
88
+ for (final def in source.document.definitions.whereType <OperationDefinitionNode >()) {
89
+ _dataClassAliasMapDFS (
90
+ typeRefPrefix: builtClassName ("${def .name !.value }Data" ),
91
+ getAliasTypeName: (fragmentName) => "${builtClassName (fragmentName )}Data" ,
92
+ selections: def.selectionSet.selections,
93
+ fragmentMap: fragmentMap,
94
+ aliasMap: aliasMap,
95
+ );
96
+ }
97
+
98
+ for (final def in source.document.definitions.whereType <FragmentDefinitionNode >()) {
99
+ _dataClassAliasMapDFS (
100
+ typeRefPrefix: builtClassName (def.name.value),
101
+ getAliasTypeName: builtClassName,
102
+ selections: def.selectionSet.selections,
103
+ fragmentMap: fragmentMap,
104
+ aliasMap: aliasMap,
105
+ );
106
+ _dataClassAliasMapDFS (
107
+ typeRefPrefix: builtClassName ("${def .name .value }Data" ),
108
+ getAliasTypeName: (fragmentName) => "${builtClassName (fragmentName )}Data" ,
109
+ selections: def.selectionSet.selections,
110
+ fragmentMap: fragmentMap,
111
+ aliasMap: aliasMap,
112
+ );
113
+ }
114
+
115
+ return aliasMap;
116
+ }
117
+
118
+ void _dataClassAliasMapDFS ({
119
+ required String typeRefPrefix,
120
+ required String Function (String fragmentName) getAliasTypeName,
121
+ required List <SelectionNode > selections,
122
+ required Map <String , SourceSelections > fragmentMap,
123
+ required Map <String , Reference > aliasMap,
124
+ }) {
125
+ if (selections.isEmpty) return ;
126
+
127
+ // flatten selections to extract untouched fragments while visiting children.
128
+ final shrunkenSelections = shrinkSelections (mergeSelections (selections, fragmentMap), fragmentMap);
129
+
130
+ // alias single fragment and finish
131
+ final selectionsWithoutTypename = shrunkenSelections.where ((s) => ! (s is FieldNode && s.name.value == "__typename" ));
132
+ if (selectionsWithoutTypename.length == 1 && selectionsWithoutTypename.first is FragmentSpreadNode ) {
133
+ final node = selectionsWithoutTypename.first as FragmentSpreadNode ;
134
+ final fragment = fragmentMap[node.name.value];
135
+ final fragmentTypeName = getAliasTypeName (node.name.value);
136
+ aliasMap[typeRefPrefix] = refer (fragmentTypeName, "${fragment !.url ?? "" }#data" );
137
+ // print("alias $typeRefPrefix => $fragmentTypeName");
138
+ return ;
139
+ }
140
+
141
+ for (final node in selectionsWithoutTypename) {
142
+ if (node is FragmentSpreadNode ) {
143
+ // exclude redefined selections from each fragment selections
144
+ final fragmentSelections = fragmentMap[node.name.value]! .selections;
145
+ final exclusiveFragmentSelections = mergeSelections (fragmentSelections, fragmentMap).where ((s1) {
146
+ if (s1 is FieldNode ) {
147
+ final name = (s1.alias ?? s1.name).value;
148
+ return selectionsWithoutTypename.whereType <FieldNode >().every ((s2) => name != (s2.alias ?? s2.name).value);
149
+ } else if (s1 is InlineFragmentNode && s1.typeCondition != null ) {
150
+ /// TODO: Handle inline fragments without a type condition
151
+ final name = s1.typeCondition! .on .name.value;
152
+ return selectionsWithoutTypename.whereType <InlineFragmentNode >().every ((s2) => name != s2.typeCondition? .on .name.value);
153
+ }
154
+ return false ;
155
+ }).toList ();
156
+
157
+ _dataClassAliasMapDFS (
158
+ typeRefPrefix: typeRefPrefix,
159
+ getAliasTypeName: getAliasTypeName,
160
+ selections: exclusiveFragmentSelections,
161
+ fragmentMap: fragmentMap,
162
+ aliasMap: aliasMap,
163
+ );
164
+ } else if (node is InlineFragmentNode ) {
165
+ if (node.typeCondition != null ) {
166
+ /// TODO: Handle inline fragments without a type condition
167
+ _dataClassAliasMapDFS (
168
+ typeRefPrefix: "${typeRefPrefix }__as${node .typeCondition !.on .name .value }" ,
169
+ getAliasTypeName: getAliasTypeName,
170
+ selections: [
171
+ ...selections.where ((s) => ! (s is InlineFragmentNode )),
172
+ ...node.selectionSet.selections,
173
+ ],
174
+ fragmentMap: fragmentMap,
175
+ aliasMap: aliasMap,
176
+ );
177
+ }
178
+ } else if (node is FieldNode && node.selectionSet != null ) {
179
+ _dataClassAliasMapDFS (
180
+ typeRefPrefix: "${typeRefPrefix }_${(node .alias ?? node .name ).value }" ,
181
+ getAliasTypeName: getAliasTypeName,
182
+ selections: node.selectionSet! .selections,
183
+ fragmentMap: fragmentMap,
184
+ aliasMap: aliasMap,
185
+ );
186
+ }
187
+ }
188
+ }
0 commit comments