Dart_apitool got an update!
The recent weeks I was working a bit on dart_apitool.
The major topics were:
Missing export detection
The way Dart allows to define the public API of a package can lead to situations in which the consumer of the API is able to receive an instance of a certain Type but can’t refer to that type directly (e.g. to define a variable or a parameter of that Type).
Let’s imagine a very simple dart package:
- lib
- some_package.dart
- src
- some_interface.dart
- some_interface_impl.dart
- used_type.dart
some_package.dart
export 'src/some_interface.dart'
export 'src/some_interface_impl.dart'
some_interface.dart
#import 'used_type.dart'
abstract class SomeInterface {
UsedType doSomethingAndReturnInstance();
}
some_interface_impl
#import 'some_interface.dart'
#import 'used_type.dart'
class SomeInterfaceImpl extends SomeInterface {
@override
UsedType doSomethingAndReturnInstance() {
return UsedType();
}
}
used_type.dart
class UsedType {
//...
void doSomething() {
//...
}
}
The consumer now can use that package by importing the main dart file, create and use SomeInterfaceImpl
, even retrieve an instance of UsedType
, interact with it but not name that type anywhere.
#import 'package:some_package/some_package.dart';
void consumer() {
//works
final si = SomeInterfaceImpl();
// calling and getting instance works
final ut = si.doSomethingAndReturnInstance();
// interacting with UsedType works
ut.doSomething();
// naming the type doesn't work
final UsedType ut2 = si.doSomethingAndReturnInstance();
}
This is because Dart hides access to UsedType
as it is not part of the public API. It is still there and can still be interacted with, though.
This is most probably not what the author of that public API intended.
Dart_apitool did know almost everything that a tool needs to know In order to detect that issue:
- it knows the public API (dart_apitool would include UsedType implicitly)
- It knows which entry points each type has (although it propagated the entry points to all implicitly included types, which got changed now)
- It is able to determine “required” types and therefore can use the same information (type flow direction) to derive potential issue candidates
So I decided to tweak dart_apitool so that it provides all the needed information to detect those cases and extended the extract command to check for that situation and print it to stdout on detection.
Additionally, there is a new --set-exit-on-missing-export
argument that will fail the command on missing exports.
Option to be less strict on required interfaces
As you know dart_apitool tries to understand if an interface is passed into the library as this makes it more likely that the interface is implemented on the consumer side.
If so dart_apitool is more strict in terms of additions to the interface as this would break consumer code that implement the interface.
At BMW, we have situations where we have an internal package that has a very small set of consumer code that is all controlled by us. To not require too frequent major version bumps by adding stuff to a public interface dart_apitool now has the option to turn off the required interfaces special treatment.
If you pass --ignore-requiredness
to the diff command then every interface will be treated the same meaning adding stuff to an interface won’t result in a breaking change.
We can do that as we control both sides and are able to do the integration (and making sure that the right minor version number is picked).
You should not use this for public packages as you can not control how your package is used and the last thing you want to do is to break the consumers code by a minor update.
Minor improvements
Besides the above, dart_apitool got some minor improvements:
@sealed
classes (pre Dart 3) are now detected and not treated as required- entry point tracking got improved (in the context of detection of missing exports) so that implicit types in the public API don’t derive the entry point from the referring file.
- Elements marked as
@override
are now ignored and not part of the extracted public API anymore.
That’s all for now. The next big step will be Dart 3 support