diff --git a/pkg/dartdev/lib/dartdev.dart b/pkg/dartdev/lib/dartdev.dart index a545f1a196d..3e75181c6ca 100644 --- a/pkg/dartdev/lib/dartdev.dart +++ b/pkg/dartdev/lib/dartdev.dart @@ -21,6 +21,7 @@ import 'src/commands/analyze.dart'; import 'src/commands/compile.dart'; import 'src/commands/create.dart'; import 'src/commands/debug_adapter.dart'; +import 'src/commands/doc.dart'; import 'src/commands/fix.dart'; import 'src/commands/language_server.dart'; import 'src/commands/migrate.dart'; @@ -114,6 +115,7 @@ class DartdevRunner extends CommandRunner { addCommand(CreateCommand(verbose: verbose)); addCommand(DebugAdapterCommand(verbose: verbose)); addCommand(CompileCommand(verbose: verbose)); + addCommand(DocCommand(verbose: verbose)); addCommand(DevToolsCommand( verbose: verbose, customDevToolsPath: sdk.devToolsBinaries, diff --git a/pkg/dartdev/lib/src/commands/doc.dart b/pkg/dartdev/lib/src/commands/doc.dart new file mode 100644 index 00000000000..d0391836529 --- /dev/null +++ b/pkg/dartdev/lib/src/commands/doc.dart @@ -0,0 +1,89 @@ +// Copyright (c) 2021, 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:async'; +import 'dart:io' as io; + +import 'package:dartdoc/dartdoc.dart'; +import 'package:dartdoc/options.dart'; +import 'package:path/path.dart' as path; + +import '../core.dart'; +import '../sdk.dart'; + +/// A command to create a new project from a set of templates. +class DocCommand extends DartdevCommand { + static const String cmdName = 'doc'; + + DocCommand({bool verbose = false}) + : super( + cmdName, + 'Generate HTML API documentation from Dart documentation comments.', + verbose, + ) { + argParser.addOption( + 'output-dir', + abbr: 'o', + defaultsTo: path.join('.', 'doc', 'api'), + help: 'Output directory', + ); + argParser.addFlag( + 'validate-links', + negatable: true, + help: 'Display context aware warnings for broken links (slow)', + ); + } + + @override + String get invocation => '${super.invocation} '; + + @override + FutureOr run() async { + // At least one argument, the input directory, is required. + if (argResults.rest.isEmpty) { + usageException("Error: Input directory not specified"); + } + + // Determine input directory. + final dir = io.Directory(argResults.rest[0]); + if (!dir.existsSync()) { + usageException("Error: Input directory doesn't exist: ${dir.path}"); + } + + // Parse options. + final options = [ + '--input=${dir.path}', + '--output=${argResults['output-dir']}', + ]; + if (argResults['validate-links']) { + options.add('--validate-links'); + } else { + options.add('--no-validate-links'); + } + + // Specify where dartdoc resources are located. + final resourcesPath = + path.absolute(sdk.sdkPath, 'bin', 'resources', 'dartdoc', 'resources'); + options.add('--resources-dir=$resourcesPath'); + + final config = await parseOptions(pubPackageMetaProvider, options); + if (config == null) { + // There was an error while parsing options. + return 2; + } + + // Call dartdoc. + if (verbose) { + log.stdout('Calling dartdoc with the following options: $options'); + } + final packageConfigProvider = PhysicalPackageConfigProvider(); + final packageBuilder = PubPackageBuilder( + config, pubPackageMetaProvider, packageConfigProvider); + final dartdoc = config.generateDocs + ? await Dartdoc.fromContext(config, packageBuilder) + : await Dartdoc.withEmptyGenerator(config, packageBuilder); + dartdoc.executeGuarded(); + return 0; + } +} diff --git a/pkg/dartdev/pubspec.yaml b/pkg/dartdev/pubspec.yaml index 0145fab20d3..aa4f9222307 100644 --- a/pkg/dartdev/pubspec.yaml +++ b/pkg/dartdev/pubspec.yaml @@ -17,6 +17,7 @@ dependencies: dart2native: path: ../dart2native dart_style: any + dartdoc: any dds: path: ../dds devtools_server: any diff --git a/pkg/dartdev/test/commands/doc_test.dart b/pkg/dartdev/test/commands/doc_test.dart new file mode 100644 index 00000000000..9f57108bba4 --- /dev/null +++ b/pkg/dartdev/test/commands/doc_test.dart @@ -0,0 +1,70 @@ +// Copyright (c) 2021, 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 'package:test/test.dart'; + +import '../utils.dart'; + +const int compileErrorExitCode = 64; + +void main() { + group('doc', defineCompileTests, timeout: longTimeout); +} + +void defineCompileTests() { + test('Passing no args fails', () async { + final p = project(); + var result = await p.run(['doc']); + expect(result.stderr, contains('Input directory not specified')); + expect(result.exitCode, compileErrorExitCode); + }); + + test('--help', () async { + final p = project(); + final result = await p.run(['doc', '--help']); + expect( + result.stdout, + contains('Usage: dart doc [arguments] '), + ); + + expect(result.exitCode, 0); + }); + + test('Document a library', () async { + final source = ''' +/// This is Foo. It uses [Bar]. +class Foo { + Bar bar; +} + +/// Bar is very nice. +class Bar { + _i = 42; +} + '''; + + final p = project(mainSrc: 'void main() { print("Hello, World"); }'); + p.file('lib/foo.dart', source); + final result = await p.run(['doc', '--validate-links', p.dirPath]); + print( + 'exit: ${result.exitCode}, stderr:\n${result.stderr}\nstdout:\n${result.stdout}'); + expect(result.stdout, contains('Documenting dartdev_temp')); + }); + + test('Document a library with broken link is flagged', () async { + final source = ''' +/// This is Foo. It uses [Baz]. +class Foo { + // Bar bar; +} + '''; + + final p = project(mainSrc: 'void main() { print("Hello, World"); }'); + p.file('lib/foo.dart', source); + final result = await p.run(['doc', '--validate-links', p.dirPath]); + print( + 'exit: ${result.exitCode}, stderr:\n${result.stderr}\nstdout:\n${result.stdout}'); + expect(result.stdout, contains('Documenting dartdev_temp')); + }); +} diff --git a/sdk/bin/dartdoc b/sdk/bin/dartdoc index e9584c1ced7..f7ad6828eeb 100755 --- a/sdk/bin/dartdoc +++ b/sdk/bin/dartdoc @@ -23,4 +23,4 @@ SNAPSHOT="$BIN_DIR/snapshots/dartdoc.dart.snapshot" # We are running the snapshot in the built SDK. DART="$BIN_DIR/dart" -exec "$DART" "--packages=$BIN_DIR/resources/dartdoc/.packages" "$SNAPSHOT" "$@" +exec "$DART" "--packages=$BIN_DIR/resources/dartdoc/.packages" "$SNAPSHOT" "--resources-dir=$BIN_DIR/resources/dartdoc/resources/" "$@"