Skip to content

Commit

Permalink
feat(ferry_generator): allow using schema from different packages
Browse files Browse the repository at this point in the history
  • Loading branch information
knaeckeKami committed Jul 26, 2024
1 parent b65c7b8 commit cde5019
Show file tree
Hide file tree
Showing 6 changed files with 137 additions and 26 deletions.
22 changes: 11 additions & 11 deletions packages/ferry_generator/lib/graphql_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ class GraphqlBuilder implements Builder {
@override
FutureOr<void> build(BuildStep buildStep) async {
SourceNode? schema;
AssetId? _schemaId;
late AssetId _schemaId;
final doc = await readDocument(buildStep, config.sourceExtension);
final docPackage = buildStep.inputId.package;
final docDirPath = p.dirname(buildStep.inputId.path);
Expand Down Expand Up @@ -82,7 +82,7 @@ class GraphqlBuilder implements Builder {
final triStateValueConfig = config.triStateOptionalsConfig;

final schemaOutputAsset =
outputAssetId(_schemaId!, schemaExtension, config.outputDir);
outputAssetId(_schemaId, schemaExtension, config.outputDir);

final schemaUrl = schemaOutputAsset.uri.toString();
final schemaAllocator = GqlAllocator(
Expand Down Expand Up @@ -123,15 +123,15 @@ class GraphqlBuilder implements Builder {
dataToVarsMode,
),
schemaExtension: buildSchemaLibrary(
doc,
p.basename(generatedFilePath(buildStep.inputId, schemaExtension)),
config.typeOverrides,
config.enumFallbackConfig,
generatePossibleTypesMap: config.shouldGeneratePossibleTypes,
allocator: schemaAllocator,
triStateValueConfig: triStateValueConfig,
generateVarsCreateFactories:
config.shouldGenerateVarsCreateFactories),
doc,
p.basename(generatedFilePath(buildStep.inputId, schemaExtension)),
config.typeOverrides,
config.enumFallbackConfig,
generatePossibleTypesMap: config.shouldGeneratePossibleTypes,
allocator: schemaAllocator,
triStateValueConfig: triStateValueConfig,
generateVarsCreateFactories: config.shouldGenerateVarsCreateFactories,
),
};

for (var entry in libs.entries) {
Expand Down
122 changes: 112 additions & 10 deletions packages/ferry_generator/lib/serializer_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import 'dart:collection';
import 'package:analyzer/dart/element/element.dart';
import 'package:build/build.dart';
import 'package:code_builder/code_builder.dart';
import 'package:gql_code_builder/serializer.dart';
import 'package:path/path.dart' as p;
import 'package:glob/glob.dart';

Expand Down Expand Up @@ -141,26 +140,129 @@ class SerializerBuilder implements Builder {
),
};

// if the schema is defined in a different package
// we need to import the serializers from that package
// and add them to the serializers of this package
final isExternalSchema = schemaId != buildStep.inputId;
final externalSerializersExpression = isExternalSchema
? refer('serializers',
_externalSchemaSerializersImport(schemaId, config))
.property('serializers')
: null;

final library = buildSerializerLibrary(
builtClasses,
outputFileName.replaceFirst('.gql.dart', '.gql.g.dart'),
additionalSerializers,
externalSerializersExpression,
);

final allocator = PickAllocator(
doNotPick: ['package:built_value/serializer.dart'],
include: [
'package:built_collection/built_collection.dart',
'package:ferry_exec/ferry_exec.dart',
...config.typeOverrides.values.map((ref) => ref.url).whereType<String>()
],
);
final allocator = PickAllocator(doNotPick: [
'package:built_value/serializer.dart',
], include: [
'package:built_collection/built_collection.dart',
'package:ferry_exec/ferry_exec.dart',
...config.typeOverrides.values.map((ref) => ref.url).whereType<String>(),
if (isExternalSchema) _externalSchemaImport(schemaId, config),
], aliasedImports: {
if (isExternalSchema)
_externalSchemaSerializersImport(schemaId, config):
'_\$external_serializers',
});

final outputId = AssetId(
schemaId.package,
buildStep.inputId.package,
p.joinAll(pathSegments(schemaId)),
);

await writeDocument(outputId, library, allocator, buildStep);
}
}

