diff --git a/lib/src/dartdoc_options.dart b/lib/src/dartdoc_options.dart index a13ff2afe1..e1861212b1 100644 --- a/lib/src/dartdoc_options.dart +++ b/lib/src/dartdoc_options.dart @@ -481,15 +481,15 @@ class _OptionValueWithContext { T get resolvedValue { if (value is List) { return (value as List) - .map((v) => pathContext.canonicalize(resolveTildePath(v))) + .map((v) => pathContext.canonicalizeWithTilde(v)) .cast() .toList() as T; } else if (value is String) { - return pathContext.canonicalize(resolveTildePath(value as String)) as T; + return pathContext.canonicalizeWithTilde(value as String) as T; } else if (value is Map) { return (value as Map) .map((String key, String value) { - return MapEntry(key, pathContext.canonicalize(resolveTildePath(value))); + return MapEntry(key, pathContext.canonicalizeWithTilde(value)); }) as T; } else { throw UnsupportedError('Type $T is not supported for resolvedValue'); @@ -1599,8 +1599,9 @@ Future>> createDartdocOptions( // to set the flutter root. DartdocOptionSyntheticOnly( 'flutterRoot', - (DartdocSyntheticOption option, Folder dir) => - resolveTildePath(Platform.environment['FLUTTER_ROOT']), + (DartdocSyntheticOption option, Folder dir) => resourceProvider + .pathContext + .resolveTildePath(Platform.environment['FLUTTER_ROOT']), resourceProvider, optionIs: OptionKind.dir, help: 'Root of the Flutter SDK, specified from environment.', diff --git a/lib/src/generator/dartdoc_generator_backend.dart b/lib/src/generator/dartdoc_generator_backend.dart index e3d04ecc96..613f31a79b 100644 --- a/lib/src/generator/dartdoc_generator_backend.dart +++ b/lib/src/generator/dartdoc_generator_backend.dart @@ -12,7 +12,7 @@ import 'package:dartdoc/src/model/package.dart'; import 'package:dartdoc/src/model/package_graph.dart'; import 'package:dartdoc/src/warnings.dart'; import 'package:mustache/mustache.dart'; -import 'package:path/path.dart' as path; +import 'package:path/path.dart' as path show Context; /// Configuration options for the Dartdoc's default backend. class DartdocGeneratorBackendOptions implements TemplateOptions { @@ -63,9 +63,10 @@ abstract class DartdocGeneratorBackend implements GeneratorBackend { final Templates templates; final SidebarGenerator sidebarForLibrary; final SidebarGenerator sidebarForContainer; + final path.Context _pathContext; DartdocGeneratorBackend( - DartdocGeneratorBackendOptions options, this.templates) + DartdocGeneratorBackendOptions options, this.templates, this._pathContext) : options = options ?? DartdocGeneratorBackendOptions(), sidebarForContainer = SidebarGenerator(templates.sidebarContainerTemplate), @@ -90,7 +91,7 @@ abstract class DartdocGeneratorBackend implements GeneratorBackend { if (!options.useBaseHref) { json = json.replaceAll(HTMLBASE_PLACEHOLDER, ''); } - writer.write(path.join('categories.json'), '${json}\n'); + writer.write(_pathContext.join('categories.json'), '${json}\n'); } @override @@ -100,7 +101,7 @@ abstract class DartdocGeneratorBackend implements GeneratorBackend { if (!options.useBaseHref) { json = json.replaceAll(HTMLBASE_PLACEHOLDER, ''); } - writer.write(path.join('index.json'), '${json}\n'); + writer.write(_pathContext.join('index.json'), '${json}\n'); } @override diff --git a/lib/src/generator/html_generator.dart b/lib/src/generator/html_generator.dart index 17be599067..013c16d6a9 100644 --- a/lib/src/generator/html_generator.dart +++ b/lib/src/generator/html_generator.dart @@ -12,20 +12,22 @@ import 'package:dartdoc/src/generator/html_resources.g.dart' as resources; import 'package:dartdoc/src/generator/resource_loader.dart'; import 'package:dartdoc/src/generator/template_data.dart'; import 'package:dartdoc/src/generator/templates.dart'; +import 'package:path/path.dart' as path show Context; Future initHtmlGenerator( DartdocGeneratorOptionContext context) async { var templates = await Templates.fromContext(context); var options = DartdocGeneratorBackendOptions.fromContext(context); - var backend = HtmlGeneratorBackend(options, templates); + var backend = HtmlGeneratorBackend( + options, templates, context.resourceProvider.pathContext); return GeneratorFrontEnd(backend); } /// Generator backend for html output. class HtmlGeneratorBackend extends DartdocGeneratorBackend { - HtmlGeneratorBackend( - DartdocGeneratorBackendOptions options, Templates templates) - : super(options, templates); + HtmlGeneratorBackend(DartdocGeneratorBackendOptions options, + Templates templates, path.Context pathContext) + : super(options, templates, pathContext); @override void generatePackage(FileWriter writer, PackageGraph graph, Package package) { diff --git a/lib/src/generator/markdown_generator.dart b/lib/src/generator/markdown_generator.dart index 4af8820a87..b4770d0ba1 100644 --- a/lib/src/generator/markdown_generator.dart +++ b/lib/src/generator/markdown_generator.dart @@ -10,20 +10,22 @@ import 'package:dartdoc/src/generator/template_data.dart'; import 'package:dartdoc/src/generator/templates.dart'; import 'package:dartdoc/src/model/package.dart'; import 'package:dartdoc/src/model/package_graph.dart'; +import 'package:path/path.dart' as path show Context; Future initMarkdownGenerator( DartdocGeneratorOptionContext context) async { var templates = await Templates.fromContext(context); var options = DartdocGeneratorBackendOptions.fromContext(context); - var backend = MarkdownGeneratorBackend(options, templates); + var backend = MarkdownGeneratorBackend( + options, templates, context.resourceProvider.pathContext); return GeneratorFrontEnd(backend); } /// Generator backend for markdown output. class MarkdownGeneratorBackend extends DartdocGeneratorBackend { - MarkdownGeneratorBackend( - DartdocGeneratorBackendOptions options, Templates templates) - : super(options, templates); + MarkdownGeneratorBackend(DartdocGeneratorBackendOptions options, + Templates templates, path.Context pathContext) + : super(options, templates, pathContext); @override void generatePackage(FileWriter writer, PackageGraph graph, Package package) { diff --git a/lib/src/generator/templates.dart b/lib/src/generator/templates.dart index 17cc496618..3ca57045ef 100644 --- a/lib/src/generator/templates.dart +++ b/lib/src/generator/templates.dart @@ -14,7 +14,7 @@ import 'package:dartdoc/src/generator/template_data.dart'; import 'package:dartdoc/src/mustachio/annotations.dart'; import 'package:meta/meta.dart'; import 'package:mustache/mustache.dart'; -import 'package:path/path.dart' as path; +import 'package:path/path.dart' as path show Context; // resource_loader and the Resource API doesn't support viewing resources like // a directory listing, so we have to explicitly list the partials. diff --git a/lib/src/io_utils.dart b/lib/src/io_utils.dart index 0e758441d4..a93dab00e5 100644 --- a/lib/src/io_utils.dart +++ b/lib/src/io_utils.dart @@ -12,28 +12,10 @@ import 'package:analyzer/file_system/file_system.dart'; import 'package:analyzer/file_system/physical_file_system.dart'; import 'package:analyzer/src/generated/sdk.dart'; import 'package:analyzer/src/test_utilities/mock_sdk.dart'; -import 'package:path/path.dart' as path; +import 'package:path/path.dart' as path show Context; Encoding utf8AllowMalformed = Utf8Codec(allowMalformed: true); -/// Return a resolved path including the home directory in place of tilde -/// references. -String resolveTildePath(String originalPath) { - if (originalPath == null || !originalPath.startsWith('~/')) { - return originalPath; - } - - String homeDir; - - if (io.Platform.isWindows) { - homeDir = path.absolute(io.Platform.environment['USERPROFILE']); - } else { - homeDir = path.absolute(io.Platform.environment['HOME']); - } - - return path.join(homeDir, originalPath.substring(2)); -} - bool isSdkLibraryDocumented(SdkLibrary library) { if (library is MockSdkLibrary) { // Not implemented in [MockSdkLibrary]. @@ -42,6 +24,31 @@ bool isSdkLibraryDocumented(SdkLibrary library) { return library.isDocumented; } +extension PathExtensions on path.Context { + /// Returns a canonicalized path including the home directory in place of + /// tilde references. + String canonicalizeWithTilde(String originalPath) => + canonicalize(resolveTildePath(originalPath)); + + /// Return a resolved path including the home directory in place of tilde + /// references. + String resolveTildePath(String originalPath) { + if (originalPath == null || !originalPath.startsWith('~/')) { + return originalPath; + } + + String homeDir; + + if (io.Platform.isWindows) { + homeDir = absolute(io.Platform.environment['USERPROFILE']); + } else { + homeDir = absolute(io.Platform.environment['HOME']); + } + + return join(homeDir, originalPath.substring(2)); + } +} + extension ResourceProviderExtensions on ResourceProvider { Folder createSystemTemp(String prefix) { if (this is PhysicalResourceProvider) { diff --git a/lib/src/model/documentable.dart b/lib/src/model/documentable.dart index d478eb0b44..67d1a47b85 100644 --- a/lib/src/model/documentable.dart +++ b/lib/src/model/documentable.dart @@ -5,7 +5,6 @@ import 'package:analyzer/file_system/file_system.dart'; import 'package:dartdoc/src/dartdoc_options.dart'; import 'package:dartdoc/src/io_utils.dart'; -import 'package:path/path.dart' as p; import 'model.dart'; @@ -83,7 +82,9 @@ mixin MarkdownFileDocumentation implements Documentable, Canonicalization { File get documentationFile; @override - String get location => p.toUri(documentationFile.path).toString(); + String get location => packageGraph.resourceProvider.pathContext + .toUri(documentationFile.path) + .toString(); @override Set get locationPieces => {location}; diff --git a/lib/src/model/documentation_comment.dart b/lib/src/model/documentation_comment.dart index 81ddd2ead4..401e9dc18d 100644 --- a/lib/src/model/documentation_comment.dart +++ b/lib/src/model/documentation_comment.dart @@ -4,7 +4,7 @@ import 'package:dartdoc/src/model/model.dart'; import 'package:dartdoc/src/render/model_element_renderer.dart'; import 'package:dartdoc/src/utils.dart'; import 'package:dartdoc/src/warnings.dart'; -import 'package:path/path.dart' as path; +import 'package:path/path.dart' as path show Context; final _templatePattern = RegExp( r'[ ]*{@template\s+(.+?)}([\s\S]+?){@endtemplate}[ ]*(\n?)', @@ -235,7 +235,8 @@ mixin DocumentationComment 'SOURCE_PATH': (sourceFileName == null || package?.packagePath == null) ? null - : path.relative(sourceFileName, from: package.packagePath), + : pathContext.relative(sourceFileName, + from: package.packagePath), 'PACKAGE_PATH': package?.packagePath, 'PACKAGE_NAME': package?.name, 'LIBRARY_NAME': library?.fullyQualifiedName, @@ -271,8 +272,8 @@ mixin DocumentationComment // Already warned about an invalid parameter if this happens. return ''; } - var lang = - args['lang'] ?? path.extension(args['src']).replaceFirst('.', ''); + var lang = args['lang'] ?? + pathContext.extension(args['src']).replaceFirst('.', ''); var replacement = match[0]; // default to fully matched string. @@ -327,14 +328,14 @@ mixin DocumentationComment var file = src + fragExtension; var region = args['region'] ?? ''; if (region.isNotEmpty) { - var dir = path.dirname(src); - var basename = path.basenameWithoutExtension(src); - var ext = path.extension(src); - file = path.join(dir, '$basename-$region$ext$fragExtension'); + var dir = pathContext.dirname(src); + var basename = pathContext.basenameWithoutExtension(src); + var ext = pathContext.extension(src); + file = pathContext.join(dir, '$basename-$region$ext$fragExtension'); } args['file'] = config.examplePathPrefix == null ? file - : path.join(config.examplePathPrefix, file); + : pathContext.join(config.examplePathPrefix, file); return args; } diff --git a/lib/src/model/library.dart b/lib/src/model/library.dart index f257f9bdbc..b7d677e6f0 100644 --- a/lib/src/model/library.dart +++ b/lib/src/model/library.dart @@ -17,7 +17,6 @@ import 'package:dartdoc/src/model/model.dart'; import 'package:dartdoc/src/package_meta.dart' show PackageMeta; import 'package:dartdoc/src/quiver.dart' as quiver; import 'package:dartdoc/src/warnings.dart'; -import 'package:path/path.dart' as path; /// Find all hashable children of a given element that are defined in the /// [LibraryElement] given at initialization. @@ -441,7 +440,7 @@ class Library extends ModelElement with Categorization, TopLevelContainer { } else if (element.name != null && element.name.isNotEmpty) { _name = element.name; } else { - _name = path.basename(source.fullName); + _name = pathContext.basename(source.fullName); if (_name.endsWith('.dart')) { _name = _name.substring(0, _name.length - '.dart'.length); } diff --git a/lib/src/model/model_element.dart b/lib/src/model/model_element.dart index f19fde382f..8c8b744b84 100644 --- a/lib/src/model/model_element.dart +++ b/lib/src/model/model_element.dart @@ -27,7 +27,7 @@ import 'package:dartdoc/src/source_linker.dart'; import 'package:dartdoc/src/tuple.dart'; import 'package:dartdoc/src/warnings.dart'; import 'package:meta/meta.dart'; -import 'package:path/path.dart' as path; +import 'package:path/path.dart' as path show Context; /// Items mapped less than zero will sort before custom annotations. /// Items mapped above zero are sorted after custom annotations. @@ -808,11 +808,13 @@ abstract class ModelElement extends Canonicalization @override String get location { - // Call nothing from here that can emit warnings or you'll cause stack overflows. + // Call nothing from here that can emit warnings or you'll cause stack + // overflows. + var sourceUri = pathContext.toUri(sourceFileName); if (characterLocation != null) { - return '(${path.toUri(sourceFileName)}:${characterLocation.toString()})'; + return '($sourceUri:${characterLocation.toString()})'; } - return '(${path.toUri(sourceFileName)})'; + return '($sourceUri)'; } /// Returns a link to extended documentation, or the empty string if that diff --git a/lib/src/model/package.dart b/lib/src/model/package.dart index c215a1b044..af1cd93a93 100644 --- a/lib/src/model/package.dart +++ b/lib/src/model/package.dart @@ -9,7 +9,7 @@ import 'package:dartdoc/src/io_utils.dart'; import 'package:dartdoc/src/model/model.dart'; import 'package:dartdoc/src/package_meta.dart'; import 'package:dartdoc/src/warnings.dart'; -import 'package:path/path.dart' as path; +import 'package:path/path.dart' as path show Context; import 'package:pub_semver/pub_semver.dart'; @Deprecated('Public variable intended to be private; will be removed as early ' @@ -168,7 +168,7 @@ class Package extends LibraryContainer // assembled from multiple locations? packageGraph.hasEmbedderSdk && packageMeta.isSdk && - libraries.any((l) => path.isWithin( + libraries.any((l) => _pathContext.isWithin( packageGraph.packageMeta.dir.path, (l.element.source.fullName))) || // autoIncludeDependencies means everything is local. @@ -279,7 +279,7 @@ class Package extends LibraryContainer String get href => '$baseHref$filePath'; @override - String get location => path.toUri(packageMeta.resolvedDir).toString(); + String get location => _pathContext.toUri(packageMeta.resolvedDir).toString(); @override String get name => _name; @@ -371,7 +371,7 @@ class Package extends LibraryContainer String _packagePath; String get packagePath { - _packagePath ??= path.canonicalize(packageMeta.dir.path); + _packagePath ??= _pathContext.canonicalize(packageMeta.dir.path); return _packagePath; } @@ -386,4 +386,6 @@ class Package extends LibraryContainer @override List get containerOrder => config.packageOrder; + + path.Context get _pathContext => _packageGraph.resourceProvider.pathContext; } diff --git a/lib/src/model/package_builder.dart b/lib/src/model/package_builder.dart index acf0c5a523..da9fd6f6f5 100644 --- a/lib/src/model/package_builder.dart +++ b/lib/src/model/package_builder.dart @@ -25,8 +25,7 @@ import 'package:dartdoc/src/package_meta.dart' import 'package:dartdoc/src/render/renderer_factory.dart'; import 'package:dartdoc/src/special_elements.dart'; import 'package:meta/meta.dart'; -// TODO(jcollins-g): do not directly import path, use ResourceProvider instead -import 'package:path/path.dart' as path; +import 'package:path/path.dart' as path show Context; /// Everything you need to instantiate a PackageGraph object for documenting. abstract class PackageBuilder { @@ -258,16 +257,16 @@ class PubPackageBuilder implements PackageBuilder { .findPackageConfig(resourceProvider.getFolder(basePackageDir)); for (var package in info.packages) { if (!filterExcludes || !config.exclude.contains(package.name)) { - packageDirs.add( - path.dirname(path.fromUri(info[package.name].packageUriRoot))); + packageDirs.add(_pathContext.dirname( + _pathContext.fromUri(info[package.name].packageUriRoot))); } } } - var sep = path.separator; + var sep = _pathContext.separator; for (var packageDir in packageDirs) { - var packageLibDir = path.join(packageDir, 'lib'); - var packageLibSrcDir = path.join(packageLibDir, 'src'); + var packageLibDir = _pathContext.join(packageDir, 'lib'); + var packageLibSrcDir = _pathContext.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. @@ -277,8 +276,8 @@ class PubPackageBuilder implements PackageBuilder { (!lib.contains('${sep}packages${sep}') || packageDir.contains('${sep}packages${sep}'))) { // Only include libraries within the lib dir that are not in 'lib/src'. - if (path.isWithin(packageLibDir, lib) && - !path.isWithin(packageLibSrcDir, lib)) { + if (_pathContext.isWithin(packageLibDir, lib) && + !_pathContext.isWithin(packageLibSrcDir, lib)) { // Only add the file if it does not contain 'part of'. var contents = resourceProvider.getFile(lib).readAsStringSync(); @@ -320,7 +319,7 @@ class PubPackageBuilder implements PackageBuilder { for (var resource in listDir(resourceProvider.getFolder(dir))) { // Skip hidden files and directories - if (path.basename(resource.path).startsWith('.')) { + if (_pathContext.basename(resource.path).startsWith('.')) { continue; } @@ -416,19 +415,22 @@ class PubPackageBuilder implements PackageBuilder { foundLibraries, specialFiles.difference(files)); } + path.Context get _pathContext => resourceProvider.pathContext; + /// If [dir] contains both a `lib` directory and a `pubspec.yaml` file treat /// it like a package and only return the `lib` dir. /// /// This ensures that packages don't have non-`lib` content documented. static Iterable _packageDirList(Folder dir) sync* { var resources = dir.getChildren(); + var pathContext = dir.provider.pathContext; var pubspec = resources.firstWhere( - (e) => e is File && path.basename(e.path) == 'pubspec.yaml', + (e) => e is File && pathContext.basename(e.path) == 'pubspec.yaml', orElse: () => null); var libDir = resources.firstWhere( - (e) => e is Folder && path.basename(e.path) == 'lib', + (e) => e is Folder && pathContext.basename(e.path) == 'lib', orElse: () => null); if (pubspec != null && libDir != null) { diff --git a/test/html_generator_test.dart b/test/html_generator_test.dart index 5b0d8f8e75..adf89243cc 100644 --- a/test/html_generator_test.dart +++ b/test/html_generator_test.dart @@ -98,7 +98,8 @@ void main() { } templates = await Templates.createDefault('html', loader: resourceLoader); - generator = GeneratorFrontEnd(HtmlGeneratorBackend(null, templates)); + generator = + GeneratorFrontEnd(HtmlGeneratorBackend(null, templates, pathContext)); projectRoot = utils.writePackage( 'my_package', resourceProvider, packageConfigProvider); diff --git a/tool/grind.dart b/tool/grind.dart index a92aee7e34..43fa69e400 100644 --- a/tool/grind.dart +++ b/tool/grind.dart @@ -37,8 +37,8 @@ void expectFileContains(String path, List items) { } /// The pub cache inherited by grinder. -final String defaultPubCache = - Platform.environment['PUB_CACHE'] ?? resolveTildePath('~/.pub-cache'); +final String defaultPubCache = Platform.environment['PUB_CACHE'] ?? + path.context.resolveTildePath('~/.pub-cache'); /// Run no more than the number of processors available in parallel. final MultiFutureTracker testFutures = @@ -107,8 +107,8 @@ Directory _sdkDocsDir; Directory get sdkDocsDir => _sdkDocsDir ??= createTempSync('sdkdocs'); -Directory cleanFlutterDir = Directory( - path.join(resolveTildePath('~/.dartdoc_grinder'), 'cleanFlutter')); +Directory cleanFlutterDir = Directory(path.join( + path.context.resolveTildePath('~/.dartdoc_grinder'), 'cleanFlutter')); Directory _flutterDir;