Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 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 | 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 /** | 5 /** |
| 6 * To generate docs for a library, run this script with the path to an | 6 * To generate docs for a library, run this script with the path to an |
| 7 * entrypoint .dart file, like: | 7 * entrypoint .dart file, like: |
| 8 * | 8 * |
| 9 * $ dart dartdoc.dart foo.dart | 9 * $ dart dartdoc.dart foo.dart |
| 10 * | 10 * |
| 11 * This will create a "docs" directory with the docs for your libraries. To | 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 | 12 * create these beautiful docs, dartdoc parses your library and every library |
| 13 * it imports (recursively). From each library, it parses all classes and | 13 * it imports (recursively). From each library, it parses all classes and |
| 14 * members, finds the associated doc comments and builds crosslinked docs from | 14 * members, finds the associated doc comments and builds crosslinked docs from |
| 15 * them. | 15 * them. |
| 16 */ | 16 */ |
| 17 #library('dartdoc'); | 17 #library('dartdoc'); |
| 18 | 18 |
| 19 #import('dart:io'); | 19 #import('dart:io'); |
| 20 #import('dart:json'); | 20 #import('dart:json'); |
| 21 #import('../../frog/lang.dart'); | 21 #import('../../frog/lang.dart'); |
| 22 #import('../../frog/file_system.dart'); | 22 #import('../../frog/file_system.dart'); |
| 23 #import('../../frog/file_system_vm.dart'); | 23 #import('../../frog/file_system_vm.dart'); |
| 24 #import('classify.dart'); | 24 #import('classify.dart'); |
| 25 #import('markdown.dart', prefix: 'md'); | 25 #import('markdown.dart', prefix: 'md'); |
| 26 #import('../../frog/minfrogc.dart', prefix: 'frog'); | |
| 26 | 27 |
| 27 #source('comment_map.dart'); | 28 #source('comment_map.dart'); |
| 28 #source('utils.dart'); | 29 #source('utils.dart'); |
| 29 | 30 |
| 30 /** | 31 /** |
| 31 * Generates completely static HTML containing everything you need to browse | 32 * Generates completely static HTML containing everything you need to browse |
| 32 * the docs. The only client side behavior is trivial stuff like syntax | 33 * the docs. The only client side behavior is trivial stuff like syntax |
| 33 * highlighting code. | 34 * highlighting code. |
| 34 */ | 35 */ |
| 35 final MODE_STATIC = 0; | 36 final MODE_STATIC = 0; |
| 36 | 37 |
| 37 /** | 38 /** |
| 38 * Generated docs do not include baked HTML navigation. Instead, a single | 39 * Generated docs do not include baked HTML navigation. Instead, a single |
| 39 * `nav.json` file is created and the appropriate navigation is generated | 40 * `nav.json` file is created and the appropriate navigation is generated |
| 40 * client-side by parsing that and building HTML. | 41 * client-side by parsing that and building HTML. |
| 41 * | 42 * |
| 42 * This dramatically reduces the generated size of the HTML since a large | 43 * This dramatically reduces the generated size of the HTML since a large |
| 43 * fraction of each static page is just redundant navigation links. | 44 * fraction of each static page is just redundant navigation links. |
| 44 * | 45 * |
| 45 * In this mode, the browser will do a XHR for nav.json which means that to | 46 * In this mode, the browser will do a XHR for nav.json which means that to |
| 46 * preview docs locally, you will need to enable requesting file:// links in | 47 * preview docs locally, you will need to enable requesting file:// links in |
| 47 * your browser or run a little local server like `python -m SimpleHTTPServer`. | 48 * your browser or run a little local server like `python -m SimpleHTTPServer`. |
| 48 */ | 49 */ |
| 49 final MODE_LIVE_NAV = 1; | 50 final MODE_LIVE_NAV = 1; |
| 50 | 51 |
| 52 void printUsage() { | |
|
kasperl
2012/05/31 13:30:23
I would consider moving printUsage down below main
| |
| 53 print(''' | |
| 54 Usage dartdoc [options] <entrypoint> | |
| 55 [options] include | |
| 56 --no-code Do not include source code in the documentation. | |
| 57 | |
| 58 --mode=static Generates completely static HTML containing | |
| 59 everything you need to browse the docs. The only | |
| 60 client side behavior is trivial stuff like syntax | |
| 61 highlighting code. | |
| 62 | |
| 63 --mode=live-nav (default) Generated docs do not include baked HTML | |
| 64 navigation. Instead, a single `nav.json` file is | |
| 65 created and the appropriate navigation is generated | |
| 66 client-side by parsing that and building HTML. | |
| 67 This dramatically reduces the generated size of | |
| 68 the HTML since a large fraction of each static page | |
| 69 is just redundant navigation links. | |
| 70 In this mode, the browser will do a XHR for | |
| 71 nav.json which means that to preview docs locally, | |
| 72 you will need to enable requesting file:// links in | |
| 73 your browser or run a little local server like | |
| 74 `python -m SimpleHTTPServer`. | |
| 75 | |
| 76 --generate-app-cache Generates the App Cache manifest file, enabling | |
| 77 offline doc viewing. | |
| 78 --generate-app-cache=true --''-- | |
| 79 | |
| 80 --out=<dir> Generates files into directory <dir>. If omitted | |
| 81 the files are generated into ./docs/ | |
| 82 '''); | |
| 83 } | |
| 84 | |
| 51 /** | 85 /** |
| 52 * Run this from the `lib/dartdoc` directory. | 86 * Run this from the `lib/dartdoc` directory. |
| 53 */ | 87 */ |
| 54 void main() { | 88 void main() { |
| 55 final args = new Options().arguments; | 89 final args = new Options().arguments; |
| 56 | 90 |
| 57 // Parse the dartdoc options. | 91 // Parse the dartdoc options. |
| 58 bool includeSource; | 92 bool includeSource; |
| 59 int mode; | 93 int mode; |
| 60 String outputDir; | 94 String outputDir; |
| 61 bool generateAppCache; | 95 bool generateAppCache; |
| 96 | |
| 97 if (args.length == 0) { | |
|
kasperl
2012/05/31 13:30:23
args.isEmpty()?
| |
| 98 print('No arguments provided.'); | |
| 99 printUsage(); | |
| 100 return; | |
| 101 } | |
| 62 | 102 |
| 63 for (int i = 0; i < args.length - 1; i++) { | 103 for (int i = 0; i < args.length - 1; i++) { |
| 64 final arg = args[i]; | 104 final arg = args[i]; |
| 65 | 105 |
| 66 switch (arg) { | 106 switch (arg) { |
| 67 case '--no-code': | 107 case '--no-code': |
| 68 includeSource = false; | 108 includeSource = false; |
| 69 break; | 109 break; |
| 70 | 110 |
| 71 case '--mode=static': | 111 case '--mode=static': |
| 72 mode = MODE_STATIC; | 112 mode = MODE_STATIC; |
| 73 break; | 113 break; |
| 74 | 114 |
| 75 case '--mode=live-nav': | 115 case '--mode=live-nav': |
| 76 mode = MODE_LIVE_NAV; | 116 mode = MODE_LIVE_NAV; |
| 77 break; | 117 break; |
| 78 | 118 |
| 79 case '--generate-app-cache': | 119 case '--generate-app-cache': |
| 80 case '--generate-app-cache=true': | 120 case '--generate-app-cache=true': |
| 81 generateAppCache = true; | 121 generateAppCache = true; |
| 82 break; | 122 break; |
| 83 | 123 |
| 84 default: | 124 default: |
| 85 if (arg.startsWith('--out=')) { | 125 if (arg.startsWith('--out=')) { |
| 86 outputDir = arg.substring('--out='.length); | 126 outputDir = arg.substring('--out='.length); |
| 87 } else { | 127 } else { |
| 88 print('Unknown option: $arg'); | 128 print('Unknown option: $arg'); |
| 129 printUsage(); | |
| 89 return; | 130 return; |
| 90 } | 131 } |
| 91 break; | 132 break; |
| 92 } | 133 } |
| 93 } | 134 } |
| 94 | 135 |
| 95 // The entrypoint of the library to generate docs for. | 136 // The entrypoint of the library to generate docs for. |
| 96 final entrypoint = args[args.length - 1]; | 137 final entrypoint = args[args.length - 1]; |
| 97 | 138 |
| 98 final files = new VMFileSystem(); | 139 final files = new VMFileSystem(); |
| 99 | 140 |
| 100 // TODO(rnystrom): Note that the following lines get munged by create-sdk to | 141 // TODO(rnystrom): Note that the following lines get munged by create-sdk to |
| 101 // work with the SDK's different file layout. If you change, be sure to test | 142 // work with the SDK's different file layout. If you change, be sure to test |
| 102 // that dartdoc still works when run from the built SDK directory. | 143 // that dartdoc still works when run from the built SDK directory. |
| 103 final frogPath = joinPaths(scriptDir, '../../frog/'); | 144 final frogPath = joinPaths(scriptDir, '../../frog/'); |
| 104 final libDir = joinPaths(frogPath, 'lib'); | 145 final libDir = joinPaths(frogPath, 'lib'); |
| 105 final compilerPath = joinPaths(frogPath, 'minfrog'); | |
| 106 | 146 |
| 107 parseOptions(frogPath, ['', '', '--libdir=$libDir'], files); | 147 parseOptions(frogPath, ['', '', '--libdir=$libDir'], files); |
| 108 initializeWorld(files); | 148 initializeWorld(files); |
| 109 | 149 |
| 110 final dartdoc = new Dartdoc(); | 150 final dartdoc = new Dartdoc(); |
| 111 | 151 |
| 112 if (includeSource != null) dartdoc.includeSource = includeSource; | 152 if (includeSource != null) dartdoc.includeSource = includeSource; |
| 113 if (mode != null) dartdoc.mode = mode; | 153 if (mode != null) dartdoc.mode = mode; |
| 114 if (outputDir != null) dartdoc.outputDir = outputDir; | 154 if (outputDir != null) dartdoc.outputDir = outputDir; |
| 115 if (generateAppCache != null) dartdoc.generateAppCache = generateAppCache; | 155 if (generateAppCache != null) dartdoc.generateAppCache = generateAppCache; |
| 116 | 156 |
| 117 cleanOutputDirectory(dartdoc.outputDir); | 157 cleanOutputDirectory(dartdoc.outputDir); |
| 118 | 158 |
| 119 // Compile the client-side code to JS. | 159 // Compile the client-side code to JS. |
| 120 final clientScript = (dartdoc.mode == MODE_STATIC) ? 'static' : 'live-nav'; | 160 final clientScript = (dartdoc.mode == MODE_STATIC) ? 'static' : 'live-nav'; |
| 121 final Future scriptCompiled = compileScript(compilerPath, libDir, | 161 final Future scriptCompiled = compileScript(libDir, |
| 122 '$scriptDir/client-$clientScript.dart', | 162 '$scriptDir/client-$clientScript.dart', |
|
kasperl
2012/05/31 13:30:23
The indentation here seems off (not your fault). I
| |
| 123 '${dartdoc.outputDir}/client-$clientScript.js'); | 163 '${dartdoc.outputDir}/client-$clientScript.js'); |
| 124 | 164 |
| 125 final Future filesCopied = copyFiles('$scriptDir/static', dartdoc.outputDir); | 165 final Future filesCopied = copyFiles('$scriptDir/static', dartdoc.outputDir); |
| 126 | 166 |
| 127 Futures.wait([scriptCompiled, filesCopied]).then((_) { | 167 Futures.wait([scriptCompiled, filesCopied]).then((_) { |
| 128 dartdoc.document(entrypoint); | 168 dartdoc.document(entrypoint); |
| 169 | |
| 170 print('Documented ${dartdoc._totalLibraries} libraries, ' + | |
|
ngeoffray
2012/05/31 13:22:56
You can remove the '+'s here, they're not needed,
kasperl
2012/05/31 13:30:23
We're deprecating using + for string concatenation
| |
| 171 '${dartdoc._totalTypes} types, and ' + | |
| 172 '${dartdoc._totalMembers} members.'); | |
| 129 }); | 173 }); |
| 130 | 174 |
| 131 print('Documented ${dartdoc._totalLibraries} libraries, ' + | |
| 132 '${dartdoc._totalTypes} types, and ' + | |
| 133 '${dartdoc._totalMembers} members.'); | |
| 134 } | 175 } |
| 135 | 176 |
| 136 /** | 177 /** |
| 137 * Gets the full path to the directory containing the entrypoint of the current | 178 * Gets the full path to the directory containing the entrypoint of the current |
| 138 * script. In other words, if you invoked dartdoc, directly, it will be the | 179 * script. In other words, if you invoked dartdoc, directly, it will be the |
| 139 * path to the directory containing `dartdoc.dart`. If you're running a script | 180 * path to the directory containing `dartdoc.dart`. If you're running a script |
| 140 * that imports dartdoc, it will be the path to that script. | 181 * that imports dartdoc, it will be the path to that script. |
| 141 */ | 182 */ |
| 142 String get scriptDir() { | 183 String get scriptDir() { |
| 143 return dirname(new File(new Options().script).fullPathSync()); | 184 return dirname(new File(new Options().script).fullPathSync()); |
| 144 } | 185 } |
| 145 | 186 |
| 146 /** | 187 /** |
| 147 * Deletes and recreates the output directory at [path] if it exists. | 188 * Deletes and recreates the output directory at [path] if it exists. |
| 148 */ | 189 */ |
| 149 void cleanOutputDirectory(String path) { | 190 void cleanOutputDirectory(String path) { |
| 150 final outputDir = new Directory(path); | 191 final outputDir = new Directory(path); |
| 151 if (outputDir.existsSync()) { | 192 if (outputDir.existsSync()) { |
| 152 outputDir.deleteRecursivelySync(); | 193 outputDir.deleteRecursivelySync(); |
| 153 } | 194 } |
| 154 | 195 |
| 155 outputDir.createSync(); | 196 try { |
| 197 //TODO(johnniwinther) hack to avoid 'file already exists' exception thrown | |
|
ngeoffray
2012/05/31 13:22:56
missing space before TODO
kasperl
2012/05/31 13:30:23
Add a space before TODO and terminate the comment
| |
| 198 // due to invalid result from dir.existsSync() (probably due to race | |
| 199 // conditions) | |
| 200 outputDir.createSync(); | |
| 201 } catch (DirectoryIOException e) { | |
| 202 // ignore | |
|
kasperl
2012/05/31 13:30:23
Start comments with uppercase and terminate with .
| |
| 203 // print('$e: $path'); | |
|
ngeoffray
2012/05/31 13:22:56
Leftover debugging?
kasperl
2012/05/31 13:30:23
We try to avoid code in comments.
| |
| 204 } | |
| 156 } | 205 } |
| 157 | 206 |
| 158 /** | 207 /** |
| 159 * Copies all of the files in the directory [from] to [to]. Does *not* | 208 * Copies all of the files in the directory [from] to [to]. Does *not* |
| 160 * recursively copy subdirectories. | 209 * recursively copy subdirectories. |
| 161 * | 210 * |
| 162 * Note: runs asynchronously, so you won't see any files copied until after the | 211 * Note: runs asynchronously, so you won't see any files copied until after the |
| 163 * event loop has had a chance to pump (i.e. after `main()` has returned). | 212 * event loop has had a chance to pump (i.e. after `main()` has returned). |
| 164 */ | 213 */ |
| 165 Future copyFiles(String from, String to) { | 214 Future copyFiles(String from, String to) { |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 178 stream.write(bytes, copyBuffer: false); | 227 stream.write(bytes, copyBuffer: false); |
| 179 stream.close(); | 228 stream.close(); |
| 180 }); | 229 }); |
| 181 }; | 230 }; |
| 182 lister.onDone = (done) => completer.complete(true); | 231 lister.onDone = (done) => completer.complete(true); |
| 183 return completer.future; | 232 return completer.future; |
| 184 } | 233 } |
| 185 | 234 |
| 186 /** | 235 /** |
| 187 * Compiles the given Dart script to a JavaScript file at [jsPath] using the | 236 * Compiles the given Dart script to a JavaScript file at [jsPath] using the |
| 188 * Dart-to-JS compiler located at [compilerPath]. | 237 * Frog compiler located. |
|
ngeoffray
2012/05/31 13:22:56
Remove located.
| |
| 189 */ | 238 */ |
| 190 Future compileScript(String compilerPath, String libDir, | 239 Future compileScript(String libDir, |
| 191 String dartPath, String jsPath) { | 240 String dartPath, String jsPath) { |
|
kasperl
2012/05/31 13:30:23
Would all the parameters fit on one line now?
| |
| 192 final completer = new Completer(); | 241 final completer = new Completer(); |
| 193 onExit(ProcessResult result) { | 242 print('Compiling $dartPath to $jsPath'); |
| 194 if (result.exitCode != 0) { | 243 |
| 195 final message = 'Non-zero exit code from $compilerPath'; | 244 var result = true; |
| 245 try { | |
| 246 result = frog.internalMain([ | |
| 247 '--libdir=$libDir', '--out=$jsPath', | |
|
kasperl
2012/05/31 13:30:23
4 space indent.
| |
| 248 '--compile-only', '--enable-type-checks', '--warnings-as-errors', | |
| 249 dartPath]); | |
| 250 } catch (Object error) { | |
|
ngeoffray
2012/05/31 13:22:56
strange indentation
kasperl
2012/05/31 13:30:23
Indentation seems off.
| |
| 251 final message = 'Error trying to execute the compiler. Error: $error'; | |
| 196 print('$message.'); | 252 print('$message.'); |
|
ngeoffray
2012/05/31 13:22:56
Note that you can just write:
print(message)
The
| |
| 197 print(result.stdout); | |
| 198 print(result.stderr); | |
| 199 throw message; | 253 throw message; |
|
kasperl
2012/05/31 13:30:23
This should probably be completer.completeExceptio
| |
| 200 } | 254 } |
| 201 completer.complete(true); | 255 if (!result) { |
| 202 } | 256 final message = 'Non-zero exit code from the compiler'; |
| 203 | |
| 204 onError(error) { | |
| 205 final message = 'Error trying to execute $compilerPath. Error: $error'; | |
| 206 print('$message.'); | 257 print('$message.'); |
| 207 throw message; | 258 throw message; |
|
kasperl
2012/05/31 13:30:23
This should probably be completer.completeExceptio
kasperl
2012/05/31 13:30:23
This should probably be completer.completeExceptio
| |
| 208 } | 259 } |
| 209 | 260 completer.complete(result); |
| 210 print('Compiling $dartPath to $jsPath'); | |
| 211 var processFuture = Process.run(compilerPath, [ | |
| 212 '--libdir=$libDir', '--out=$jsPath', | |
| 213 '--compile-only', '--enable-type-checks', '--warnings-as-errors', | |
| 214 dartPath]); | |
| 215 | |
| 216 processFuture.handleException(onError); | |
| 217 processFuture.then(onExit); | |
| 218 | 261 |
| 219 return completer.future; | 262 return completer.future; |
| 220 } | 263 } |
| 221 | 264 |
| 222 class Dartdoc { | 265 class Dartdoc { |
| 223 | 266 |
| 224 /** Set to `false` to not include the source code in the generated docs. */ | 267 /** Set to `false` to not include the source code in the generated docs. */ |
| 225 bool includeSource = true; | 268 bool includeSource = true; |
| 226 | 269 |
| 227 /** | 270 /** |
| (...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 344 | 387 |
| 345 void startFile(String path) { | 388 void startFile(String path) { |
| 346 _filePath = path; | 389 _filePath = path; |
| 347 _file = new StringBuffer(); | 390 _file = new StringBuffer(); |
| 348 } | 391 } |
| 349 | 392 |
| 350 void endFile() { | 393 void endFile() { |
| 351 final outPath = '$outputDir/$_filePath'; | 394 final outPath = '$outputDir/$_filePath'; |
| 352 final dir = new Directory(dirname(outPath)); | 395 final dir = new Directory(dirname(outPath)); |
| 353 if (!dir.existsSync()) { | 396 if (!dir.existsSync()) { |
| 354 dir.createSync(); | 397 //TODO(johnniwinther) hack to avoid 'file already exists' exception thrown |
|
ngeoffray
2012/05/31 13:22:56
Space before TODO
kasperl
2012/05/31 13:30:23
Space before TODO. Terminate comment with . Consid
| |
| 398 // due to invalid result from dir.existsSync() (probably due to race | |
| 399 // conditions) | |
| 400 try { | |
| 401 dir.createSync(); | |
| 402 } catch (DirectoryIOException e) { | |
| 403 // ignore | |
|
kasperl
2012/05/31 13:30:23
// Ignore.
| |
| 404 } | |
| 355 } | 405 } |
| 356 | 406 |
| 357 world.files.writeString(outPath, _file.toString()); | 407 world.files.writeString(outPath, _file.toString()); |
| 358 _filePath = null; | 408 _filePath = null; |
| 359 _file = null; | 409 _file = null; |
| 360 } | 410 } |
| 361 | 411 |
| 362 void write(String s) { | 412 void write(String s) { |
| 363 _file.add(s); | 413 _file.add(s); |
| 364 } | 414 } |
| (...skipping 983 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1348 toCache.onFile = (filename) { | 1398 toCache.onFile = (filename) { |
| 1349 if (filename.endsWith('appcache.manifest')) { | 1399 if (filename.endsWith('appcache.manifest')) { |
| 1350 return; | 1400 return; |
| 1351 } | 1401 } |
| 1352 var relativePath = filename.substring(pathPrefixLength + 1); | 1402 var relativePath = filename.substring(pathPrefixLength + 1); |
| 1353 write("$relativePath\n"); | 1403 write("$relativePath\n"); |
| 1354 }; | 1404 }; |
| 1355 toCache.onDone = (done) => endFile(); | 1405 toCache.onDone = (done) => endFile(); |
| 1356 toCache.list(recursive: true); | 1406 toCache.list(recursive: true); |
| 1357 } | 1407 } |
| 1358 } | 1408 } |
| OLD | NEW |