move io utils to their own library, release 1.9.1 (#74)

diff --git a/.travis.yml b/.travis.yml
index 655bf3d..09fc296 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -7,6 +7,11 @@
   - dartfmt
   - dartanalyzer: --fatal-warnings .
 
+matrix:
+  include:
+  - dart: dev
+    script: pub run build_runner test -- -p chrome
+
 # Only building master means that we don't run two builds for each pull request.
 branches:
   only: [master]
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a86d1a5..1cd45d0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,8 @@
+## 1.9.1
+
+- Remove accidental transitive import of `dart:io` from entrypoints that are
+  supposed to be cross-platform compatible.
+
 ## 1.9.0
 
 - Based on new JSON file format with more content.
diff --git a/lib/src/discovery.dart b/lib/src/discovery.dart
index 5d3172f..8ac6a01 100644
--- a/lib/src/discovery.dart
+++ b/lib/src/discovery.dart
@@ -11,7 +11,7 @@
 import "package_config_impl.dart";
 import "package_config_json.dart";
 import "packages_file.dart" as packages_file;
-import "util.dart" show defaultLoader, pathJoin;
+import "util_io.dart" show defaultLoader, pathJoin;
 
 final Uri packageConfigJsonPath = Uri(path: ".dart_tool/package_config.json");
 final Uri dotPackagesPath = Uri(path: ".packages");
diff --git a/lib/src/package_config_io.dart b/lib/src/package_config_io.dart
index d59972f..954be6b 100644
--- a/lib/src/package_config_io.dart
+++ b/lib/src/package_config_io.dart
@@ -14,6 +14,7 @@
 import "package_config_json.dart";
 import "packages_file.dart" as packages_file;
 import "util.dart";
+import "util_io.dart";
 
 /// Reads a package configuration file.
 ///
diff --git a/lib/src/packages_io_impl.dart b/lib/src/packages_io_impl.dart
index b2277e7..c623f4d 100644
--- a/lib/src/packages_io_impl.dart
+++ b/lib/src/packages_io_impl.dart
@@ -12,7 +12,7 @@
 
 import "packages_impl.dart";
 
-import "util.dart";
+import "util_io.dart";
 
 /// A [Packages] implementation based on a local directory.
 class FilePackagesDirectoryPackages extends PackagesBase {
diff --git a/lib/src/util.dart b/lib/src/util.dart
index 548f61d..50b140f 100644
--- a/lib/src/util.dart
+++ b/lib/src/util.dart
@@ -5,9 +5,6 @@
 /// Utility methods used by more than one library in the package.
 library package_config.util;
 
-import 'dart:io';
-import 'dart:typed_data';
-
 import "errors.dart";
 
 // All ASCII characters that are valid in a package name, with space
@@ -215,102 +212,6 @@
   }
 }
 
