gql-dart / ferry

Stream-based strongly typed GraphQL client for Dart
https://ferrygraphql.com/
MIT License
602 stars 116 forks source link

Feature Request: Ability to generate to non-`lib/` directory #529

Open btrautmann opened 1 year ago

btrautmann commented 1 year ago

I'm hoping to determine whether there's any interest in allowing generated code to be placed outside of the lib directory...More specifically, the test directory.

I'm currently working on a library for internal use that generates factories that produce fakes of objects in .data.gql.dart files. As part of its test suite, I want to be able to actually run Ferry in the test directory. This requires the following file structure:

package/
├─ build.yaml
├─ lib/
├─ test/
│  ├─ __generated__/
│  ├─ custom_serializers/
│  ├─ custom_types/
│  ├─ schema.graphql
│  ├─ one.graphql
│  ├─ two.graphql

However, when running build_runner, (at least) two errors are produced:

UnexpectedOutputException

[SEVERE] ferry_generator:serializer_builder on lib/$lib$ (cached):

UnexpectedOutputException: graphql_faker|test/__generated__/serializers.gql.dart
Expected only: {graphql_faker|lib/__generated__/serializers.gql.dart}
package:build/src/builder/build_step_impl.dart 190:7    BuildStepImpl._checkOutput
package:build/src/builder/build_step_impl.dart 151:5    BuildStepImpl.writeAsString
package:ferry_generator/src/utils/writer.dart 29:20     writeDocument
package:ferry_generator/serializer_builder.dart 118:11  SerializerBuilder.build
package:build/src/generate/run_builder.dart 83:7        runBuilder.buildForInput
dart:async/future.dart 525:21                           Future.wait.<fn>

Type resolution

1. Make field total have non-dynamic type. If you are already specifying a type, please make sure the type is correctly imported.
package:built_value_generator/src/value_source_class.dart 740:28  ValueSourceClass.generateCode
package:built_value_generator/built_value_generator.dart 67:52    BuiltValueGenerator.generate
package:source_gen/src/builder.dart 355:23                        _generate

I understand that resolving types gets tricky when they can be either in lib/ or test/ but I've worked around similar challenges in the library I mentioned above. I wonder if it's worth the effort or if this use case is just too niche?

Workaround

For now I've been able to work around this by placing everything in lib/src/test (essentially a fake test directory). Because my library is a dev_dependency I think this is fine, but it's less than ideal for maintenance/contributions.

knaeckeKami commented 1 year ago

Hi!

I think this is a valid use case!

However, I can think of a few challenges:

since tests are not full packages, we cannot import them from the package root. I think we would need to import them relatively to the current file. (ferry generates several files, some of which depend on others)

since the serializer in lib/ cannot access files in test/ we would need to generate another serializer for the requests in test/.

You can make your workaround a little cleaner by adding another package just for the tests.

project_root
├─ graphql-faker
│    ├─ lib
├─ graphql-faker-tests
│    ├─ lib
│    │    ├─ test.graphql
│    ├─ test
│                 

Given the complexity and the available workarounds this is not a high priority for me, however I would welcome pull requests should you be willing to tackle this.

btrautmann commented 1 year ago

You can make your workaround a little cleaner by adding another package just for the tests.

I liked this idea, but after noodling on it for a minute I realized it's not quite feasible (in my case at least). If we assume I move just the ferry-generated objects to a graphql_faker_test package, I'd need to make that package a dev dependency of the primary package. I'd need to do this because the actual tests themselves must remain in the primary package because they rely on the builder class and other private classes that shouldn't be exported. This is a requirement of using source_gen_test which greatly simplifies the testing of source_gen Builders.

I think the best way forward is making ferry a bit more flexible, and I'll take a look to see how feasible this is. Thanks for the feedback!

knaeckeKami commented 1 year ago

I think the best way forward is making ferry a bit more flexible, and I'll take a look to see how feasible this is. Thanks for the feedback!

I think it's totally doable! If you need some pointers on where to start feel free to reach out.

FYI, most of the code generation logic is actually in gql_code_builder which is also used by other graphql generators.