├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── analysis_options.yaml ├── bin ├── generate.dart └── generate_with_web.dart ├── lib ├── ghpages_generator.dart └── index_generator.dart ├── pubspec.yaml └── update-ghpages.dart /.gitignore: -------------------------------------------------------------------------------- 1 | pubspec.lock 2 | packages 3 | .packages -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | # v0.3.1 (2018-01-17) 3 | 4 | Widen `args` dependency to include 1.x.x. 5 | 6 | # v0.3.0 (2015-05-18) 7 | 8 | Use `path.current` instead of `Platform.script` as default dir for `root-dir` to work with `pub global run`. 9 | 10 | # v0.2.5 (2015-05-12) 11 | 12 | Wider [args](https://pub.dartlang.org/packages/args) dependency. 13 | 14 | # v0.2.4 (2015-01-12) 15 | 16 | Provide executables for [pub globally activation](https://www.dartlang.org/tools/pub/cmd/pub-global.html). 17 | 18 | # v0.2.3 (2015-01-12) 19 | 20 | Ignore `.pub`. 21 | 22 | # v0.2.2 (2014-09-01) 23 | 24 | Switch to the new deferred loading syntax (needs DartSDK >= 1.6). 25 | 26 | # v0.2.1 (2014-07-18) 27 | 28 | Add a generator of index.html files with `withIndexGeneration`. 29 | 30 | # v0.2.0 (2014-07-11) 31 | 32 | Add utility functions to simplify the publication of examples at root with : 33 | 34 | ```dart 35 | new gh.Generator() 36 | ..withExamples = true 37 | ..generate(doCustomTask: gh.moveExampleAtRoot); 38 | ``` 39 | 40 | ## breaking change 41 | 42 | `setExamples` has been replaced by `withExamples`. 43 | 44 | # v0.1.2 (2014-07-11) 45 | 46 | Use `git -f add` to avoid problem with global .gitignore containing `packages` 47 | 48 | # v0.1.1 (2014-05-09) 49 | 50 | Add utility functions to simplify the publication of dartdoc at root with : 51 | 52 | ```dart 53 | new gh.Generator() 54 | ..setDartDoc(['lib/ghpages_generator.dart'], includeSdk: false, 55 | excludedLibs: ['path'], startPage: 'ghpages_generator') 56 | ..generate(doCustomTask: gh.moveDartDocAtRoot); 57 | ``` 58 | 59 | # Semantic Version Conventions 60 | 61 | http://semver.org/ 62 | 63 | - *Stable*: All even numbered minor versions are considered API stable: 64 | i.e.: v1.0.x, v1.2.x, and so on. 65 | - *Development*: All odd numbered minor versions are considered API unstable: 66 | i.e.: v0.9.x, v1.1.x, and so on. 67 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, Alexandre Ardhuin 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Dart Gh-Pages Generator 2 | 3 | This project allows to create/update the _gh-pages_ branch based on _examples_, 4 | _dartdoc_, _docs_, _web_ and/or custom files. 5 | 6 | Basically a new commit is done in the _gh-pages_ branch with updated files 7 | generated. Then you only need to _push_ this branch on _github_. 8 | 9 | ## Usage 10 | 11 | ### Running it 12 | 13 | The package exposes a simple command-line wrapper around the core generating 14 | library. The easiest way to invoke it is to 15 | [globally activate](https://www.dartlang.org/tools/pub/cmd/pub-global.html) the 16 | package and let pub put its executable on your path: 17 | 18 | $ pub global activate ghpages_generator 19 | $ generate_ghpages ... 20 | 21 | If you don't want `generate_ghpages` on your path, you can run it explicitly: 22 | 23 | $ pub global activate ghpages_generator --no-executables 24 | $ pub global run ghpages_generator:generate ... 25 | 26 | #### update with web 27 | 28 | # update the gh-pages branch with the result of `pub build web` 29 | $> generate_ghpages_with_web 30 | 31 | #### custom 32 | 33 | You can use `generate_ghpages` with several options/flags to update the 34 | gh-pages (See `generate_ghpages -h`). 35 | 36 | If you need more fine controls on the generation you have to use the api as 37 | described bellow. 38 | 39 | ### Using the API 40 | 41 | 1. Add a `dev_dependency` in your `pubspec.yaml` to _ghpages_generator_. 42 | 1. Create a Dart script to define how is built you ghpages. 43 | 44 | #### create dartdoc 45 | 46 | Here's how is generated 47 | gh-pages for this package](http://a14n.github.io/dart-ghpages-generator) : 48 | 49 | ```dart 50 | import 'package:ghpages_generator/ghpages_generator.dart' as gh; 51 | 52 | main() { 53 | new gh.Generator() 54 | ..setDartDoc(['lib/ghpages_generator.dart'], includeSdk: false, 55 | excludedLibs: ['path'], startPage: 'ghpages_generator') 56 | ..generate(doCustomTask: gh.moveDartDocAtRoot); 57 | } 58 | ``` 59 | 60 | ### Actions 61 | 62 | Here's the available actions : 63 | 64 | - generate the _dartdoc_ with `setDartDoc` 65 | - compile and deploy the _example_ directory with `withExamples` 66 | - compile and deploy the _web_ directory with `withWeb` 67 | - add the `docs` directory with `withDocs` 68 | - add the `index.html` files with `withIndexGeneration` 69 | - add static files with `templateDir` : all files in the template directory are 70 | pasted to the _gh-pages_ branch 71 | 72 | ## License ## 73 | Apache 2.0 74 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | analyzer: 2 | strong-mode: true -------------------------------------------------------------------------------- /bin/generate.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, Alexandre Ardhuin 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import 'package:args/args.dart'; 16 | import 'package:ghpages_generator/ghpages_generator.dart'; 17 | 18 | final argParser = new ArgParser(allowTrailingOptions: false) 19 | ..addOption('root-dir', help: 'The path of the package directory.') 20 | ..addOption('template-dir', 21 | help: 22 | 'Indicates a template directory from which all files will be paste in "gh-pages".') 23 | ..addFlag('help', abbr: 'h', negatable: false, help: 'Display usage') 24 | ..addFlag('with-examples', 25 | negatable: false, 26 | help: 27 | 'Specify that the build of `example` directory have to be paste in "gh-pages".') 28 | ..addFlag('with-web', 29 | negatable: false, 30 | help: 31 | 'Specify that the build of `web` directory have to be paste in "gh-pages".') 32 | ..addFlag('with-docs', 33 | negatable: false, 34 | help: 'Specify that the `doc` directory have to be paste in "gh-pages".') 35 | ..addFlag('with-index-generation', 36 | negatable: false, 37 | help: 38 | 'Specify that your script has to generate index pages automatically if there does not exist'); 39 | 40 | main(List args) { 41 | final argResult = argParser.parse(args); 42 | if (argResult['help']) { 43 | print(argParser.usage); 44 | return; 45 | } 46 | final generator = new Generator(rootDir: argResult['root-dir']) 47 | ..templateDir = argResult['template-dir'] 48 | ..withExamples = argResult['with-examples'] 49 | ..withWeb = argResult['with-web'] 50 | ..withDocs = argResult['with-docs'] 51 | ..withIndexGeneration = argResult['with-index-generation']; 52 | generator.generate(); 53 | } 54 | -------------------------------------------------------------------------------- /bin/generate_with_web.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, Alexandre Ardhuin 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import 'package:ghpages_generator/ghpages_generator.dart'; 16 | 17 | main(List args) => updateWithWebOnly(); 18 | -------------------------------------------------------------------------------- /lib/ghpages_generator.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013, Alexandre Ardhuin 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | library ghpages_generator; 16 | 17 | import 'dart:async'; 18 | import 'dart:io'; 19 | 20 | import 'package:path/path.dart' as path; 21 | 22 | import 'index_generator.dart' deferred as ig; 23 | 24 | /// Update the gh-pages branch with the pub build of web folder. 25 | Future updateWithWebOnly({doCustomTask(String workDir)}) { 26 | final generator = new Generator()..withWeb = true; 27 | return generator.generate(doCustomTask: (String workDir) { 28 | moveWebAtRoot(workDir); 29 | if (doCustomTask != null) return doCustomTask(workDir); 30 | }); 31 | } 32 | 33 | /// Move the dartdoc folder at the root. 34 | void moveDartDocAtRoot(String workDir) { 35 | _moveContent(workDir, 'dartdoc'); 36 | } 37 | 38 | /// Move the web folder at the root. 39 | void moveWebAtRoot(String workDir) { 40 | _moveContent(workDir, 'web'); 41 | } 42 | 43 | /// Move the example folder at the root. 44 | void moveExampleAtRoot(String workDir) { 45 | _moveContent(workDir, 'example'); 46 | } 47 | 48 | /// Move all files and directory from a [from] folderinto the [base] folder. 49 | /// The [from] folder must be a direct child of [base]. 50 | /// For instance : 51 | /// 52 | /// _moveContent(workDir, 'web'); 53 | void _moveContent(String base, String from) { 54 | new Directory(path.join(base, from)) 55 | .listSync() 56 | .forEach((e) => e.renameSync(path.join(base, path.basename(e.path)))); 57 | _delete(base, [from]); 58 | } 59 | 60 | /// This class allows to generate a new version of gh-pages. You can choose 61 | /// what you want to put in gh-pages : examples, dartdoc, docs and/or custom 62 | /// files. 63 | /// 64 | /// As sample here's how the gh-pages for this package is generated : 65 | /// 66 | /// new gh.Generator() 67 | /// ..setDartDoc(['lib/ghpages_generator.dart'], excludedLibs: ['path']) 68 | /// ..templateDir = 'gh-pages-template' 69 | /// ..generate(); 70 | /// 71 | class Generator { 72 | String _rootDir; 73 | String _workDir; 74 | String _gitRemoteOnRoot; 75 | 76 | List _docGenOptions; 77 | List _docGenFiles; 78 | 79 | bool _examples = false; 80 | bool _web = false; 81 | bool _docs = false; 82 | String _templateDir; 83 | bool _indexGeneration = false; 84 | 85 | /// Create a [Generator] based on the current directory where script is 86 | /// launched. 87 | /// 88 | /// You can customize the name of the directory with the [rootDir] named 89 | /// parameter. 90 | Generator({String rootDir}) { 91 | final timestamp = new DateTime.now().millisecondsSinceEpoch; 92 | _rootDir = rootDir != null ? rootDir : path.absolute(path.current); 93 | _workDir = path.join( 94 | _rootDir, '../${path.basename(_rootDir)}-ghpages-${timestamp}'); 95 | _gitRemoteOnRoot = 'origin-${timestamp}'; 96 | } 97 | 98 | /// The directory use to build the gh-pages branch. 99 | String get workDir => _workDir; 100 | 101 | /// Specify that _dartDoc_ have to be generated for the given [files] using 102 | /// the `docgen` command line tool. 103 | /// 104 | /// Options can be set through the named parameters. By default, _dartDoc_ is 105 | /// generated in `docs/dartdoc`. 106 | setDartDoc(List files, {bool includePrivate, bool includeSdk, 107 | bool parseSdk, String introduction, List excludedLibs, 108 | bool includeDependentPackages, String startPage}) { 109 | _docGenFiles = files; 110 | _docGenOptions = ['--compile', '--package-root=packages']; 111 | if (includePrivate == true) { 112 | _docGenOptions.add('--include-private'); 113 | } 114 | if (includeSdk == true) { 115 | _docGenOptions.add('--include-sdk'); 116 | } 117 | if (parseSdk == true) { 118 | _docGenOptions.add('--parse-sdk'); 119 | } 120 | if (introduction != null) { 121 | _docGenOptions.add('--introduction=$introduction'); 122 | } 123 | if (excludedLibs != null) { 124 | excludedLibs.forEach((e) => _docGenOptions.add('--exclude-lib=$e')); 125 | } 126 | if (includeDependentPackages == true) { 127 | _docGenOptions.add('--include-dependent-packages'); 128 | } 129 | if (startPage != null) { 130 | _docGenOptions.add('--start-page=$startPage'); 131 | } 132 | } 133 | 134 | /// Specify that the `example` directory have to be paste in _gh-pages_. 135 | set withExamples(bool value) => _examples = value; 136 | 137 | /// Specify that the `web` directory have to be paste in _gh-pages_. 138 | set withWeb(bool value) => _web = value; 139 | 140 | /// Specify that the `docs` directory have to be paste in _gh-pages_. 141 | set withDocs(bool value) => _docs = value; 142 | 143 | /// Indicates a template directory from which all files will be paste in 144 | /// _gh-pages_. 145 | set templateDir(String templateDir) => _templateDir = templateDir; 146 | 147 | /// Specify that your script has to generate index pages automatically 148 | /// if there does not exist in [_workDir] and sub-directories of [_workDir] 149 | set withIndexGeneration(bool value) => _indexGeneration = value; 150 | 151 | /// Generate gh-pages. A [doCustomTask] method can be set to perform custom 152 | /// operations just before committing files. 153 | Future generate({doCustomTask(String workDir)}) async { 154 | new Directory(_workDir).createSync(); 155 | try { 156 | _copy(_rootDir, _workDir, ['.git']); 157 | 158 | // git clone and preparation of the gh-pages branch 159 | await _run('git', ['reset', '--hard']); 160 | await _run('git', ['remote', 'add', _gitRemoteOnRoot, _rootDir]); 161 | final resultCheckout = await _run('git', ['checkout', 'gh-pages']); 162 | if (resultCheckout.exitCode != 0) { 163 | await _run('git', ['checkout', '--orphan', 'gh-pages']); 164 | } 165 | await _run('git', ['rm', '-rf', '.']); 166 | 167 | // copy of directories 168 | final elementsToCopy = ['pubspec.yaml', 'pubspec.lock', 'lib']; 169 | if (_examples) elementsToCopy.add('example'); 170 | if (_web) elementsToCopy.add('web'); 171 | if (_docs) elementsToCopy.add('docs'); 172 | _copy(_rootDir, _workDir, elementsToCopy, 173 | accept: (pathToCopy) => path.basename(pathToCopy) != 'packages'); 174 | 175 | // get deps 176 | await _run('pub', ['get']); 177 | 178 | if (_examples) { 179 | print('examples compilation...'); 180 | 181 | await _run('pub', ['build', 'example']); 182 | 183 | // move build to example and remove web 184 | _delete(_workDir, ['example']); 185 | new Directory(path.join(_workDir, 'build', 'example')) 186 | .renameSync(path.join(_workDir, 'example')); 187 | } 188 | 189 | if (_web) { 190 | print('web compilation...'); 191 | 192 | await _run('pub', ['build', 'web']); 193 | 194 | // move build to example and remove web 195 | _delete(_workDir, ['web']); 196 | new Directory(path.join(_workDir, 'build', 'web')) 197 | .renameSync(path.join(_workDir, 'web')); 198 | } 199 | 200 | if (_docGenFiles != null && _docGenFiles.isNotEmpty) { 201 | print('dartDoc generation...'); 202 | await _run('docgen', [] 203 | ..addAll(_docGenOptions) 204 | ..addAll(_docGenFiles)); 205 | 206 | new Directory( 207 | path.join(_workDir, 'dartdoc-viewer', 'client', 'out', 'web')) 208 | .renameSync(path.join(_workDir, 'dartdoc')); 209 | new Directory(path.join(_workDir, 'dartdoc', 'packages')).deleteSync( 210 | recursive: true); 211 | new Directory(path.join( 212 | _workDir, 'dartdoc-viewer', 'client', 'out', 'packages')) 213 | .renameSync(path.join(_workDir, 'dartdoc', 'packages')); 214 | } 215 | 216 | _delete(_workDir, [ 217 | 'build', 218 | 'packages', 219 | 'lib', 220 | 'pubspec.yaml', 221 | 'pubspec.lock', 222 | 'dartdoc-viewer', 223 | '.pub' 224 | ]); 225 | 226 | if (_templateDir != null) { 227 | final template = path.join(_rootDir, _templateDir); 228 | _copy(template, _workDir, new Directory(template) 229 | .listSync() 230 | .map((e) => path.basename(e.path))); 231 | } 232 | 233 | if (_indexGeneration) { 234 | await ig.loadLibrary(); 235 | await new ig.IndexGenerator.fromPath(_workDir).generate(); 236 | } 237 | 238 | if (doCustomTask != null) await doCustomTask(_workDir); 239 | 240 | await _run('git', ['add', '-f', '.']); 241 | await _run('git', ['commit', '-m', 'update gh-pages']); 242 | await _run('git', ['push', _gitRemoteOnRoot, 'gh-pages']); 243 | 244 | print("Your gh-pages has been updated."); 245 | print("You can now push it on github."); 246 | } finally { 247 | new Directory(_workDir).deleteSync(recursive: true); 248 | } 249 | } 250 | 251 | Future _run(String executable, List arguments) => 252 | Process.run(executable, arguments, workingDirectory: _workDir); 253 | } 254 | 255 | void _delete(String dir, List elements) { 256 | elements.forEach((e) { 257 | final name = path.join(dir, e); 258 | if (FileSystemEntity.isDirectorySync(name)) { 259 | new Directory(name).deleteSync(recursive: true); 260 | } else if (FileSystemEntity.isFileSync(name)) { 261 | new File(name).deleteSync(); 262 | } 263 | }); 264 | } 265 | 266 | void _copy( 267 | String sourceDirPath, String targetDirPath, Iterable elementsToCopy, 268 | {bool accept(String sourcePath)}) { 269 | for (final element in elementsToCopy) { 270 | final sourcePath = path.join(sourceDirPath, element); 271 | 272 | // next if not acceptable 273 | if (accept != null && !accept(sourcePath)) continue; 274 | 275 | // copy 276 | final targetPath = path.join(targetDirPath, element); 277 | if (FileSystemEntity.isDirectorySync(sourcePath)) { 278 | new Directory(targetPath).createSync(); 279 | _copy(sourcePath, targetPath, new Directory(sourcePath) 280 | .listSync() 281 | .map((e) => path.basename(e.path)), accept: accept); 282 | } else if (FileSystemEntity.isFileSync(sourcePath)) { 283 | new File(targetPath) 284 | ..createSync() 285 | ..writeAsBytesSync(new File(sourcePath).readAsBytesSync()); 286 | } 287 | } 288 | } 289 | -------------------------------------------------------------------------------- /lib/index_generator.dart: -------------------------------------------------------------------------------- 1 | library ghpages_generator.index_generator; 2 | 3 | import 'dart:io'; 4 | import 'dart:async'; 5 | 6 | import 'package:path/path.dart' as path; 7 | 8 | /// A generator for index pages such as 'index.html'. 9 | /// 10 | /// The following example code generates gh-pages and 11 | /// automatically generates `index.html` if `index.html` doesn't exist 12 | /// in the `example` directory: 13 | /// 14 | /// new gh.Generator() 15 | /// ..withExamples = true; 16 | /// ..generate(doCustomTask: (workDir) { 17 | /// gh.moveExampleAtRoot(workDir); 18 | /// return new IndexGenerator.fromPath(workDir).generate(); 19 | /// }); 20 | class IndexGenerator { 21 | static const _NOTHING = const Object(); 22 | 23 | static const DEFAULT_INDEX_FILE_NAME = 'index.html'; 24 | static const DEFAULT_EXCLUDES = const ['packages', '.git']; 25 | 26 | /// The target directory. 27 | Directory baseDir; 28 | 29 | List _indexes; 30 | 31 | /// The generated index files. 32 | /// This value is `null` if [generate] wasn't executed. 33 | List get indexes => _indexes; 34 | 35 | /// The index page name. 36 | /// 37 | /// default: `index.html` 38 | String indexFileName = DEFAULT_INDEX_FILE_NAME; 39 | 40 | /// The flag whether this generator overwrite existing index pages. 41 | /// 42 | /// default: `false` 43 | bool overwrite = false; 44 | 45 | /// The flag whether this generator generates all index pages in all sub 46 | /// directories. 47 | /// 48 | /// default: `true` 49 | bool recursive = true; 50 | 51 | /// The directory names which are not indexed. 52 | /// default: `[DEFAULT_EXCLUDES]` 53 | List excludes = DEFAULT_EXCLUDES; 54 | 55 | /// The index page content builder 56 | /// default: [defaultHtmlWriter] 57 | HtmlWriter htmlWriter = defaultHtmlWriter; 58 | 59 | // constructors 60 | 61 | IndexGenerator(this.baseDir); 62 | IndexGenerator.fromPath(String path) : this(new Directory(path)); 63 | 64 | // public methods 65 | 66 | /// Generates index files which named [indexFileName]. 67 | /// 68 | /// Return a future of generated files. 69 | Future> generate() { 70 | if (_indexes != null) { 71 | print('Warning: you might generate more than once.'); 72 | } 73 | Stream dirs = 74 | recursive ? _dirs : new Stream.fromIterable([baseDir]); 75 | return _generate(dirs); 76 | } 77 | 78 | /// Deletes index files generated with [generate]. 79 | /// 80 | /// Return a future of deleted (generated) files. 81 | Future> delete() { 82 | if (_indexes == null) { 83 | throw new StateError('Cannot delete: you might not generate index pages'); 84 | } 85 | var deleteds = _indexes.map((f) { 86 | print('Delete index page: ${f.path}'); 87 | return f.delete(); 88 | }); 89 | return Future.wait(deleteds); 90 | } 91 | 92 | // private methods 93 | 94 | Future> _generate(Stream dirs) { 95 | Stream targetIndexes = 96 | dirs.map((d) => new File(path.join(d.path, indexFileName))); 97 | 98 | if (!overwrite) { 99 | targetIndexes = targetIndexes.asyncExpand(_removeIfExists); 100 | } 101 | 102 | return targetIndexes 103 | .asyncExpand((f) => _generateIndex(f).asStream()) 104 | .toList() 105 | .then((i) { 106 | _indexes = i; 107 | return _indexes; 108 | }); 109 | } 110 | 111 | Future _generateIndex(File idx) { 112 | print('Create index page: ${idx.path}'); 113 | 114 | var filesFuture = idx.parent.list().toList(); 115 | var outputFuture = idx.open(mode: FileMode.WRITE); 116 | 117 | return Future.wait([filesFuture, outputFuture]).then((List args) { 118 | List files = args[0]; 119 | RandomAccessFile output = args[1]; 120 | return htmlWriter(output, baseDir, idx.parent, files) 121 | .then((_) => output.close()); 122 | }).then((_) => idx); 123 | } 124 | 125 | Stream get _dirs { 126 | var subs = baseDir 127 | .list(recursive: true) 128 | .where((e) => e is Directory) 129 | .where((d) => !_isInExcludes(d.path)); 130 | var base = new Stream.fromIterable([baseDir]); 131 | 132 | return _mergeStream([base, subs]); 133 | } 134 | 135 | bool _isInExcludes(String p) { 136 | var relPath = path.split(path.relative(p, from: baseDir.path)); 137 | return excludes 138 | .any((e) => relPath.any((rp) => e.allMatches(rp).length == 0)); 139 | } 140 | 141 | Stream _mergeStream(Iterable streams) { 142 | int openStreams = streams.length; 143 | StreamController c = new StreamController(); 144 | streams.forEach((s) { 145 | s.listen(c.add) 146 | ..onError(c.addError) 147 | ..onDone(() { 148 | openStreams--; 149 | if (openStreams == 0) c.close(); 150 | }); 151 | }); 152 | return c.stream; 153 | } 154 | 155 | Stream _removeIfExists(File file) => 156 | _removeIf(file.exists().then((b) => !b), file); 157 | 158 | Stream _removeIf(Future condition, element) => condition 159 | .then((e) => e ? element : _NOTHING) 160 | .asStream() 161 | .where((i) => i != _NOTHING); 162 | 163 | static Future defaultHtmlWriter(RandomAccessFile output, Directory base, 164 | Directory target, List children) { 165 | var p = (target.path == base.path) 166 | ? '/' 167 | : path.relative(target.path, from: base.path); 168 | var title = 'Index of $p'; 169 | 170 | List ignoreds = [ 171 | r'^packages/$', 172 | r'^\.git/$', 173 | r'\.precompiled\.js$', 174 | r'\.part\.js$', 175 | r'^index\.html$' 176 | ].map((p) => new RegExp(p)).toList(growable: true); 177 | List childNames = children 178 | .map((e) => path.basename(e.path) + ((e is Directory) ? '/' : '')) 179 | .where((n) => ignoreds.every((i) => i.allMatches(n).isEmpty)) 180 | .toList(growable: false); 181 | 182 | var content = new StringBuffer() 183 | ..writeln('') 184 | ..writeln('') 185 | ..writeln('$title') 186 | ..writeln('

