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 |