Skip to content

Commit

Permalink
simpler version of reuse fragments with less scope
Browse files Browse the repository at this point in the history
  • Loading branch information
knaeckeKami committed Jan 7, 2024
1 parent 22b04ae commit ad82936
Show file tree
Hide file tree
Showing 5 changed files with 116 additions and 57 deletions.
39 changes: 0 additions & 39 deletions codegen/end_to_end_test/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,42 +15,3 @@ dev_dependencies:
build: ^2.0.0
build_runner: ^2.0.0
test: ^1.16.8
dependency_overrides:
gql_pedantic:
path: ../../gql_pedantic
gql:
path: ../../gql
gql_exec:
path: ../../links/gql_exec
gql_link:
path: ../../links/gql_link
gql_websocket_link:
path: ../../links/gql_websocket_link
gql_tristate_value:
path: ../gql_tristate_value
gql_transform_link:
path: ../../links/gql_transform_link
gql_http_link:
path: ../../links/gql_http_link
gql_error_link:
path: ../../links/gql_error_link
gql_code_builder:
path: ../gql_code_builder
gql_build:
path: ../gql_build
gql_example_http_auth_link:
path: ../../examples/gql_example_http_auth_link
gql_dio_link:
path: ../../links/gql_dio_link
gql_example_dio_link:
path: ../../examples/gql_example_dio_link
gql_example_cli_github:
path: ../../examples/gql_example_cli_github
gql_example_cli:
path: ../../examples/gql_example_cli
gql_example_build:
path: ../../examples/gql_example_build
gql_dedupe_link:
path: ../../links/gql_dedupe_link
end_to_end_test_tristate:
path: ../end_to_end_test_tristate
33 changes: 20 additions & 13 deletions codegen/gql_code_builder/lib/data.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,28 +19,35 @@ Library buildDataLibrary(
generateMaybeWhenExtensionMethod: false,
),
]) {
final operationDataClasses = docSource.document.definitions
.whereType<OperationDefinitionNode>()
.expand(
(op) => buildOperationDataClasses(
op,
docSource,
schemaSource,
typeOverrides,
whenExtensionConfig,
),
)
.toList();

final fragmentRefMap = <BuiltSet<SelectionNode>, Reference>{};


final fragmentDataClasses = docSource.document.definitions
.whereType<FragmentDefinitionNode>()
.expand(
(frag) => buildFragmentDataClasses(
frag,
frag,
docSource,
schemaSource,
typeOverrides,
whenExtensionConfig, fragmentRefMap,
),
)
.toList();



final operationDataClasses = docSource.document.definitions
.whereType<OperationDefinitionNode>()
.expand(
(op) => buildOperationDataClasses(
op,
docSource,
schemaSource,
typeOverrides,
whenExtensionConfig,
fragmentRefMap,
),
)
.toList();
Expand Down
9 changes: 5 additions & 4 deletions codegen/gql_code_builder/lib/src/common.dart
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ Method buildGetter({
required TypeNode typeNode,
required SourceNode schemaSource,
Map<String, Reference> typeOverrides = const {},
Reference? fragmentRef,
String? typeRefPrefix,
bool built = true,
bool isOverride = false,
Expand All @@ -161,10 +162,10 @@ Method buildGetter({
...typeOverrides,
};

final returnType = _typeRef(
typeNode,
typeMap,
);
final returnType = fragmentRef ?? _typeRef(
typeNode,
typeMap,
);

return Method(
(b) => b
Expand Down
4 changes: 4 additions & 0 deletions codegen/gql_code_builder/lib/src/inline_fragment_classes.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import "package:built_collection/built_collection.dart";
import "package:code_builder/code_builder.dart";
import "package:gql/ast.dart";
import "package:gql_code_builder/src/config/when_extension_config.dart";
Expand Down Expand Up @@ -26,6 +27,7 @@ List<Spec> buildInlineFragmentClasses({
required List<InlineFragmentNode> inlineFragments,
required bool built,
required InlineFragmentSpreadWhenExtensionConfig whenExtensionConfig,
required Map<BuiltSet<SelectionNode>, Reference> fragmentRefMap,
}) {
final whenExtension = inlineFragmentWhenExtension(
baseTypeName: name,
Expand Down Expand Up @@ -73,6 +75,7 @@ List<Spec> buildInlineFragmentClasses({
},
built: built,
whenExtensionConfig: whenExtensionConfig,
fragmentRefMap: fragmentRefMap,
),

/// TODO: Handle inline fragments without a type condition
Expand All @@ -96,6 +99,7 @@ List<Spec> buildInlineFragmentClasses({
name: SourceSelections(url: null, selections: selections)
},
built: built,
fragmentRefMap: fragmentRefMap,
whenExtensionConfig: whenExtensionConfig),
),
];
Expand Down
88 changes: 87 additions & 1 deletion codegen/gql_code_builder/lib/src/operation/data.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import "package:built_collection/built_collection.dart";
import "package:code_builder/code_builder.dart";
import "package:collection/collection.dart";
import "package:gql/ast.dart";
import "package:gql_code_builder/src/config/when_extension_config.dart";

Expand All @@ -13,6 +15,7 @@ List<Spec> buildOperationDataClasses(
SourceNode schemaSource,
Map<String, Reference> typeOverrides,
InlineFragmentSpreadWhenExtensionConfig whenExtensionConfig,
Map<BuiltSet<SelectionNode>, Reference> fragmentRefMap,
) {
if (op.name == null) {
throw Exception("Operations must be named");
Expand All @@ -34,6 +37,7 @@ List<Spec> buildOperationDataClasses(
fragmentMap: fragmentMap,
superclassSelections: {},
whenExtensionConfig: whenExtensionConfig,
fragmentRefMap: fragmentRefMap,
);
}

Expand All @@ -43,12 +47,25 @@ List<Spec> buildFragmentDataClasses(
SourceNode schemaSource,
Map<String, Reference> typeOverrides,
InlineFragmentSpreadWhenExtensionConfig whenExtensionConfig,
Map<BuiltSet<SelectionNode>, Reference> fragmentRefMap,
) {
final fragmentMap = _fragmentMap(docSource);
final selections = mergeSelections(
frag.selectionSet.selections,
fragmentMap,
);

final set = BuiltSet.of(selections.withoutFragmentSpreads);

if(fragmentRefMap.containsKey(set)){
print("***** warning: duplicated fragment found: ${frag.name.value} in ${docSource.url}, previous defintion with same data ${fragmentRefMap[set]}");
}

fragmentRefMap[set] = refer(
builtClassName("${frag.name.value}Data"),
(docSource.url ?? "") + "#data",
);

return [
// abstract class that will implemented by any class that uses the fragment
...buildSelectionSetDataClasses(
Expand All @@ -61,6 +78,7 @@ List<Spec> buildFragmentDataClasses(
superclassSelections: {},
built: false,
whenExtensionConfig: whenExtensionConfig,
fragmentRefMap: fragmentRefMap,
),
// concrete built_value data class for fragment
...buildSelectionSetDataClasses(
Expand All @@ -77,6 +95,7 @@ List<Spec> buildFragmentDataClasses(
)
},
whenExtensionConfig: whenExtensionConfig,
fragmentRefMap: fragmentRefMap,
),
];
}
Expand Down Expand Up @@ -127,6 +146,7 @@ List<Spec> buildSelectionSetDataClasses({
required Map<String, SourceSelections> superclassSelections,
bool built = true,
required InlineFragmentSpreadWhenExtensionConfig whenExtensionConfig,
required Map<BuiltSet<SelectionNode>, Reference> fragmentRefMap,
}) {
for (final selection in selections.whereType<FragmentSpreadNode>()) {
if (!fragmentMap.containsKey(selection.name.value)) {
Expand All @@ -146,6 +166,8 @@ List<Spec> buildSelectionSetDataClasses({
.expand((selections) => selections.selections)
.toSet();

final fieldsThatAreSingleFragmentSpreads = <FieldNode>{};

final fieldGetters = selections.whereType<FieldNode>().map<Method>(
(node) {
final nameNode = node.alias ?? node.name;
Expand All @@ -157,11 +179,30 @@ List<Spec> buildSelectionSetDataClasses({
typeDef,
node.name.value,
);

final getterSelections = node.selectionSet;

Reference? fragmentRef;

if(getterSelections != null){
final withoutFragmentSpreads = getterSelections.selections.withoutFragmentSpreads.toBuiltSet();

final hasMatchingFragment = fragmentRefMap[withoutFragmentSpreads];

if(hasMatchingFragment != null){
fieldsThatAreSingleFragmentSpreads.add(node);
}

fragmentRef = hasMatchingFragment;

}

return buildGetter(
nameNode: nameNode,
typeNode: typeNode,
schemaSource: schemaSource,
typeOverrides: typeOverrides,
fragmentRef: fragmentRef,
typeRefPrefix: node.selectionSet != null ? builtClassName(name) : null,
built: built,
isOverride: superclassSelectionNodes.contains(node),
Expand All @@ -185,6 +226,7 @@ List<Spec> buildSelectionSetDataClasses({
inlineFragments: inlineFragments,
built: built,
whenExtensionConfig: whenExtensionConfig,
fragmentRefMap: fragmentRefMap,
)
else if (!built)
Class(
Expand Down Expand Up @@ -222,7 +264,7 @@ List<Spec> buildSelectionSetDataClasses({
...selections
.whereType<FieldNode>()
.where(
(field) => field.selectionSet != null,
(field) => field.selectionSet != null && !fieldsThatAreSingleFragmentSpreads.contains(field),
)
.expand(
(field) => buildSelectionSetDataClasses(
Expand All @@ -246,6 +288,7 @@ List<Spec> buildSelectionSetDataClasses({
),
built: inlineFragments.isNotEmpty ? false : built,
whenExtensionConfig: whenExtensionConfig,
fragmentRefMap: fragmentRefMap,
),
),
];
Expand Down Expand Up @@ -379,3 +422,46 @@ TypeNode _getFieldTypeNode(
)
.type;
}

extension IsSingleFragmentSpread on SelectionSetNode {

Iterable<SelectionNode> get withoutTypeName => selections.where((selection) => selection is! FieldNode || selection.name.value != "__typename");

bool get isSingleFragmentSpread => withoutTypeName.length == 1 && withoutTypeName.first is FragmentSpreadNode;

Iterable<FragmentSpreadNode> get fragmentSpreads => selections.whereType<FragmentSpreadNode>();

Iterable<InlineFragmentNode> get inlineFragments => selections.whereType<InlineFragmentNode>();

Iterable<FieldNode> get fields => selections.whereType<FieldNode>();


}

extension WithoutFragmentSpreads on Iterable<SelectionNode> {
Iterable<SelectionNode> get withoutFragmentSpreads => where((selection) => selection is! FragmentSpreadNode)
.map((e) {
if(e is FieldNode){
if(e.selectionSet == null) return e;
return FieldNode(
name: e.name,
alias: e.alias,
arguments: e.arguments,
directives: e.directives,
selectionSet: SelectionSetNode(
selections: e.selectionSet!.selections.withoutFragmentSpreads.toList(),
),
);
}
if(e is InlineFragmentNode){
return InlineFragmentNode(
typeCondition: e.typeCondition,
directives: e.directives,
selectionSet: SelectionSetNode(
selections: e.selectionSet.selections.withoutFragmentSpreads.toList(),
),
);
}
return e;
});
}

0 comments on commit ad82936

Please sign in to comment.