-
Notifications
You must be signed in to change notification settings - Fork 44
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Run and process dartdoc output to create pub-data.json and the covera…
…ge report. (#1270) * Run and process dartdoc to create pub-data.json and the coverage report. * Do not retry dartdoc. * Refactored package_context.dart code.
- Loading branch information
Showing
35 changed files
with
825 additions
and
185 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
// Copyright (c) 2023, 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:convert'; | ||
import 'dart:io'; | ||
|
||
import 'package:path/path.dart' as p; | ||
|
||
import '../model.dart'; | ||
import '../report/_common.dart'; | ||
import 'dartdoc_index.dart'; | ||
import 'index_to_pubdata.dart'; | ||
import 'pub_dartdoc_data.dart'; | ||
|
||
final dartdocSubsectionHeadline = | ||
'20% or more of the public API has dartdoc comments'; | ||
|
||
Future<PubDartdocData> generateAndSavePubDataJson( | ||
String dartdocOutputDir) async { | ||
final content = | ||
await File(p.join(dartdocOutputDir, 'index.json')).readAsString(); | ||
final index = DartdocIndex.parseJsonText(content); | ||
final data = dataFromDartdocIndex(index); | ||
await File(p.join(dartdocOutputDir, 'pub-data.json')) | ||
.writeAsString(json.encode(data.toJson())); | ||
return data; | ||
} | ||
|
||
Subsection dartdocFailedSubsection(String reason) { | ||
return Subsection( | ||
dartdocSubsectionHeadline, | ||
[RawParagraph(reason)], | ||
0, | ||
10, | ||
ReportStatus.failed, | ||
); | ||
} | ||
|
||
Future<Subsection> createDocumentationCoverageSection( | ||
PubDartdocData data) async { | ||
final documented = data.coverage?.documented ?? 0; | ||
final total = data.coverage?.total ?? 0; | ||
final symbolsMissingDocumentation = data.apiElements | ||
?.where((e) => e.documentation == null || e.documentation!.isEmpty) | ||
.map((e) => e.name) | ||
.toList(); | ||
|
||
final maxPoints = 10; | ||
final ratio = total <= 0 ? 1.0 : documented / total; | ||
final accepted = ratio >= 0.2; | ||
final percent = (100.0 * ratio).toStringAsFixed(1); | ||
final summary = StringBuffer(); | ||
final grantedPoints = accepted ? maxPoints : 0; | ||
summary.write( | ||
'$documented out of $total API elements ($percent %) have documentation comments.'); | ||
|
||
if (!accepted) { | ||
summary.write('\n\n' | ||
'Providing good documentation for libraries, classes, functions, and other API ' | ||
'elements improves code readability and helps developers find and use your API. ' | ||
'Document at least 20% of the public API elements.'); | ||
} | ||
|
||
if (symbolsMissingDocumentation != null && | ||
symbolsMissingDocumentation.isNotEmpty) { | ||
summary.write('\n\n' | ||
'Some symbols that are missing documentation: ' | ||
'${symbolsMissingDocumentation.take(5).map((e) => '`$e`').join(', ')}.'); | ||
} | ||
|
||
return Subsection( | ||
dartdocSubsectionHeadline, | ||
[RawParagraph(summary.toString())], | ||
grantedPoints, | ||
maxPoints, | ||
grantedPoints == maxPoints ? ReportStatus.passed : ReportStatus.partial, | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
// Copyright (c) 2023, 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:convert'; | ||
|
||
import 'package:json_annotation/json_annotation.dart'; | ||
|
||
part 'dartdoc_index.g.dart'; | ||
|
||
// TODO(https://github.com/dart-lang/pana/issues/1273): remove these and use a different pub-data.json file format. | ||
const kindNames = <int, String>{ | ||
0: 'accessor', | ||
1: 'constant', | ||
2: 'constructor', | ||
3: 'class', | ||
4: 'dynamic', | ||
5: 'enum', | ||
6: 'extension', | ||
7: 'extension type', | ||
8: 'function', | ||
9: 'library', | ||
10: 'method', | ||
11: 'mixin', | ||
12: 'Never', | ||
13: 'package', | ||
14: 'parameter', | ||
15: 'prefix', | ||
16: 'property', | ||
17: 'SDK', | ||
18: 'topic', | ||
19: 'top-level constant', | ||
20: 'top-level property', | ||
21: 'typedef', | ||
22: 'type parameter', | ||
}; | ||
|
||
/// The parsed content of the `index.json` generated by dartdoc. | ||
class DartdocIndex { | ||
final List<DartdocIndexEntry> entries; | ||
|
||
DartdocIndex(this.entries); | ||
|
||
factory DartdocIndex.parseJsonText(String content) { | ||
return DartdocIndex.fromJsonList(json.decode(content) as List); | ||
} | ||
|
||
factory DartdocIndex.fromJsonList(List jsonList) { | ||
final list = jsonList | ||
.map((item) => DartdocIndexEntry.fromJson(item as Map<String, dynamic>)) | ||
.toList(); | ||
return DartdocIndex(list); | ||
} | ||
|
||
late final libraryRelativeUrls = Map<String, String>.fromEntries( | ||
entries | ||
.where((e) => e.isLibrary && e.qualifiedName != null && e.href != null) | ||
.map( | ||
(e) => MapEntry<String, String>( | ||
e.qualifiedName!.split('.').first, | ||
e.href!, | ||
), | ||
), | ||
); | ||
|
||
String toJsonText() => json.encode(entries); | ||
} | ||
|
||
@JsonSerializable(includeIfNull: false) | ||
class DartdocIndexEntry { | ||
final String? name; | ||
final String? qualifiedName; | ||
final String? href; | ||
final int? kind; | ||
final int? packageRank; | ||
final int? overriddenDepth; | ||
final String? packageName; | ||
final String? desc; | ||
final DartdocIndexEntryEnclosedBy? enclosedBy; | ||
|
||
DartdocIndexEntry({ | ||
required this.name, | ||
required this.qualifiedName, | ||
required this.href, | ||
this.kind, | ||
this.packageRank, | ||
this.overriddenDepth, | ||
this.packageName, | ||
this.desc, | ||
this.enclosedBy, | ||
}); | ||
|
||
factory DartdocIndexEntry.fromJson(Map<String, dynamic> json) => | ||
_$DartdocIndexEntryFromJson(json); | ||
|
||
Map<String, dynamic> toJson() => _$DartdocIndexEntryToJson(this); | ||
|
||
/// Wether the entry is a top-level library. | ||
bool get isLibrary => href != null && href!.endsWith('-library.html'); | ||
bool get isClass => href != null && href!.endsWith('-class.html'); | ||
} | ||
|
||
@JsonSerializable(includeIfNull: false) | ||
class DartdocIndexEntryEnclosedBy { | ||
final String? name; | ||
final int? kind; | ||
final String? href; | ||
|
||
DartdocIndexEntryEnclosedBy({ | ||
this.name, | ||
this.kind, | ||
this.href, | ||
}); | ||
|
||
factory DartdocIndexEntryEnclosedBy.fromJson(Map<String, dynamic> json) => | ||
_$DartdocIndexEntryEnclosedByFromJson(json); | ||
|
||
Map<String, dynamic> toJson() => _$DartdocIndexEntryEnclosedByToJson(this); | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
// Copyright (c) 2023, 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:convert'; | ||
import 'dart:io'; | ||
|
||
import 'package:path/path.dart' as p; | ||
|
||
import '../utils.dart' show yamlToJson; | ||
|
||
Future<void> normalizeDartdocOptionsYaml(String packageDir) async { | ||
final optionsFile = File(p.join(packageDir, 'dartdoc_options.yaml')); | ||
Map<String, dynamic>? originalContent; | ||
try { | ||
originalContent = yamlToJson(await optionsFile.readAsString()); | ||
} on IOException { | ||
// pass, ignore missing file | ||
} on FormatException { | ||
// pass, ignore broken file | ||
} | ||
final updatedContent = _customizeDartdocOptions(originalContent); | ||
await optionsFile.writeAsString(json.encode(updatedContent)); | ||
} | ||
|
||
/// Returns a new, pub-specific dartdoc options based on [original]. | ||
/// | ||
/// dartdoc_options.yaml allows to change how doc content is generated. | ||
/// To provide uniform experience across the pub site, and to reduce the | ||
/// potential attack surface (HTML-, and code-injections, code executions), | ||
/// we do not support every option. | ||
/// | ||
/// https://github.com/dart-lang/dartdoc#dartdoc_optionsyaml | ||
/// | ||
/// Discussion on the enabled options: | ||
/// https://github.com/dart-lang/pub-dev/issues/4521#issuecomment-779821098 | ||
Map<String, dynamic> _customizeDartdocOptions(Map<String, dynamic>? original) { | ||
final passThroughOptions = <String, dynamic>{}; | ||
if (original != null && | ||
original.containsKey('dartdoc') && | ||
original['dartdoc'] is Map<String, dynamic>) { | ||
final dartdoc = original['dartdoc'] as Map<String, dynamic>; | ||
for (final key in _passThroughKeys) { | ||
if (dartdoc.containsKey(key)) { | ||
passThroughOptions[key] = dartdoc[key]; | ||
} | ||
} | ||
} | ||
return <String, dynamic>{ | ||
'dartdoc': <String, dynamic>{ | ||
...passThroughOptions, | ||
'showUndocumentedCategories': true, | ||
}, | ||
}; | ||
} | ||
|
||
final _passThroughKeys = <String>[ | ||
'categories', | ||
'categoryOrder', | ||
// Note: consider enabling after checking that the relative path doesn't escape the package folder | ||
// 'examplePathPrefix', | ||
'exclude', | ||
'include', | ||
'nodoc', | ||
]; |
Oops, something went wrong.