-Future<Uint8List> defaultLoader(Uri uri) async {
-  if (uri.isScheme("file")) {
-    var file = File.fromUri(uri);
-    try {
-      return file.readAsBytes();
-    } catch (_) {
-      return null;
-    }
-  }
-  if (uri.isScheme("http") || uri.isScheme("https")) {
-    return _httpGet(uri);
-  }
-  throw UnsupportedError("Default URI unsupported scheme: $uri");
-}
-
-Future<Uint8List /*?*/ > _httpGet(Uri uri) async {
-  assert(uri.isScheme("http") || uri.isScheme("https"));
-  var client = HttpClient();
-  var request = await client.getUrl(uri);
-  var response = await request.close();
-  if (response.statusCode != HttpStatus.ok) {
-    return null;
-  }
-  var splitContent = await response.toList();
-  var totalLength = 0;
-  if (splitContent.length == 1) {
-    var part = splitContent[0];
-    if (part is Uint8List) {
-      return part;
-    }
-  }
-  for (var list in splitContent) {
-    totalLength += list.length;
-  }
-  var result = Uint8List(totalLength);
-  var offset = 0;
-  for (Uint8List contentPart in splitContent) {
-    result.setRange(offset, offset + contentPart.length, contentPart);
-    offset += contentPart.length;
-  }
-  return result;
-}
-
-/// The file name of a path.
-///
-/// The file name is everything after the last occurrence of
-/// [Platform.pathSeparator], or the entire string if no
-/// path separator occurs in the string.
-String fileName(String path) {
-  var separator = Platform.pathSeparator;
-  var lastSeparator = path.lastIndexOf(separator);
-  if (lastSeparator < 0) return path;
-  return path.substring(lastSeparator + separator.length);
-}
-
-/// The directory name of a path.
-///
-/// The directory name is everything before the last occurrence of
-/// [Platform.pathSeparator], or the empty string if no
-/// path separator occurs in the string.
-String dirName(String path) {
-  var separator = Platform.pathSeparator;
-  var lastSeparator = path.lastIndexOf(separator);
-  if (lastSeparator < 0) return "";
-  return path.substring(0, lastSeparator);
-}
-
-/// Join path parts with the [Platform.pathSeparator].
-///
-/// If a part ends with a path separator, then no extra separator is
-/// inserted.
-String pathJoin(String part1, String part2, [String part3]) {
-  var separator = Platform.pathSeparator;
-  var separator1 = part1.endsWith(separator) ? "" : separator;
-  if (part3 == null) {
-    return "$part1$separator1$part2";
-  }
-  var separator2 = part2.endsWith(separator) ? "" : separator;
-  return "$part1$separator1$part2$separator2$part3";
-}
-
-/// Join an unknown number of path parts with [Platform.pathSeparator].
-///
-/// If a part ends with a path separator, then no extra separator is
-/// inserted.
-String pathJoinAll(Iterable<String> parts) {
-  var buffer = StringBuffer();
-  var separator = "";
-  for (var part in parts) {
-    buffer..write(separator)..write(part);
-    separator =
-        part.endsWith(Platform.pathSeparator) ? "" : Platform.pathSeparator;
-  }
-  return buffer.toString();
-}
-
 // Character constants used by this package.
 /// "Line feed" control character.
 const int $lf = 0x0a;
diff --git a/lib/src/util_io.dart b/lib/src/util_io.dart
new file mode 100644
index 0000000..7f21a8d
--- /dev/null
+++ b/lib/src/util_io.dart
@@ -0,0 +1,106 @@
+// Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+/// Utility methods requiring dart:io and used by more than one library in the
+/// package.
+library package_config.util_io;
+
+import 'dart:io';
+import 'dart:typed_data';
+
+Future<Uint8List> defaultLoader(Uri uri) async {
+  if (uri.isScheme("file")) {
+    var file = File.fromUri(uri);
+    try {
+      return file.readAsBytes();
+    } catch (_) {
+      return null;
+    }
+  }
+  if (uri.isScheme("http") || uri.isScheme("https")) {
+    return _httpGet(uri);
+  }
+  throw UnsupportedError("Default URI unsupported scheme: $uri");
+}
+
+Future<Uint8List /*?*/ > _httpGet(Uri uri) async {
+  assert(uri.isScheme("http") || uri.isScheme("https"));
+  var client = HttpClient();
+  var request = await client.getUrl(uri);
+  var response = await request.close();
+  if (response.statusCode != HttpStatus.ok) {
+    return null;
+  }
+  var splitContent = await response.toList();
+  var totalLength = 0;
+  if (splitContent.length == 1) {
+    var part = splitContent[0];
+    if (part is Uint8List) {
+      return part;
+    }
+  }
+  for (var list in splitContent) {
+    totalLength += list.length;
+  }
+  var result = Uint8List(totalLength);
+  var offset = 0;
+  for (Uint8List contentPart in splitContent) {
+    result.setRange(offset, offset + contentPart.length, contentPart);
+    offset += contentPart.length;
+  }
+  return result;
+}
+
+/// The file name of a path.
+///
+/// The file name is everything after the last occurrence of
+/// [Platform.pathSeparator], or the entire string if no
+/// path separator occurs in the string.
+String fileName(String path) {
+  var separator = Platform.pathSeparator;
+  var lastSeparator = path.lastIndexOf(separator);
+  if (lastSeparator < 0) return path;
+  return path.substring(lastSeparator + separator.length);
+}
+
+/// The directory name of a path.
+///
+/// The directory name is everything before the last occurrence of
+/// [Platform.pathSeparator], or the empty string if no
+/// path separator occurs in the string.
+String dirName(String path) {
+  var separator = Platform.pathSeparator;
+  var lastSeparator = path.lastIndexOf(separator);
+  if (lastSeparator < 0) return "";
+  return path.substring(0, lastSeparator);
+}
+
+/// Join path parts with the [Platform.pathSeparator].
+///
+/// If a part ends with a path separator, then no extra separator is
+/// inserted.
+String pathJoin(String part1, String part2, [String part3]) {
+  var separator = Platform.pathSeparator;
+  var separator1 = part1.endsWith(separator) ? "" : separator;
+  if (part3 == null) {
+    return "$part1$separator1$part2";
+  }
+  var separator2 = part2.endsWith(separator) ? "" : separator;
+  return "$part1$separator1$part2$separator2$part3";
+}
+
+/// Join an unknown number of path parts with [Platform.pathSeparator].
+///
+/// If a part ends with a path separator, then no extra separator is
+/// inserted.
+String pathJoinAll(Iterable<String> parts) {
+  var buffer = StringBuffer();
+  var separator = "";
+  for (var part in parts) {
+    buffer..write(separator)..write(part);
+    separator =
+        part.endsWith(Platform.pathSeparator) ? "" : Platform.pathSeparator;
+  }
+  return buffer.toString();
+}
diff --git a/pubspec.yaml b/pubspec.yaml
index abaddd9..5299fdf 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,5 +1,5 @@
 name: package_config
