Updating your pub package to Dart 2

Dart 2 is imminent. If you own a package on pub.dartlang.org, you might want to make sure it’s ready.

Take inventory

Head over to pub and search for packages associated with your email. If you’ve been around the Dart ecosystem for a while (like me), you might have forgotten about ever writing some of these.

If you go to pub.dartlang.org and search for email:your.email@example.com, you’ll see all the Dart packages you own.

Decide which ones are worth updating

Some packages are timeless, and some not so much. For example, my simple_preprocessor package for adding C-style macros can safely die in Dart 1 land. It was useful at some point, but only to me, and not any more. Pub does a decent job of hiding these old packages from users, sorting them at the very bottom of everything. (In the future, we might add the possibility for package owners to flag their own packages with the [DEPRECATED] flag.)

These are all mine, and I’m happy for these to keep those scores.

Pub now helpfully scores every package, and one of the criteria is popularity. If your package has popularity > 0, then it’s definitely being used by people other than you, and it’s worth updating.

You get to this visualization by clicking the last tab — the one that has the score in a circle.

Install Dart 2 (and 1)

If you’re using Flutter, you already have a preview release of Dart 2 (in $YOUR_FLUTTER_PATH/bin/cache/dart-sdk/bin/dart). But that's version 2.0.0-dev.58.0 so I recommend that you install the latest one via the standalone Dart SDK process. As of this writing, that will give you version 2.0.0-dev.69.1.

I found it useful to keep Dart 1 around. You can do that via homebrew’s switch command, but that's assuming that you still have Dart 1 in your homebrew cache — and it's also quite annoying to switch from one version to another all the time. So, if you're like me, you'll just go to the manual download page, download the latest Dart 1 SDK as a zip file, unzip it somewhere, and then make an alias like this in your .bash_profile:

alias dart1=/path/to/dart-1.24.3-sdk/bin/dart
alias dartanalyzer1=/path/to/dart-1.24.3-sdk/bin/dartanalyzer
alias pub1=/path/to/dart-1.24.3-sdk/bin/pub

This lets you freely switch from running things in Dart 1 world and Dart 2 world. You should be able to do this now:

$ dart --version
Dart VM version: 2.0.0-dev.69.1 (Fri Jul 20 18:30:52 2018 +0200) on "macos_x64"
$ dart1 --version
Dart VM version: 1.24.3 (Wed Dec 13 23:26:59 2017) on "macos_x64"

Version 1.24.3 will likely be the last version of Dart 1, so I feel it's okay to install it manually like this.

Migrate the code

There’s a migration guide here: dartlang.org/dart-2. Go read it, it has really useful bits of information, like the existence of the dart2_fix tool, the dartfmt --fix flag, and many more.

If you haven’t touched your package in a while, a common set of things to do first is:

  • Add .dart_tool/ into .gitignore
  • Bump the SDK version constraints in pubspec.yaml from something like sdk: ">=1.8.0 <2.0.0" to something like sdk: ">=2.0.0-dev <3.0.0" (or sdk: ">=1.24.3 <3.0.0" if you want to keep Dart 1 compatibility)
  • Bump all dependencies (and dev_dependencies) in pubspec.yaml to Dart-2-compatible versions (check their change log to find the version). Avoid pinning dependencies to the very latest version unless you absolutely need to. Having a broader range will give you package users more freedom to pick their own dependency versions.
  • Run pub upgrade (not just pub get). Even better, run _PUB_TEST_SDK_VERSION=2.0.0 pub upgrade (explained here).

Then you’re ready to fix errors, if any. This is where the Dart 2 migration guide (linked above) is helpful, with pages like Fixing Common Type Problems.

When you have no more errors but still have warnings, don’t forget that you can use dart2_fix and dartfmt --fix (again, see the helpful migration guide linked above) to get rid of the lints and warnings automatically. dart2_fix deals with changes to constant names (like JSON becoming json) and dartfmt --fix removes unnecessary new and const. Don't do it yourself when you don't need to.

What’s left after this step are mostly deprecation warnings. For example, instead of int.parse(string, (_) => -1), you now do int.tryParse(string) ?? -1. Integers are now 64-bit, so if you want really really big numbers, you need to use the new BigInt class. That kind of thing.

Optional: add CI testing

If your package isn’t already continuously tested, now is a good chance to start. If you want to go with Travis, and assuming you have tests, then it’s pretty simple.

You create a new file in the root of your package called .travis.yml and fill it with something like this:

language: dart
dart:
- dev
script:
- dartanalyzer --fatal-warnings --fatal-lints .
- pub run test

You need to tell Travis to use the dev version of Dart (i.e. Dart 2) for now because by default it uses stable (which is, as of this writing, still Dart 1).

Once Dart 2 lands, you might want to change this back, or use Travis’s build matrix to test on various versions.

By the way, the script: part isn't necessary. It's useful if you want to prevent code with warnings or lints from ever reaching your repo. If you're not that hardcore, leave that out. Travis will run pub run test for you.

(There’s more you can do in your .travis.yml file. Read the Travis Dart docs here.)

Once you have the .travis.yml file set up, go to your Travis profile, enable Travis for this particular repository, then push your changes (including the .travis.yml file). If you're like me, you won't make it quite right on the first few tries. That's okay, just keep at it until Travis runs and the result is green.

Then, take the badge and put it in your README.md. You earned it. (And potential users of your package will find it super useful.)

Oh, and do make use of Travis’s awesome “cron job” functionality. It’ll rerun your test every day/week/month for you even when there’s no change on your repo.

Add a change log

While we’re at this, add a CHANGELOG.md file (assuming you don’t have it yet). Take this as a fresh start and just add the first record for this change, i.e. something like this:

## 1.2.3

- Upgrade to Dart 2

Bump your own version

If you only made some small internal changes, and no changes to your API, you can bump just the minor version. (At least that’s my reading of the semver spec.)

But do feel free to bump to the next major version. Especially if you’re not yet v1 (e.g. your version is something like 0.1.3) and yet there are people building software on top of your package. This is as good an excuse as any to release a stable version.

Upload to pub

Run pub publish.

Now wait a few minutes and review the package’s page on pub.dartlang.org. It should get a better score, but — more importantly — it’s now ready for Dart 2.