diff --git a/CHANGELOG.md b/CHANGELOG.md index 00a05562..3456d0af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ ## 5.6.0 - feat: add CSV support -- feat: improve generic type detection for lists (e.g. `List>`,`List>`) +- feat: improve generic type detection for lists (e.g. `List>`,`List>`) - feat: make optional class members (from interfaces) non-nullable when possible ## 5.5.0 diff --git a/example/pubspec.lock b/example/pubspec.lock index 722798c1..7b9c398e 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -91,7 +91,7 @@ packages: name: characters url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.2.0" charcode: dependency: transitive description: @@ -407,7 +407,7 @@ packages: name: vector_math url: "https://pub.dartlang.org" source: hosted - version: "2.1.0" + version: "2.1.1" watcher: dependency: transitive description: diff --git a/lib/src/builder/translation_model_builder.dart b/lib/src/builder/translation_model_builder.dart index bccf4974..df4500a1 100644 --- a/lib/src/builder/translation_model_builder.dart +++ b/lib/src/builder/translation_model_builder.dart @@ -572,21 +572,13 @@ class TranslationModelBuilder { }).toSet(); } else if (child is ListNode) { parameters = {}; // lists never have parameters - if (child.genericType != null) { - returnType = 'List<${child.genericType}>'; - } else { - returnType = 'List'; - } + returnType = 'List<${child.genericType}>'; } else if (child is ObjectNode) { switch (child.type) { case ObjectNodeType.classType: case ObjectNodeType.map: parameters = {}; // objects never have parameters - if (child.genericType != null) { - returnType = 'Map'; - } else { - returnType = 'Map'; - } + returnType = 'Map'; break; case ObjectNodeType.pluralCardinal: case ObjectNodeType.pluralOrdinal: diff --git a/lib/src/generator/generate_translations.dart b/lib/src/generator/generate_translations.dart index 3af88cd5..a8330057 100644 --- a/lib/src/generator/generate_translations.dart +++ b/lib/src/generator/generate_translations.dart @@ -127,8 +127,7 @@ void _generateClass( 'String$optional $key${_toParameterList(value.params, value.paramTypeMap)} => \'${value.content}\';'); } } else if (value is ListNode) { - String type = value.genericType != null ? value.genericType! : 'dynamic'; - buffer.write('List<$type>$optional get $key => '); + buffer.write('List<${value.genericType}>$optional get $key => '); _generateList(config, base, locale, hasPluralResolver, buffer, queue, className, value.entries, 0); } else if (value is ObjectNode) { @@ -146,9 +145,8 @@ void _generateClass( break; case ObjectNodeType.map: // inline map - String type = - value.genericType != null ? value.genericType! : 'dynamic'; - buffer.write('Map$optional get $key => '); + buffer + .write('Map$optional get $key => '); _generateMap(config, base, locale, hasPluralResolver, buffer, queue, childClassNoLocale, value.entries, 0); break; diff --git a/lib/src/model/node.dart b/lib/src/model/node.dart index a8549a4b..7bb8259a 100644 --- a/lib/src/model/node.dart +++ b/lib/src/model/node.dart @@ -24,8 +24,8 @@ abstract class Node { abstract class IterableNode extends Node { /// If not null, then all its children have a specific interface. /// This overwrites the [plainStrings] attribute. - String? _genericType; - String? get genericType => _genericType; + String _genericType; + String get genericType => _genericType; IterableNode(String path, this._genericType) : super(path); @@ -61,7 +61,7 @@ class ObjectNode extends IterableNode { entries.values .every((child) => child is TextNode && child.params.isEmpty) ? 'String' - : null); + : 'dynamic'); void setInterface(Interface interface) { _interface = interface; @@ -77,7 +77,7 @@ class ListNode extends IterableNode { ListNode({required String path, required this.entries}) : super(path, _determineGenericType(entries)); - static String? _determineGenericType(List entries) { + static String _determineGenericType(List entries) { if (entries.every((child) => child is TextNode && child.params.isEmpty)) { return 'String'; } @@ -98,9 +98,9 @@ class ListNode extends IterableNode { childGenericType = 'dynamic'; // default } } - return 'Map'; + return 'Map'; } - return null; + return 'dynamic'; } @override diff --git a/pubspec.lock b/pubspec.lock index b27436e5..a72e30df 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -28,7 +28,7 @@ packages: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.8.1" + version: "2.8.2" boolean_selector: dependency: transitive description: @@ -98,7 +98,7 @@ packages: name: characters url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.2.0" charcode: dependency: transitive description: @@ -276,7 +276,7 @@ packages: name: matcher url: "https://pub.dartlang.org" source: hosted - version: "0.12.10" + version: "0.12.11" meta: dependency: transitive description: @@ -435,21 +435,21 @@ packages: name: test url: "https://pub.dartlang.org" source: hosted - version: "1.17.10" + version: "1.17.12" test_api: dependency: transitive description: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.4.2" + version: "0.4.3" test_core: dependency: transitive description: name: test_core url: "https://pub.dartlang.org" source: hosted - version: "0.4.0" + version: "0.4.2" timing: dependency: transitive description: @@ -470,7 +470,7 @@ packages: name: vector_math url: "https://pub.dartlang.org" source: hosted - version: "2.1.0" + version: "2.1.1" vm_service: dependency: transitive description: diff --git a/test/integration/resources/expected.output b/test/integration/resources/expected.output index ca92bd91..32bca386 100644 --- a/test/integration/resources/expected.output +++ b/test/integration/resources/expected.output @@ -153,7 +153,7 @@ mixin PageData { mixin EndData { List get stringPages; - List> get pages; + List> get pages; } // extensions for AppLocale @@ -363,7 +363,7 @@ class _TranslationsEndEn with EndData { '1st Page', '2nd Page', ]; - @override List> get pages => [ + @override List> get pages => [ { 'unknown': 'Unknown Error', }, @@ -450,7 +450,7 @@ class _TranslationsEndDe with EndData implements _TranslationsEndEn { '1. Seite', '2. Seite', ]; - @override List> get pages => [ + @override List> get pages => [ { 'unknown': 'Unbekannter Fehler', }, diff --git a/test/unit/model/node_test.dart b/test/unit/model/node_test.dart index ac8a38a6..c45ece2b 100644 --- a/test/unit/model/node_test.dart +++ b/test/unit/model/node_test.dart @@ -5,6 +5,202 @@ import 'package:test/test.dart'; void main() { const localeEnum = 'AppLocale.en'; + group(ListNode, () { + final interpolation = StringInterpolation.braces; + + test('Plain strings', () { + final node = ListNode(path: '', entries: [ + TextNode( + path: '', + raw: 'Hello', + interpolation: interpolation, + localeEnum: localeEnum, + ), + TextNode( + path: '', + raw: 'Hi', + interpolation: interpolation, + localeEnum: localeEnum, + ), + ]); + + expect(node.genericType, 'String'); + }); + + test('Parameterized strings', () { + final node = ListNode(path: '', entries: [ + TextNode( + path: '', + raw: 'Hello', + interpolation: interpolation, + localeEnum: localeEnum, + ), + TextNode( + path: '', + raw: 'Hi {name}', + interpolation: interpolation, + localeEnum: localeEnum, + ), + ]); + + expect(node.genericType, 'dynamic'); + }); + + test('Nested list', () { + final node = ListNode(path: '', entries: [ + ListNode(path: '', entries: [ + TextNode( + path: '', + raw: 'Hello', + interpolation: interpolation, + localeEnum: localeEnum, + ), + TextNode( + path: '', + raw: 'Hi {name}', + interpolation: interpolation, + localeEnum: localeEnum, + ), + ]), + ListNode(path: '', entries: [ + TextNode( + path: '', + raw: 'Hello', + interpolation: interpolation, + localeEnum: localeEnum, + ), + TextNode( + path: '', + raw: 'Hi {name}', + interpolation: interpolation, + localeEnum: localeEnum, + ), + ]), + ]); + + expect(node.genericType, 'List'); + }); + + test('Deeper Nested list', () { + final node = ListNode(path: '', entries: [ + ListNode(path: '', entries: [ + ListNode(path: '', entries: [ + TextNode( + path: '', + raw: 'Hello', + interpolation: interpolation, + localeEnum: localeEnum, + ), + TextNode( + path: '', + raw: 'Hi', + interpolation: interpolation, + localeEnum: localeEnum, + ), + ]), + ]), + ListNode(path: '', entries: [ + ListNode(path: '', entries: [ + TextNode( + path: '', + raw: 'Hello', + interpolation: interpolation, + localeEnum: localeEnum, + ), + TextNode( + path: '', + raw: 'Hi', + interpolation: interpolation, + localeEnum: localeEnum, + ), + ]), + ListNode(path: '', entries: [ + TextNode( + path: '', + raw: 'Hello', + interpolation: interpolation, + localeEnum: localeEnum, + ), + TextNode( + path: '', + raw: 'Hi', + interpolation: interpolation, + localeEnum: localeEnum, + ) + ]), + ]), + ]); + + expect(node.genericType, 'List>'); + }); + + test('Class', () { + final node = ListNode(path: '', entries: [ + ObjectNode( + path: '', + entries: { + 'key0': TextNode( + path: '', + raw: 'Hi', + interpolation: interpolation, + localeEnum: localeEnum, + ), + }, + type: ObjectNodeType.classType, + contextHint: null, + ), + ObjectNode( + path: '', + entries: { + 'key0': TextNode( + path: '', + raw: 'Hi', + interpolation: interpolation, + localeEnum: localeEnum, + ), + }, + type: ObjectNodeType.classType, + contextHint: null, + ), + ]); + + expect(node.genericType, 'dynamic'); + }); + + test('Map', () { + final node = ListNode(path: '', entries: [ + ObjectNode( + path: '', + entries: { + 'key0': TextNode( + path: '', + raw: 'Hi', + interpolation: interpolation, + localeEnum: localeEnum, + ), + }, + type: ObjectNodeType.map, + contextHint: null, + ), + ObjectNode( + path: '', + entries: { + 'key0': TextNode( + path: '', + raw: 'Hi', + interpolation: interpolation, + localeEnum: localeEnum, + ), + }, + type: ObjectNodeType.map, + contextHint: null, + ), + ]); + + expect(node.genericType, 'Map'); + }); + }); + group(TextNode, () { group(StringInterpolation.dart, () { test('no arguments', () {