From 9eafa9337d81d527cea4e1ee6d439c92d0d4aae2 Mon Sep 17 00:00:00 2001 From: Tobias Ottenweller Date: Tue, 3 Jun 2025 21:44:48 +0200 Subject: [PATCH 1/2] chore: fix or ignore linter issues in generated code --- .../lib/src/analysis_options_generator.dart | 35 +++++++++++++++++ .../tonik_generate/lib/src/generator.dart | 6 +++ .../lib/src/model/class_generator.dart | 25 ++++++++++++ .../lib/src/pubspec_generator.dart | 1 + .../lib/src/util/format_with_header.dart | 11 +----- .../src/model/class_json_generator_test.dart | 38 +++++++++++++++++++ 6 files changed, 106 insertions(+), 10 deletions(-) create mode 100644 packages/tonik_generate/lib/src/analysis_options_generator.dart diff --git a/packages/tonik_generate/lib/src/analysis_options_generator.dart b/packages/tonik_generate/lib/src/analysis_options_generator.dart new file mode 100644 index 0000000..1d65ffc --- /dev/null +++ b/packages/tonik_generate/lib/src/analysis_options_generator.dart @@ -0,0 +1,35 @@ +import 'dart:io'; +import 'package:path/path.dart' as path; + +void generateAnalysisOptions({ + required String outputDirectory, + required String package, +}) { + final packageDir = path.join(outputDirectory, package); + final analysisOptionsFile = File( + path.join(packageDir, 'analysis_options.yaml'), + ); + + if (!analysisOptionsFile.parent.existsSync()) { + analysisOptionsFile.parent.createSync(recursive: true); + } + + const content = ''' +include: package:lints/recommended.yaml + +analyzer: + errors: + lines_longer_than_80_chars: ignore + unnecessary_raw_strings: ignore + unnecessary_brace_in_string_interps: ignore + no_leading_underscores_for_local_identifiers: ignore + cascade_invocations: ignore + deprecated_member_use_from_same_package: ignore + no_leading_underscores_for_library_prefixes: ignore + unused_import: ignore + prefer_is_empty: ignore + unnecessary_nullable_for_final_variable_declarations: ignore +'''; + + analysisOptionsFile.writeAsStringSync(content); +} diff --git a/packages/tonik_generate/lib/src/generator.dart b/packages/tonik_generate/lib/src/generator.dart index 09c8eaf..a35fb18 100644 --- a/packages/tonik_generate/lib/src/generator.dart +++ b/packages/tonik_generate/lib/src/generator.dart @@ -1,4 +1,5 @@ import 'package:tonik_core/tonik_core.dart'; +import 'package:tonik_generate/src/analysis_options_generator.dart'; import 'package:tonik_generate/src/api_client/api_client_file_generator.dart'; import 'package:tonik_generate/src/api_client/api_client_generator.dart'; import 'package:tonik_generate/src/library_generator.dart'; @@ -132,6 +133,11 @@ class Generator { package: package, ); + generateAnalysisOptions( + outputDirectory: outputDirectory, + package: package, + ); + modelGenerator.writeFiles( apiDocument: apiDocument, outputDirectory: outputDirectory, diff --git a/packages/tonik_generate/lib/src/model/class_generator.dart b/packages/tonik_generate/lib/src/model/class_generator.dart index dd604c4..8f5f4cf 100644 --- a/packages/tonik_generate/lib/src/model/class_generator.dart +++ b/packages/tonik_generate/lib/src/model/class_generator.dart @@ -183,6 +183,26 @@ class ClassGenerator { Constructor _buildFromSimpleConstructor(String className, ClassModel model) { final normalizedProperties = normalizeProperties(model.properties.toList()); + + // If there are no properties, just return the constructor call + if (normalizedProperties.isEmpty) { + return Constructor( + (b) => + b + ..factory = true + ..name = 'fromSimple' + ..requiredParameters.add( + Parameter( + (b) => + b + ..name = 'value' + ..type = refer('String?', 'dart:core'), + ), + ) + ..body = Code('return $className();'), + ); + } + final propertyAssignments = >[]; for (var i = 0; i < normalizedProperties.length; i++) { final prop = normalizedProperties[i]; @@ -258,6 +278,11 @@ class ClassGenerator { Code _buildFromJsonBody(String className, ClassModel model) { final normalizedProperties = normalizeProperties(model.properties.toList()); + // If there are no properties, just return the constructor call + if (normalizedProperties.isEmpty) { + return Block.of([Code('return $className();')]); + } + final codes = [ Code("final map = json.decodeMap(context: '$className');"), ]; diff --git a/packages/tonik_generate/lib/src/pubspec_generator.dart b/packages/tonik_generate/lib/src/pubspec_generator.dart index ef578fb..257a253 100644 --- a/packages/tonik_generate/lib/src/pubspec_generator.dart +++ b/packages/tonik_generate/lib/src/pubspec_generator.dart @@ -25,6 +25,7 @@ dependencies: big_decimal: ^0.5.0 collection: ^1.17.0 dio: ^5.8.0+1 + lints: ^6.0.0 meta: ^1.16.0 tonik_util: ^0.0.5 '''; diff --git a/packages/tonik_generate/lib/src/util/format_with_header.dart b/packages/tonik_generate/lib/src/util/format_with_header.dart index 6a51ab5..d6f305d 100644 --- a/packages/tonik_generate/lib/src/util/format_with_header.dart +++ b/packages/tonik_generate/lib/src/util/format_with_header.dart @@ -1,19 +1,10 @@ import 'package:dart_style/dart_style.dart'; extension FormatWithHeader on DartFormatter { - static const _ignores = [ - 'lines_longer_than_80_chars', - 'unnecessary_raw_strings', - 'unnecessary_brace_in_string_interps', - 'no_leading_underscores_for_local_identifiers', - 'cascade_invocations', - 'prefer_is_empty', - ]; - String formatWithHeader(String code) { return format(''' // Generated code - do not modify by hand -${_ignores.map((i) => '// ignore_for_file: $i').join('\n')} + $code'''); } } diff --git a/packages/tonik_generate/test/src/model/class_json_generator_test.dart b/packages/tonik_generate/test/src/model/class_json_generator_test.dart index 2b391bb..7fb5505 100644 --- a/packages/tonik_generate/test/src/model/class_json_generator_test.dart +++ b/packages/tonik_generate/test/src/model/class_json_generator_test.dart @@ -597,5 +597,43 @@ void main() { contains(collapseWhitespace(expectedMethod)), ); }); + + test('generates fromJson method for class without properties', () { + final model = ClassModel( + context: context, + name: 'EmptyClass', + properties: const [], + ); + + const expectedMethod = ''' + factory EmptyClass.fromJson(Object? json) { + return EmptyClass(); + }'''; + + final generatedClass = generator.generateClass(model); + expect( + collapseWhitespace(format(generatedClass.accept(emitter).toString())), + contains(collapseWhitespace(expectedMethod)), + ); + }); + + test('generates fromSimple method for class without properties', () { + final model = ClassModel( + context: context, + name: 'EmptyClass', + properties: const [], + ); + + const expectedMethod = ''' + factory EmptyClass.fromSimple(String? value) { + return EmptyClass(); + }'''; + + final generatedClass = generator.generateClass(model); + expect( + collapseWhitespace(format(generatedClass.accept(emitter).toString())), + contains(collapseWhitespace(expectedMethod)), + ); + }); }); } From 11e0fbd74bb86ba1a01ce4fc76372adc7f6f2d01 Mon Sep 17 00:00:00 2001 From: Tobias Ottenweller Date: Tue, 3 Jun 2025 21:50:34 +0200 Subject: [PATCH 2/2] chore: add additional badge --- packages/tonik/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/tonik/README.md b/packages/tonik/README.md index 4328338..7a7673d 100644 --- a/packages/tonik/README.md +++ b/packages/tonik/README.md @@ -8,7 +8,8 @@ pub verion pub likes stars on github -tests +tests +