Validating Dart Dependencies and You!

September 30, 2019

Have you ever run into issues with Dart dependencies? We have too, and that's why we created the dependency_validator.

Dependency what?

The dependency_validator is a new open-source tool the App Frameworks team has published that validates that a package's dependencies follow Pub conventions as well as best practices.

By comparing a package's declared dependencies in the pubspec.yaml with the actual usage of dependencies via import statements, this tool is able to identify various dependency-related issues.

Some examples of the kinds of issues it can recognize include: 

  • Missing dependencies 
  • Unnecessary dependencies 
  • Miscategorized dependencies

Missing

Let's say you have package "a" that imports package "b", but does not declare it as a dependency in pubspec.yaml.

name: a

dependencies:

  # none declared! :(

 

The dependency_validator is able to catch this!

workspaces/a $ pub run dependency_validator

These packages are used in lib/ but are not dependencies:

  * b

Unnecessary Dependencies

The key word here is "unnecessary;" if one package doesn't use any code from another package, then it doesn't need to declare that as a dependency.

Package "a" is trying to be a good citizen by updating dependency version constraints even though it doesn't import anything from "c", and in doing so has created a dependency version lock.

name: a

dependencies:

  b: ^1.0.0

  c: ^1.5.1

 

name: b

dependencies:

  c: ">=1.0.0 <1.5.0"

Again, the dependency_validator has your back.

workspaces/a $ pub run dependency_validator

 

These packages may be unused, or you may be using executables or assets from these packages:

  * c

Miscategorized Dependencies

This is very similar to the example above but with a subtle difference. Now package "a" has no dependency on "c". And "b" has moved their dependency of "c" to the dev_dependencies section, but still consumes "c" within their lib/ directory.

What happens here is when "a" consumes "b," there is a dependency that does not get fetched.

name: a

dependencies:

  b: ^1.0.0

 

name: b

dependencies:

  # none declared! :(

dev_dependencies:

  c: ^1.0.0

You guessed it, the dependency_validator can help you out of this jam!

workspaces/b $ pub run dependency_validator

 

These packages are used in lib/ but are not dependencies:

  * c

What else can it do?

But wait, there's more! The dependency_validator can catch five types of infractions:

  • Missing dependencies
    • As elaborated on above, a missing dependency is a dependency that is used within lib/ or bin/ but not declared in pubspec.yaml.
  • Missing developer dependencies
    • A missing dev dependency is a dependency that is used within test/, web/, tool/, or example/ but not declared in pubspec.yaml.
  • Under-promoted
    • This happens when a dependency is used within lib/ or bin/ but only declared as a "dev dependency."
  • Over-promoted
    • Conversely, this is when a dependency is only used within test/, web/, tool/, or example/ but declared as a dependency.
  • Unused
    • A dependency is unused when it is declared as a dev dependency or dependency but not used at all in the package.

Okay, I’m convinced. Now how can I use it?

  1. Add dependency_validator as a dev dependency in your pubspec.yaml:

yaml dev_dependencies: dependency_validator: version: ^1.1.0

  1. Run pub run dependency_validator from the root of your package!

But what if I don't want it to fail for every little thing?

Out of the box, the dependency_validator is very strict and will fail on any infraction. That's not always desired, so we added some options to make the behavior a bit more configurable.

  • --no-fatal-missing (not recommended):
    • Do not fail when there is a missing entry in the dependencies section.
  • --no-fatal-dev-missing:
    • Do not fail when there is a missing entry in the dev_dependencies section.
  • --no-fatal-under-promoted (not recommended):
    • Do not fail when there is a package that is used within lib/ but only declared as a dev_dependency.
  • --no-fatal-over-promoted (not recommended):
    • Do not fail when there is a package that is only used within web/ but declared as a dependency.
  • --no-fatal-unused (not recommended):
    • Do not fail when there is a package that is declared as either a dependency or dev_dependency but not imported anywhere.
  • --ignore:
    • List of comma separated packages not to warn for, intended to be used for packages that are brought in for the executables or transformers (dartdoc and dart2js_script_rewriter).
  • --exclude-dir
    • List of comma separated directories to exclude when looking for infractions. Useful when you have a sub package, with its own pubspec.yaml, in example/ or test/.

Now what?

The dependency_validator has helped us a great deal as we've been integrating Experiences into Unified Wdesk. And in order to share the love, we will be making PRs into some products to add dependency_validator to their CI.