String _externalSchemaImport(AssetId schemaId, BuilderConfig config) {
final outPutId = outputAssetId(schemaId, schemaExtension, config.outputDir);

return 'package:${outPutId.package}/${outPutId.path.replaceFirst(RegExp('^lib/'), '')}';
}

String _externalSchemaSerializersImport(
AssetId schemaId, BuilderConfig config) {
final outPutId = outputAssetId(schemaId, schemaExtension, config.outputDir);

final serializersPathSegments = outPutId.pathSegments
..removeAt(0)
..removeLast()
..add('serializers.gql.dart');

final outPutPath = p.joinAll(serializersPathSegments);

return 'package:${outPutId.package}/$outPutPath';
}

Expression withCustomSerializers(
Expression serializersExpression,
Set<Expression> customSerializers,
) =>
customSerializers.fold(
serializersExpression,
(exp, serializer) => exp.cascade("add").call([serializer]),
);

Library buildSerializerLibrary(
Set<ClassElement> builtClasses,
String partDirectiveUrl,
Set<Expression> additionalSerializers,
Expression? externalSerializers,
) =>
Library(
(b) => b
..directives.add(Directive.part(partDirectiveUrl))
..body.addAll([
withCustomSerializers(
declareFinal(
"_serializersBuilder",
type: refer(
"SerializersBuilder", "package:built_value/serializer.dart"),
)
.assign(
refer(r"_$serializers"),
)
.property("toBuilder")
.call([]),
additionalSerializers,
)
._conditionalWrap(
externalSerializers != null,
(expr) => expr.cascade("addAll").call([externalSerializers!]),
)
.cascade("addPlugin")
.call([
refer(
"StandardJsonPlugin",
"package:built_value/standard_json_plugin.dart",
).call([])
]).statement,
refer("@SerializersFor", "package:built_value/serializer.dart").call([
literalList(
builtClasses
.map<Reference>(
(c) => refer(c.name, c.source.uri.toString()),
)
.toList()
..sort((a, b) => a.symbol!.compareTo(b.symbol!)),
)
]),
declareFinal("serializers",
type: refer(
"Serializers", "package:built_value/serializer.dart"))
.assign(refer("_serializersBuilder"))
.property("build")
.call([]).statement,
]),
);

extension on Expression {
Expression _conditionalWrap(
bool condition, Expression Function(Expression) wrap) =>
condition ? wrap(this) : this;
}
13 changes: 11 additions & 2 deletions packages/ferry_generator/lib/src/allocators/pick_allocator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@ import 'package:code_builder/code_builder.dart';
class PickAllocator implements Allocator {
final List<String> doNotPick;
final List<String> include;
final Map<String, String> aliasedImports;

final Map<String, List<String>?> _imports = {};

PickAllocator({
this.doNotPick = const [],
this.include = const [],
this.aliasedImports = const {},
}) {
for (final url in include) {
_imports[url] = null;
Expand All @@ -30,6 +32,9 @@ class PickAllocator implements Allocator {
} else if (doNotPick.contains(url) || include.contains(url)) {
_imports.putIfAbsent(url, () => null);
return symbol;
} else if (aliasedImports.containsKey(url)) {
final alias = aliasedImports[url]!;
return '$alias.$symbol';
}

_imports.update(url, (symbols) => symbols?..add(symbol),
Expand All @@ -39,9 +44,13 @@ class PickAllocator implements Allocator {
}

@override
Iterable<Directive> get imports => _imports.entries.map(
Iterable<Directive> get imports => _imports.entries
.map(
(u) => u.value == null
? Directive.import(u.key)
: Directive.import(u.key, show: u.value!),
);
)
.followedBy(aliasedImports.entries.map(
(e) => Directive.import(e.key, as: e.value),
));
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit cde5019

Please sign in to comment.