diff --git a/CHANGELOG.md b/CHANGELOG.md index 511eb2b..806b3f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## 2.0.6 +- Fix: class parse on wasm. + ## 2.0.5 - Fix: replaceInstance now dont throw errors. diff --git a/lib/src/bind.dart b/lib/src/bind.dart index ac6a535..4baa127 100644 --- a/lib/src/bind.dart +++ b/lib/src/bind.dart @@ -1,6 +1,7 @@ // ignore_for_file: public_member_api_docs import 'package:auto_injector/auto_injector.dart'; +import 'package:meta/meta.dart'; enum BindType { instance, @@ -58,6 +59,32 @@ class Bind { ); } + /// This is used for testing different environments like wasm, js, macos, etc. + /// + /// It's not recommended to use this in production code. + @visibleForTesting + factory Bind.fromConstructorString({ + required Function constructor, + required String constructorString, + required BindType type, + BindConfig? config, + T? instance, + String? key, + }) { + final className = _resolveClassName(constructorString); + final params = _extractParams(constructorString); + + return Bind._( + constructor: constructor, + className: (key != null) ? null : className, + params: params, + type: type, + config: config, + instance: instance, + key: key, + ); + } + factory Bind.empty(String className) { return Bind._( constructor: () => null, @@ -194,7 +221,7 @@ class Bind { ); }).toList(); - params.addAll(allParam); + params.insertAll(0, allParam); } return params; @@ -205,7 +232,17 @@ class Bind { final isDynamicOrObjectType = ['dynamic', 'Object'].contains(typeName); if (!isDynamicOrObjectType) return typeName; - final className = constructorString.split(' => ').last; - return className; + final classNameRegex = RegExp(' => (.*) from: | => (.*) from | => (.*)'); + final classNameMatch = classNameRegex.firstMatch(constructorString); + final className = classNameMatch?.group(1) ?? + classNameMatch?.group(2) ?? + classNameMatch?.group(3); + if (className == null) { + throw Exception( + 'Unable to resolve class name from constructor: $constructorString', + ); + } + // remove generics + return className.replaceAll(RegExp('<.*>'), ''); } } diff --git a/pubspec.yaml b/pubspec.yaml index 2d67ae9..7800a8b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: auto_injector description: Automatic Dependency Injection System, but without build_runner :) -version: 2.0.5 +version: 2.0.6 repository: https://github.com/Flutterando/auto_injector environment: diff --git a/test/src/platform_specifics_test.dart b/test/src/platform_specifics_test.dart new file mode 100644 index 0000000..8977fc5 --- /dev/null +++ b/test/src/platform_specifics_test.dart @@ -0,0 +1,148 @@ +import 'package:auto_injector/auto_injector.dart'; +import 'package:auto_injector/src/bind.dart'; +import 'package:test/test.dart'; + +void main() { + test('Class1', () { + ValidationUtils.validateClass1(Class1.macos); + ValidationUtils.validateClass1(Class1.js); + ValidationUtils.validateClass1(Class1.wasm); + }); + test('Class2', () { + ValidationUtils.validateClass2(Class2.macos); + ValidationUtils.validateClass2(Class2.js); + ValidationUtils.validateClass2(Class2.wasm); + }); + test('Class3', () { + ValidationUtils.validateClass3(Class3.macos); + ValidationUtils.validateClass3(Class3.js); + ValidationUtils.validateClass3(Class3.wasm); + }); +} + +class ValidationUtils { + static void validateClass1(String constructorString) { + final classData = Bind.fromConstructorString( + constructorString: constructorString, + constructor: Class1.new, + type: BindType.singleton, + ); + expect(classData.className, 'Class1'); + expect(classData.params.length, 3); + expect(classData.params[0].className, 'int'); + expect(classData.params[0].runtimeType, PositionalParam); + expect(classData.params[0].isNullable, false); + expect(classData.params[0].isRequired, true); + + expect(classData.params[1], isA()); + final param1 = classData.params[1] as NamedParam; + expect(param1.named, const Symbol('var2')); + expect(param1.className, 'int'); + expect(param1.isNullable, false); + expect(param1.isRequired, false); + + expect(classData.params[2].runtimeType, NamedParam); + final param2 = classData.params[2] as NamedParam; + expect(param2.named, const Symbol('var3')); + expect(param2.className, 'bool'); + expect(param2.isNullable, true); + expect(param2.isRequired, false); + } + + static void validateClass2(String constructorString) { + final classData = Bind.fromConstructorString( + constructorString: constructorString, + constructor: Class1.new, + type: BindType.singleton, + ); + expect(classData.className, 'Class2'); + expect(classData.params.length, 3); + expect(classData.params[0].className, 'int'); + expect(classData.params[0].runtimeType, PositionalParam); + expect(classData.params[0].isNullable, false); + expect(classData.params[0].isRequired, true); + + expect(classData.params[1], isA()); + final param1 = classData.params[1] as NamedParam; + expect(param1.named, const Symbol('var2')); + expect(param1.className, 'int'); + expect(param1.isNullable, false); + expect(param1.isRequired, false); + + expect(classData.params[2].runtimeType, NamedParam); + final param2 = classData.params[2] as NamedParam; + expect(param2.named, const Symbol('var3')); + expect(param2.className, 'bool'); + expect(param2.isNullable, true); + expect(param2.isRequired, false); + } + + static void validateClass3(String constructorString) { + final classData = Bind.fromConstructorString( + constructorString: constructorString, + constructor: Class1.new, + type: BindType.singleton, + ); + expect(classData.className, 'Class3'); + expect(classData.params.length, 3); + expect(classData.params[0].runtimeType, PositionalParam); + final param0 = classData.params[0] as PositionalParam; + expect(param0.className, 'int'); + expect(param0.isNullable, false); + expect(param0.isRequired, true); + + expect(classData.params[1], isA()); + final param1 = classData.params[1] as NamedParam; + expect(param1.named, const Symbol('var2')); + expect(param1.className, 'int'); + expect(param1.isNullable, false); + expect(param1.isRequired, false); + + expect(classData.params[2].runtimeType, NamedParam); + final param2 = classData.params[2] as NamedParam; + expect(param2.named, const Symbol('var3')); + expect(param2.className, 'bool'); + expect(param2.isNullable, true); + expect(param2.isRequired, false); + } +} + +class Class1 { + final int var1; + final int var2; + final bool? var3; + + const Class1(this.var1, {this.var2 = 0, this.var3}); + static String js = + "Closure: (int, {int var2, bool? var3}) => Class1 from: ['_#new#tearOff'](var1, opts) {"; + static String wasm = 'Closure: (int, {int var2, bool? var3}) => Class1'; + static String macos = + "Closure: (int, {int var2, bool? var3}) => Class1 from Function 'Class1.': static."; +} + +class Class2 { + final int var1; + final int var2; + final bool? var3; + + Class2(this.var1, {this.var2 = 0, this.var3}); + static String js = + "Closure: (int, {int var2, bool? var3}) => Class2 from: ['_#new#tearOff'](var1, opts) {"; + static String wasm = 'Closure: (int, {int var2, bool? var3}) => Class2'; + static String macos = + "Closure: (int, {int var2, bool? var3}) => Class2 from Function 'Class2.': static."; +} + +class Class3> { + final int var1; + final int var2; + final bool? var3; + + Class3(this.var1, {this.var2 = 0, this.var3}); + static String js = + "Closure: (int, {int var2, bool? var3}) => Class3 from: ['_#new#tearOff'](T, var1, opts) {"; + static String wasm = + 'Closure: (int, {int var2, bool? var3}) => Class3'; + static String macos = + "Closure: (int, {int var2, bool? var3}) => Class3 from Function 'Class3.': static."; +}