| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | |
| 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. | |
| 4 | |
| 5 /** | |
| 6 * To generate docs for a library, run this script with the path to an | |
| 7 * entrypoint .dart file, like: | |
| 8 * | |
| 9 * $ dart dartdoc.dart foo.dart | |
| 10 * | |
| 11 * This will create a "docs" directory with the docs for your libraries. To | |
| 12 * create these beautiful docs, dartdoc parses your library and every library | |
| 13 * it imports (recursively). From each library, it parses all classes and | |
| 14 * members, finds the associated doc comments and builds crosslinked docs from | |
| 15 * them. | |
| 16 */ | |
| 17 library dartdoc; | |
| 18 | |
| 19 import 'dart:async'; | |
| 20 import 'dart:io'; | |
| 21 | |
| 22 import '../lib/dartdoc.dart'; | |
| 23 import '../lib/src/dartdoc/utils.dart'; | |
| 24 import 'package:args/args.dart'; | |
| 25 import 'package:path/path.dart' as path; | |
| 26 | |
| 27 /** | |
| 28 * Run this from the `lib/_internal/dartdoc` directory. | |
| 29 */ | |
| 30 main(List<String> arguments) { | |
| 31 // Need this because ArgParser.getUsage doesn't show command invocation. | |
| 32 final USAGE = 'Usage dartdoc [options] <entrypoint(s)>\n[options] include:'; | |
| 33 | |
| 34 final dartdoc = new Dartdoc(); | |
| 35 | |
| 36 final argParser = new ArgParser(); | |
| 37 | |
| 38 String libPath = path.join(scriptDir, '..', '..', '..', '..'); | |
| 39 | |
| 40 String packageRoot; | |
| 41 | |
| 42 argParser.addFlag('no-code', | |
| 43 help: 'Do not include source code in the documentation.', | |
| 44 defaultsTo: false, negatable: false, | |
| 45 callback: (noCode) => dartdoc.includeSource = !noCode); | |
| 46 | |
| 47 argParser.addOption('mode', abbr: 'm', | |
| 48 help: 'Define how HTML pages are generated.', | |
| 49 allowed: ['static', 'live-nav'], allowedHelp: { | |
| 50 'static': 'Generates completely static HTML containing\n' | |
| 51 'everything you need to browse the docs. The only\n' | |
| 52 'client side behavior is trivial stuff like syntax\n' | |
| 53 'highlighting code, and the find-as-you-type search\n' | |
| 54 'box.', | |
| 55 'live-nav': '(Default) Generated docs do not included baked HTML\n' | |
| 56 'navigation. Instead a single `nav.json` file is\n' | |
| 57 'created and the appropriate navigation is generated\n' | |
| 58 'client-side by parsing that and building HTML.\n' | |
| 59 '\tThis dramatically reduces the generated size of\n' | |
| 60 'the HTML since a large fraction of each static page\n' | |
| 61 'is just redundant navigation links.\n' | |
| 62 '\tIn this mode, the browser will do a XHR for\n' | |
| 63 'nav.json which means that to preview docs locallly,\n' | |
| 64 'you will need to enable requesting file:// links in\n' | |
| 65 'your browser or run a little local server like\n' | |
| 66 '`python -m SimpleHTTPServer`.'}, | |
| 67 defaultsTo: 'live-nav', | |
| 68 callback: (genMode) { | |
| 69 dartdoc.mode = (genMode == 'static' ? MODE_STATIC : MODE_LIVE_NAV); | |
| 70 }); | |
| 71 | |
| 72 argParser.addFlag('generate-app-cache', | |
| 73 help: 'Generates the App Cache manifest file, enabling\n' | |
| 74 'offline doc viewing.', | |
| 75 defaultsTo: false, negatable: false, | |
| 76 callback: (generate) => dartdoc.generateAppCache = generate); | |
| 77 | |
| 78 argParser.addFlag('omit-generation-time', | |
| 79 help: 'Omits generation timestamp from output.', | |
| 80 defaultsTo: false, negatable: false, | |
| 81 callback: (genTimestamp) => dartdoc.omitGenerationTime = genTimestamp); | |
| 82 | |
| 83 argParser.addFlag('verbose', abbr: 'v', | |
| 84 help: 'Print verbose information during generation.', | |
| 85 defaultsTo: false, negatable: false, | |
| 86 callback: (verb) => dartdoc.verbose = verb); | |
| 87 | |
| 88 argParser.addFlag('include-api', | |
| 89 help: 'Include the used API libraries in the generated\n' | |
| 90 'documentation. If the --link-api option is used,\n' | |
| 91 'this option is ignored.', | |
| 92 defaultsTo: false, negatable: false, | |
| 93 callback: (incApi) => dartdoc.includeApi = incApi); | |
| 94 | |
| 95 argParser.addFlag('link-api', | |
| 96 help: 'Link to the online language API in the generated\n' | |
| 97 'documentation. The option overrides inclusion\n' | |
| 98 'through --include-api or --include-lib.', | |
| 99 defaultsTo: false, negatable: false, | |
| 100 callback: (linkApi) => dartdoc.linkToApi = linkApi); | |
| 101 | |
| 102 argParser.addFlag('show-private', | |
| 103 help: 'Document private types and members.', | |
| 104 defaultsTo: false, | |
| 105 callback: (showPrivate) => dartdoc.showPrivate = showPrivate); | |
| 106 | |
| 107 argParser.addFlag('inherit-from-object', | |
| 108 help: 'Show members inherited from Object.', | |
| 109 defaultsTo: false, negatable: false, | |
| 110 callback: (inherit) => dartdoc.inheritFromObject = inherit); | |
| 111 | |
| 112 argParser.addFlag('enable-diagnostic-colors', negatable: false); | |
| 113 | |
| 114 argParser.addOption('out', | |
| 115 help: 'Generates files into directory specified. If\n' | |
| 116 'omitted the files are generated into ./docs/', | |
| 117 callback: (outDir) { | |
| 118 if(outDir != null) { | |
| 119 dartdoc.outputDir = outDir; | |
| 120 } | |
| 121 }); | |
| 122 | |
| 123 argParser.addOption('include-lib', | |
| 124 help: 'Use this option to explicitly specify which\n' | |
| 125 'libraries to include in the documentation. If\n' | |
| 126 'omitted, all used libraries are included by\n' | |
| 127 'default. Specify a comma-separated list of\n' | |
| 128 'library names, or call this option multiple times.', | |
| 129 callback: (incLibs) { | |
| 130 if(!incLibs.isEmpty) { | |
| 131 List<String> allLibs = new List<String>(); | |
| 132 for(final lst in incLibs) { | |
| 133 var someLibs = lst.split(','); | |
| 134 for(final lib in someLibs) { | |
| 135 allLibs.add(lib); | |
| 136 } | |
| 137 } | |
| 138 dartdoc.includedLibraries = allLibs; | |
| 139 } | |
| 140 }, allowMultiple: true); | |
| 141 | |
| 142 argParser.addOption('exclude-lib', | |
| 143 help: 'Use this option to explicitly specify which\n' | |
| 144 'libraries to exclude from the documentation. If\n' | |
| 145 'omitted, no libraries are excluded. Specify a\n' | |
| 146 'comma-separated list of library names, or call\n' | |
| 147 'this option multiple times.', | |
| 148 callback: (excLibs) { | |
| 149 if(!excLibs.isEmpty) { | |
| 150 List<String> allLibs = new List<String>(); | |
| 151 for(final lst in excLibs) { | |
| 152 var someLibs = lst.split(','); | |
| 153 for(final lib in someLibs) { | |
| 154 allLibs.add(lib); | |
| 155 } | |
| 156 } | |
| 157 dartdoc.excludedLibraries = allLibs; | |
| 158 } | |
| 159 }, allowMultiple: true); | |
| 160 | |
| 161 argParser.addOption('package-root', | |
| 162 help: 'Sets the package directory to the specified directory.\n' | |
| 163 'If omitted the package directory is the closest packages directory to' | |
| 164 ' the entrypoint.', | |
| 165 callback: (packageDir) { | |
| 166 if(packageDir != null) { | |
| 167 packageRoot = packageDir; | |
| 168 } | |
| 169 }); | |
| 170 | |
| 171 argParser.addOption('library-root', | |
| 172 help: 'Sets the library root directory to the specified directory.', | |
| 173 callback: (libraryRoot) { | |
| 174 if (libraryRoot != null) { | |
| 175 libPath = libraryRoot; | |
| 176 } | |
| 177 }); | |
| 178 | |
| 179 // TODO(amouravski): This method is deprecated. Remove on April 22. | |
| 180 argParser.addOption('pkg', | |
| 181 help: 'Deprecated: same as --package-root.', | |
| 182 callback: (packageDir) { | |
| 183 if(packageDir != null) { | |
| 184 packageRoot = packageDir; | |
| 185 } | |
| 186 }); | |
| 187 | |
| 188 dartdoc.dartdocPath = path.join(libPath, 'lib', '_internal', 'dartdoc'); | |
| 189 | |
| 190 if (arguments.isEmpty) { | |
| 191 print('No arguments provided.'); | |
| 192 print(USAGE); | |
| 193 print(argParser.getUsage()); | |
| 194 exit(1); | |
| 195 } | |
| 196 | |
| 197 final entrypoints = <Uri>[]; | |
| 198 try { | |
| 199 final option = argParser.parse(arguments, allowTrailingOptions: true); | |
| 200 | |
| 201 // This checks to see if the root of all entrypoints is the same. | |
| 202 // If it is not, then we display a warning, as package imports might fail. | |
| 203 var entrypointRoot; | |
| 204 for (final entrypoint in option.rest) { | |
| 205 var uri = Uri.parse(entrypoint); | |
| 206 | |
| 207 // If it looks like it was a file path (no scheme, or a one letter scheme | |
| 208 // which is likely a drive letter on Windows), turn it into a file URL. | |
| 209 if (uri.scheme == '' || uri.scheme.length == 1) { | |
| 210 uri = path.toUri(entrypoint); | |
| 211 } | |
| 212 | |
| 213 entrypoints.add(uri); | |
| 214 | |
| 215 if (uri.scheme != 'file') continue; | |
| 216 if (entrypointRoot == null) { | |
| 217 entrypointRoot = path.dirname(entrypoint); | |
| 218 } else if (entrypointRoot != path.dirname(entrypoint)) { | |
| 219 print('Warning: entrypoints are at different directories. "package:"' | |
| 220 ' imports may fail.'); | |
| 221 } | |
| 222 } | |
| 223 } on FormatException catch (e) { | |
| 224 print(e.message); | |
| 225 print(USAGE); | |
| 226 print(argParser.getUsage()); | |
| 227 exit(1); | |
| 228 } | |
| 229 | |
| 230 if (entrypoints.isEmpty) { | |
| 231 print('No entrypoints provided.'); | |
| 232 print(argParser.getUsage()); | |
| 233 exit(1); | |
| 234 } | |
| 235 | |
| 236 if (packageRoot == null) packageRoot = _getPackageRoot(entrypoints); | |
| 237 | |
| 238 cleanOutputDirectory(dartdoc.outputDir); | |
| 239 | |
| 240 // Start the analysis and documentation. | |
| 241 dartdoc.documentLibraries(entrypoints, libPath, packageRoot) | |
| 242 // Prepare the dart2js script code and copy static resources. | |
| 243 // TODO(amouravski): move compileScript out and pre-generate the client | |
| 244 // scripts. This takes a long time and the js hardly ever changes. | |
| 245 .then((_) => compileScript(dartdoc.mode, dartdoc.outputDir, libPath, | |
| 246 dartdoc.tmpPath)) | |
| 247 .then((_) => copyDirectory( | |
| 248 path.join(libPath, 'lib', '_internal', 'dartdoc', 'static'), | |
| 249 dartdoc.outputDir)) | |
| 250 .then((_) { | |
| 251 print(dartdoc.status); | |
| 252 if (dartdoc.totals == 0) { | |
| 253 exit(1); | |
| 254 } | |
| 255 }) | |
| 256 .catchError((e, trace) { | |
| 257 print('Error: generation failed: ${e}'); | |
| 258 if (trace != null) print("StackTrace: $trace"); | |
| 259 dartdoc.cleanup(); | |
| 260 exit(1); | |
| 261 }) | |
| 262 .whenComplete(() => dartdoc.cleanup()); | |
| 263 } | |
| 264 | |
| 265 String _getPackageRoot(List<Uri> entrypoints) { | |
| 266 // Check if there's a `packages` directory in the entry point directory. | |
| 267 var fileEntrypoint = entrypoints.firstWhere( | |
| 268 (entrypoint) => entrypoint.scheme == 'file', | |
| 269 orElse: () => null); | |
| 270 if (fileEntrypoint == null) return; | |
| 271 | |
| 272 var script = path.normalize(path.absolute(path.fromUri(fileEntrypoint))); | |
| 273 var dir = path.join(path.dirname(script), 'packages/'); | |
| 274 if (new Directory(dir).existsSync()) return dir; | |
| 275 | |
| 276 // If there is not, then check if the entrypoint is somewhere in a `lib` | |
| 277 // directory. | |
| 278 var parts = path.split(path.dirname(script)); | |
| 279 var libDir = parts.lastIndexOf('lib'); | |
| 280 if (libDir > 0) { | |
| 281 return path.join(path.joinAll(parts.take(libDir)), 'packages'); | |
| 282 } else { | |
| 283 return null; | |
| 284 } | |
| 285 } | |
| OLD | NEW |