-version: 1.9.0
+version: 1.9.1
 description: Support for working with Package Configuration files.
 homepage: https://github.com/dart-lang/package_config
 
@@ -14,3 +14,6 @@
   test: ^1.6.4
   matcher: ^0.12.5
   pedantic: 1.8.0
+  build_runner: ^1.0.0
+  build_web_compilers: ^2.0.0
+  build_test: ^0.10.0
diff --git a/test/discovery_test.dart b/test/discovery_test.dart
index e4c93a5..1a9a61c 100644
--- a/test/discovery_test.dart
+++ b/test/discovery_test.dart
@@ -2,6 +2,7 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+@TestOn('vm')
 library package_config.discovery_test;
 
 import "dart:io";
@@ -9,6 +10,7 @@
 import "package:package_config/package_config.dart";
 
 import "src/util.dart";
+import "src/util_io.dart";
 
 const packagesFile = """
 # A comment
diff --git a/test/discovery_uri_test.dart b/test/discovery_uri_test.dart
index 109693c..52fca3f 100644
--- a/test/discovery_uri_test.dart
+++ b/test/discovery_uri_test.dart
@@ -2,6 +2,7 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+@TestOn('vm')
 library package_config.discovery_test;
 
 import "package:test/test.dart";
diff --git a/test/legacy/discovery_analysis_test.dart b/test/legacy/discovery_analysis_test.dart
index 7d08f7b..4be636d 100644
--- a/test/legacy/discovery_analysis_test.dart
+++ b/test/legacy/discovery_analysis_test.dart
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 
 @deprecated
+@TestOn('vm')
 library package_config.discovery_analysis_test;
 
 import "dart:async";
diff --git a/test/legacy/discovery_test.dart b/test/legacy/discovery_test.dart
index 684abcb..72874c8 100644
--- a/test/legacy/discovery_test.dart
+++ b/test/legacy/discovery_test.dart
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 
 @deprecated
+@TestOn('vm')
 library package_config.discovery_test;
 
 import "dart:async";
diff --git a/test/src/util.dart b/test/src/util.dart
index 2b91b21..6e689b7 100644
--- a/test/src/util.dart
+++ b/test/src/util.dart
@@ -3,65 +3,9 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'dart:convert';
-import "dart:io";
 import 'dart:typed_data';
 
 import "package:test/test.dart";
-import "package:package_config/src/util.dart";
-
-/// Creates a directory structure from [description] and runs [fileTest].
-///
-/// Description is a map, each key is a file entry. If the value is a map,
-/// it's a subdirectory, otherwise it's a file and the value is the content
-/// as a string.
-/// Introduces a group to hold the [setUp]/[tearDown] logic.
-void fileTest(String name, Map<String, Object> description,
-    void fileTest(Directory directory)) {
-  group("file-test", () {
-    var tempDir = Directory.systemTemp.createTempSync("pkgcfgtest");
-    setUp(() {
-      _createFiles(tempDir, description);
-    });
-    tearDown(() {
-      tempDir.deleteSync(recursive: true);
-    });
-    test(name, () => fileTest(tempDir));
-  });
-}
-
-/// Creates a set of files under a new temporary directory.
-/// Returns the temporary directory.
-///
-/// The [description] is a map from file names to content.
-/// If the content is again a map, it represents a subdirectory
-/// with the content as description.
-/// Otherwise the content should be a string,
-/// which is written to the file as UTF-8.
-Directory createTestFiles(Map<String, Object> description) {
-  var target = Directory.systemTemp.createTempSync("pkgcfgtest");
-  _createFiles(target, description);
-  return target;
-}
-
-// Creates temporary files in the target directory.
-void _createFiles(Directory target, Map<Object, Object> description) {
-  description.forEach((name, content) {
-    var entryName = pathJoin(target.path, "$name");
-    if (content is Map<Object, Object>) {
-      _createFiles(Directory(entryName)..createSync(), content);
-    } else {
-      File(entryName).writeAsStringSync(content, flush: true);
-    }
-  });
-}
-
-/// Creates a [Directory] for a subdirectory of [parent].
-Directory subdir(Directory parent, String dirName) =>
-    Directory(pathJoinAll([parent.path, ...dirName.split("/")]));
-
-/// Creates a [File] for an entry in the [directory] directory.
-File dirFile(Directory directory, String fileName) =>
-    File(pathJoin(directory.path, fileName));
 
 /// Creates a package: URI.
 Uri pkg(String packageName, String packagePath) {
diff --git a/test/src/util_io.dart b/test/src/util_io.dart
new file mode 100644
index 0000000..d05618a
--- /dev/null
+++ b/test/src/util_io.dart
@@ -0,0 +1,62 @@
+// Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import "dart:io";
+
+import "package:test/test.dart";
+import "package:package_config/src/util_io.dart";
+
+/// Creates a directory structure from [description] and runs [fileTest].
+///
+/// Description is a map, each key is a file entry. If the value is a map,
+/// it's a subdirectory, otherwise it's a file and the value is the content
+/// as a string.
+/// Introduces a group to hold the [setUp]/[tearDown] logic.
+void fileTest(String name, Map<String, Object> description,
+    void fileTest(Directory directory)) {
+  group("file-test", () {
+    var tempDir = Directory.systemTemp.createTempSync("pkgcfgtest");
+    setUp(() {
+      _createFiles(tempDir, description);
+    });
+    tearDown(() {
+      tempDir.deleteSync(recursive: true);
+    });
+    test(name, () => fileTest(tempDir));
+  });
+}
+
+/// Creates a set of files under a new temporary directory.
+/// Returns the temporary directory.
+///
+/// The [description] is a map from file names to content.
+/// If the content is again a map, it represents a subdirectory
+/// with the content as description.
+/// Otherwise the content should be a string,
+/// which is written to the file as UTF-8.
+Directory createTestFiles(Map<String, Object> description) {
+  var target = Directory.systemTemp.createTempSync("pkgcfgtest");
+  _createFiles(target, description);
+  return target;
+}
+
+// Creates temporary files in the target directory.
+void _createFiles(Directory target, Map<Object, Object> description) {
+  description.forEach((name, content) {
+    var entryName = pathJoin(target.path, "$name");
+    if (content is Map<Object, Object>) {
+      _createFiles(Directory(entryName)..createSync(), content);
+    } else {
+      File(entryName).writeAsStringSync(content, flush: true);
+    }
+  });
+}
+
+/// Creates a [Directory] for a subdirectory of [parent].
+Directory subdir(Directory parent, String dirName) =>
+    Directory(pathJoinAll([parent.path, ...dirName.split("/")]));
+
+/// Creates a [File] for an entry in the [directory] directory.
+File dirFile(Directory directory, String fileName) =>
+    File(pathJoin(directory.path, fileName));