OLD | NEW |
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 /// **docgen** is a tool for creating machine readable representations of Dart | 5 /// **docgen** is a tool for creating machine readable representations of Dart |
6 /// code metadata, including: classes, members, comments and annotations. | 6 /// code metadata, including: classes, members, comments and annotations. |
7 /// | 7 /// |
8 /// docgen is run on a `.dart` file or a directory containing `.dart` files. | 8 /// docgen is run on a `.dart` file or a directory containing `.dart` files. |
9 /// | 9 /// |
10 /// $ dart docgen.dart [OPTIONS] [FILE/DIR] | 10 /// $ dart docgen.dart [OPTIONS] [FILE/DIR] |
(...skipping 18 matching lines...) Expand all Loading... |
29 import '../../../sdk/lib/_internal/compiler/implementation/mirrors/dart2js_mirro
r.dart' | 29 import '../../../sdk/lib/_internal/compiler/implementation/mirrors/dart2js_mirro
r.dart' |
30 as dart2js; | 30 as dart2js; |
31 import '../../../sdk/lib/_internal/compiler/implementation/mirrors/mirrors.dart'
; | 31 import '../../../sdk/lib/_internal/compiler/implementation/mirrors/mirrors.dart'
; |
32 import '../../../sdk/lib/_internal/compiler/implementation/mirrors/mirrors_util.
dart' | 32 import '../../../sdk/lib/_internal/compiler/implementation/mirrors/mirrors_util.
dart' |
33 as dart2js_util; | 33 as dart2js_util; |
34 import '../../../sdk/lib/_internal/compiler/implementation/source_file_provider.
dart'; | 34 import '../../../sdk/lib/_internal/compiler/implementation/source_file_provider.
dart'; |
35 import '../../../sdk/lib/_internal/libraries.dart'; | 35 import '../../../sdk/lib/_internal/libraries.dart'; |
36 | 36 |
37 var logger = new Logger('Docgen'); | 37 var logger = new Logger('Docgen'); |
38 | 38 |
| 39 const DEFAULT_OUTPUT_DIRECTORY = 'docs'; |
| 40 |
| 41 var _outputDirectory; |
| 42 |
39 const String USAGE = 'Usage: dart docgen.dart [OPTIONS] fooDir/barFile'; | 43 const String USAGE = 'Usage: dart docgen.dart [OPTIONS] fooDir/barFile'; |
40 | 44 |
41 | 45 |
42 List<String> skippedAnnotations = const [ | 46 List<String> skippedAnnotations = const [ |
43 'metadata.DocsEditable', '_js_helper.JSName', '_js_helper.Creates', | 47 'metadata.DocsEditable', '_js_helper.JSName', '_js_helper.Creates', |
44 '_js_helper.Returns']; | 48 '_js_helper.Returns']; |
45 | 49 |
46 /// Set of libraries declared in the SDK, so libraries that can be accessed | 50 /// Set of libraries declared in the SDK, so libraries that can be accessed |
47 /// when running dart by default. | 51 /// when running dart by default. |
48 Iterable<LibraryMirror> _sdkLibraries; | 52 Iterable<LibraryMirror> _sdkLibraries; |
49 | 53 |
50 /// The dart:core library, which contains all types that are always available | 54 /// The dart:core library, which contains all types that are always available |
51 /// without import. | 55 /// without import. |
52 LibraryMirror _coreLibrary; | 56 LibraryMirror _coreLibrary; |
53 | 57 |
54 /// Support for [:foo:]-style code comments to the markdown parser. | 58 /// Support for [:foo:]-style code comments to the markdown parser. |
55 List<markdown.InlineSyntax> markdownSyntaxes = | 59 List<markdown.InlineSyntax> markdownSyntaxes = |
56 [new markdown.CodeSyntax(r'\[:\s?((?:.|\n)*?)\s?:\]')]; | 60 [new markdown.CodeSyntax(r'\[:\s?((?:.|\n)*?)\s?:\]')]; |
57 | 61 |
58 /// Index of all indexable items. This also ensures that no class is | 62 /// Index of all indexable items. This also ensures that no class is |
59 /// created more than once. | 63 /// created more than once. |
60 Map<String, Indexable> entityMap = new Map<String, Indexable>(); | 64 Map<String, Indexable> entityMap = new Map<String, Indexable>(); |
61 | 65 |
62 /// This is set from the command line arguments flag --include-private | 66 /// This is set from the command line arguments flag --include-private |
63 bool _includePrivate = false; | 67 bool _includePrivate = false; |
64 | 68 |
| 69 /// Library names to explicitly exclude. |
| 70 /// |
| 71 /// Set from the command line option |
| 72 /// --exclude-lib. |
| 73 List<String> _excluded; |
| 74 |
65 // TODO(janicejl): Make MDN content generic or pluggable. Maybe move | 75 // TODO(janicejl): Make MDN content generic or pluggable. Maybe move |
66 // MDN-specific code to its own library that is imported into the default impl? | 76 // MDN-specific code to its own library that is imported into the default impl? |
67 /// Map of all the comments for dom elements from MDN. | 77 /// Map of all the comments for dom elements from MDN. |
68 Map _mdn; | 78 Map _mdn; |
69 | 79 |
70 /// Docgen constructor initializes the link resolver for markdown parsing. | 80 /// Docgen constructor initializes the link resolver for markdown parsing. |
71 /// Also initializes the command line arguments. | 81 /// Also initializes the command line arguments. |
72 /// | 82 /// |
73 /// [packageRoot] is the packages directory of the directory being analyzed. | 83 /// [packageRoot] is the packages directory of the directory being analyzed. |
74 /// If [includeSdk] is `true`, then any SDK libraries explicitly imported will | 84 /// If [includeSdk] is `true`, then any SDK libraries explicitly imported will |
75 /// also be documented. | 85 /// also be documented. |
76 /// If [parseSdk] is `true`, then all Dart SDK libraries will be documented. | 86 /// If [parseSdk] is `true`, then all Dart SDK libraries will be documented. |
77 /// This option is useful when only the SDK libraries are needed. | 87 /// This option is useful when only the SDK libraries are needed. |
78 /// | 88 /// |
79 /// Returned Future completes with true if document generation is successful. | 89 /// Returned Future completes with true if document generation is successful. |
80 Future<bool> docgen(List<String> files, {String packageRoot, | 90 Future<bool> docgen(List<String> files, {String packageRoot, |
81 bool outputToYaml: true, bool includePrivate: false, bool includeSdk: false, | 91 bool outputToYaml: true, bool includePrivate: false, bool includeSdk: false, |
82 bool parseSdk: false, bool append: false, String introduction: ''}) { | 92 bool parseSdk: false, bool append: false, String introduction: '', |
| 93 out: DEFAULT_OUTPUT_DIRECTORY, List<String> excludeLibraries}) { |
| 94 _excluded = excludeLibraries; |
83 _includePrivate = includePrivate; | 95 _includePrivate = includePrivate; |
| 96 _outputDirectory = out; |
84 if (!append) { | 97 if (!append) { |
85 var dir = new Directory('docs'); | 98 var dir = new Directory(_outputDirectory); |
86 if (dir.existsSync()) dir.deleteSync(recursive: true); | 99 if (dir.existsSync()) dir.deleteSync(recursive: true); |
87 } | 100 } |
88 | 101 |
89 if (packageRoot == null && !parseSdk) { | 102 if (packageRoot == null && !parseSdk) { |
90 var type = FileSystemEntity.typeSync(files.first); | 103 var type = FileSystemEntity.typeSync(files.first); |
91 if (type == FileSystemEntityType.DIRECTORY) { | 104 if (type == FileSystemEntityType.DIRECTORY) { |
92 packageRoot = _findPackageRoot(files.first); | 105 packageRoot = _findPackageRoot(files.first); |
93 } else if (type == FileSystemEntityType.FILE) { | 106 } else if (type == FileSystemEntityType.FILE) { |
94 logger.warning('WARNING: No package root defined. If Docgen fails, try ' | 107 logger.warning('WARNING: No package root defined. If Docgen fails, try ' |
95 'again by setting the --package-root option.'); | 108 'again by setting the --package-root option.'); |
96 } | 109 } |
97 } | 110 } |
98 logger.info('Package Root: ${packageRoot}'); | 111 logger.info('Package Root: ${packageRoot}'); |
| 112 var requestedLibraries = _listLibraries(files); |
| 113 var allLibraries = []..addAll(requestedLibraries); |
| 114 if (includeSdk) { |
| 115 allLibraries.addAll(_listSdk()); |
| 116 } |
99 | 117 |
100 return getMirrorSystem(files, packageRoot: packageRoot, parseSdk: parseSdk) | 118 return getMirrorSystem(allLibraries, packageRoot: packageRoot, |
| 119 parseSdk: parseSdk) |
101 .then((MirrorSystem mirrorSystem) { | 120 .then((MirrorSystem mirrorSystem) { |
102 if (mirrorSystem.libraries.isEmpty) { | 121 if (mirrorSystem.libraries.isEmpty) { |
103 throw new StateError('No library mirrors were created.'); | 122 throw new StateError('No library mirrors were created.'); |
104 } | 123 } |
105 var librariesWeAskedFor = _listLibraries(files); | 124 var availableLibraries = mirrorSystem.libraries.values.where( |
106 var librariesWeGot = mirrorSystem.libraries.values.where( | |
107 (each) => each.uri.scheme == 'file'); | 125 (each) => each.uri.scheme == 'file'); |
108 _sdkLibraries = mirrorSystem.libraries.values.where( | 126 _sdkLibraries = mirrorSystem.libraries.values.where( |
109 (each) => each.uri.scheme == 'dart'); | 127 (each) => each.uri.scheme == 'dart'); |
110 _coreLibrary = _sdkLibraries.singleWhere((lib) => | 128 _coreLibrary = _sdkLibraries.singleWhere((lib) => |
111 lib.uri.toString().startsWith('dart:core')); | 129 lib.uri.toString().startsWith('dart:core')); |
112 var librariesWeGotByPath = new Map.fromIterables( | 130 var availableLibrariesByPath = new Map.fromIterables( |
113 librariesWeGot.map((each) => each.uri.toFilePath()), | 131 availableLibraries.map((each) => each.uri.toFilePath()), |
114 librariesWeGot); | 132 availableLibraries); |
115 var librariesToDocument = librariesWeAskedFor.map( | 133 var librariesToDocument = requestedLibraries.map( |
116 (each) => librariesWeGotByPath.putIfAbsent(each, | 134 (each) => availableLibrariesByPath.putIfAbsent(each, |
117 () => throw "Missing library $each")).toList(); | 135 () => throw "Missing library $each")).toList(); |
118 librariesToDocument.addAll((includeSdk || parseSdk) ? _sdkLibraries : []); | 136 librariesToDocument.addAll((includeSdk || parseSdk) ? _sdkLibraries : []); |
| 137 librariesToDocument.removeWhere((x) => _excluded.contains(x.simpleName)); |
119 _documentLibraries(librariesToDocument, includeSdk: includeSdk, | 138 _documentLibraries(librariesToDocument, includeSdk: includeSdk, |
120 outputToYaml: outputToYaml, append: append, parseSdk: parseSdk, | 139 outputToYaml: outputToYaml, append: append, parseSdk: parseSdk, |
121 introduction: introduction); | 140 introduction: introduction); |
122 return true; | 141 return true; |
123 }); | 142 }); |
124 } | 143 } |
125 | 144 |
126 /// For a library's [mirror], determine the name of the package (if any) we | 145 /// For a library's [mirror], determine the name of the package (if any) we |
127 /// believe it came from (because of its file URI). | 146 /// believe it came from (because of its file URI). |
128 /// | 147 /// |
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
202 // Only add the file if it does not contain 'part of' | 221 // Only add the file if it does not contain 'part of' |
203 // TODO(janicejl): Remove when Issue(12406) is resolved. | 222 // TODO(janicejl): Remove when Issue(12406) is resolved. |
204 var contents = new File(f).readAsStringSync(); | 223 var contents = new File(f).readAsStringSync(); |
205 if (!(contents.contains(new RegExp('\npart of ')) || | 224 if (!(contents.contains(new RegExp('\npart of ')) || |
206 contents.startsWith(new RegExp('part of ')))) { | 225 contents.startsWith(new RegExp('part of ')))) { |
207 libraries.add(f); | 226 libraries.add(f); |
208 logger.info('Added to libraries: $f'); | 227 logger.info('Added to libraries: $f'); |
209 } | 228 } |
210 } | 229 } |
211 }); | 230 }); |
212 return libraries; | 231 return libraries.map(path.absolute).map(path.normalize).toList(); |
213 } | 232 } |
214 | 233 |
215 String _findPackageRoot(String directory) { | 234 String _findPackageRoot(String directory) { |
216 var files = listDir(directory, recursive: true); | 235 var files = listDir(directory, recursive: true); |
217 // Return '' means that there was no pubspec.yaml and therefor no packageRoot. | 236 // Return '' means that there was no pubspec.yaml and therefor no packageRoot. |
218 String packageRoot = files.firstWhere((f) => | 237 String packageRoot = files.firstWhere((f) => |
219 f.endsWith('${path.separator}pubspec.yaml'), orElse: () => ''); | 238 f.endsWith('${path.separator}pubspec.yaml'), orElse: () => ''); |
220 if (packageRoot != '') { | 239 if (packageRoot != '') { |
221 packageRoot = path.join(path.dirname(packageRoot), 'packages'); | 240 packageRoot = path.join(path.dirname(packageRoot), 'packages'); |
222 } | 241 } |
(...skipping 15 matching lines...) Expand all Loading... |
238 if (info.documented) { | 257 if (info.documented) { |
239 sdk.add('dart:$name'); | 258 sdk.add('dart:$name'); |
240 logger.info('Add to SDK: ${sdk.last}'); | 259 logger.info('Add to SDK: ${sdk.last}'); |
241 } | 260 } |
242 }); | 261 }); |
243 return sdk; | 262 return sdk; |
244 } | 263 } |
245 | 264 |
246 /// Analyzes set of libraries by getting a mirror system and triggers the | 265 /// Analyzes set of libraries by getting a mirror system and triggers the |
247 /// documentation of the libraries. | 266 /// documentation of the libraries. |
248 Future<MirrorSystem> getMirrorSystem(List<String> args, {String packageRoot, | 267 Future<MirrorSystem> getMirrorSystem(List<String> libraries, |
249 bool parseSdk: false}) { | 268 {String packageRoot, bool parseSdk: false}) { |
250 var libraries = !parseSdk ? _listLibraries(args) : _listSdk(); | |
251 if (libraries.isEmpty) throw new StateError('No Libraries.'); | 269 if (libraries.isEmpty) throw new StateError('No Libraries.'); |
252 // Finds the root of SDK library based off the location of docgen. | 270 // Finds the root of SDK library based off the location of docgen. |
253 | 271 |
254 var root = findRootDirectory(); | 272 var root = findRootDirectory(); |
255 var sdkRoot = path.normalize(path.absolute(path.join(root, 'sdk'))); | 273 var sdkRoot = path.normalize(path.absolute(path.join(root, 'sdk'))); |
256 logger.info('SDK Root: ${sdkRoot}'); | 274 logger.info('SDK Root: ${sdkRoot}'); |
257 return _analyzeLibraries(libraries, sdkRoot, packageRoot: packageRoot); | 275 return _analyzeLibraries(libraries, sdkRoot, packageRoot: packageRoot); |
258 } | 276 } |
259 | 277 |
260 String findRootDirectory() { | 278 String findRootDirectory() { |
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
316 // giant list of subclasses to be printed out. | 334 // giant list of subclasses to be printed out. |
317 if (includeSdk) (entityMap['dart-core.Object'] as Class).subclasses.clear(); | 335 if (includeSdk) (entityMap['dart-core.Object'] as Class).subclasses.clear(); |
318 | 336 |
319 var filteredEntities = entityMap.values.where(_isVisible); | 337 var filteredEntities = entityMap.values.where(_isVisible); |
320 | 338 |
321 // Outputs a JSON file with all libraries and their preview comments. | 339 // Outputs a JSON file with all libraries and their preview comments. |
322 // This will help the viewer know what libraries are available to read in. | 340 // This will help the viewer know what libraries are available to read in. |
323 var libraryMap; | 341 var libraryMap; |
324 var linkResolver = (name) => fixReference(name, null, null, null); | 342 var linkResolver = (name) => fixReference(name, null, null, null); |
325 if (append) { | 343 if (append) { |
326 var docsDir = listDir('docs'); | 344 var docsDir = listDir(_outputDirectory); |
327 if (!docsDir.contains('docs/library_list.json')) { | 345 if (!docsDir.contains('$_outputDirectory/library_list.json')) { |
328 throw new StateError('No library_list.json'); | 346 throw new StateError('No library_list.json'); |
329 } | 347 } |
330 libraryMap = | 348 libraryMap = |
331 JSON.decode(new File('docs/library_list.json').readAsStringSync()); | 349 JSON.decode(new File('$_outputDirectory/library_list.json').readAsString
Sync()); |
332 libraryMap['libraries'].addAll(filteredEntities | 350 libraryMap['libraries'].addAll(filteredEntities |
333 .where((e) => e is Library) | 351 .where((e) => e is Library) |
334 .map((e) => e.previewMap)); | 352 .map((e) => e.previewMap)); |
335 if (introduction.isNotEmpty) { | 353 if (introduction.isNotEmpty) { |
336 var intro = libraryMap['introduction']; | 354 var intro = libraryMap['introduction']; |
337 if (intro.isNotEmpty) intro += '<br/><br/>'; | 355 if (intro.isNotEmpty) intro += '<br/><br/>'; |
338 intro += markdown.markdownToHtml( | 356 intro += markdown.markdownToHtml( |
339 new File(introduction).readAsStringSync(), | 357 new File(introduction).readAsStringSync(), |
340 linkResolver: linkResolver, inlineSyntaxes: markdownSyntaxes); | 358 linkResolver: linkResolver, inlineSyntaxes: markdownSyntaxes); |
341 libraryMap['introduction'] = intro; | 359 libraryMap['introduction'] = intro; |
(...skipping 20 matching lines...) Expand all Loading... |
362 // Outputs all the qualified names documented with their type. | 380 // Outputs all the qualified names documented with their type. |
363 // This will help generate search results. | 381 // This will help generate search results. |
364 _writeToFile(filteredEntities.map((e) => | 382 _writeToFile(filteredEntities.map((e) => |
365 '${e.qualifiedName} ${e.typeName}').join('\n') + '\n', | 383 '${e.qualifiedName} ${e.typeName}').join('\n') + '\n', |
366 'index.txt', append: append); | 384 'index.txt', append: append); |
367 var index = new Map.fromIterables( | 385 var index = new Map.fromIterables( |
368 filteredEntities.map((e) => e.qualifiedName), | 386 filteredEntities.map((e) => e.qualifiedName), |
369 filteredEntities.map((e) => e.typeName)); | 387 filteredEntities.map((e) => e.typeName)); |
370 if (append) { | 388 if (append) { |
371 var previousIndex = | 389 var previousIndex = |
372 JSON.decode(new File('docs/index.json').readAsStringSync()); | 390 JSON.decode(new File('$_outputDirectory/index.json').readAsStringSync())
; |
373 index.addAll(previousIndex); | 391 index.addAll(previousIndex); |
374 } | 392 } |
375 _writeToFile(JSON.encode(index), 'index.json'); | 393 _writeToFile(JSON.encode(index), 'index.json'); |
376 } | 394 } |
377 | 395 |
378 Library generateLibrary(dart2js.Dart2JsLibraryMirror library) { | 396 Library generateLibrary(dart2js.Dart2JsLibraryMirror library) { |
379 var result = new Library(docName(library), | 397 var result = new Library(docName(library), |
380 (actualLibrary) => _commentToHtml(library, actualLibrary), | 398 (actualLibrary) => _commentToHtml(library, actualLibrary), |
381 _classes(library.classes), | 399 _classes(library.classes), |
382 _methods(library.functions), | 400 _methods(library.functions), |
(...skipping 381 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
764 if (mirror is ClassMirror && !mirror.isTypedef) { | 782 if (mirror is ClassMirror && !mirror.isTypedef) { |
765 var innerList = []; | 783 var innerList = []; |
766 mirror.typeArguments.forEach((e) { | 784 mirror.typeArguments.forEach((e) { |
767 innerList.add(new Type(docName(e), _typeGenerics(e))); | 785 innerList.add(new Type(docName(e), _typeGenerics(e))); |
768 }); | 786 }); |
769 return innerList; | 787 return innerList; |
770 } | 788 } |
771 return []; | 789 return []; |
772 } | 790 } |
773 | 791 |
774 /// Writes text to a file in the 'docs' directory. | 792 /// Writes text to a file in the output directory. |
775 void _writeToFile(String text, String filename, {bool append: false}) { | 793 void _writeToFile(String text, String filename, {bool append: false}) { |
776 Directory dir = new Directory('docs'); | 794 if (text == null) return; |
| 795 Directory dir = new Directory(_outputDirectory); |
777 if (!dir.existsSync()) { | 796 if (!dir.existsSync()) { |
778 dir.createSync(); | 797 dir.createSync(); |
779 } | 798 } |
780 // We assume there's a single extra level of directory structure for packages. | 799 // We assume there's a single extra level of directory structure for packages. |
781 if (path.split(filename).length > 1) { | 800 if (path.split(filename).length > 1) { |
782 var subdir = new Directory(path.join('docs', path.dirname(filename))); | 801 var subdir = new Directory(path.join(_outputDirectory, path.dirname(filename
))); |
783 if (!subdir.existsSync()) { | 802 if (!subdir.existsSync()) { |
784 subdir.createSync(); | 803 subdir.createSync(); |
785 } | 804 } |
786 } | 805 } |
787 | 806 File file = new File(path.join(_outputDirectory, filename)); |
788 File file = new File('docs/$filename'); | |
789 if (!file.existsSync()) { | |
790 file.createSync(); | |
791 } | |
792 file.writeAsStringSync(text, mode: append ? FileMode.APPEND : FileMode.WRITE); | 807 file.writeAsStringSync(text, mode: append ? FileMode.APPEND : FileMode.WRITE); |
793 } | 808 } |
794 | 809 |
795 /// Transforms the map by calling toMap on each value in it. | 810 /// Transforms the map by calling toMap on each value in it. |
796 Map recurseMap(Map inputMap) { | 811 Map recurseMap(Map inputMap) { |
797 var outputMap = {}; | 812 var outputMap = {}; |
798 inputMap.forEach((key, value) { | 813 inputMap.forEach((key, value) { |
799 if (value is Map) { | 814 if (value is Map) { |
800 outputMap[key] = recurseMap(value); | 815 outputMap[key] = recurseMap(value); |
801 } else { | 816 } else { |
(...skipping 632 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1434 /// Remove statics from the map of inherited items before adding them. | 1449 /// Remove statics from the map of inherited items before adding them. |
1435 Map _filterStatics(Map items) { | 1450 Map _filterStatics(Map items) { |
1436 var result = {}; | 1451 var result = {}; |
1437 items.forEach((name, item) { | 1452 items.forEach((name, item) { |
1438 if (!item.isStatic) { | 1453 if (!item.isStatic) { |
1439 result[name] = item; | 1454 result[name] = item; |
1440 } | 1455 } |
1441 }); | 1456 }); |
1442 return result; | 1457 return result; |
1443 } | 1458 } |
OLD | NEW |