From 3b552b139cafc5a4a1d3bb7d38ac55b47a09b9eb Mon Sep 17 00:00:00 2001 From: evanweible-wf Date: Fri, 28 Aug 2015 14:26:54 -0500 Subject: [PATCH] New Task: documentation generation via dartdoc. --- .gitignore | 9 ++- README.md | 11 +++ lib/src/dart_dev_cli.dart | 2 + lib/src/platform_util/api.dart | 14 ++++ lib/src/platform_util/mock_platform_util.dart | 14 ++++ lib/src/platform_util/platform_util.dart | 14 ++++ .../platform_util/standard_platform_util.dart | 14 ++++ lib/src/tasks/config.dart | 2 + lib/src/tasks/coverage/exceptions.dart | 14 ++++ lib/src/tasks/docs/api.dart | 73 +++++++++++++++++++ lib/src/tasks/docs/cli.dart | 57 +++++++++++++++ lib/src/tasks/docs/config.dart | 19 +++++ lib/src/tasks/init/api.dart | 1 + pubspec.yaml | 1 + test/fixtures/docs/docs/lib/main.dart | 5 ++ test/fixtures/docs/docs/pubspec.yaml | 6 ++ .../docs/no_dartdoc_package/pubspec.yaml | 5 ++ test/integration/docs_test.dart | 54 ++++++++++++++ 18 files changed, 314 insertions(+), 1 deletion(-) create mode 100644 lib/src/tasks/docs/api.dart create mode 100644 lib/src/tasks/docs/cli.dart create mode 100644 lib/src/tasks/docs/config.dart create mode 100644 test/fixtures/docs/docs/lib/main.dart create mode 100644 test/fixtures/docs/docs/pubspec.yaml create mode 100644 test/fixtures/docs/no_dartdoc_package/pubspec.yaml create mode 100644 test/integration/docs_test.dart diff --git a/.gitignore b/.gitignore index f67505ec..fd3b4bcd 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,13 @@ packages pubspec.lock +# generated docs +/doc/api/ + +# generated coverage /coverage/ + +# generated assets in test fixtures /test/fixtures/coverage/browser/coverage/ -/test/fixtures/coverage/vm/coverage/ \ No newline at end of file +/test/fixtures/coverage/vm/coverage/ +/test/fixtures/docs/docs/doc/api/ diff --git a/README.md b/README.md index 96046538..db195bfd 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ All Dart (https://dartlang.org) projects eventually share a common set of develo - Code coverage - Consistent code formatting - Static analysis to detect issues +- Documentation generation - Examples for manual testing/exploration - Applying a LICENSE file to all source files @@ -62,6 +63,7 @@ static analysis - you just need to know how to use the `dart_dev` tool. - **Coverage:** collects coverage over test suites (unit, integration, and functional) and generates a report. Uses the [`coverage` package](https://github.com/dart-lang/coverage). - **Code Formatting:** runs the [`dartfmt` tool from the `dart_style` package](https://github.com/dart-lang/dart_style) over source code. - **Static Analysis:** runs the [`dartanalyzer`](https://www.dartlang.org/tools/analyzer/) over source code. +- **Documentation Generation:** runs the tool from [the `dartdoc` package](https://github.com/dart-lang/dartdoc) to generate docs. - **Serving Examples:** uses [`pub serve`](https://www.dartlang.org/tools/pub/cmd/pub-serve.html) to serve the project examples. - **Applying a License to Source Files:** copies a LICENSE file to all applicable files. @@ -75,6 +77,7 @@ dev_dependencies: coverage: "^0.7.2" dart_dev: "^1.0.0" dart_style: "^0.2.0" + dartdoc: "^0.4.0" test: "^0.12.0" ``` @@ -130,6 +133,7 @@ see the help usage. Try it out by running any of the following tasks: ddev analyze ddev copy-license ddev coverage +ddev docs ddev examples ddev format ddev test @@ -138,6 +142,7 @@ ddev test pub run dart_dev analyze pub run dart_dev copy-license pub run dart_dev coverage +pub run dart_dev docs pub run dart_dev examples pub run dart_dev format pub run dart_dev test @@ -158,6 +163,7 @@ main(args) async { // config.analyze // config.copyLicense // config.coverage + // config.docs // config.examples // config.format // config.init @@ -201,6 +207,9 @@ Name | Type | Default | Description > > Otherwise, visit http://ltp.sourceforge.net/coverage/lcov.php +### `docs` config +There are currently no project-configuration settings for the `docs` task. + ### `examples` Config All configuration options for the `examples` task are found on the `config.examples` object. @@ -250,6 +259,7 @@ Supported tasks: analyze copy-license coverage + docs examples format init @@ -259,6 +269,7 @@ Supported tasks: - Static analysis: `ddev analyze` - Applying license to source files: `ddev copy-license` - Code coverage: `ddev coverage` +- Documentation generation: `ddev docs` - Serving examples: `ddev examples` - Dart formatter: `ddev format` - Initialization: `ddev init` diff --git a/lib/src/dart_dev_cli.dart b/lib/src/dart_dev_cli.dart index f26524c5..a17489ff 100644 --- a/lib/src/dart_dev_cli.dart +++ b/lib/src/dart_dev_cli.dart @@ -32,6 +32,7 @@ import 'package:dart_dev/src/tasks/config.dart'; import 'package:dart_dev/src/tasks/analyze/cli.dart'; import 'package:dart_dev/src/tasks/copy_license/cli.dart'; import 'package:dart_dev/src/tasks/coverage/cli.dart'; +import 'package:dart_dev/src/tasks/docs/cli.dart'; import 'package:dart_dev/src/tasks/examples/cli.dart'; import 'package:dart_dev/src/tasks/format/cli.dart'; import 'package:dart_dev/src/tasks/init/cli.dart'; @@ -56,6 +57,7 @@ dev(List args) async { registerTask(new AnalyzeCli(), config.analyze); registerTask(new CopyLicenseCli(), config.copyLicense); registerTask(new CoverageCli(), config.coverage); + registerTask(new DocsCli(), config.docs); registerTask(new ExamplesCli(), config.examples); registerTask(new FormatCli(), config.format); registerTask(new InitCli(), config.init); diff --git a/lib/src/platform_util/api.dart b/lib/src/platform_util/api.dart index 2fecd166..8861c25c 100644 --- a/lib/src/platform_util/api.dart +++ b/lib/src/platform_util/api.dart @@ -1,3 +1,17 @@ +// Copyright 2015 Workiva Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + library dart_dev.src.platform_util.api; import 'dart:async'; diff --git a/lib/src/platform_util/mock_platform_util.dart b/lib/src/platform_util/mock_platform_util.dart index 51ae89be..aed146a5 100644 --- a/lib/src/platform_util/mock_platform_util.dart +++ b/lib/src/platform_util/mock_platform_util.dart @@ -1,3 +1,17 @@ +// Copyright 2015 Workiva Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + library dart_dev.src.platform_util.mock_platform_util; import 'dart:async'; diff --git a/lib/src/platform_util/platform_util.dart b/lib/src/platform_util/platform_util.dart index f32799b9..4bb7ad74 100644 --- a/lib/src/platform_util/platform_util.dart +++ b/lib/src/platform_util/platform_util.dart @@ -1,3 +1,17 @@ +// Copyright 2015 Workiva Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + library dart_dev.src.platform_util.platform_util; import 'dart:async'; diff --git a/lib/src/platform_util/standard_platform_util.dart b/lib/src/platform_util/standard_platform_util.dart index db000040..40882a2c 100644 --- a/lib/src/platform_util/standard_platform_util.dart +++ b/lib/src/platform_util/standard_platform_util.dart @@ -1,3 +1,17 @@ +// Copyright 2015 Workiva Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + library dart_dev.src.platform_util.standard_platform_util; import 'dart:async'; diff --git a/lib/src/tasks/config.dart b/lib/src/tasks/config.dart index 5e4eef27..2a9f2760 100644 --- a/lib/src/tasks/config.dart +++ b/lib/src/tasks/config.dart @@ -17,6 +17,7 @@ library dart_dev.src.tasks.config; import 'package:dart_dev/src/tasks/analyze/config.dart'; import 'package:dart_dev/src/tasks/copy_license/config.dart'; import 'package:dart_dev/src/tasks/coverage/config.dart'; +import 'package:dart_dev/src/tasks/docs/config.dart'; import 'package:dart_dev/src/tasks/examples/config.dart'; import 'package:dart_dev/src/tasks/format/config.dart'; import 'package:dart_dev/src/tasks/init/config.dart'; @@ -28,6 +29,7 @@ class Config { AnalyzeConfig analyze = new AnalyzeConfig(); CopyLicenseConfig copyLicense = new CopyLicenseConfig(); CoverageConfig coverage = new CoverageConfig(); + DocsConfig docs = new DocsConfig(); ExamplesConfig examples = new ExamplesConfig(); FormatConfig format = new FormatConfig(); InitConfig init = new InitConfig(); diff --git a/lib/src/tasks/coverage/exceptions.dart b/lib/src/tasks/coverage/exceptions.dart index 9bb97c74..8810f9bd 100644 --- a/lib/src/tasks/coverage/exceptions.dart +++ b/lib/src/tasks/coverage/exceptions.dart @@ -1,3 +1,17 @@ +// Copyright 2015 Workiva Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + library dart_dev.src.tasks.coverage.exceptions; const String missingLcovMessage = ''' diff --git a/lib/src/tasks/docs/api.dart b/lib/src/tasks/docs/api.dart new file mode 100644 index 00000000..f95c482b --- /dev/null +++ b/lib/src/tasks/docs/api.dart @@ -0,0 +1,73 @@ +// Copyright 2015 Workiva Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +library dart_dev.src.tasks.docs.api; + +import 'dart:async'; +import 'dart:io'; + +import 'package:dart_dev/util.dart' show TaskProcess; + +import 'package:dart_dev/src/tasks/task.dart'; + +class DocsFailure implements Exception {} + +class DocsResult extends TaskResult { + final Directory output; + DocsResult() + : super.success(), + output = new Directory('doc/api/'); +} + +class DocsTask extends Task { + static Future start() async { + DocsTask task = new DocsTask._(); + task._run(); + return task; + } + + static Future run() async { + DocsTask task = new DocsTask._(); + return task._run(); + } + + Stream _dartdocStderr; + Stream _dartdocStdout; + String _pubCommand; + + Completer _done = new Completer(); + + DocsTask._(); + + Future get done => _done.future; + Stream get errorOutput => _dartdocStderr; + Stream get output => _dartdocStdout; + String get pubCommand => _pubCommand; + + Future _run() async { + var executable = 'pub'; + var args = ['run', 'dartdoc']; + _pubCommand = '$executable ${args.join(' ')}'; + TaskProcess process = new TaskProcess(executable, args); + _dartdocStderr = process.stderr; + _dartdocStdout = process.stdout; + await process.done; + if (await process.exitCode > 0) { + _done.completeError(new DocsFailure()); + } else { + _done.complete(new DocsResult()); + } + return _done.future; + } +} diff --git a/lib/src/tasks/docs/cli.dart b/lib/src/tasks/docs/cli.dart new file mode 100644 index 00000000..8cedfd07 --- /dev/null +++ b/lib/src/tasks/docs/cli.dart @@ -0,0 +1,57 @@ +// Copyright 2015 Workiva Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +library dart_dev.src.tasks.docs.cli; + +import 'dart:async'; +import 'dart:io'; + +import 'package:args/args.dart'; +import 'package:dart_dev/util.dart' show hasImmediateDependency, reporter; +import 'package:path/path.dart' as path; + +import 'package:dart_dev/src/tasks/cli.dart'; +import 'package:dart_dev/src/tasks/docs/api.dart'; + +class DocsCli extends TaskCli { + ArgParser argParser = new ArgParser() + ..addFlag('open', + defaultsTo: true, help: 'Opens the docs site after being generated.'); + + final String command = 'docs'; + + Future run(ArgResults parsedArgs) async { + if (!hasImmediateDependency('dartdoc')) return new CliResult.fail( + 'Package "dartdoc" must be an immediate dependency in order to run its executables.'); + + bool open = TaskCli.valueOf('open', parsedArgs, true); + + DocsResult result; + try { + DocsTask task = await DocsTask.start(); + reporter.logGroup(task.pubCommand, + outputStream: task.output, errorStream: task.errorOutput); + result = await task.done; + } on DocsFailure catch (e) { + return new CliResult.fail('$e'); + } catch (e, stackTrace) { + return new CliResult.fail('$e\n$stackTrace'); + } + if (open) { + await Process.run('open', [path.join(result.output.path, 'index.html')]); + } + return new CliResult.success( + 'Documentation generated: ${result.output.path}'); + } +} diff --git a/lib/src/tasks/docs/config.dart b/lib/src/tasks/docs/config.dart new file mode 100644 index 00000000..370660da --- /dev/null +++ b/lib/src/tasks/docs/config.dart @@ -0,0 +1,19 @@ +// Copyright 2015 Workiva Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +library dart_dev.src.tasks.docs.config; + +import 'package:dart_dev/src/tasks/config.dart'; + +class DocsConfig extends TaskConfig {} diff --git a/lib/src/tasks/init/api.dart b/lib/src/tasks/init/api.dart index a6ce83a0..1e572a25 100644 --- a/lib/src/tasks/init/api.dart +++ b/lib/src/tasks/init/api.dart @@ -32,6 +32,7 @@ main(List args) async { // config.analyze // config.copyLicense // config.coverage + // config.docs // config.examples // config.format // config.test diff --git a/pubspec.yaml b/pubspec.yaml index d9e28827..0ed2a246 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -14,6 +14,7 @@ dependencies: args: "^0.13.0" coverage: "^0.7.2" dart_style: "^0.2.0" + dartdoc: "^0.4.0" path: "^1.3.6" test: "^0.12.0" yaml: "^2.1.0" diff --git a/test/fixtures/docs/docs/lib/main.dart b/test/fixtures/docs/docs/lib/main.dart new file mode 100644 index 00000000..a224ed2c --- /dev/null +++ b/test/fixtures/docs/docs/lib/main.dart @@ -0,0 +1,5 @@ +/// A test project with documentation. +library docs; + +/// Determine if this project has documentation! +bool hasDocumentation() => true; \ No newline at end of file diff --git a/test/fixtures/docs/docs/pubspec.yaml b/test/fixtures/docs/docs/pubspec.yaml new file mode 100644 index 00000000..5a03ad1f --- /dev/null +++ b/test/fixtures/docs/docs/pubspec.yaml @@ -0,0 +1,6 @@ +name: test_no_test_package +version: 0.0.0 +dev_dependencies: + dart_dev: + path: ../../../.. + dartdoc: "^0.4.0" \ No newline at end of file diff --git a/test/fixtures/docs/no_dartdoc_package/pubspec.yaml b/test/fixtures/docs/no_dartdoc_package/pubspec.yaml new file mode 100644 index 00000000..bf0bd1a9 --- /dev/null +++ b/test/fixtures/docs/no_dartdoc_package/pubspec.yaml @@ -0,0 +1,5 @@ +name: docs_no_dartdoc_package +version: 0.0.0 +dev_dependencies: + dart_dev: + path: ../../../.. \ No newline at end of file diff --git a/test/integration/docs_test.dart b/test/integration/docs_test.dart new file mode 100644 index 00000000..d84d19a2 --- /dev/null +++ b/test/integration/docs_test.dart @@ -0,0 +1,54 @@ +// Copyright 2015 Workiva Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +@TestOn('vm') +library dart_dev.test.integration.docs_test; + +import 'dart:async'; +import 'dart:io'; + +import 'package:dart_dev/util.dart' show TaskProcess; +import 'package:path/path.dart' as path; +import 'package:test/test.dart'; + +const String projectWithDocs = 'test/fixtures/docs/docs'; +const String projectWithoutDartdoc = 'test/fixtures/docs/no_dartdoc_package'; + +Future generateDocsFor(String projectPath) async { + await Process.run('pub', ['get'], workingDirectory: projectPath); + + var args = ['run', 'dart_dev', 'docs', '--no-open']; + TaskProcess process = + new TaskProcess('pub', args, workingDirectory: projectPath); + await process.done; + + return (await process.exitCode) == 0; +} + +void main() { + group('Docs task', () { + test('should generate docs for a valid project', () async { + expect(await generateDocsFor(projectWithDocs), isTrue); + expect( + FileSystemEntity + .isFileSync(path.join(projectWithDocs, 'doc/api/index.html')), + isTrue); + }); + + test('should fail if the "dartdoc" package is not an immediate dependency', + () async { + expect(await generateDocsFor(projectWithoutDartdoc), isFalse); + }); + }); +}