What’s new in DCM 1.18.0
Today we’re excited to announce the release of DCM 1.18.0!
This release includes 8 new rules, improved "New Version" notifications, new grouping option for IntelliJ plugin, automatic DCM version detection by the "setup-dcm" action and general UX improvements 🚀.
❗️ All DCM versions prior to 1.11.0 have been discontinued.
❗️ "check-unnecessary-nullable" command has been removed.
Let's go through the highlights of this release! (And you can find the full list of changes in our changelog)
DCM Teams Console update
We have finished testing the DCM Teams Console and are starting to more actively enable it for all DCM users.
If you still have questions about the DCM Teams Console or want it to be enabled for your team, please reach out via Discord or email. We are looking forward to your feedback!
General command improvements
New global "--show-installation-hint" option
All commands now support a new global option to show how the current DCM version can be installed on all supported platforms.
Improved hint on how to install newly available DCM version
Once there is a new version available, DCM will now show a correct installation hint for each platform.
For Mac OS:
brew tap CQLabs/dcm && brew upgrade dcm
and if you have a global version constraint, it will suggest the exact version that matches the constraint:
brew tap CQLabs/dcm && brew install [email protected]
For Linux:
apt-get update && apt-get install dcm
# and with the constraint
apt-get update && apt-get install dcm=1.17.3-1
For Windows:
choco upgrade dcm
# and with the constraint
choco install dcm --version 1.17.3
Sticky table header for HTML reports
All HTML reports now have a sticky table header to simplify managing large lists of widgets / files.
Unused localization improvements
Usually a generated localization class has some fields / getter that are not intended to be used to access localization strings (for example, localeName
and delegate
).
abstract class AppLocalizations {
AppLocalizations(String locale) : localeName = intl.Intl.canonicalizedLocale(locale.toString());
final String localeName;
static AppLocalizations? of(BuildContext context) {
return Localizations.of<AppLocalizations>(context, AppLocalizations);
}
static const LocalizationsDelegate<AppLocalizations> delegate = _AppLocalizationsDelegate();
...
}
With this release such fields will not be reported as unused.
Check dependencies improvements
Prior to this release, the command would report "vector_graphics_compiler" (or any other package used as an asset transformer) as unused (since it's not referenced anywhere else).
For example,
flutter:
assets:
# Apply transformer to a single SVG file
- path: assets/icon_comment.svg
transformers:
- package: vector_graphics_compiler
# Apply transformer to a folder of SVGs
- path: assets/svg/
transformers:
- package: vector_graphics_compiler
Now, any asset transformer package is correctly marked as used.
Analyze widgets improvements
With this release the command supports additional widget types, including: "HookWidgets", "StatefulHookWidgets", "HookConsumerWidgets", "StatefulHookConsumerWidgets", "ConsumerWidgets" and "ConsumerStatefulWidgets".
They all now have their own widget type in the report:
VS Code extension updates
"New version" notification
"New version" notification now has a new button to quickly copy the update command (unique for each platform).
New IDE commands
This release includes 4 new commands for the VS Code extension:
- Show / hide unused code
- Show / hide unused files
- Open rules page
- Open metrics page
IntelliJ plugin updates
New button to filter, sort and group issues
Prior to this release the plugin had a button only for severity filters.
Now there is a new button that shows a popup with different view options: filtering, sorting and grouping.
New grouping mode
DCM panel now supports a new mode to group issues by rule name (and show how many problems each rule has).
New buttons to toggle unused code / files modes
DCM panel buttons now include two new buttons to toggle unused code / unused file modes without the need to open settings or creating a shortcut.
"New version" notification (IntelliJ)
"New version" notification now has a new button to quickly copy the update command (unique for each platform).
GitHub Actions updates
To further simplify DCM version synchronization within the team and on CI/CD, "setup-dcm" action now supports a new installation version called auto
.
It allows installing the latest DCM version that matches the version constraint provided in the dcm_global.yaml
config file.
For example, if the dcm_global.yaml
has a version constraint set as < 1.18.0
, then the action will install DCM 1.17.3
as the latest available version that matches this constraint.
Rule updates
"member-ordering" improvements
This release includes two notable changes to this rule.
First, the rule now supports named getters and setters. For example, you can now configure the rule to group hashCode
and ==
via hash-code-getter
and ==-method
config options! 🚀
And the second change is that field-getter-setter
config now also works for static members.
"prefer-single-declaration-per-file" improvements
With this release, this rule correctly handles edge-cases with sealed
, final
, interface
and base
declarations.
For example,
sealed class SomePublicClass {}
class Extends extends SomePublicClass {}
class Implements implements SomePublicClass {}
class MixesIn with SomePublicClass {}
class SomeClass {}
here, since the sealed class family can not be moved to separate files, the rule will only trigger for SomeClass
.
New rules
Common
avoid-unsafe-reduce. Warns when the .reduce
collection method is called on a potentially empty collection.
Calling .reduce
on an empty collection will result in a runtime exception.
For example,
void fn() {
final list = <int>[];
final sum = list.reduce((a, b) => a + b);
}
here, this code will throw at runtime.
To avoid this bug, check for the collection to be non-empty before calling .reduce
:
void fn() {
final list = <int>[];
final sum = list.isEmpty ? null : list.reduce((a, b) => a + b);
// OR
if (list.isNotEmpty) {
final sum = list.reduce((a, b) => a + b);
}
}
avoid-returning-cascades. Warns when a cascade expression is being returned from a function.
Returning cascade expressions can add additional confusion to the code reader as to what is actually being returned.
For example,
class Cow {
void moo() {}
}
Cow getCow() {
return Cow..moo();
}
here, the rule will suggest separating the instance creation and method invocation.
prefer-named-parameters. Suggests converting positional parameters to named parameters when a declaration has a certain number of positional parameters.
For example,
class Some {
final String value;
final String another;
const Some(this.value, this.another);
}
here, it's relatively easy to accidentally create an instance of Some
with the wrong order of arguments (since both parameters are String
).
To avoid that, the rule will suggest making at least one of them named:
class Some {
final String value;
final String another;
const Some(this.value, {required this.another});
}
avoid-only-rethrow. Warns when a catch clause has only a rethrow
expression.
For example,
void main() {
try {
...
} on Object catch (error) {
rethrow;
}
}
here, even though the error is being caught, having only the rethrow
expression does not help with actually handling the error as it will propagated further.
To avoid that, either remove the try / catch
block or add additional logic to the error handler:
void main() {
try {
...
} on Object catch (error) {
if (error is Exception) {
// handle
...
return;
}
rethrow;
}
}
prefer-abstract-final-static-class. Suggests adding abstract final
to classes with only static members to avoid them being instantiated or being used in inheritance.
For example,
class Static {
static final field = 'some value';
}
here, this class can be accidentally used in an implements
, extends
or with
clause.
To avoid that, just mark this class abstract final
.
GetX
avoid-getx-rx-inside-build. Warns when GetX Rx primitives are instantiated inside the build
method.
For example,
class _SomeState extends State<Some> {
Widget build(BuildContext context) {
final count = 0.obs;
...
}
}
here, count
will be recreated on each rebuild.
To avoid that, create a field instead:
class _SomeState extends State<Some> {
final count = 0.obs;
Widget build(BuildContext context) {
...
}
}
dispose-getx-fields. Warns when a widget state field is not disposed in the onClose
method.
For example,
class _ShinyWidgetController extends GetxController {
final _someDisposable = SomeDisposable();
final _anotherDisposable = AnotherDisposable();
void onClose() {
_someDisposable.dispose();
super.onClose();
}
}
here, _anotherDisposable
is not disposed in the onClose
method.
always-remove-getx-listener. Warns when a GetX event listener is added but never removed.
For example,
class VideoViewerController extends GetxController {
final _someListener = Listener();
final _anotherListener = Listener();
void onInit() {
_someListener.addListener(listener);
_anotherListener.addListener(listener);
}
void onClose() {
_someListener.removeListener(listener);
}
void listener() {
...
};
}
here, _anotherListener
's listener is not removed in the onClose
method.
What’s next
Baseline for all commands. To further simplify DCM integration into existing projects, we want to expand baseline support from just "dcm analyze" to all commands that can produce analysis issues ("dcm check-unused-code", "dcm check-unused-files" and other).
Support for all commands in DCM GitHub action. One of the ideas behind "dcm run" was to integrate it into DCM GitHub action so that it's not limited to just "dcm analyze". Expanding the number of supported commands will make the action even better.
Documentation improvements. We plan to improve the overall state of the documentation, including a rewrite of the Getting Started section, introducing several user guides, as well as updating the example rules and adding more information about the problem behind each rule. We hope these changes will help us better explain DCM's features and help you get started with the product faster.
Sharing your feedback
If there is something you miss from DCM right now or want us to make something better or have any general feedback - join our Discord server!