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