diff --git a/bin/dartdoc.dart b/bin/dartdoc.dart index 9824affa8a..90cf481a8c 100644 --- a/bin/dartdoc.dart +++ b/bin/dartdoc.dart @@ -44,11 +44,6 @@ main(List arguments) async { } Directory sdkDir = getSdkDir(); - if (sdkDir == null) { - stderr.writeln(" Error: unable to locate the Dart SDK."); - exit(1); - } - final bool sdkDocs = args['sdk-docs']; final bool showProgress = args['show-progress']; @@ -61,52 +56,14 @@ main(List arguments) async { inputDir = args['input']; } - if (!inputDir.existsSync()) { - stderr.writeln( - " fatal error: unable to locate the input directory at ${inputDir - .path}."); - exit(1); - } - - String url = args['hosted-url']; - - List headerFilePaths = - args['header'].map(_resolveTildePath).toList() as List; - for (String headerFilePath in headerFilePaths) { - if (!new File(headerFilePath).existsSync()) { - stderr.writeln( - " fatal error: unable to locate header file: ${headerFilePath}."); - exit(1); - } - } - - List footerFilePaths = - args['footer'].map(_resolveTildePath).toList() as List; - for (String footerFilePath in footerFilePaths) { - if (!new File(footerFilePath).existsSync()) { - stderr.writeln( - " fatal error: unable to locate footer file: ${footerFilePath}."); - exit(1); - } - } - - List footerTextFilePaths = - args['footer-text'].map(_resolveTildePath).toList() as List; - + List footerTextFilePaths = []; // If we're generating docs for the Dart SDK, we insert a copyright footer. if (sdkDocs) { Uri footerCopyrightUri = await Isolate.resolvePackageUri( Uri.parse('package:dartdoc/resources/sdk_footer_text.html')); footerTextFilePaths = [footerCopyrightUri.toFilePath()]; } - - for (String footerFilePath in footerTextFilePaths) { - if (!new File(footerFilePath).existsSync()) { - stderr.writeln( - " fatal error: unable to locate footer-text file: ${footerFilePath}."); - exit(1); - } - } + footerTextFilePaths.addAll(args['footer-text']); Directory outputDir = new Directory(pathLib.join(Directory.current.path, defaultOutDir)); @@ -212,17 +169,6 @@ main(List arguments) async { logInfo("Generating documentation for '${packageMeta}' into " "${outputDir.absolute.path}${Platform.pathSeparator}"); - var generators = await initGenerators(url, args['rel-canonical-prefix'], - headerFilePaths: headerFilePaths, - footerFilePaths: footerFilePaths, - footerTextFilePaths: footerTextFilePaths, - faviconPath: args['favicon'], - prettyIndexJson: args['pretty-index-json']); - - for (var generator in generators) { - generator.onFileCreated.listen(logProgress); - } - DartSdk sdk = new FolderBasedDartSdk(PhysicalResourceProvider.INSTANCE, PhysicalResourceProvider.INSTANCE.getFolder(sdkDir.path)); @@ -255,6 +201,11 @@ main(List arguments) async { examplePathPrefix: args['example-path-prefix'], excludeLibraries: args['exclude'], excludePackages: args['exclude-packages'], + faviconPath: args['favicon'], + footerFilePaths: args['footer'], + footerTextFilePaths: footerTextFilePaths, + headerFilePaths: args['header'], + hostedUrl: args['hosted-url'], includeExternals: args['include-external'], includeLibraries: args['include'], includeSource: args['include-source'], @@ -262,8 +213,10 @@ main(List arguments) async { packageOrder: args['package-order'].isEmpty ? args['category-order'] : args['package-order'], + prettyIndexJson: args['pretty-index-json'], reexportMinConfidence: double.parse(args['ambiguous-reexport-scorer-min-confidence']), + relCanonicalPrefix: args['rel-canonical-prefix'], sdkDir: sdkDir, sdkVersion: sdk.sdkVersion, showWarnings: args['show-warnings'], @@ -271,7 +224,8 @@ main(List arguments) async { verboseWarnings: args['verbose-warnings'], ); - DartDoc dartdoc = new DartDoc(config, generators, outputDir, packageMeta); + DartDoc dartdoc = + await DartDoc.withDefaultGenerators(config, outputDir, packageMeta); dartdoc.onCheckProgress.listen(logProgress); await Chain.capture(() async { diff --git a/lib/dartdoc.dart b/lib/dartdoc.dart index 3968d84bca..67ec6f3503 100644 --- a/lib/dartdoc.dart +++ b/lib/dartdoc.dart @@ -41,7 +41,7 @@ const String version = '0.18.1'; final String defaultOutDir = pathLib.join('doc', 'api'); /// Initialize and setup the generators. -Future> initGenerators(String url, String relCanonicalPrefix, +Future> _initGenerators(String url, String relCanonicalPrefix, {List headerFilePaths, List footerFilePaths, List footerTextFilePaths, @@ -75,10 +75,32 @@ class DartDoc extends PackageBuilder { final StreamController _onCheckProgress = new StreamController(sync: true); - DartDoc(DartDocConfig config, this.generators, this.outputDir, + DartDoc._(DartDocConfig config, this.generators, this.outputDir, PackageMeta packageMeta) : super(config, packageMeta); + /// An asynchronous factory method that builds Dartdoc's file writers + /// and returns a DartDoc object with them. + static withDefaultGenerators(DartDocConfig config, Directory outputDir, + PackageMeta packageMeta) async { + var generators = await _initGenerators( + config.hostedUrl, config.relCanonicalPrefix, + headerFilePaths: config.headerFilePaths, + footerFilePaths: config.footerFilePaths, + footerTextFilePaths: config.footerTextFilePaths, + faviconPath: config.faviconPath, + prettyIndexJson: config.prettyIndexJson); + for (var generator in generators) { + generator.onFileCreated.listen(logProgress); + } + return new DartDoc._(config, generators, outputDir, packageMeta); + } + + factory DartDoc.withoutGenerators( + DartDocConfig config, Directory outputDir, PackageMeta packageMeta) { + return new DartDoc._(config, [], outputDir, packageMeta); + } + Stream get onCheckProgress => _onCheckProgress.stream; @override @@ -146,14 +168,17 @@ class DartDoc extends PackageBuilder { "in ${seconds.toStringAsFixed(1)} seconds"); _stopwatch.reset(); - // Create the out directory. - if (!outputDir.existsSync()) outputDir.createSync(recursive: true); + if (generators.isNotEmpty) { + // Create the out directory. + if (!outputDir.existsSync()) outputDir.createSync(recursive: true); - for (var generator in generators) { - await generator.generate(packageGraph, outputDir.path); - writtenFiles.addAll(generator.writtenFiles.map(pathLib.normalize)); + for (var generator in generators) { + await generator.generate(packageGraph, outputDir.path); + writtenFiles.addAll(generator.writtenFiles.map(pathLib.normalize)); + } + if (config.validateLinks) validateLinks(packageGraph, outputDir.path); } - if (config.validateLinks) validateLinks(packageGraph, outputDir.path); + int warnings = packageGraph.packageWarningCounter.warningCount; int errors = packageGraph.packageWarningCounter.errorCount; if (warnings == 0 && errors == 0) { diff --git a/lib/src/config.dart b/lib/src/config.dart index 628e49ce79..69f99dd1b6 100644 --- a/lib/src/config.dart +++ b/lib/src/config.dart @@ -8,9 +8,26 @@ 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'; +String _resolveTildePath(String originalPath) { + if (originalPath == null || !originalPath.startsWith('~/')) { + return originalPath; + } + + String homeDir; + + if (Platform.isWindows) { + homeDir = pathLib.absolute(Platform.environment['USERPROFILE']); + } else { + homeDir = pathLib.absolute(Platform.environment['HOME']); + } + + return pathLib.join(homeDir, originalPath.substring(2)); +} + /// Class representing values possibly local to a particular [ModelElement]. class LocalConfig { final Map> categoryMap; @@ -30,12 +47,19 @@ class DartDocConfig { final List excludeLibraries; final List excludePackages; final String examplePathPrefix; + List footerFilePaths; + List footerTextFilePaths; + List headerFilePaths; + final String faviconPath; final List includeExternals; final List includeLibraries; + final String hostedUrl; final bool includeSource; final Directory inputDir; final List packageOrder; + final bool prettyIndexJson; final double reexportMinConfidence; + final String relCanonicalPrefix; final Directory sdkDir; final String sdkVersion; final bool showWarnings; @@ -48,18 +72,54 @@ class DartDocConfig { this.examplePathPrefix, this.excludeLibraries, this.excludePackages, + this.faviconPath, + this.footerFilePaths, + this.footerTextFilePaths, + this.headerFilePaths, + this.hostedUrl, this.includeExternals, this.includeLibraries, this.includeSource, this.inputDir, this.packageOrder, + this.prettyIndexJson, this.reexportMinConfidence, + this.relCanonicalPrefix, this.sdkDir, this.sdkVersion, this.showWarnings, this.validateLinks, this.verboseWarnings, - ); + ) { + if (sdkDir == null || !sdkDir.existsSync()) { + throw new DartDocFailure("Error: unable to locate the Dart SDK."); + } + + footerFilePaths = footerFilePaths.map((p) => _resolveTildePath(p)).toList(); + for (String footerFilePath in footerFilePaths) { + if (!new File(footerFilePath).existsSync()) { + throw new DartDocFailure( + "fatal error: unable to locate footer file: ${footerFilePath}."); + } + } + + footerTextFilePaths = + footerTextFilePaths.map((p) => _resolveTildePath(p)).toList(); + for (String footerTextFilePath in footerTextFilePaths) { + if (!new File(footerTextFilePath).existsSync()) { + throw new DartDocFailure( + "fatal error: unable to locate footer-text file: ${footerTextFilePath}."); + } + } + + headerFilePaths = headerFilePaths.map((p) => _resolveTildePath(p)).toList(); + for (String headerFilePath in headerFilePaths) { + if (!new File(headerFilePath).existsSync()) { + throw new DartDocFailure( + "fatal error: unable to locate header file: ${headerFilePath}."); + } + } + } factory DartDocConfig.fromParameters({ bool addCrossdart: false, @@ -68,12 +128,19 @@ class DartDocConfig { String examplePathPrefix, List excludeLibraries, List excludePackages, + String faviconPath, + List footerFilePaths, + List footerTextFilePaths, + List headerFilePaths, + String hostedUrl, List includeExternals, List includeLibraries, bool includeSource: true, Directory inputDir, List packageOrder, + bool prettyIndexJson: false, double reexportMinConfidence: 0.1, + String relCanonicalPrefix, Directory sdkDir, String sdkVersion, bool showWarnings: false, @@ -87,12 +154,19 @@ class DartDocConfig { examplePathPrefix, excludeLibraries ?? const [], excludePackages ?? const [], + faviconPath, + footerFilePaths ?? const [], + footerTextFilePaths ?? const [], + headerFilePaths ?? const [], + hostedUrl, includeExternals ?? const [], includeLibraries ?? const [], includeSource, inputDir, packageOrder ?? const [], + prettyIndexJson, reexportMinConfidence, + relCanonicalPrefix, sdkDir ?? getSdkDir(), sdkVersion, showWarnings, diff --git a/lib/src/package_meta.dart b/lib/src/package_meta.dart index e967b4d82d..2dbe86ad9d 100644 --- a/lib/src/package_meta.dart +++ b/lib/src/package_meta.dart @@ -7,6 +7,7 @@ library dartdoc.package_meta; import 'dart:io'; import 'package:analyzer/dart/element/element.dart'; +import 'package:dartdoc/dartdoc.dart'; import 'package:dartdoc/src/sdk.dart'; import 'package:path/path.dart' as pathLib; import 'package:yaml/yaml.dart'; @@ -15,6 +16,10 @@ import 'logging.dart'; Map _packageMetaCache = {}; +class PackageMetaFailure extends DartDocFailure { + PackageMetaFailure(String message) : super(message); +} + abstract class PackageMeta { final Directory dir; @@ -49,9 +54,15 @@ abstract class PackageMeta { factory PackageMeta.fromDir(Directory dir) { Directory original = dir.absolute; dir = original; + if (!original.existsSync()) { + throw new PackageMetaFailure( + "fatal error: unable to locate the input directory at ${original.path}."); + } + if (!_packageMetaCache.containsKey(dir.path)) { PackageMeta packageMeta; // There are pubspec.yaml files inside the SDK. Ignore them. + // TODO(jcollins-g): allow specifying alternate SDK directories (#1617) if (pathLib.isWithin(getSdkDir().absolute.path, dir.path) || getSdkDir().path == dir.path) { packageMeta = new _SdkMeta(getSdkDir()); diff --git a/lib/src/sdk.dart b/lib/src/sdk.dart index 91a709e599..a26d8d9899 100644 --- a/lib/src/sdk.dart +++ b/lib/src/sdk.dart @@ -5,6 +5,7 @@ import 'dart:io'; /// Use config.sdkDir instead outside of initialization. +// TODO(jcollins-g): Avoid this function in PackageMeta, too. Directory getSdkDir() { File vmExecutable = new File(Platform.resolvedExecutable); return vmExecutable.parent.parent; diff --git a/pubspec.lock b/pubspec.lock index 90a643ab3c..c14f22efa6 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -408,4 +408,4 @@ packages: source: hosted version: "2.1.13" sdks: - dart: ">=2.0.0-dev.23.0 <=2.0.0-dev.44.0" + dart: ">=2.0.0-dev.23.0 <=2.0.0-dev.47.0" diff --git a/test/dartdoc_test.dart b/test/dartdoc_test.dart index cb0855146e..f3a68ce527 100644 --- a/test/dartdoc_test.dart +++ b/test/dartdoc_test.dart @@ -29,9 +29,8 @@ void main() { test('generate docs for ${pathLib.basename(testPackageDir.path)} works', () async { PackageMeta meta = new PackageMeta.fromDir(testPackageDir); - DartDoc dartdoc = new DartDoc( + DartDoc dartdoc = new DartDoc.withoutGenerators( new DartDocConfig.fromParameters(inputDir: testPackageDir), - [], tempDir, meta); @@ -48,9 +47,8 @@ void main() { test('generate docs for ${pathLib.basename(testPackageBadDir.path)} fails', () async { PackageMeta meta = new PackageMeta.fromDir(testPackageBadDir); - DartDoc dartdoc = new DartDoc( + DartDoc dartdoc = new DartDoc.withoutGenerators( new DartDocConfig.fromParameters(inputDir: testPackageBadDir), - [], tempDir, meta); @@ -64,9 +62,8 @@ void main() { test('generate docs for a package that does not have a readme', () async { PackageMeta meta = new PackageMeta.fromDir(testPackageWithNoReadme); - DartDoc dartdoc = new DartDoc( + DartDoc dartdoc = new DartDoc.withoutGenerators( new DartDocConfig.fromParameters(inputDir: testPackageWithNoReadme), - [], tempDir, meta); @@ -82,10 +79,9 @@ void main() { test('generate docs including a single library', () async { PackageMeta meta = new PackageMeta.fromDir(testPackageDir); - DartDoc dartdoc = new DartDoc( + DartDoc dartdoc = new DartDoc.withoutGenerators( new DartDocConfig.fromParameters( inputDir: testPackageDir, includeLibraries: ['fake']), - [], tempDir, meta); @@ -101,10 +97,9 @@ void main() { test('generate docs excluding a single library', () async { PackageMeta meta = new PackageMeta.fromDir(testPackageDir); - DartDoc dartdoc = new DartDoc( + DartDoc dartdoc = new DartDoc.withoutGenerators( new DartDocConfig.fromParameters( inputDir: testPackageDir, excludeLibraries: ['fake']), - [], tempDir, meta); @@ -122,10 +117,9 @@ void main() { test('generate docs for package with embedder yaml', () async { PackageMeta meta = new PackageMeta.fromDir(testPackageWithEmbedderYaml); if (meta.needsPubGet) meta.runPubGet(); - DartDoc dartdoc = new DartDoc( + DartDoc dartdoc = new DartDoc.withoutGenerators( new DartDocConfig.fromParameters( inputDir: testPackageWithEmbedderYaml), - [], tempDir, meta);