Add support for a dartdoc_options.yaml. (#1638)

* Fix or disable miscellaneous lints in dartdoc

* Clean up a few more lints.

* Rename category to package in all remaining references

* dartfmt

* Review comments

* Intermediate state

* basic dartdoc options now working

* Fix test failure (accidentally caching config info)

* dartfmt

* dartfmt

* one last rename

* Remove overly complicated config inheritance; we can add it back if we truly need this

* dartfmt

* Rename path/p import directive to pathLib everywhere.

* dartfmt
diff --git a/bin/dartdoc.dart b/bin/dartdoc.dart
index 27252db..5187c04 100644
--- a/bin/dartdoc.dart
+++ b/bin/dartdoc.dart
@@ -16,7 +16,7 @@
 import 'package:dartdoc/dartdoc.dart';
 import 'package:dartdoc/src/logging.dart';
 import 'package:logging/logging.dart' as logging;
-import 'package:path/path.dart' as path;
+import 'package:path/path.dart' as pathLib;
 import 'package:stack_trace/stack_trace.dart';
 
 /// Analyzes Dart files and generates a representation of included libraries,
@@ -112,7 +112,7 @@
   }
 
   Directory outputDir =
-      new Directory(path.join(Directory.current.path, defaultOutDir));
+      new Directory(pathLib.join(Directory.current.path, defaultOutDir));
   if (args['output'] != null) {
     outputDir = new Directory(_resolveTildePath(args['output']));
   }
@@ -421,10 +421,10 @@
   String homeDir;
 
   if (Platform.isWindows) {
-    homeDir = path.absolute(Platform.environment['USERPROFILE']);
+    homeDir = pathLib.absolute(Platform.environment['USERPROFILE']);
   } else {
-    homeDir = path.absolute(Platform.environment['HOME']);
+    homeDir = pathLib.absolute(Platform.environment['HOME']);
   }
 
-  return path.join(homeDir, originalPath.substring(2));
+  return pathLib.join(homeDir, originalPath.substring(2));
 }
diff --git a/lib/dartdoc.dart b/lib/dartdoc.dart
index 1d1c0f7..6c169c6 100644
--- a/lib/dartdoc.dart
+++ b/lib/dartdoc.dart
@@ -16,7 +16,7 @@
 import 'package:dartdoc/src/utils.dart';
 import 'package:html/dom.dart' show Element, Document;
 import 'package:html/parser.dart' show parse;
-import 'package:path/path.dart' as path;
+import 'package:path/path.dart' as pathLib;
 
 import 'package:tuple/tuple.dart';
 import 'src/config.dart';
@@ -38,7 +38,7 @@
 // Update when pubspec version changes.
 const String version = '0.17.1+1';
 
