Skip to content

Commit eda28d6

Browse files
authored
feat: Add experimental input builders (#369)
1 parent f3dc33a commit eda28d6

11 files changed

Lines changed: 1004 additions & 20 deletions

File tree

packages/graphql_codegen/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
# 1.1.0
2+
3+
- Add new experimental input builders for better `null` / *undefined* handling. Enable by setting `EXPERIMENTAL_enable_input_builders: true`.
4+
15
# 1.0.1
26

37
- Don't remove `when` and `maybeWhen` when `disableCopyWithGeneration = true`.

packages/graphql_codegen/lib/src/config/config.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@ class GraphQLCodegenConfig {
6868
final bool disableContextReplacement;
6969
final bool disableCopyWithGeneration;
7070

71+
@JsonKey(name: 'EXPERIMENTAL_enable_input_builders')
72+
final bool enableInputBuilders;
73+
7174
GraphQLCodegenConfig({
7275
this.clients = const {},
7376
this.disableContextReplacement = false,
@@ -82,6 +85,7 @@ class GraphQLCodegenConfig {
8285
this.extraKeywords = const [],
8386
this.outputDirectory = '.',
8487
this.disableCopyWithGeneration = false,
88+
this.enableInputBuilders = false,
8589
});
8690

8791
@override

packages/graphql_codegen/lib/src/config/config.g.dart

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/graphql_codegen/lib/src/printer/base/copy.dart

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,10 @@ import 'package:code_builder/code_builder.dart';
33
import 'package:graphql_codegen/src/context/context.dart';
44
import 'package:graphql_codegen/src/printer/base/deep_copy.dart';
55
import 'package:graphql_codegen/src/printer/base/property.dart';
6+
import 'package:graphql_codegen/src/printer/base/undefined.dart';
67
import 'package:graphql_codegen/src/printer/clients/utils.dart';
78
import 'package:graphql_codegen/src/printer/context.dart';
89

9-
const _undefined = '_undefined';
10-
1110
Expression _printDefaultConstructor(
1211
PrintContext c,
1312
String name,
@@ -20,9 +19,9 @@ Expression _printDefaultConstructor(
2019
property.name,
2120
(property.isRequired
2221
? refer(parameterName)
23-
.equalTo(refer(_undefined))
22+
.equalTo(refer(kUndefinedFieldName))
2423
.or(refer(parameterName).equalTo(literalNull))
25-
: refer(parameterName).equalTo(refer(_undefined)))
24+
: refer(parameterName).equalTo(refer(kUndefinedFieldName)))
2625
.conditional(refer('_instance').property(parameterName),
2726
refer(parameterName).asA(propertyType)));
2827
}));
@@ -156,17 +155,7 @@ List<Spec> printCopyWithClasses(
156155
)
157156
..modifier = FieldModifier.final$,
158157
),
159-
Field(
160-
(b) => b
161-
..name = _undefined
162-
..static = true
163-
..modifier = FieldModifier.constant
164-
..assignment = literalMap(
165-
{},
166-
refer('dynamic'),
167-
refer('dynamic'),
168-
).code,
169-
)
158+
printUndefinedField(),
170159
])
171160
..methods = ListBuilder(<Method>[
172161
Method(
@@ -179,7 +168,7 @@ List<Spec> printCopyWithClasses(
179168
(b) => b
180169
..name = c.namePrinter.printPropertyName(property.name)
181170
..named = true
182-
..defaultTo = refer(_undefined).code
171+
..defaultTo = refer(kUndefinedFieldName).code
183172
..type = refer('Object?'),
184173
);
185174
}),

packages/graphql_codegen/lib/src/printer/base/input.dart

Lines changed: 123 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import 'package:graphql_codegen/src/printer/base/copy.dart';
77
import 'package:graphql_codegen/src/printer/base/equality.dart';
88
import 'package:graphql_codegen/src/printer/base/json.dart';
99
import 'package:graphql_codegen/src/printer/base/property.dart';
10+
import 'package:graphql_codegen/src/printer/base/undefined.dart';
1011
import 'package:graphql_codegen/src/printer/clients/utils.dart';
1112
import 'package:graphql_codegen/src/printer/context.dart';
1213
import 'package:graphql_codegen/src/printer/utils.dart';
@@ -84,6 +85,12 @@ List<Spec> _printInputClasses({
8485
]).code,
8586
),
8687
)
88+
else if (context.context.config.enableInputBuilders)
89+
_printInputBuilderConstructor(
90+
context: context,
91+
properties: properties,
92+
name: name,
93+
)
8794
else
8895
Constructor(
8996
(b) => b
@@ -142,7 +149,10 @@ List<Spec> _printInputClasses({
142149
(b) => b
143150
..name = kDataVariableName
144151
..type = dynamicMap,
145-
)
152+
),
153+
if (context.context.config.enableInputBuilders &&
154+
!context.context.isOneOf)
155+
printUndefinedField(),
146156
])
147157
..methods = ListBuilder([
148158
...properties.map((e) => Method(
@@ -209,9 +219,10 @@ List<Spec> _printInputClasses({
209219
final propName = context.namePrinter.printPropertyName(prop.name);
210220
return [
211221
if (prop.isNonNull)
212-
Code('if(${propName} != _undefined && ${propName} != null)')
222+
Code(
223+
'if(${propName} != ${kUndefinedFieldName} && ${propName} != null)')
213224
else
214-
Code('if(${propName} != _undefined)'),
225+
Code('if(${propName} != ${kUndefinedFieldName})'),
215226
literalString(prop.name.value).code,
216227
Code(':'),
217228
refer(propName).asA(printClassPropertyType(context, prop)).code,
@@ -225,6 +236,115 @@ List<Spec> _printInputClasses({
225236
];
226237
}
227238

239+
Constructor _printInputBuilderConstructor(
240+
{required PrintContext context,
241+
required String Function(Name) name,
242+
required Iterable<ContextProperty> properties}) {
243+
final buildPropertyMapEntry = (ContextProperty property) => MapEntry(
244+
context.namePrinter.printPropertyName(
245+
property.name,
246+
),
247+
property.hasDefaultValue
248+
? asNullable(
249+
printClassPropertyType(
250+
context,
251+
property,
252+
),
253+
)
254+
: printClassPropertyType(
255+
context,
256+
property,
257+
),
258+
);
259+
final fnName = 'fn';
260+
return Constructor(
261+
(b) => b
262+
..factory = true
263+
..requiredParameters = ListBuilder<Parameter>(
264+
[
265+
Parameter(
266+
(b) => b
267+
..name = fnName
268+
..type = FunctionType(
269+
(b) => b
270+
..returnType = refer(name(context.path))
271+
..requiredParameters = ListBuilder<Reference>(
272+
[
273+
FunctionType(
274+
(b) => b
275+
..namedParameters = MapBuilder(
276+
Map.fromEntries(
277+
properties
278+
.where((property) => !property.isRequired)
279+
.map(buildPropertyMapEntry),
280+
),
281+
)
282+
..namedRequiredParameters = MapBuilder(
283+
Map.fromEntries(
284+
properties
285+
.where((property) => property.isRequired)
286+
.map(buildPropertyMapEntry),
287+
),
288+
)
289+
..returnType = refer(
290+
name(context.path),
291+
),
292+
)
293+
],
294+
),
295+
),
296+
)
297+
],
298+
)
299+
..body = refer(fnName).call(
300+
[
301+
Method(
302+
(b) => b
303+
..optionalParameters = ListBuilder(
304+
properties.map(
305+
(property) => Parameter(
306+
(b) => b
307+
..name = context.namePrinter.printPropertyName(
308+
property.name,
309+
)
310+
..named = true
311+
..required = property.isRequired
312+
..defaultTo = property.isRequired
313+
? null
314+
: refer(kUndefinedFieldName).code
315+
..type = property.isRequired
316+
? printClassPropertyType(context, property)
317+
: refer('Object?'),
318+
),
319+
),
320+
)
321+
..body = refer(name(context.path)).property('_').call([
322+
CodeExpression(Code(
323+
"""
324+
{
325+
${properties.map((property) {
326+
final key = property.name.value;
327+
final value = context.namePrinter.printPropertyName(
328+
property.name,
329+
);
330+
final entry = "r'${key}': ${value},";
331+
if (property.isRequired) {
332+
return entry;
333+
}
334+
return """
335+
if (${value} != ${kUndefinedFieldName}) ${entry}
336+
""";
337+
}).join()}
338+
}
339+
""",
340+
)),
341+
]).code,
342+
).closure
343+
],
344+
).code,
345+
);
346+
}
347+
228348
Code _printToJson(PrintContext context, Iterable<ContextProperty> properties) {
229349
const resultDataVariableName = r'result$data';
230350
return Block.of([
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import 'package:code_builder/code_builder.dart';
2+
3+
const kUndefinedFieldName = '_undefined';
4+
5+
Field printUndefinedField() {
6+
return Field(
7+
(b) => b
8+
..name = kUndefinedFieldName
9+
..static = true
10+
..modifier = FieldModifier.constant
11+
..assignment = literalMap(
12+
{},
13+
refer('dynamic'),
14+
refer('dynamic'),
15+
).code,
16+
);
17+
}

packages/graphql_codegen/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ description: |
33
Simple, opinionated, codegen library for GraphQL. It allows you to
44
generate serializers and client helpers to easily call and parse your data.
55
6-
version: 1.0.1
6+
version: 1.1.0
77
homepage: https://github.com/heftapp/graphql_codegen/tree/main/packages/graphql_codegen
88
repository: https://github.com/heftapp/graphql_codegen/tree/main/packages/graphql_codegen
99

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
input I1 {
2+
s: String!
3+
sMaybe: String
4+
nested_input: I1
5+
children: [I1!]
6+
children2: [[I1]]
7+
_min: Int
8+
eMaybe: E
9+
e: E!
10+
es: [E!]!
11+
i2: I2
12+
}
13+
14+
input I2 {
15+
foobar: Foobar
16+
}
17+
18+
enum E {
19+
FOO
20+
BAR
21+
BAZ
22+
}
23+
scalar Foobar

0 commit comments

Comments
 (0)