Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(dynamite): introduce dynamite config to set ignore directives #1025

Merged
merged 1 commit into from
Oct 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions packages/dynamite/dynamite/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
Provides a [Dart Build System](https://github.com/dart-lang/build) builder for generating clients from an [OpenAPI specifications](https://swagger.io/specification/).

The builder generates code if it find files with an `.openapi.json` or `.openapi.yaml` extension in the lib directory.

# Build configuration

You can configure code generation by setting values in the `build.yaml`.

```yaml
targets:
$default:
builders:
dynamite:
options:
# Options configure how source code is generated.
#
# The following are sensible default values that ignores the schemas for the coverage.
analyzer_ignores:
- camel_case_types
- discarded_futures
- public_member_api_docs
- unreachable_switch_case
coverage_ignores:
- 'const .*\._\(\);'
- 'factory .*\.fromJson\(Map<String, dynamic> json\) => _jsonSerializers\.deserializeWith\(serializer, json\)!;'
- 'Map<String, dynamic> toJson\(\) => _jsonSerializers\.serializeWith\(serializer, this\)! as Map<String, dynamic>;'
- 'static BuiltSet<.*> get values => _\$.*Values;'

```
2 changes: 1 addition & 1 deletion packages/dynamite/dynamite/lib/builder.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import 'package:build/build.dart';
import 'package:dynamite/dynamite.dart';

Builder openAPIBuilder(final BuilderOptions options) => OpenAPIBuilder();
Builder openAPIBuilder(final BuilderOptions options) => OpenAPIBuilder(options);
58 changes: 32 additions & 26 deletions packages/dynamite/dynamite/lib/src/builder/imports.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,35 @@ import 'package:code_builder/code_builder.dart';
import 'package:dynamite/src/builder/state.dart';
import 'package:path/path.dart' as p;

List<Spec> generateImports(final AssetId outputId, final State state) => [
const Code('// ignore_for_file: camel_case_types'),
const Code('// ignore_for_file: discarded_futures'),
const Code('// ignore_for_file: public_member_api_docs'),
const Code('// ignore_for_file: unreachable_switch_case'),
Directive.import('dart:convert'),
Directive.import('dart:typed_data'),
const Code(''),
Directive.import('package:built_collection/built_collection.dart'),
Directive.import('package:built_value/built_value.dart'),
Directive.import('package:built_value/json_object.dart'),
Directive.import('package:built_value/serializer.dart'),
Directive.import('package:built_value/standard_json_plugin.dart'),
Directive.import('package:collection/collection.dart'),
Directive.import('package:dynamite_runtime/built_value.dart'),
Directive.import('package:dynamite_runtime/http_client.dart'),
Directive.import('package:dynamite_runtime/models.dart'),
Directive.import('package:dynamite_runtime/utils.dart'),
Directive.import('package:meta/meta.dart'),
Directive.import('package:universal_io/io.dart'),
const Code(''),
if (state.hasResolvedBuiltTypes) ...[
Directive.part(p.basename(outputId.changeExtension('.g.dart').path)),
const Code(''),
],
];
Iterable<Spec> generateImports(final AssetId outputId, final State state) sync* {
final analyzerIgnores = state.buildConfig.analyzerIgnores;
if (analyzerIgnores != null) {
for (final rule in analyzerIgnores) {
yield Code('// ignore_for_file: $rule');
}
}

yield* [
Directive.import('dart:convert'),
Directive.import('dart:typed_data'),
const Code(''),
Directive.import('package:built_collection/built_collection.dart'),
Directive.import('package:built_value/built_value.dart'),
Directive.import('package:built_value/json_object.dart'),
Directive.import('package:built_value/serializer.dart'),
Directive.import('package:built_value/standard_json_plugin.dart'),
Directive.import('package:collection/collection.dart'),
Directive.import('package:dynamite_runtime/built_value.dart'),
Directive.import('package:dynamite_runtime/http_client.dart'),
Directive.import('package:dynamite_runtime/models.dart'),
Directive.import('package:dynamite_runtime/utils.dart'),
Directive.import('package:meta/meta.dart'),
Directive.import('package:universal_io/io.dart'),
const Code(''),
];

if (state.hasResolvedBuiltTypes) {
yield Directive.part(p.basename(outputId.changeExtension('.g.dart').path));
yield const Code('');
}
}
5 changes: 4 additions & 1 deletion packages/dynamite/dynamite/lib/src/builder/state.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import 'package:code_builder/code_builder.dart';
import 'package:dynamite/src/models/dynamite_config/config.dart';
import 'package:dynamite/src/models/type_result.dart';

class State {
State();
State(this.buildConfig);

final DynamiteConfig buildConfig;

final output = <Spec>[];
final resolvedTypes = <TypeResult>{};
Expand Down
1 change: 1 addition & 0 deletions packages/dynamite/dynamite/lib/src/models/config.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export 'dynamite_config/config.dart';
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import 'package:built_collection/built_collection.dart';
import 'package:built_value/built_value.dart';
import 'package:built_value/serializer.dart';
import 'package:built_value/standard_json_plugin.dart';

part 'config.g.dart';

/// The configuration used by the dynamite builder.
abstract class DynamiteConfig implements Built<DynamiteConfig, DynamiteConfigBuilder> {
factory DynamiteConfig([final void Function(DynamiteConfigBuilder) updates]) = _$DynamiteConfig;

const DynamiteConfig._();

/// Constructs the dynamite config from a json like map.
factory DynamiteConfig.fromJson(final Map<String, dynamic> json) => _serializers.deserializeWith(serializer, json)!;

/// Serializes this configuration to json.
Map<String, dynamic> toJson() => _serializers.serializeWith(serializer, this)! as Map<String, dynamic>;

static Serializer<DynamiteConfig> get serializer => _$dynamiteConfigSerializer;

static const String configPath = 'dynamite.yaml';

/// A set of lint rules to ignore for the entire generated file.
@BuiltValueField(wireName: 'analyzer_ignores')
BuiltSet<String>? get analyzerIgnores;

/// A set of regular expressions used to exclude parts from code coverage.
///
/// All matches will be wrapped in `// coverage:ignore-start` and `// coverage:ignore-end` blocks.
@BuiltValueField(wireName: 'coverage_ignores')
BuiltSet<String>? get coverageIgnores;
}

@SerializersFor([
DynamiteConfig,
])
final Serializers _serializers = (_$_serializers.toBuilder()..addPlugin(StandardJsonPlugin())).build();

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

42 changes: 21 additions & 21 deletions packages/dynamite/dynamite/lib/src/openapi_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,15 @@ import 'package:dynamite/src/builder/generate_schemas.dart';
import 'package:dynamite/src/builder/imports.dart';
import 'package:dynamite/src/builder/serializer.dart';
import 'package:dynamite/src/builder/state.dart';
import 'package:dynamite/src/models/config.dart';
import 'package:dynamite/src/models/openapi.dart' as openapi;
import 'package:version/version.dart';

class OpenAPIBuilder implements Builder {
OpenAPIBuilder(
final BuilderOptions options,
) : buildConfig = DynamiteConfig.fromJson(options.config);

@override
final buildExtensions = const {
'.openapi.json': ['.openapi.dart'],
Expand All @@ -26,6 +31,9 @@ class OpenAPIBuilder implements Builder {
/// The maximum openapi version supported by this builder.
static final Version maxSupportedVersion = minSupportedVersion.incrementMajor();

/// The configuration for this builder.
final DynamiteConfig buildConfig;

@override
Future<void> build(final BuildStep buildStep) async {
try {
Expand Down Expand Up @@ -54,7 +62,7 @@ class OpenAPIBuilder implements Builder {
throw Exception('Only OpenAPI between $minSupportedVersion and $maxSupportedVersion are supported.');
}

final state = State();
final state = State(buildConfig);

// Imports need to be generated after everything else so we know if we need the local part directive,
// but they need to be added to the beginning of the output.
Expand All @@ -64,28 +72,20 @@ class OpenAPIBuilder implements Builder {
..addAll(buildSerializer(state))
..insertAll(0, generateImports(outputId, state));

final patterns = [
RegExp(
r'const .*\._\(\);',
),
RegExp(
r'factory .*\.fromJson\(Map<String, dynamic> json\) => _jsonSerializers\.deserializeWith\(serializer, json\)!;',
),
RegExp(
r'Map<String, dynamic> toJson\(\) => _jsonSerializers\.serializeWith\(serializer, this\)! as Map<String, dynamic>;',
),
RegExp(
r'static BuiltSet<.*> get values => _\$.*Values;',
),
];

var outputString = output.build().map((final e) => e.accept(emitter)).join('\n');
for (final pattern in patterns) {
outputString = outputString.replaceAllMapped(
pattern,
(final match) => ' // coverage:ignore-start\n${match.group(0)}\n // coverage:ignore-end',
);

final coverageIgnores = state.buildConfig.coverageIgnores;
if (coverageIgnores != null) {
for (final ignore in coverageIgnores) {
final pattern = RegExp(ignore);

outputString = outputString.replaceAllMapped(
pattern,
(final match) => ' // coverage:ignore-start\n${match.group(0)}\n // coverage:ignore-end',
);
}
}

final formatter = DartFormatter(pageWidth: 120);
await buildStep.writeAsString(
outputId,
Expand Down
Loading