-final String defaultOutDir = path.join('doc', 'api');
+final String defaultOutDir = pathLib.join('doc', 'api');
 
 /// Initialize and setup the generators.
 Future<List<Generator>> initGenerators(String url, String relCanonicalPrefix,
@@ -169,7 +169,7 @@
 
     for (var generator in generators) {
       await generator.generate(packageGraph, outputDir.path);
-      writtenFiles.addAll(generator.writtenFiles.map(path.normalize));
+      writtenFiles.addAll(generator.writtenFiles.map(pathLib.normalize));
     }
     if (config.validateLinks) validateLinks(packageGraph, outputDir.path);
     int warnings = packageGraph.packageWarningCounter.warningCount;
@@ -209,12 +209,12 @@
     Set<Warnable> warnOnElements;
 
     // Make all paths relative to origin.
-    if (path.isWithin(origin, warnOn)) {
-      warnOn = path.relative(warnOn, from: origin);
+    if (pathLib.isWithin(origin, warnOn)) {
+      warnOn = pathLib.relative(warnOn, from: origin);
     }
     if (referredFrom != null) {
-      if (path.isWithin(origin, referredFrom)) {
-        referredFrom = path.relative(referredFrom, from: origin);
+      if (pathLib.isWithin(origin, referredFrom)) {
+        referredFrom = pathLib.relative(referredFrom, from: origin);
       }
       // Source paths are always relative.
       if (_hrefs[referredFrom] != null) {
@@ -245,13 +245,13 @@
 
   void _doOrphanCheck(
       PackageGraph packageGraph, String origin, Set<String> visited) {
-    String normalOrigin = path.normalize(origin);
-    String staticAssets = path.joinAll([normalOrigin, 'static-assets', '']);
-    String indexJson = path.joinAll([normalOrigin, 'index.json']);
+    String normalOrigin = pathLib.normalize(origin);
+    String staticAssets = pathLib.joinAll([normalOrigin, 'static-assets', '']);
+    String indexJson = pathLib.joinAll([normalOrigin, 'index.json']);
     bool foundIndexJson = false;
     for (FileSystemEntity f
         in new Directory(normalOrigin).listSync(recursive: true)) {
-      var fullPath = path.normalize(f.path);
+      var fullPath = pathLib.normalize(f.path);
       if (f is Directory) {
         continue;
       }
@@ -304,8 +304,8 @@
 
   void _doSearchIndexCheck(
       PackageGraph packageGraph, String origin, Set<String> visited) {
-    String fullPath = path.joinAll([origin, 'index.json']);
-    String indexPath = path.joinAll([origin, 'index.html']);
+    String fullPath = pathLib.joinAll([origin, 'index.json']);
+    String indexPath = pathLib.joinAll([origin, 'index.html']);
     File file = new File("$fullPath");
     if (!file.existsSync()) {
       return null;
@@ -320,10 +320,10 @@
     found.add(indexPath);
     for (Map<String, String> entry in jsonData) {
       if (entry.containsKey('href')) {
-        String entryPath = path.joinAll([origin, entry['href']]);
+        String entryPath = pathLib.joinAll([origin, entry['href']]);
         if (!visited.contains(entryPath)) {
           _warn(packageGraph, PackageWarning.brokenLink, entryPath,
-              path.normalize(origin),
+              pathLib.normalize(origin),
               referredFrom: fullPath);
         }
         found.add(entryPath);
@@ -333,7 +333,7 @@
     Set<String> missing_from_search = visited.difference(found);
     for (String s in missing_from_search) {
       _warn(packageGraph, PackageWarning.missingFromSearchIndex, s,
-          path.normalize(origin),
+          pathLib.normalize(origin),
           referredFrom: fullPath);
     }
   }
@@ -342,14 +342,14 @@
       String pathToCheck,
       [String source, String fullPath]) {
     if (fullPath == null) {
-      fullPath = path.joinAll([origin, pathToCheck]);
-      fullPath = path.normalize(fullPath);
+      fullPath = pathLib.joinAll([origin, pathToCheck]);
+      fullPath = pathLib.normalize(fullPath);
     }
 
     Tuple2 stringLinksAndHref = _getStringLinksAndHref(fullPath);
     if (stringLinksAndHref == null) {
       _warn(packageGraph, PackageWarning.brokenLink, pathToCheck,
-          path.normalize(origin),
+          pathLib.normalize(origin),
           referredFrom: source);
       _onCheckProgress.add(pathToCheck);
       // Remove so that we properly count that the file doesn't exist for
@@ -376,13 +376,13 @@
       if (uri == null || !uri.hasAuthority && !uri.hasFragment) {
         var full;
         if (baseHref != null) {
-          full = '${path.dirname(pathToCheck)}/$baseHref/$href';
+          full = '${pathLib.dirname(pathToCheck)}/$baseHref/$href';
         } else {
-          full = '${path.dirname(pathToCheck)}/$href';
+          full = '${pathLib.dirname(pathToCheck)}/$href';
         }
-        var newPathToCheck = path.normalize(full);
-        String newFullPath = path.joinAll([origin, newPathToCheck]);
-        newFullPath = path.normalize(newFullPath);
+        var newPathToCheck = pathLib.normalize(full);
+        String newFullPath = pathLib.joinAll([origin, newPathToCheck]);
+        newFullPath = pathLib.normalize(newFullPath);
         if (!visited.contains(newFullPath)) {
           toVisit.add(new Tuple2(newPathToCheck, newFullPath));
           visited.add(newFullPath);
diff --git a/lib/src/config.dart b/lib/src/config.dart
index ae08e10..03b752e 100644
--- a/lib/src/config.dart
+++ b/lib/src/config.dart
@@ -6,6 +6,38 @@
 
 import 'dart:io';
 
+import 'package:analyzer/dart/element/element.dart';
+import 'package:dartdoc/dartdoc.dart';
+import 'package:path/path.dart' as pathLib;
+
+import 'model.dart';
+
+/// Class representing values possibly local to a particular [ModelElement].
+class LocalConfig {
+  final Map<String, Set<String>> categoryMap;
+  final PackageMeta packageMeta;
+
+  LocalConfig._(this.categoryMap, this.packageMeta);
+
+  factory LocalConfig.fromLibrary(LibraryElement element) {
+    return new LocalConfig._({}, getPackageMeta(element));
+  }
+
+  static PackageMeta getPackageMeta(LibraryElement element) {
+    String sourcePath = element.source.fullName;
+    File file = new File(pathLib.canonicalize(sourcePath));
+    Directory dir = file.parent;
+    while (dir.parent.path != dir.path && dir.existsSync()) {
+      File pubspec = new File(pathLib.join(dir.path, 'pubspec.yaml'));
+      if (pubspec.existsSync()) {
+        return new PackageMeta.fromDir(dir);
+      }
+      dir = dir.parent;
+    }
+    return null;
+  }
+}
+
 class Config {
   final Directory inputDir;
   final bool showWarnings;
diff --git a/lib/src/dartdoc_options.dart b/lib/src/dartdoc_options.dart
new file mode 100644
index 0000000..95d8108
--- /dev/null
+++ b/lib/src/dartdoc_options.dart
@@ -0,0 +1,109 @@
+// Copyright (c) 2017, 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.
+
+///
+/// dartdoc's dartdoc_options.yaml configuration file follows similar loading
+/// semantics to that of analysis_options.yaml,
+/// [documented here](https://www.dartlang.org/guides/language/analysis-options).
+/// It searches parent directories until it finds an analysis_options.yaml file,
+/// and uses built-in defaults if one is not found.
+///
+library dartdoc.dartdoc_options;
+
+import 'dart:io';
+
+import 'package:path/path.dart' as pathLib;
+import 'package:yaml/yaml.dart';
+
+import 'logging.dart';
+
+final Map<String, DartdocOptions> _dartdocOptionsCache = {};
+
+abstract class DartdocOptions {
+  DartdocOptions();
+
+  /// Path to the dartdoc options file, or '<default>' if this object is the
+  /// default setting. Intended for printing only.
+  String get _path;
+
+  /// A list indicating the preferred subcategory sorting order.
+  List<String> get categoryOrder;
+
+  factory DartdocOptions.fromDir(Directory dir) {
+    if (!_dartdocOptionsCache.containsKey(dir.absolute.path)) {
+      _dartdocOptionsCache[dir.absolute.path] =
+          new DartdocOptions._fromDir(dir);
+    }
+    return _dartdocOptionsCache[dir.absolute.path];
+  }
+
+  /// Search for a dartdoc_options file in this and parent directories.
+  factory DartdocOptions._fromDir(Directory dir) {
+    if (!dir.existsSync()) return new _DefaultDartdocOptions();
+
+    File f;
+    dir = dir.absolute;
+
+    while (true) {
+      f = new File(pathLib.join(dir.path, 'dartdoc_options.yaml'));
+      if (f.existsSync() || dir.parent.path == dir.path) break;
+      dir = dir.parent.absolute;
+    }
+
+    DartdocOptions parent;
+    if (dir.parent.path != dir.path) {
+      parent = new DartdocOptions.fromDir(dir.parent);
+    } else {
+      parent = new _DefaultDartdocOptions();
+    }
+    if (f.existsSync()) {
+      return new _FileDartdocOptions(f);
+    }
+    return parent;
+  }
+}
+
+class _DefaultDartdocOptions extends DartdocOptions {
+  _DefaultDartdocOptions() : super();
+
+  @override
+  String get _path => '<default>';
+
+  @override
+  List<String> get categoryOrder => new List.unmodifiable([]);
+}
+
+class _FileDartdocOptions extends DartdocOptions {
+  File dartdocOptionsFile;
+  Map _dartdocOptions;
+  _FileDartdocOptions(this.dartdocOptionsFile) : super() {
+    Map allDartdocOptions = loadYaml(dartdocOptionsFile.readAsStringSync());
+    if (allDartdocOptions.containsKey('dartdoc')) {
+      _dartdocOptions = allDartdocOptions['dartdoc'];
+    } else {
+      _dartdocOptions = {};
+      logWarning("${_path}: must contain 'dartdoc' section");
+    }
+  }
+
+  @override
+  String get _path => dartdocOptionsFile.path;
+
+  List<String> _categoryOrder;
+  @override
+  List<String> get categoryOrder {
+    if (_categoryOrder == null) {
+      _categoryOrder = [];
+      if (_dartdocOptions.containsKey('categoryOrder')) {
+        if (_dartdocOptions['categoryOrder'] is YamlList) {
+          _categoryOrder.addAll(_dartdocOptions['categoryOrder']);
+        } else {
+          logWarning("${_path}: categoryOrder must be a list (ignoring)");
+        }
+      }
+      _categoryOrder = new List.unmodifiable(_categoryOrder);
+    }
+    return _categoryOrder;
+  }
+}
diff --git a/lib/src/html/html_generator.dart b/lib/src/html/html_generator.dart
index d8916ff..012eda0 100644
--- a/lib/src/html/html_generator.dart
+++ b/lib/src/html/html_generator.dart
@@ -7,7 +7,7 @@
 import 'dart:async' show Future, StreamController, Stream;
 import 'dart:io' show File;
 
-import 'package:path/path.dart' as p;
+import 'package:path/path.dart' as pathLib;
 
 import '../generator.dart';
 import '../model.dart';
@@ -82,7 +82,7 @@
       // docs somehow.  Check data.self.isCanonical and callers for bugs.
       assert(allowOverwrite || !writtenFiles.contains(filePath));
 
-      var file = new File(p.join(outputDirectoryPath, filePath));
+      var file = new File(pathLib.join(outputDirectoryPath, filePath));
       var parent = file.parent;
       if (!parent.existsSync()) {
         parent.createSync(recursive: true);
diff --git a/lib/src/html/html_generator_instance.dart b/lib/src/html/html_generator_instance.dart
index 80b87c6..cbbfdb4 100644
--- a/lib/src/html/html_generator_instance.dart
+++ b/lib/src/html/html_generator_instance.dart
@@ -8,7 +8,7 @@
 
 import 'package:collection/collection.dart' show compareNatural;
 import 'package:dartdoc/src/model_utils.dart';
-import 'package:path/path.dart' as path;
+import 'package:path/path.dart' as pathLib;
 
 import '../logging.dart';
 import '../model.dart';
@@ -41,7 +41,7 @@
     if (_options.faviconPath != null) {
       var bytes = new File(_options.faviconPath).readAsBytesSync();
       // Allow overwrite of favicon.
-      _writer(path.join('static-assets', 'favicon.png'), bytes,
+      _writer(pathLib.join('static-assets', 'favicon.png'), bytes,
           allowOverwrite: true);
     }
   }
@@ -81,7 +81,7 @@
     });
 
     String json = encoder.convert(indexItems);
-    _writer(path.join('index.json'), '${json}\n');
+    _writer(pathLib.join('index.json'), '${json}\n');
   }
 
   void _generateDocs() {
@@ -177,14 +177,15 @@
     }
     TemplateData data = new LibraryTemplateData(_options, packageGraph, lib);
 
-    _build(path.join(lib.dirName, '${lib.fileName}'),
+    _build(pathLib.join(lib.dirName, '${lib.fileName}'),
         _templates.libraryTemplate, data);
   }
 
   void generateClass(PackageGraph packageGraph, Library lib, Class clazz) {
     TemplateData data =
         new ClassTemplateData(_options, packageGraph, lib, clazz);
-    _build(path.joinAll(clazz.href.split('/')), _templates.classTemplate, data);
+    _build(
+        pathLib.joinAll(clazz.href.split('/')), _templates.classTemplate, data);
   }
 
   void generateConstructor(PackageGraph packageGraph, Library lib, Class clazz,
@@ -192,14 +193,15 @@
     TemplateData data = new ConstructorTemplateData(
         _options, packageGraph, lib, clazz, constructor);
 
-    _build(path.joinAll(constructor.href.split('/')),
+    _build(pathLib.joinAll(constructor.href.split('/')),
         _templates.constructorTemplate, data);
   }
 
   void generateEnum(PackageGraph packageGraph, Library lib, Enum eNum) {
     TemplateData data = new EnumTemplateData(_options, packageGraph, lib, eNum);
 
-    _build(path.joinAll(eNum.href.split('/')), _templates.enumTemplate, data);
+    _build(
+        pathLib.joinAll(eNum.href.split('/')), _templates.enumTemplate, data);
   }
 
   void generateFunction(
@@ -207,8 +209,8 @@
     TemplateData data =
         new FunctionTemplateData(_options, packageGraph, lib, function);
 
-    _build(path.joinAll(function.href.split('/')), _templates.functionTemplate,
-        data);
+    _build(pathLib.joinAll(function.href.split('/')),
+        _templates.functionTemplate, data);
   }
 
   void generateMethod(
@@ -216,8 +218,8 @@
     TemplateData data =
         new MethodTemplateData(_options, packageGraph, lib, clazz, method);
 
-    _build(
-        path.joinAll(method.href.split('/')), _templates.methodTemplate, data);
+    _build(pathLib.joinAll(method.href.split('/')), _templates.methodTemplate,
+        data);
   }
 
   void generateConstant(
@@ -225,8 +227,8 @@
     TemplateData data =
         new ConstantTemplateData(_options, packageGraph, lib, clazz, property);
 
-    _build(path.joinAll(property.href.split('/')), _templates.constantTemplate,
-        data);
+    _build(pathLib.joinAll(property.href.split('/')),
+        _templates.constantTemplate, data);
   }
 
   void generateProperty(
@@ -234,8 +236,8 @@
     TemplateData data =
         new PropertyTemplateData(_options, packageGraph, lib, clazz, property);
 
-    _build(path.joinAll(property.href.split('/')), _templates.propertyTemplate,
-        data);
+    _build(pathLib.joinAll(property.href.split('/')),
+        _templates.propertyTemplate, data);
   }
 
   void generateTopLevelProperty(
@@ -243,7 +245,7 @@
     TemplateData data =
         new TopLevelPropertyTemplateData(_options, packageGraph, lib, property);
 
-    _build(path.joinAll(property.href.split('/')),
+    _build(pathLib.joinAll(property.href.split('/')),
         _templates.topLevelPropertyTemplate, data);
   }
 
@@ -252,7 +254,7 @@
     TemplateData data =
         new TopLevelConstTemplateData(_options, packageGraph, lib, property);
 
-    _build(path.joinAll(property.href.split('/')),
+    _build(pathLib.joinAll(property.href.split('/')),
         _templates.topLevelConstantTemplate, data);
   }
 
@@ -261,7 +263,7 @@
     TemplateData data =
         new TypedefTemplateData(_options, packageGraph, lib, typeDef);
 
-    _build(path.joinAll(typeDef.href.split('/')), _templates.typeDefTemplate,
+    _build(pathLib.joinAll(typeDef.href.split('/')), _templates.typeDefTemplate,
         data);
   }
 
@@ -274,7 +276,7 @@
             'encountered $resourcePath');
       }
       String destFileName = resourcePath.substring(prefix.length);
-      _writer(path.join('static-assets', destFileName),
+      _writer(pathLib.join('static-assets', destFileName),
           await loader.loadAsBytes(resourcePath));
     }
   }
diff --git a/lib/src/io_utils.dart b/lib/src/io_utils.dart
index f99a69d..5e98556 100644
--- a/lib/src/io_utils.dart
+++ b/lib/src/io_utils.dart
@@ -9,7 +9,7 @@
 import 'dart:convert';
 import 'dart:io';
 
-import 'package:path/path.dart' as path;
+import 'package:path/path.dart' as pathLib;
 
 /// Lists the contents of [dir].
 ///
@@ -36,7 +36,7 @@
 
     for (var entity in listDir(new Directory(dir))) {
       // Skip hidden files and directories
-      if (path.basename(entity.path).startsWith('.')) {
+      if (pathLib.basename(entity.path).startsWith('.')) {
         continue;
       }
 
diff --git a/lib/src/model.dart b/lib/src/model.dart
index 70dd661..33d5ab4 100644
--- a/lib/src/model.dart
+++ b/lib/src/model.dart
@@ -36,10 +36,12 @@
     show ExecutableMember, Member, ParameterMember;
 import 'package:analyzer/src/dart/analysis/driver.dart';
 import 'package:collection/collection.dart';
+import 'package:dartdoc/src/dartdoc_options.dart';
 import 'package:dartdoc/src/io_utils.dart';
+import 'package:dartdoc/src/sdk.dart';
 import 'package:front_end/src/byte_store/byte_store.dart';
 import 'package:front_end/src/base/performance_logger.dart';
-import 'package:path/path.dart' as p;
+import 'package:path/path.dart' as pathLib;
 import 'package:tuple/tuple.dart';
 import 'package:package_config/discovery.dart' as package_config;
 
@@ -59,7 +61,7 @@
   if (__crossdartJson == null) {
     if (config != null) {
       var crossdartFile =
-          new File(p.join(config.inputDir.path, "crossdart.json"));
+          new File(pathLib.join(config.inputDir.path, "crossdart.json"));
       if (crossdartFile.existsSync()) {
         __crossdartJson = json.decode(crossdartFile.readAsStringSync())
             as Map<String, Map<String, List<Map<String, dynamic>>>>;
@@ -2159,12 +2161,12 @@
     if (name.startsWith('file:')) {
       // restoreUri doesn't do anything for the package we're documenting.
       String canonicalPackagePath =
-          '${p.canonicalize(defaultPackage.dir.path)}${p.separator}lib${p.separator}';
+          '${pathLib.canonicalize(defaultPackage.dir.path)}${pathLib.separator}lib${pathLib.separator}';
       String canonicalElementPath =
-          p.canonicalize(element.source.uri.toFilePath());
+          pathLib.canonicalize(element.source.uri.toFilePath());
       assert(canonicalElementPath.startsWith(canonicalPackagePath));
-      List<String> pathSegments = [defaultPackage.name]..addAll(
-          p.split(canonicalElementPath.replaceFirst(canonicalPackagePath, '')));
+      List<String> pathSegments = [defaultPackage.name]..addAll(pathLib
+          .split(canonicalElementPath.replaceFirst(canonicalPackagePath, '')));
       Uri libraryUri = new Uri(
         scheme: 'package',
         pathSegments: pathSegments,
@@ -2185,10 +2187,10 @@
 
   static PackageMeta getPackageMeta(LibraryElement element) {
     String sourcePath = element.source.fullName;
-    File file = new File(p.canonicalize(sourcePath));
+    File file = new File(pathLib.canonicalize(sourcePath));
     Directory dir = file.parent;
     while (dir.parent.path != dir.path && dir.existsSync()) {
-      File pubspec = new File(p.join(dir.path, 'pubspec.yaml'));
+      File pubspec = new File(pathLib.join(dir.path, 'pubspec.yaml'));
       if (pubspec.existsSync()) {
         return new PackageMeta.fromDir(dir);
       }
@@ -2972,9 +2974,9 @@
   String get elementLocation {
     // Call nothing from here that can emit warnings or you'll cause stack overflows.
     if (lineAndColumn != null) {
-      return "(${p.toUri(sourceFileName)}:${lineAndColumn.item1}:${lineAndColumn.item2})";
+      return "(${pathLib.toUri(sourceFileName)}:${lineAndColumn.item1}:${lineAndColumn.item2})";
     }
-    return "(${p.toUri(sourceFileName)})";
+    return "(${pathLib.toUri(sourceFileName)})";
   }
 
   /// Returns a link to extended documentation, or the empty string if that
@@ -3462,11 +3464,12 @@
     RegExp exampleRE = new RegExp(r'{@example\s+([^}]+)}');
     return rawdocs.replaceAllMapped(exampleRE, (match) {
       var args = _getExampleArgs(match[1]);
-      var lang = args['lang'] ?? p.extension(args['src']).replaceFirst('.', '');
+      var lang =
+          args['lang'] ?? pathLib.extension(args['src']).replaceFirst('.', '');
 
       var replacement = match[0]; // default to fully matched string.
 
-      var fragmentFile = new File(p.join(dirPath, args['file']));
+      var fragmentFile = new File(pathLib.join(dirPath, args['file']));
       if (fragmentFile.existsSync()) {
         replacement = fragmentFile.readAsStringSync();
         if (!lang.isEmpty) {
@@ -3566,14 +3569,14 @@
     var file = src + fragExtension;
     var region = args['region'] ?? '';
     if (!region.isEmpty) {
-      var dir = p.dirname(src);
-      var basename = p.basenameWithoutExtension(src);
-      var ext = p.extension(src);
-      file = p.join(dir, '$basename-$region$ext$fragExtension');
+      var dir = pathLib.dirname(src);
+      var basename = pathLib.basenameWithoutExtension(src);
+      var ext = pathLib.extension(src);
+      file = pathLib.join(dir, '$basename-$region$ext$fragExtension');
     }
     args['file'] = config?.examplePathPrefix == null
         ? file
-        : p.join(config.examplePathPrefix, file);
+        : pathLib.join(config.examplePathPrefix, file);
     return args;
   }
 }
@@ -4480,15 +4483,60 @@
 
 class Package implements Comparable<Package> {
   final String name;
+
+  // Initialized by [PackageGraph.PackageGraph].
   final List<Library> _libraries = [];
   PackageGraph packageGraph;
 
   Package(this.name, this.packageGraph);
 
+  DartdocOptions _dartdocOptions;
+  DartdocOptions get dartdocOptions {
+    if (_dartdocOptions == null) {
+      _dartdocOptions = new DartdocOptions.fromDir(new Directory(packagePath));
+    }
+    return _dartdocOptions;
+  }
+
+  bool get isSdk => packageMeta.isSdk;
+
+  String _packagePath;
+  String get packagePath {
+    if (_packagePath == null) {
+      if (isSdk) {
+        _packagePath = getSdkDir().path;
+      } else {
+        assert(_libraries.isNotEmpty);
+        File file = new File(
+            pathLib.canonicalize(_libraries.first.element.source.fullName));
+        Directory dir = file.parent;
+        while (dir.parent.path != dir.path && dir.existsSync()) {
+          File pubspec = new File(pathLib.join(dir.path, 'pubspec.yaml'));
+          if (pubspec.existsSync()) {
+            _packagePath = dir.absolute.path;
+            break;
+          }
+          dir = dir.parent;
+        }
+      }
+    }
+    return _packagePath;
+  }
+
   List<Library> get libraries => _libraries;
 
   Iterable<Library> get publicLibraries => filterNonPublic(libraries);
 
+  PackageMeta _packageMeta;
+  // TODO(jcollins-g): packageMeta should be passed in with the object rather
+  // than calculated indirectly from libraries.
+  PackageMeta get packageMeta {
+    if (_packageMeta == null) {
+      _packageMeta = _libraries.first.packageMeta;
+    }
+    return _packageMeta;
+  }
+
   @override
   String toString() => name;
 
@@ -4901,7 +4949,7 @@
   String toString() => element.name;
 }
 
-/// Everything you need to instantiate a Package object for documenting.
+/// Everything you need to instantiate a PackageGraph object for documenting.
 class PackageBuilder {
   final bool autoIncludeDependencies;
   final List<String> excludes;
@@ -5124,25 +5172,25 @@
   /// library files in the "lib" directory to document.
   Iterable<String> findFilesToDocumentInPackage(
       String basePackageDir, bool autoIncludeDependencies) sync* {
-    final String sep = p.separator;
+    final String sep = pathLib.separator;
 
     Set<String> packageDirs = new Set()..add(basePackageDir);
 
     if (autoIncludeDependencies) {
       Map<String, Uri> info = package_config
           .findPackagesFromFile(
-              new Uri.file(p.join(basePackageDir, 'pubspec.yaml')))
+              new Uri.file(pathLib.join(basePackageDir, 'pubspec.yaml')))
           .asMap();
       for (String packageName in info.keys) {
         if (!excludes.contains(packageName)) {
-          packageDirs.add(p.dirname(info[packageName].toFilePath()));
+          packageDirs.add(pathLib.dirname(info[packageName].toFilePath()));
         }
       }
     }
 
     for (String packageDir in packageDirs) {
-      var packageLibDir = p.join(packageDir, 'lib');
-      var packageLibSrcDir = p.join(packageLibDir, 'src');
+      var packageLibDir = pathLib.join(packageDir, 'lib');
+      var packageLibSrcDir = pathLib.join(packageLibDir, 'src');
       // To avoid analyzing package files twice, only files with paths not
       // containing '/packages' will be added. The only exception is if the file
       // to analyze already has a '/package' in its path.
@@ -5152,8 +5200,8 @@
             (!lib.contains('${sep}packages${sep}') ||
                 packageDir.contains('${sep}packages${sep}'))) {
           // Only include libraries within the lib dir that are not in lib/src
-          if (p.isWithin(packageLibDir, lib) &&
-              !p.isWithin(packageLibSrcDir, lib)) {
+          if (pathLib.isWithin(packageLibDir, lib) &&
+              !pathLib.isWithin(packageLibSrcDir, lib)) {
             // Only add the file if it does not contain 'part of'
             var contents = new File(lib).readAsStringSync();
 
@@ -5218,11 +5266,11 @@
     var entities = dir.listSync();
 
     var pubspec = entities.firstWhere(
-        (e) => e is File && p.basename(e.path) == 'pubspec.yaml',
+        (e) => e is File && pathLib.basename(e.path) == 'pubspec.yaml',
         orElse: () => null);
 
     var libDir = entities.firstWhere(
-        (e) => e is Directory && p.basename(e.path) == 'lib',
+        (e) => e is Directory && pathLib.basename(e.path) == 'lib',
         orElse: () => null);
 
     if (pubspec != null && libDir != null) {
diff --git a/lib/src/package_meta.dart b/lib/src/package_meta.dart
index e8f2739..46e8bd8 100644
--- a/lib/src/package_meta.dart
+++ b/lib/src/package_meta.dart
@@ -6,7 +6,7 @@
 
 import 'dart:io';
 
-import 'package:path/path.dart' as path;
+import 'package:path/path.dart' as pathLib;
 import 'package:yaml/yaml.dart';
 
 import 'logging.dart';
@@ -81,7 +81,7 @@
   Map _pubspec;
 
   _FilePackageMeta(Directory dir) : super(dir) {
-    File f = new File(path.join(dir.path, 'pubspec.yaml'));
+    File f = new File(pathLib.join(dir.path, 'pubspec.yaml'));
     if (f.existsSync()) {
       _pubspec = loadYaml(f.readAsStringSync());
     } else {
@@ -94,12 +94,12 @@
 
   @override
   bool get needsPubGet =>
-      !(new File(path.join(dir.path, '.packages')).existsSync());
+      !(new File(pathLib.join(dir.path, '.packages')).existsSync());
 
   @override
   void runPubGet() {
     String pubPath =
-        path.join(path.dirname(Platform.resolvedExecutable), 'pub');
+        pathLib.join(pathLib.dirname(Platform.resolvedExecutable), 'pub');
     if (Platform.isWindows) pubPath += '.bat';
 
     ProcessResult result =
@@ -174,7 +174,7 @@
 
   for (String name in fileNames) {
     for (File f in files) {
-      String baseName = path.basename(f.path).toLowerCase();
+      String baseName = pathLib.basename(f.path).toLowerCase();
       if (baseName == name) return f;
       if (baseName.startsWith(name)) return f;
     }
@@ -201,7 +201,7 @@
   String get name => 'Dart SDK';
   @override
   String get version =>
-      new File(path.join(dir.path, 'version')).readAsStringSync().trim();
+      new File(pathLib.join(dir.path, 'version')).readAsStringSync().trim();
   @override
   String get description =>
       'The Dart SDK is a set of tools and libraries for the '
@@ -213,7 +213,7 @@
   FileContents getReadmeContents() {
     File f = sdkReadmePath != null
         ? new File(sdkReadmePath)
-        : new File(path.join(dir.path, 'lib', 'api_readme.md'));
+        : new File(pathLib.join(dir.path, 'lib', 'api_readme.md'));
     return f.existsSync() ? new FileContents(f) : null;
   }
 
diff --git a/test/compare_output_test.dart b/test/compare_output_test.dart
index 3a594d4..23a52b2 100644
--- a/test/compare_output_test.dart
+++ b/test/compare_output_test.dart
@@ -8,7 +8,7 @@
 import 'dart:io';
 import 'dart:mirrors';
 
-import 'package:path/path.dart' as path;
+import 'package:path/path.dart' as pathLib;
 import 'package:test/test.dart';
 
 const List<String> _filesToIgnore = const <String>['.DS_Store'];
@@ -21,17 +21,17 @@
     (reflect(main) as ClosureMirror).function.location.sourceUri;
 
 String get _testPackageDocsPath =>
-    path.fromUri(_currentFileUri.resolve('../testing/test_package_docs'));
+    pathLib.fromUri(_currentFileUri.resolve('../testing/test_package_docs'));
 
 String get _testPackagePath =>
-    path.fromUri(_currentFileUri.resolve('../testing/test_package'));
+    pathLib.fromUri(_currentFileUri.resolve('../testing/test_package'));
 
 void main() {
   group('compare outputs', () {
     Directory tempDir;
 
     var dartdocBin =
-        path.fromUri(_currentFileUri.resolve('../bin/dartdoc.dart'));
+        pathLib.fromUri(_currentFileUri.resolve('../bin/dartdoc.dart'));
 
     setUp(() {
       tempDir = Directory.systemTemp.createTempSync('dartdoc.test.');
@@ -101,8 +101,8 @@
             'diff',
             '--no-index',
             '--no-color',
-            path.join(_testPackageDocsPath, k),
-            path.join(tempDir.path, k)
+            pathLib.join(_testPackageDocsPath, k),
+            pathLib.join(tempDir.path, k)
           ];
           result = Process.runSync(gitBinName, args);
           assert(result.exitCode != 0);
@@ -186,7 +186,7 @@
 
     test('--footer-text includes text', () {
       String footerTextPath =
-          path.join(Directory.systemTemp.path, 'footer.txt');
+          pathLib.join(Directory.systemTemp.path, 'footer.txt');
       new File(footerTextPath).writeAsStringSync(' footer text include ');
 
       var args = <String>[
@@ -208,7 +208,7 @@
         fail('dartdoc failed');
       }
 
-      File outFile = new File(path.join(tempDir.path, 'index.html'));
+      File outFile = new File(pathLib.join(tempDir.path, 'index.html'));
       expect(outFile.readAsStringSync(), contains('footer text include'));
     });
 
@@ -242,18 +242,18 @@
     var type = match[1];
     var p = match[2];
 
-    if (_filesToIgnore.any((i) => path.basename(p) == i)) {
+    if (_filesToIgnore.any((i) => pathLib.basename(p) == i)) {
       continue;
     }
 
     if (type == 'A') {
-      expect(path.isWithin(tempPath, p), isTrue,
+      expect(pathLib.isWithin(tempPath, p), isTrue,
           reason: '`$p` should be within $tempPath');
-      p = path.relative(p, from: tempPath);
+      p = pathLib.relative(p, from: tempPath);
     } else {
-      expect(path.isWithin(sourcePath, p), isTrue,
+      expect(pathLib.isWithin(sourcePath, p), isTrue,
           reason: '`$p` should be within $sourcePath');
-      p = path.relative(p, from: sourcePath);
+      p = pathLib.relative(p, from: sourcePath);
     }
 
     values[p] = type;
diff --git a/test/dartdoc_options_test.dart b/test/dartdoc_options_test.dart
new file mode 100644
index 0000000..c878c13
--- /dev/null
+++ b/test/dartdoc_options_test.dart
@@ -0,0 +1,84 @@
+// Copyright (c) 2018, 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.
+
+library dartdoc.options_test;
+
+import 'dart:io';
+
+import 'package:dartdoc/src/dartdoc_options.dart';
+import 'package:path/path.dart' as pathLib;
+import 'package:test/test.dart';
+
+void main() {
+  Directory tempDir;
+  Directory firstDir;
+  Directory secondDir;
+  Directory secondDirFirstSub;
+  Directory secondDirSecondSub;
+
+  File dartdocOptionsOne;
+  File dartdocOptionsTwo;
+  File dartdocOptionsTwoFirstSub;
+
+  setUpAll(() {
+    tempDir = Directory.systemTemp.createTempSync('options_test');
+    firstDir = new Directory(pathLib.join(tempDir.path, 'firstDir'))
+      ..createSync();
+    secondDir = new Directory(pathLib.join(tempDir.path, 'secondDir'))
+      ..createSync();
+
+    secondDirFirstSub = new Directory(pathLib.join(secondDir.path, 'firstSub'))
+      ..createSync();
+    secondDirSecondSub =
+        new Directory(pathLib.join(secondDir.path, 'secondSub'))..createSync();
+
+    dartdocOptionsOne =
+        new File(pathLib.join(firstDir.path, 'dartdoc_options.yaml'));
+    dartdocOptionsTwo =
+        new File(pathLib.join(secondDir.path, 'dartdoc_options.yaml'));
+    dartdocOptionsTwoFirstSub =
+        new File(pathLib.join(secondDirFirstSub.path, 'dartdoc_options.yaml'));
+
+    dartdocOptionsOne.writeAsStringSync('''
+dartdoc:
+  categoryOrder: ['options_one']
+        ''');
+    dartdocOptionsTwo.writeAsStringSync('''
+dartdoc:
+  categoryOrder: ['options_two']
+        ''');
+    dartdocOptionsTwoFirstSub.writeAsStringSync('''
+dartdoc:
+  categoryOrder: ['options_two_first_sub']
+    ''');
+  });
+
+  tearDownAll(() {
+    tempDir.deleteSync(recursive: true);
+  });
+
+  group('dartdoc options', () {
+    group('options file finding and loading', () {
+      test('DartdocOptions loads defaults', () {
+        DartdocOptions options = new DartdocOptions.fromDir(tempDir);
+        expect(options.categoryOrder, isEmpty);
+      });
+
+      test('DartdocOptions loads a file', () {
+        DartdocOptions options = new DartdocOptions.fromDir(firstDir);
+        expect(options.categoryOrder, orderedEquals(['options_one']));
+      });
+
+      test('DartdocOptions loads a file in parent directories', () {
+        DartdocOptions options = new DartdocOptions.fromDir(secondDirSecondSub);
+        expect(options.categoryOrder, orderedEquals(['options_two']));
+      });
+
+      test('DartdocOptions loads the override file instead of parents', () {
+        DartdocOptions options = new DartdocOptions.fromDir(secondDirFirstSub);
+        expect(options.categoryOrder, orderedEquals(['options_two_first_sub']));
+      });
+    });
+  });
+}
diff --git a/test/dartdoc_test.dart b/test/dartdoc_test.dart
index a2e0238..85dc9f7 100644
--- a/test/dartdoc_test.dart
+++ b/test/dartdoc_test.dart
@@ -10,7 +10,7 @@
 import 'package:dartdoc/src/model.dart';
 import 'package:dartdoc/src/package_meta.dart';
 import 'package:dartdoc/src/sdk.dart';
-import 'package:path/path.dart' as path;
+import 'package:path/path.dart' as pathLib;
 import 'package:test/test.dart';
 
 import 'src/utils.dart';
@@ -28,7 +28,7 @@
       delete(tempDir);
     });
 
-    test('generate docs for ${path.basename(testPackageDir.path)} works',
+    test('generate docs for ${pathLib.basename(testPackageDir.path)} works',
         () async {
       PackageMeta meta = new PackageMeta.fromDir(testPackageDir);
       DartDoc dartdoc = new DartDoc(
@@ -43,7 +43,7 @@
       expect(p.publicLibraries, hasLength(10));
     });
 
-    test('generate docs for ${path.basename(testPackageBadDir.path)} fails',
+    test('generate docs for ${pathLib.basename(testPackageBadDir.path)} fails',
         () async {
       PackageMeta meta = new PackageMeta.fromDir(testPackageBadDir);
       DartDoc dartdoc = new DartDoc(
diff --git a/test/html_generator_test.dart b/test/html_generator_test.dart
index a459011..95d094f 100644
--- a/test/html_generator_test.dart
+++ b/test/html_generator_test.dart
@@ -9,7 +9,7 @@
 import 'package:dartdoc/src/html/html_generator.dart';
 import 'package:dartdoc/src/html/templates.dart';
 import 'package:dartdoc/src/html/resources.g.dart';
-import 'package:path/path.dart' as p;
+import 'package:path/path.dart' as pathLib;
 import 'package:test/test.dart';
 
 void main() {
@@ -87,12 +87,12 @@
 
       test('resources are put into the right place', () {
         Directory output =
-            new Directory(p.join(tempOutput.path, 'static-assets'));
+            new Directory(pathLib.join(tempOutput.path, 'static-assets'));
         expect(output, doesExist);
 
-        for (var resource in resource_names.map(
-            (r) => p.relative(Uri.parse(r).path, from: 'dartdoc/resources'))) {
-          expect(new File(p.join(output.path, resource)), doesExist);
+        for (var resource in resource_names.map((r) =>
+            pathLib.relative(Uri.parse(r).path, from: 'dartdoc/resources'))) {
+          expect(new File(pathLib.join(output.path, resource)), doesExist);
         }
       });
     });
diff --git a/test/model_test.dart b/test/model_test.dart
index 2b308cf..08d7c4f 100644
--- a/test/model_test.dart
+++ b/test/model_test.dart
@@ -10,7 +10,7 @@
 import 'package:dartdoc/src/model.dart';
 import 'package:dartdoc/src/warnings.dart';
 import 'package:dartdoc/src/sdk.dart';
-import 'package:path/path.dart' as p;
+import 'package:path/path.dart' as pathLib;
 import 'package:test/test.dart';
 
 import 'src/utils.dart' as utils;
@@ -1365,7 +1365,8 @@
     });
 
     tearDown(() {
-      var file = new File(p.join(Directory.current.path, "crossdart.json"));
+      var file =
+          new File(pathLib.join(Directory.current.path, "crossdart.json"));
       if (file.existsSync()) {
         file.deleteSync();
       }
@@ -1492,7 +1493,7 @@
       expect(offset, isNonNegative,
           reason: "Can't find convertToMap function in ${fakePath}");
       if (Platform.isWindows) fakePath = fakePath.replaceAll('/', r'\\');
-      new File(p.join(Directory.current.path, "crossdart.json"))
+      new File(pathLib.join(Directory.current.path, "crossdart.json"))
           .writeAsStringSync("""
               {"$fakePath":
                 {"references":[{"offset":${offset},"end":${offset+3},"remotePath":"http://www.example.com/fake.dart"}]}}
diff --git a/test/package_meta_test.dart b/test/package_meta_test.dart
index 91d1d32..12e131f 100644
--- a/test/package_meta_test.dart
+++ b/test/package_meta_test.dart
@@ -8,7 +8,7 @@
 
 import 'package:dartdoc/src/package_meta.dart';
 import 'package:dartdoc/src/sdk.dart';
-import 'package:path/path.dart' as path;
+import 'package:path/path.dart' as pathLib;
 import 'package:test/test.dart';
 
 void main() {
@@ -16,8 +16,8 @@
     PackageMeta p;
 
     setUp(() {
-      var d = new Directory(
-          path.join(Directory.current.path, 'testing/test_package_not_valid'));
+      var d = new Directory(pathLib.join(
+          Directory.current.path, 'testing/test_package_not_valid'));
       if (!d.existsSync()) {
         throw "$d cannot be found";
       }
diff --git a/test/src/utils.dart b/test/src/utils.dart
index c8710c0..0b8e3e2 100644
--- a/test/src/utils.dart
+++ b/test/src/utils.dart
@@ -12,7 +12,7 @@
 import 'package:dartdoc/src/model.dart';
 import 'package:dartdoc/src/package_meta.dart';
 import 'package:dartdoc/src/sdk.dart';
-import 'package:path/path.dart' as p;
+import 'package:path/path.dart' as pathLib;
 
 Directory sdkDir;
 PackageMeta sdkPackageMeta;
@@ -48,7 +48,7 @@
 }
 
 Future<PackageGraph> bootSdkPackage() {
-  Directory dir = new Directory(p.current);
+  Directory dir = new Directory(pathLib.current);
   return new PackageBuilder(
           dir, [], [], sdkDir, sdkPackageMeta, [], [], true, false)
       .buildPackageGraph();
diff --git a/test/template_test.dart b/test/template_test.dart
index 359093e..dea263b 100644
--- a/test/template_test.dart
+++ b/test/template_test.dart
@@ -7,7 +7,7 @@
 import 'dart:io';
 
 import 'package:mustache4dart/mustache4dart.dart';
-import 'package:path/path.dart' as p;
+import 'package:path/path.dart' as pathLib;
 import 'package:test/test.dart';
 
 void main() {
@@ -17,7 +17,8 @@
 
       setUp(() {
         if (sitemap == null) {
-          var templatePath = p.join(p.current, 'lib/templates/sitemap.xml');
+          var templatePath =
+              pathLib.join(pathLib.current, 'lib/templates/sitemap.xml');
           File tmplFile = new File(templatePath);
           var siteMapTmpl = tmplFile.readAsStringSync();
           sitemap = compile(siteMapTmpl);
diff --git a/testing/test_package/lib/example.dart b/testing/test_package/lib/example.dart
index 6334920..c7664db 100644
--- a/testing/test_package/lib/example.dart
+++ b/testing/test_package/lib/example.dart
@@ -73,7 +73,7 @@
   ParameterizedTypedef<T> aInheritedTypedefReturningMethod();
   AnotherParameterizedClass<T> aInheritedField;
   AnotherParameterizedClass<T> get aInheritedGetter;
-  ParameterizedClass<T> operator+ (ParameterizedClass<T> other);
+  ParameterizedClass<T> operator +(ParameterizedClass<T> other);
   set aInheritedSetter(AnotherParameterizedClass<T> thingToSet);
 }
 
diff --git a/testing/test_package/lib/fake.dart b/testing/test_package/lib/fake.dart
index ad099b5..5dccf53 100644
--- a/testing/test_package/lib/fake.dart
+++ b/testing/test_package/lib/fake.dart
@@ -89,7 +89,6 @@
   Map<X, Y> convertToMap() => null;
 }
 
-
 /// This is a class with a table.
 ///
 /// It has multiple sentences before the table.  Because testing is a good
@@ -124,7 +123,6 @@
   void aMethod(String parameter) {}
 }
 
-
 Map<dynamic, String> mapWithDynamicKeys = {};
 
 /// Useful for annotations.
@@ -344,10 +342,12 @@
   String get documentedPartialFieldInSubclassOnly => "overridden getter";
 
   @override
+
   /// Docs for setter of implicitGetterExplicitSetter.
   set implicitGetterExplicitSetter(String x) {}
 
   @override
+
   /// Getter doc for explicitGetterImplicitSetter
   List<int> get explicitGetterImplicitSetter => new List<int>();
 
@@ -760,6 +760,7 @@
 }
 
 class InheritingClassOne extends _PrivateClassDefiningSomething {}
+
 class InheritingClassTwo extends _PrivateClassDefiningSomething {}
 
 class ReferringClass {
@@ -774,11 +775,12 @@
 /// Test an edge case for cases where inherited ExecutableElements can come
 /// both from private classes and public interfaces.  The test makes sure the
 /// class still takes precedence (#1561).
-abstract class MIEEMixinWithOverride<K, V> = MIEEBase<K, V> with _MIEEPrivateOverride<K, V>;
+abstract class MIEEMixinWithOverride<K, V> = MIEEBase<K, V>
+    with _MIEEPrivateOverride<K, V>;
 
 abstract class _MIEEPrivateOverride<K, V> implements MIEEThing<K, V> {
   // ignore: annotate_overrides
-  void operator[]=(K key, V value) {
+  void operator []=(K key, V value) {
     throw new UnsupportedError("Never use this");
   }
 }
@@ -791,5 +793,5 @@
 }
 
 abstract class MIEEThing<K, V> {
-  void operator[]=(K key, V value);
-}
\ No newline at end of file
+  void operator []=(K key, V value);
+}
diff --git a/tool/grind.dart b/tool/grind.dart
index 1f09cd1..452210a 100644
--- a/tool/grind.dart
+++ b/tool/grind.dart
@@ -8,7 +8,7 @@
 import 'package:dartdoc/src/io_utils.dart';
 import 'package:dartdoc/src/model_utils.dart';
 import 'package:grinder/grinder.dart';
-import 'package:path/path.dart' as path;
+import 'package:path/path.dart' as pathLib;
 import 'package:yaml/yaml.dart' as yaml;
 
 main([List<String> args]) => grind(args);
@@ -24,7 +24,7 @@
 Directory get sdkDocsDir => tempdirsCache.memoized1(createTempSync, 'sdkdocs');
 Directory get flutterDir => tempdirsCache.memoized1(createTempSync, 'flutter');
 Directory get testPackage =>
-    new Directory(path.joinAll(['testing', 'test_package']));
+    new Directory(pathLib.joinAll(['testing', 'test_package']));
 Directory get testPackageDocsDir =>
     tempdirsCache.memoized1(createTempSync, 'test_package');
 
@@ -39,13 +39,14 @@
 }
 
 final Directory flutterDirDevTools =
-    new Directory(path.join(flutterDir.path, 'dev', 'tools'));
+    new Directory(pathLib.join(flutterDir.path, 'dev', 'tools'));
 
 /// Creates a throwaway pub cache and returns the environment variables
 /// necessary to use it.
 Map<String, String> _createThrowawayPubCache() {
   final Directory pubCache = Directory.systemTemp.createTempSync('pubcache');
-  final Directory pubCacheBin = new Directory(path.join(pubCache.path, 'bin'));
+  final Directory pubCacheBin =
+      new Directory(pathLib.join(pubCache.path, 'bin'));
   pubCacheBin.createSync();
   return new Map.fromIterables([
     'PUB_CACHE',
@@ -233,7 +234,7 @@
       Platform.resolvedExecutable,
       [
         '--checked',
-        path.join('bin', 'dartdoc.dart'),
+        pathLib.join('bin', 'dartdoc.dart'),
         '--output',
         '${sdkDocsPath}',
         '--sdk-docs',
@@ -257,7 +258,7 @@
       Platform.resolvedExecutable,
       [
         '--checked',
-        path.join(cwd, 'bin', 'dartdoc.dart'),
+        pathLib.join(cwd, 'bin', 'dartdoc.dart'),
         '--output',
         outputDir,
         '--auto-include-dependencies',
@@ -351,7 +352,7 @@
       '--port',
       '9000',
       '--path',
-      path.join(originalDartdocFlutter.absolute.path, 'dev', 'docs', 'doc'),
+      pathLib.join(originalDartdocFlutter.absolute.path, 'dev', 'docs', 'doc'),
     ]);
     Future current = launcher.runStreamed(sdkBin('pub'), [
       'run',
@@ -359,7 +360,7 @@
       '--port',
       '9001',
       '--path',
-      path.join(flutterDir.absolute.path, 'dev', 'docs', 'doc'),
+      pathLib.join(flutterDir.absolute.path, 'dev', 'docs', 'doc'),
     ]);
     await Future.wait([original, current]);
   }
@@ -377,7 +378,7 @@
     '--port',
     '8001',
     '--path',
-    path.join(flutterDir.path, 'dev', 'docs', 'doc'),
+    pathLib.join(flutterDir.path, 'dev', 'docs', 'doc'),
   ]);
 }
 
@@ -387,26 +388,26 @@
   Map<String, String> env = _createThrowawayPubCache();
   await _buildFlutterDocs(
       flutterDir.path, new Future.value(Directory.current.path), env);
-  String index =
-      new File(path.join(flutterDir.path, 'dev', 'docs', 'doc', 'index.html'))
-          .readAsStringSync();
+  String index = new File(
+          pathLib.join(flutterDir.path, 'dev', 'docs', 'doc', 'index.html'))
+      .readAsStringSync();
   stdout.write(index);
 }
 
 Future<List<Map>> _buildFlutterDocs(
     String flutterPath, Future<String> futureCwd, Map<String, String> env,
     [String label]) async {
-  env['PATH'] = '${path.join(flutterPath, "bin")}:${env['PATH']}';
+  env['PATH'] = '${pathLib.join(flutterPath, "bin")}:${env['PATH']}';
   var launcher = new SubprocessLauncher(
       'build-flutter-docs${label == null ? "" : "-$label"}', env);
   await launcher.runStreamed(
       'git', ['clone', 'https://github.com/flutter/flutter.git', '.'],
       workingDirectory: flutterPath);
-  String flutterBin = path.join('bin', 'flutter');
+  String flutterBin = pathLib.join('bin', 'flutter');
   String flutterCacheDart =
-      path.join(flutterPath, 'bin', 'cache', 'dart-sdk', 'bin', 'dart');
+      pathLib.join(flutterPath, 'bin', 'cache', 'dart-sdk', 'bin', 'dart');
   String flutterCachePub =
-      path.join(flutterPath, 'bin', 'cache', 'dart-sdk', 'bin', 'pub');
+      pathLib.join(flutterPath, 'bin', 'cache', 'dart-sdk', 'bin', 'pub');
   await launcher.runStreamed(
     flutterBin,
     ['--version'],
@@ -420,14 +421,14 @@
   await launcher.runStreamed(
     flutterCachePub,
     ['get'],
-    workingDirectory: path.join(flutterPath, 'dev', 'tools'),
+    workingDirectory: pathLib.join(flutterPath, 'dev', 'tools'),
   );
   await launcher.runStreamed(
       flutterCachePub, ['global', 'activate', '-spath', '.'],
       workingDirectory: await futureCwd);
   return await launcher.runStreamed(
     flutterCacheDart,
-    [path.join('dev', 'tools', 'dartdoc.dart'), '-c', '--json'],
+    [pathLib.join('dev', 'tools', 'dartdoc.dart'), '-c', '--json'],
     workingDirectory: flutterPath,
   );
 }
@@ -443,8 +444,8 @@
   if (version != null) args.addAll(<String>['-v', version]);
   args.add(pubPackageName);
   await launcher.runStreamed('pub', args);
-  Directory cache =
-      new Directory(path.join(env['PUB_CACHE'], 'hosted', 'pub.dartlang.org'));
+  Directory cache = new Directory(
+      pathLib.join(env['PUB_CACHE'], 'hosted', 'pub.dartlang.org'));
   Directory pubPackageDir =
       cache.listSync().firstWhere((e) => e.path.contains(pubPackageName));
   await launcher.runStreamed('pub', ['get'],
@@ -453,12 +454,12 @@
       Platform.resolvedExecutable,
       [
         '--checked',
-        path.join(Directory.current.absolute.path, 'bin', 'dartdoc.dart'),
+        pathLib.join(Directory.current.absolute.path, 'bin', 'dartdoc.dart'),
         '--json',
         '--show-progress',
       ],
       workingDirectory: pubPackageDir.absolute.path);
-  return path.join(pubPackageDir.absolute.path, 'doc', 'api');
+  return pathLib.join(pubPackageDir.absolute.path, 'doc', 'api');
 }
 
 @Task(
@@ -545,12 +546,13 @@
 
 @Task('Make sure all the resource files are present')
 indexResources() {
-  var sourcePath = path.join('lib', 'resources');
+  var sourcePath = pathLib.join('lib', 'resources');
   if (!new Directory(sourcePath).existsSync()) {
     throw new StateError('lib/resources directory not found');
   }
-  var outDir = new Directory(path.join('lib'));
-  var out = new File(path.join(outDir.path, 'src', 'html', 'resources.g.dart'));
+  var outDir = new Directory(pathLib.join('lib'));
+  var out =
+      new File(pathLib.join(outDir.path, 'src', 'html', 'resources.g.dart'));
   out.createSync(recursive: true);
   var buffer = new StringBuffer()
     ..write('// WARNING: This file is auto-generated. Do not taunt.\n\n')
@@ -596,8 +598,8 @@
 updateTestPackageDocs() async {
   var launcher = new SubprocessLauncher('update-test-package-docs');
   var testPackageDocs =
-      new Directory(path.join('testing', 'test_package_docs'));
-  var testPackage = new Directory(path.join('testing', 'test_package'));
+      new Directory(pathLib.join('testing', 'test_package_docs'));
+  var testPackage = new Directory(pathLib.join('testing', 'test_package'));
   await launcher.runStreamed(sdkBin('pub'), ['get'],
       workingDirectory: testPackage.path);
   delete(testPackageDocs);
@@ -607,7 +609,7 @@
       Platform.resolvedExecutable,
       [
         '--checked',
-        path.join('..', '..', 'bin', 'dartdoc.dart'),
+        pathLib.join('..', '..', 'bin', 'dartdoc.dart'),
         '--auto-include-dependencies',
         '--example-path-prefix',
         'examples',
@@ -649,8 +651,8 @@
   }
   log('$libsLength dart: libraries found');
 
-  var futureConstFile =
-      joinFile(sdkDocsDir, [path.join('dart-async', 'Future', 'Future.html')]);
+  var futureConstFile = joinFile(
+      sdkDocsDir, [pathLib.join('dart-async', 'Future', 'Future.html')]);
   if (!futureConstFile.existsSync()) {
     fail('no Future.html found for dart:async Future constructor');
   }