$title

'); 187 | 188 | if (childNames.isEmpty) { 189 | content.writeln('

No contents

'); 190 | } else { 191 | content 192 | ..writeln('
    ') 193 | ..writeAll(childNames.map((f) => '
  • $f
  • '), '\n') 194 | ..writeln('
'); 195 | } 196 | return output.writeString(content.toString()); 197 | } 198 | } 199 | 200 | typedef Future HtmlWriter(RandomAccessFile output, Directory base, 201 | Directory target, List children); 202 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: ghpages_generator 2 | version: 0.3.1 3 | author: Alexandre Ardhuin 4 | description: This package allows to generate gh-pages branch for Github with dartdoc and examples 5 | homepage: https://github.com/a14n/dart-ghpages-generator 6 | environment: 7 | sdk: '>=1.9.0 <2.0.0' 8 | dependencies: 9 | args: '>=0.12.0 <2.0.0' 10 | path: '>=1.0.0 <2.0.0' 11 | executables: 12 | generate_ghpages: generate 13 | generate_ghpages_with_web: generate_with_web 14 | -------------------------------------------------------------------------------- /update-ghpages.dart: -------------------------------------------------------------------------------- 1 | import 'package:ghpages_generator/ghpages_generator.dart' as gh; 2 | 3 | main() { 4 | new gh.Generator() 5 | ..setDartDoc(['lib/ghpages_generator.dart'], 6 | includeSdk: false, 7 | excludedLibs: ['path'], 8 | startPage: 'ghpages_generator') 9 | ..generate(doCustomTask: gh.moveDartDocAtRoot); 10 | } 11 | --------------------------------------------------------------------------------