Revert "Refactor html generator into frontend and backend (#2115)" (#2120)
This reverts commit 983b3d6d9d2210cd80733c0b617816bd85a25937.
diff --git a/lib/dartdoc.dart b/lib/dartdoc.dart
index 721bcec..66a8a8a 100644
--- a/lib/dartdoc.dart
+++ b/lib/dartdoc.dart
@@ -44,58 +44,10 @@
: super(optionSet, dir);
}
-class DartdocFileWriter implements FileWriter {
- final String outputDir;
- final Map<String, Warnable> _fileElementMap = {};
- @override
- final Set<String> writtenFiles = Set();
-
- DartdocFileWriter(this.outputDir);
-
- @override
- void write(String filePath, Object content,
- {bool allowOverwrite, Warnable element}) {
- // Replace '/' separators with proper separators for the platform.
- String outFile = path.joinAll(filePath.split('/'));
-
- allowOverwrite ??= false;
- if (!allowOverwrite) {
- if (_fileElementMap.containsKey(outFile)) {
- assert(element != null,
- 'Attempted overwrite of ${outFile} without corresponding element');
- Warnable originalElement = _fileElementMap[outFile];
- Iterable<Warnable> referredFrom =
- originalElement != null ? [originalElement] : null;
- element?.warn(PackageWarning.duplicateFile,
- message: outFile, referredFrom: referredFrom);
- }
- }
- _fileElementMap[outFile] = element;
-
- var file = File(path.join(outputDir, outFile));
- var parent = file.parent;
- if (!parent.existsSync()) {
- parent.createSync(recursive: true);
- }
-
- if (content is String) {
- file.writeAsStringSync(content);
- } else if (content is List<int>) {
- file.writeAsBytesSync(content);
- } else {
- throw ArgumentError.value(
- content, 'content', '`content` must be `String` or `List<int>`.');
- }
-
- writtenFiles.add(outFile);
- logProgress(outFile);
- }
-}
-
/// Generates Dart documentation for all public Dart libraries in the given
/// directory.
class Dartdoc extends PackageBuilder {
- final Generator generator;
+ final List<Generator> generators;
final Set<String> writtenFiles = Set();
Directory outputDir;
@@ -103,20 +55,29 @@
final StreamController<String> _onCheckProgress =
StreamController(sync: true);
- Dartdoc._(DartdocOptionContext config, this.generator) : super(config) {
+ Dartdoc._(DartdocOptionContext config, this.generators) : super(config) {
outputDir = Directory(config.output)..createSync(recursive: true);
+ generators.forEach((g) => g.onFileCreated.listen(logProgress));
}
/// An asynchronous factory method that builds Dartdoc's file writers
/// and returns a Dartdoc object with them.
static Future<Dartdoc> withDefaultGenerators(
DartdocGeneratorOptionContext config) async {
- return Dartdoc._(config, await initHtmlGenerator(config));
+ List<Generator> generators = await initHtmlGenerators(config);
+ return Dartdoc._(config, generators);
}
/// An asynchronous factory method that builds
static Future<Dartdoc> withEmptyGenerator(DartdocOptionContext config) async {
- return Dartdoc._(config, await initEmptyGenerator(config));
+ List<Generator> generators = await initEmptyGenerators(config);
+ return Dartdoc._(config, generators);
+ }
+
+ /// Basic synchronous factory that gives a stripped down Dartdoc that won't
+ /// use generators. Useful for testing.
+ factory Dartdoc.withoutGenerators(DartdocOptionContext config) {
+ return Dartdoc._(config, []);
}
Stream<String> get onCheckProgress => _onCheckProgress.stream;
@@ -133,20 +94,19 @@
double seconds;
packageGraph = await buildPackageGraph();
seconds = _stopwatch.elapsedMilliseconds / 1000.0;
- int libs = packageGraph.libraries.length;
- logInfo("Initialized dartdoc with ${libs} librar${libs == 1 ? 'y' : 'ies'} "
+ logInfo(
+ "Initialized dartdoc with ${packageGraph.libraries.length} librar${packageGraph.libraries.length == 1 ? 'y' : 'ies'} "
"in ${seconds.toStringAsFixed(1)} seconds");
_stopwatch.reset();
- final generator = this.generator;
- if (generator != null) {
+ if (generators.isNotEmpty) {
// Create the out directory.
if (!outputDir.existsSync()) outputDir.createSync(recursive: true);
- DartdocFileWriter writer = DartdocFileWriter(outputDir.path);
- await generator.generate(packageGraph, writer);
-
- writtenFiles.addAll(writer.writtenFiles);
+ for (var generator in generators) {
+ await generator.generate(packageGraph, outputDir.path);
+ writtenFiles.addAll(generator.writtenFiles.keys.map(path.normalize));
+ }
if (config.validateLinks && writtenFiles.isNotEmpty) {
validateLinks(packageGraph, outputDir.path);
}
@@ -162,8 +122,8 @@
}
seconds = _stopwatch.elapsedMilliseconds / 1000.0;
- libs = packageGraph.localPublicLibraries.length;
- logInfo("Documented ${libs} public librar${libs == 1 ? 'y' : 'ies'} "
+ logInfo(
+ "Documented ${packageGraph.localPublicLibraries.length} public librar${packageGraph.localPublicLibraries.length == 1 ? 'y' : 'ies'} "
"in ${seconds.toStringAsFixed(1)} seconds");
return DartdocResults(config.topLevelPackageMeta, packageGraph, outputDir);
}
diff --git a/lib/src/empty_generator.dart b/lib/src/empty_generator.dart
index 595a666..666df06 100644
--- a/lib/src/empty_generator.dart
+++ b/lib/src/empty_generator.dart
@@ -4,7 +4,6 @@
import 'package:dartdoc/src/dartdoc_options.dart';
import 'package:dartdoc/src/generator.dart';
-import 'package:dartdoc/src/logging.dart';
import 'package:dartdoc/src/model/model.dart';
import 'package:dartdoc/src/model_utils.dart';
import 'package:dartdoc/src/warnings.dart';
@@ -14,23 +13,34 @@
/// it were.
class EmptyGenerator extends Generator {
@override
- Future generate(PackageGraph _packageGraph, FileWriter writer) {
- logProgress(_packageGraph.defaultPackage.documentationAsHtml);
+ Future generate(PackageGraph _packageGraph, String outputDirectoryPath) {
+ _onFileCreated.add(_packageGraph.defaultPackage.documentationAsHtml);
for (var package in Set.from([_packageGraph.defaultPackage])
..addAll(_packageGraph.localPackages)) {
for (var category in filterNonDocumented(package.categories)) {
- logProgress(category.documentationAsHtml);
+ _onFileCreated.add(category.documentationAsHtml);
}
for (Library lib in filterNonDocumented(package.libraries)) {
filterNonDocumented(lib.allModelElements)
- .forEach((m) => logProgress(m.documentationAsHtml));
+ .forEach((m) => _onFileCreated.add(m.documentationAsHtml));
}
}
return null;
}
+
+ final StreamController<void> _onFileCreated = StreamController(sync: true);
+
+ @override
+
+ /// Implementation fires on each model element processed rather than
+ /// file creation.
+ Stream<void> get onFileCreated => _onFileCreated.stream;
+
+ @override
+ final Map<String, Warnable> writtenFiles = {};
}
-Future<Generator> initEmptyGenerator(DartdocOptionContext config) async {
- return EmptyGenerator();
+Future<List<Generator>> initEmptyGenerators(DartdocOptionContext config) async {
+ return [EmptyGenerator()];
}
diff --git a/lib/src/generator.dart b/lib/src/generator.dart
index 28a0e8e..6ea61e1 100644
--- a/lib/src/generator.dart
+++ b/lib/src/generator.dart
@@ -15,23 +15,20 @@
import 'package:dartdoc/src/warnings.dart';
import 'package:path/path.dart' as path;
-abstract class FileWriter {
- /// All filenames written by this generator.
- Set<String> get writtenFiles;
-
- /// Write [content] to a file at [filePath].
- void write(String filePath, Object content,
- {bool allowOverwrite, Warnable element});
-}
-
/// An abstract class that defines a generator that generates documentation for
/// a given package.
///
/// Generators can generate documentation in different formats: html, json etc.
abstract class Generator {
- /// Generate the documentation for the given package using the specified
- /// writer. Completes the returned future when done.
- Future generate(PackageGraph packageGraph, FileWriter writer);
+ /// Generate the documentation for the given package in the specified
+ /// directory. Completes the returned future when done.
+ Future generate(PackageGraph packageGraph, String outputDirectoryPath);
+
+ /// Fires when a file is created.
+ Stream<void> get onFileCreated;
+
+ /// Fetches all filenames written by this generator.
+ Map<String, Warnable> get writtenFiles;
}
/// Dartdoc options related to generators generally.
diff --git a/lib/src/generator_frontend.dart b/lib/src/generator_frontend.dart
deleted file mode 100644
index ff65f1e..0000000
--- a/lib/src/generator_frontend.dart
+++ /dev/null
@@ -1,343 +0,0 @@
-// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-import 'dart:async';
-import 'dart:io' show File;
-
-import 'package:dartdoc/src/generator.dart';
-import 'package:dartdoc/src/logging.dart';
-import 'package:dartdoc/src/model/model.dart';
-import 'package:dartdoc/src/model_utils.dart';
-import 'package:dartdoc/src/warnings.dart';
-import 'package:path/path.dart' as path;
-
-/// [Generator] that delegates rendering to a [GeneratorBackend] and delegates
-/// file creation to a [FileWriter].
-class GeneratorFrontEnd implements Generator {
- final GeneratorBackend _generatorBackend;
-
- GeneratorFrontEnd(this._generatorBackend);
-
- @override
- Future generate(PackageGraph packageGraph, FileWriter writer) async {
- List<Indexable> indexElements = <Indexable>[];
- _generateDocs(packageGraph, writer, indexElements);
- await _generatorBackend.generateAdditionalFiles(writer, packageGraph);
-
- List<Categorization> categories = indexElements
- .whereType<Categorization>()
- .where((e) => e.hasCategorization)
- .toList();
- _generatorBackend.generateCategoryJson(writer, categories);
- _generatorBackend.generateSearchIndex(writer, indexElements);
- }
-
- // Traverses the package graph and collects elements for the search index.
- void _generateDocs(PackageGraph packageGraph, FileWriter writer,
- List<Indexable> indexAccumulator) {
- if (packageGraph == null) return;
-
- logInfo('documenting ${packageGraph.defaultPackage.name}');
- _generatorBackend.generatePackage(
- writer, packageGraph, packageGraph.defaultPackage);
-
- for (var package in packageGraph.localPackages) {
- for (var category in filterNonDocumented(package.categories)) {
- logInfo('Generating docs for category ${category.name} from '
- '${category.package.fullyQualifiedName}...');
- indexAccumulator.add(category);
- _generatorBackend.generateCategory(writer, packageGraph, category);
- }
-
- for (var lib in filterNonDocumented(package.libraries)) {
- logInfo('Generating docs for library ${lib.name} from '
- '${lib.element.source.uri}...');
- if (!lib.isAnonymous && !lib.hasDocumentation) {
- packageGraph.warnOnElement(lib, PackageWarning.noLibraryLevelDocs);
- }
- indexAccumulator.add(lib);
- _generatorBackend.generateLibrary(writer, packageGraph, lib);
-
- for (var clazz in filterNonDocumented(lib.allClasses)) {
- indexAccumulator.add(clazz);
- _generatorBackend.generateClass(writer, packageGraph, lib, clazz);
-
- for (var constructor in filterNonDocumented(clazz.constructors)) {
- if (!constructor.isCanonical) continue;
-
- indexAccumulator.add(constructor);
- _generatorBackend.generateConstructor(
- writer, packageGraph, lib, clazz, constructor);
- }
-
- for (var constant in filterNonDocumented(clazz.constants)) {
- if (!constant.isCanonical) continue;
-
- indexAccumulator.add(constant);
- _generatorBackend.generateConstant(
- writer, packageGraph, lib, clazz, constant);
- }
-
- for (var property in filterNonDocumented(clazz.staticProperties)) {
- if (!property.isCanonical) continue;
-
- indexAccumulator.add(property);
- _generatorBackend.generateProperty(
- writer, packageGraph, lib, clazz, property);
- }
-
- for (var property in filterNonDocumented(clazz.allInstanceFields)) {
- if (!property.isCanonical) continue;
-
- indexAccumulator.add(property);
- _generatorBackend.generateProperty(
- writer, packageGraph, lib, clazz, property);
- }
-
- for (var method in filterNonDocumented(clazz.allInstanceMethods)) {
- if (!method.isCanonical) continue;
-
- indexAccumulator.add(method);
- _generatorBackend.generateMethod(
- writer, packageGraph, lib, clazz, method);
- }
-
- for (var operator in filterNonDocumented(clazz.allOperators)) {
- if (!operator.isCanonical) continue;
-
- indexAccumulator.add(operator);
- _generatorBackend.generateMethod(
- writer, packageGraph, lib, clazz, operator);
- }
-
- for (var method in filterNonDocumented(clazz.staticMethods)) {
- if (!method.isCanonical) continue;
-
- indexAccumulator.add(method);
- _generatorBackend.generateMethod(
- writer, packageGraph, lib, clazz, method);
- }
- }
-
- for (var extension in filterNonDocumented(lib.extensions)) {
- indexAccumulator.add(extension);
- _generatorBackend.generateExtension(
- writer, packageGraph, lib, extension);
-
- for (var constant in filterNonDocumented(extension.constants)) {
- indexAccumulator.add(constant);
- _generatorBackend.generateConstant(
- writer, packageGraph, lib, extension, constant);
- }
-
- for (var property
- in filterNonDocumented(extension.staticProperties)) {
- indexAccumulator.add(property);
- _generatorBackend.generateProperty(
- writer, packageGraph, lib, extension, property);
- }
-
- for (var method
- in filterNonDocumented(extension.allPublicInstanceMethods)) {
- indexAccumulator.add(method);
- _generatorBackend.generateMethod(
- writer, packageGraph, lib, extension, method);
- }
-
- for (var method in filterNonDocumented(extension.staticMethods)) {
- indexAccumulator.add(method);
- _generatorBackend.generateMethod(
- writer, packageGraph, lib, extension, method);
- }
-
- for (var operator in filterNonDocumented(extension.allOperators)) {
- indexAccumulator.add(operator);
- _generatorBackend.generateMethod(
- writer, packageGraph, lib, extension, operator);
- }
-
- for (var property
- in filterNonDocumented(extension.allInstanceFields)) {
- indexAccumulator.add(property);
- _generatorBackend.generateProperty(
- writer, packageGraph, lib, extension, property);
- }
- }
-
- for (var mixin in filterNonDocumented(lib.mixins)) {
- indexAccumulator.add(mixin);
- _generatorBackend.generateMixin(writer, packageGraph, lib, mixin);
-
- for (var constructor in filterNonDocumented(mixin.constructors)) {
- if (!constructor.isCanonical) continue;
-
- indexAccumulator.add(constructor);
- _generatorBackend.generateConstructor(
- writer, packageGraph, lib, mixin, constructor);
- }
-
- for (var constant in filterNonDocumented(mixin.constants)) {
- if (!constant.isCanonical) continue;
- indexAccumulator.add(constant);
- _generatorBackend.generateConstant(
- writer, packageGraph, lib, mixin, constant);
- }
-
- for (var property in filterNonDocumented(mixin.staticProperties)) {
- if (!property.isCanonical) continue;
-
- indexAccumulator.add(property);
- _generatorBackend.generateConstant(
- writer, packageGraph, lib, mixin, property);
- }
-
- for (var property in filterNonDocumented(mixin.allInstanceFields)) {
- if (!property.isCanonical) continue;
-
- indexAccumulator.add(property);
- _generatorBackend.generateConstant(
- writer, packageGraph, lib, mixin, property);
- }
-
- for (var method in filterNonDocumented(mixin.allInstanceMethods)) {
- if (!method.isCanonical) continue;
-
- indexAccumulator.add(method);
- _generatorBackend.generateMethod(
- writer, packageGraph, lib, mixin, method);
- }
-
- for (var operator in filterNonDocumented(mixin.allOperators)) {
- if (!operator.isCanonical) continue;
-
- indexAccumulator.add(operator);
- _generatorBackend.generateMethod(
- writer, packageGraph, lib, mixin, operator);
- }
-
- for (var method in filterNonDocumented(mixin.staticMethods)) {
- if (!method.isCanonical) continue;
-
- indexAccumulator.add(method);
- _generatorBackend.generateMethod(
- writer, packageGraph, lib, mixin, method);
- }
- }
-
- for (var eNum in filterNonDocumented(lib.enums)) {
- indexAccumulator.add(eNum);
- _generatorBackend.generateEnum(writer, packageGraph, lib, eNum);
-
- for (var property in filterNonDocumented(eNum.allInstanceFields)) {
- indexAccumulator.add(property);
- _generatorBackend.generateConstant(
- writer, packageGraph, lib, eNum, property);
- }
- for (var operator in filterNonDocumented(eNum.allOperators)) {
- indexAccumulator.add(operator);
- _generatorBackend.generateMethod(
- writer, packageGraph, lib, eNum, operator);
- }
- for (var method in filterNonDocumented(eNum.allInstanceMethods)) {
- indexAccumulator.add(method);
- _generatorBackend.generateMethod(
- writer, packageGraph, lib, eNum, method);
- }
- }
-
- for (var constant in filterNonDocumented(lib.constants)) {
- indexAccumulator.add(constant);
- _generatorBackend.generateTopLevelConstant(
- writer, packageGraph, lib, constant);
- }
-
- for (var property in filterNonDocumented(lib.properties)) {
- indexAccumulator.add(property);
- _generatorBackend.generateTopLevelProperty(
- writer, packageGraph, lib, property);
- }
-
- for (var function in filterNonDocumented(lib.functions)) {
- indexAccumulator.add(function);
- _generatorBackend.generateFunction(
- writer, packageGraph, lib, function);
- }
-
- for (var typeDef in filterNonDocumented(lib.typedefs)) {
- indexAccumulator.add(typeDef);
- _generatorBackend.generateTypeDef(writer, packageGraph, lib, typeDef);
- }
- }
- }
- }
-}
-
-abstract class GeneratorBackend {
- /// Emit json describing the [categories] defined by the package.
- void generateCategoryJson(FileWriter writer, List<Categorization> categories);
-
- /// Emit json catalog of [indexedElements] for use with a search index.
- void generateSearchIndex(FileWriter writer, List<Indexable> indexedElements);
-
- /// Emit documentation content for the [package].
- void generatePackage(FileWriter writer, PackageGraph graph, Package package);
-
- /// Emit documentation content for the [category].
- void generateCategory(
- FileWriter writer, PackageGraph graph, Category category);
-
- /// Emit documentation content for the [library].
- void generateLibrary(FileWriter writer, PackageGraph graph, Library library);
-
- /// Emit documentation content for the [clazz].
- void generateClass(
- FileWriter writer, PackageGraph graph, Library library, Class clazz);
-
- /// Emit documentation content for the [eNum].
- void generateEnum(
- FileWriter writer, PackageGraph graph, Library library, Enum eNum);
-
- /// Emit documentation content for the [mixin].
- void generateMixin(
- FileWriter writer, PackageGraph graph, Library library, Mixin mixin);
-
- /// Emit documentation content for the [constructor].
- void generateConstructor(FileWriter writer, PackageGraph graph,
- Library library, Class clazz, Constructor constructor);
-
- /// Emit documentation content for the [field].
- void generateConstant(FileWriter writer, PackageGraph graph, Library library,
- Container clazz, Field field);
-
- /// Emit documentation content for the [field].
- void generateProperty(FileWriter writer, PackageGraph graph, Library library,
- Container clazz, Field field);
-
- /// Emit documentation content for the [method].
- void generateMethod(FileWriter writer, PackageGraph graph, Library library,
- Container clazz, Method method);
-
- /// Emit documentation content for the [extension].
- void generateExtension(FileWriter writer, PackageGraph graph, Library library,
- Extension extension);
-
- /// Emit documentation content for the [function].
- void generateFunction(FileWriter writer, PackageGraph graph, Library library,
- ModelFunction function);
-
- /// Emit documentation content for the [constant].
- void generateTopLevelConstant(FileWriter writer, PackageGraph graph,
- Library library, TopLevelVariable constant);
-
- /// Emit documentation content for the [property].
- void generateTopLevelProperty(FileWriter writer, PackageGraph graph,
- Library library, TopLevelVariable property);
-
- /// Emit documentation content for the [typedef].
- void generateTypeDef(
- FileWriter writer, PackageGraph graph, Library library, Typedef typedef);
-
- /// Emit files not specific to a Dart language element.
- void generateAdditionalFiles(FileWriter writer, PackageGraph graph);
-}
diff --git a/lib/src/generator_utils.dart b/lib/src/generator_utils.dart
deleted file mode 100644
index 59fa128..0000000
--- a/lib/src/generator_utils.dart
+++ /dev/null
@@ -1,76 +0,0 @@
-// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-import 'dart:convert';
-
-import 'package:collection/collection.dart';
-import 'package:dartdoc/src/model/categorization.dart';
-import 'package:dartdoc/src/model/enclosed_element.dart';
-import 'package:dartdoc/src/model/indexable.dart';
-
-/// Convenience function to generate category JSON since different generators
-/// will likely want the same content for this.
-String generateCategoryJson(Iterable<Categorization> categories, bool pretty) {
- var encoder = pretty ? JsonEncoder.withIndent(' ') : JsonEncoder();
- final List<Map> indexItems = categories.map((Categorization e) {
- Map data = {
- 'name': e.name,
- 'qualifiedName': e.fullyQualifiedName,
- 'href': e.href,
- 'type': e.kind,
- };
-
- if (e.hasCategoryNames) data['categories'] = e.categoryNames;
- if (e.hasSubCategoryNames) data['subcategories'] = e.subCategoryNames;
- if (e.hasImage) data['image'] = e.image;
- if (e.hasSamples) data['samples'] = e.samples;
- return data;
- }).toList();
-
- indexItems.sort((a, b) {
- var value = compareNatural(a['qualifiedName'], b['qualifiedName']);
- if (value == 0) {
- value = compareNatural(a['type'], b['type']);
- }
- return value;
- });
-
- return encoder.convert(indexItems);
-}
-
-/// Convenience function to generate search index JSON since different
-/// generators will likely want the same content for this.
-String generateSearchIndexJson(
- Iterable<Indexable> indexedElements, bool pretty) {
- var encoder = pretty ? JsonEncoder.withIndent(' ') : JsonEncoder();
- final List<Map> indexItems = indexedElements.map((Indexable e) {
- Map data = {
- 'name': e.name,
- 'qualifiedName': e.fullyQualifiedName,
- 'href': e.href,
- 'type': e.kind,
- 'overriddenDepth': e.overriddenDepth,
- };
- if (e is EnclosedElement) {
- EnclosedElement ee = e as EnclosedElement;
- data['enclosedBy'] = {
- 'name': ee.enclosingElement.name,
- 'type': ee.enclosingElement.kind
- };
-
- data['qualifiedName'] = e.fullyQualifiedName;
- }
- return data;
- }).toList();
-
- indexItems.sort((a, b) {
- var value = compareNatural(a['qualifiedName'], b['qualifiedName']);
- if (value == 0) {
- value = compareNatural(a['type'], b['type']);
- }
- return value;
- });
-
- return encoder.convert(indexItems);
-}
diff --git a/lib/src/html/html_generator.dart b/lib/src/html/html_generator.dart
index 1224005..29be73c 100644
--- a/lib/src/html/html_generator.dart
+++ b/lib/src/html/html_generator.dart
@@ -4,14 +4,173 @@
library dartdoc.html_generator;
-import 'dart:async' show Future;
+import 'dart:async' show Future, StreamController, Stream;
+import 'dart:io' show Directory, File;
+import 'dart:isolate';
import 'package:dartdoc/dartdoc.dart';
+import 'package:dartdoc/src/empty_generator.dart';
import 'package:dartdoc/src/generator.dart';
-import 'package:dartdoc/src/generator_frontend.dart';
-import 'package:dartdoc/src/html/html_generator_backend.dart';
+import 'package:dartdoc/src/html/html_generator_instance.dart';
+import 'package:dartdoc/src/html/template_data.dart';
+import 'package:dartdoc/src/html/templates.dart';
+import 'package:dartdoc/src/model/model.dart';
+import 'package:dartdoc/src/warnings.dart';
+import 'package:path/path.dart' as path;
-Future<Generator> initHtmlGenerator(GeneratorContext context) async {
- var backend = await HtmlGeneratorBackend.fromContext(context);
- return GeneratorFrontEnd(backend);
+typedef Renderer = String Function(String input);
+
+// Generation order for libraries:
+// constants
+// typedefs
+// properties
+// functions
+// enums
+// classes
+// exceptions
+//
+// Generation order for classes:
+// constants
+// static properties
+// static methods
+// properties
+// constructors
+// operators
+// methods
+
+class HtmlGenerator extends Generator {
+ final Templates _templates;
+ final HtmlGeneratorOptions _options;
+ HtmlGeneratorInstance _instance;
+
+ final StreamController<void> _onFileCreated = StreamController(sync: true);
+
+ @override
+ Stream<void> get onFileCreated => _onFileCreated.stream;
+
+ @override
+ final Map<String, Warnable> writtenFiles = {};
+
+ static Future<HtmlGenerator> create(
+ {HtmlGeneratorOptions options,
+ List<String> headers,
+ List<String> footers,
+ List<String> footerTexts}) async {
+ var templates;
+ String dirname = options?.templatesDir;
+ if (dirname != null) {
+ Directory templateDir = Directory(dirname);
+ templates = await Templates.fromDirectory(templateDir,
+ headerPaths: headers,
+ footerPaths: footers,
+ footerTextPaths: footerTexts);
+ } else {
+ templates = await Templates.createDefault(
+ headerPaths: headers,
+ footerPaths: footers,
+ footerTextPaths: footerTexts);
+ }
+
+ return HtmlGenerator._(options ?? HtmlGeneratorOptions(), templates);
+ }
+
+ HtmlGenerator._(this._options, this._templates);
+
+ @override
+
+ /// Actually write out the documentation for [packageGraph].
+ /// Stores the HtmlGeneratorInstance so we can access it in [writtenFiles].
+ Future generate(PackageGraph packageGraph, String outputDirectoryPath) async {
+ assert(_instance == null);
+
+ var enabled = true;
+ void write(String filePath, Object content,
+ {bool allowOverwrite, Warnable element}) {
+ allowOverwrite ??= false;
+ if (!enabled) {
+ throw StateError('`write` was called after `generate` completed.');
+ }
+ if (!allowOverwrite) {
+ if (writtenFiles.containsKey(filePath)) {
+ assert(element != null,
+ 'Attempted overwrite of ${filePath} without corresponding element');
+ Warnable originalElement = writtenFiles[filePath];
+ Iterable<Warnable> referredFrom =
+ originalElement != null ? [originalElement] : null;
+ element?.warn(PackageWarning.duplicateFile,
+ message: filePath, referredFrom: referredFrom);
+ }
+ }
+
+ var file = File(path.join(outputDirectoryPath, filePath));
+ var parent = file.parent;
+ if (!parent.existsSync()) {
+ parent.createSync(recursive: true);
+ }
+
+ if (content is String) {
+ file.writeAsStringSync(content);
+ } else if (content is List<int>) {
+ file.writeAsBytesSync(content);
+ } else {
+ throw ArgumentError.value(
+ content, 'content', '`content` must be `String` or `List<int>`.');
+ }
+ _onFileCreated.add(file);
+ writtenFiles[filePath] = element;
+ }
+
+ try {
+ _instance =
+ HtmlGeneratorInstance(_options, _templates, packageGraph, write);
+ await _instance.generate();
+ } finally {
+ enabled = false;
+ }
+ }
+}
+
+class HtmlGeneratorOptions implements HtmlOptions {
+ final String faviconPath;
+ final bool prettyIndexJson;
+ final String templatesDir;
+
+ @override
+ final String relCanonicalPrefix;
+
+ @override
+ final String toolVersion;
+
+ @override
+ final bool useBaseHref;
+
+ HtmlGeneratorOptions(
+ {this.relCanonicalPrefix,
+ this.faviconPath,
+ String toolVersion,
+ this.prettyIndexJson = false,
+ this.templatesDir,
+ this.useBaseHref = false})
+ : this.toolVersion = toolVersion ?? 'unknown';
+}
+
+/// Initialize and setup the generators.
+Future<List<Generator>> initHtmlGenerators(GeneratorContext context) async {
+ // TODO(jcollins-g): Rationalize based on GeneratorContext all the way down
+ // through the generators.
+ HtmlGeneratorOptions options = HtmlGeneratorOptions(
+ relCanonicalPrefix: context.relCanonicalPrefix,
+ toolVersion: dartdocVersion,
+ faviconPath: context.favicon,
+ prettyIndexJson: context.prettyIndexJson,
+ templatesDir: context.templatesDir,
+ useBaseHref: context.useBaseHref);
+ return [
+ await HtmlGenerator.create(
+ options: options,
+ headers: context.header,
+ footers: context.footer,
+ footerTexts: context.footerTextPaths,
+ )
+ ];
}
diff --git a/lib/src/html/html_generator_backend.dart b/lib/src/html/html_generator_backend.dart
deleted file mode 100644
index da0dfb1..0000000
--- a/lib/src/html/html_generator_backend.dart
+++ /dev/null
@@ -1,237 +0,0 @@
-// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-import 'dart:io';
-
-import 'package:dartdoc/dartdoc.dart';
-import 'package:dartdoc/src/generator_frontend.dart';
-import 'package:dartdoc/src/generator_utils.dart' as generator_util;
-import 'package:dartdoc/src/html/resource_loader.dart' as loader;
-import 'package:dartdoc/src/html/resources.g.dart' as resources;
-import 'package:dartdoc/src/html/template_data.dart';
-import 'package:dartdoc/src/html/templates.dart';
-import 'package:dartdoc/src/model/model.dart';
-import 'package:dartdoc/src/model/package.dart';
-import 'package:dartdoc/src/model/package_graph.dart';
-import 'package:dartdoc/src/warnings.dart';
-import 'package:mustache/mustache.dart';
-import 'package:path/path.dart' as path;
-
-/// Configuration options for the html backend.
-class HtmlBackendOptions implements HtmlOptions {
- @override
- final String relCanonicalPrefix;
- @override
- final String toolVersion;
-
- final String favicon;
-
- final bool prettyIndexJson;
-
- @override
- final bool useBaseHref;
-
- HtmlBackendOptions(
- {this.relCanonicalPrefix,
- this.toolVersion,
- this.favicon,
- this.prettyIndexJson = false,
- this.useBaseHref = false});
-}
-
-/// GeneratorBackend for html output.
-class HtmlGeneratorBackend implements GeneratorBackend {
- final HtmlBackendOptions _options;
- final Templates _templates;
-
- static Future<HtmlGeneratorBackend> fromContext(
- GeneratorContext context) async {
- Templates templates = await Templates.fromContext(context);
- // TODO(jcollins-g): Rationalize based on GeneratorContext all the way down
- // through the generators.
- HtmlOptions options = HtmlBackendOptions(
- relCanonicalPrefix: context.relCanonicalPrefix,
- toolVersion: dartdocVersion,
- favicon: context.favicon,
- prettyIndexJson: context.prettyIndexJson,
- useBaseHref: context.useBaseHref,
- );
- return HtmlGeneratorBackend(options, templates);
- }
-
- HtmlGeneratorBackend(HtmlBackendOptions options, this._templates)
- : this._options = (options ?? HtmlBackendOptions());
-
- /// Helper method to bind template data and emit the content to the writer.
- void _render(FileWriter writer, String filename, Template template,
- TemplateData data) {
- String content = template.renderString(data);
- if (!_options.useBaseHref) {
- content = content.replaceAll(HTMLBASE_PLACEHOLDER, data.htmlBase);
- }
- writer.write(filename, content,
- element: data.self is Warnable ? data.self : null);
- }
-
- @override
- void generateCategoryJson(
- FileWriter writer, List<Categorization> categories) {
- String json = generator_util.generateCategoryJson(
- categories, _options.prettyIndexJson);
- if (!_options.useBaseHref) {
- json = json.replaceAll(HTMLBASE_PLACEHOLDER, '');
- }
- writer.write(path.join('categories.json'), '${json}\n');
- }
-
- @override
- void generateSearchIndex(FileWriter writer, List<Indexable> indexedElements) {
- String json = generator_util.generateSearchIndexJson(
- indexedElements, _options.prettyIndexJson);
- if (!_options.useBaseHref) {
- json = json.replaceAll(HTMLBASE_PLACEHOLDER, '');
- }
- writer.write(path.join('index.json'), '${json}\n');
- }
-
- @override
- void generatePackage(FileWriter writer, PackageGraph graph, Package package) {
- TemplateData data = PackageTemplateData(_options, graph, package);
- _render(writer, package.filePath, _templates.indexTemplate, data);
- _render(writer, '__404error.html', _templates.errorTemplate, data);
- }
-
- @override
- void generateCategory(
- FileWriter writer, PackageGraph packageGraph, Category category) {
- TemplateData data = CategoryTemplateData(_options, packageGraph, category);
- _render(writer, category.filePath, _templates.categoryTemplate, data);
- }
-
- @override
- void generateLibrary(
- FileWriter writer, PackageGraph packageGraph, Library lib) {
- TemplateData data = LibraryTemplateData(_options, packageGraph, lib);
- _render(writer, lib.filePath, _templates.libraryTemplate, data);
- }
-
- @override
- void generateClass(
- FileWriter writer, PackageGraph packageGraph, Library lib, Class clazz) {
- TemplateData data = ClassTemplateData(_options, packageGraph, lib, clazz);
- _render(writer, clazz.filePath, _templates.classTemplate, data);
- }
-
- @override
- void generateExtension(FileWriter writer, PackageGraph packageGraph,
- Library lib, Extension extension) {
- TemplateData data =
- ExtensionTemplateData(_options, packageGraph, lib, extension);
- _render(writer, extension.filePath, _templates.extensionTemplate, data);
- }
-
- @override
- void generateMixin(
- FileWriter writer, PackageGraph packageGraph, Library lib, Mixin mixin) {
- TemplateData data = MixinTemplateData(_options, packageGraph, lib, mixin);
- _render(writer, mixin.filePath, _templates.mixinTemplate, data);
- }
-
- @override
- void generateConstructor(FileWriter writer, PackageGraph packageGraph,
- Library lib, Class clazz, Constructor constructor) {
- TemplateData data = ConstructorTemplateData(
- _options, packageGraph, lib, clazz, constructor);
-
- _render(writer, constructor.filePath, _templates.constructorTemplate, data);
- }
-
- @override
- void generateEnum(
- FileWriter writer, PackageGraph packageGraph, Library lib, Enum eNum) {
- TemplateData data = EnumTemplateData(_options, packageGraph, lib, eNum);
-
- _render(writer, eNum.filePath, _templates.enumTemplate, data);
- }
-
- @override
- void generateFunction(FileWriter writer, PackageGraph packageGraph,
- Library lib, ModelFunction function) {
- TemplateData data =
- FunctionTemplateData(_options, packageGraph, lib, function);
-
- _render(writer, function.filePath, _templates.functionTemplate, data);
- }
-
- @override
- void generateMethod(FileWriter writer, PackageGraph packageGraph, Library lib,
- Container clazz, Method method) {
- TemplateData data =
- MethodTemplateData(_options, packageGraph, lib, clazz, method);
-
- _render(writer, method.filePath, _templates.methodTemplate, data);
- }
-
- @override
- void generateConstant(FileWriter writer, PackageGraph packageGraph,
- Library lib, Container clazz, Field property) =>
- generateProperty(writer, packageGraph, lib, clazz, property);
-
- @override
- void generateProperty(FileWriter writer, PackageGraph packageGraph,
- Library lib, Container clazz, Field property) {
- TemplateData data =
- PropertyTemplateData(_options, packageGraph, lib, clazz, property);
-
- _render(writer, property.filePath, _templates.propertyTemplate, data);
- }
-
- @override
- void generateTopLevelProperty(FileWriter writer, PackageGraph packageGraph,
- Library lib, TopLevelVariable property) {
- TemplateData data =
- TopLevelPropertyTemplateData(_options, packageGraph, lib, property);
-
- _render(
- writer, property.filePath, _templates.topLevelPropertyTemplate, data);
- }
-
- @override
- void generateTopLevelConstant(FileWriter writer, PackageGraph packageGraph,
- Library lib, TopLevelVariable property) =>
- generateTopLevelProperty(writer, packageGraph, lib, property);
-
- @override
- void generateTypeDef(FileWriter writer, PackageGraph packageGraph,
- Library lib, Typedef typeDef) {
- TemplateData data =
- TypedefTemplateData(_options, packageGraph, lib, typeDef);
-
- _render(writer, typeDef.filePath, _templates.typeDefTemplate, data);
- }
-
- @override
- void generateAdditionalFiles(FileWriter writer, PackageGraph graph) async {
- await _copyResources(writer);
- if (_options.favicon != null) {
- // Allow overwrite of favicon.
- var bytes = File(_options.favicon).readAsBytesSync();
- writer.write(path.join('static-assets', 'favicon.png'), bytes,
- allowOverwrite: true);
- }
- }
-
- Future _copyResources(FileWriter writer) async {
- final prefix = 'package:dartdoc/resources/';
- for (String resourcePath in resources.resource_names) {
- if (!resourcePath.startsWith(prefix)) {
- throw StateError('Resource paths must start with $prefix, '
- 'encountered $resourcePath');
- }
- String destFileName = resourcePath.substring(prefix.length);
- writer.write(path.join('static-assets', destFileName),
- await loader.loadAsBytes(resourcePath));
- }
- }
-}
diff --git a/lib/src/html/html_generator_instance.dart b/lib/src/html/html_generator_instance.dart
new file mode 100644
index 0000000..e82fe1b
--- /dev/null
+++ b/lib/src/html/html_generator_instance.dart
@@ -0,0 +1,413 @@
+// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async' show Future;
+import 'dart:convert' show JsonEncoder;
+import 'dart:io' show File;
+
+import 'package:collection/collection.dart' show compareNatural;
+import 'package:dartdoc/src/html/html_generator.dart' show HtmlGeneratorOptions;
+import 'package:dartdoc/src/html/resource_loader.dart' as loader;
+import 'package:dartdoc/src/html/resources.g.dart' as resources;
+import 'package:dartdoc/src/html/template_data.dart';
+import 'package:dartdoc/src/html/templates.dart';
+import 'package:dartdoc/src/logging.dart';
+import 'package:dartdoc/src/model/model.dart';
+import 'package:dartdoc/src/model_utils.dart';
+import 'package:dartdoc/src/warnings.dart';
+import 'package:mustache/mustache.dart';
+import 'package:path/path.dart' as path;
+
+typedef FileWriter = void Function(String path, Object content,
+ {bool allowOverwrite, Warnable element});
+
+class HtmlGeneratorInstance {
+ final HtmlGeneratorOptions _options;
+ final Templates _templates;
+ final PackageGraph _packageGraph;
+ final List<Indexable> _indexedElements = <Indexable>[];
+ final FileWriter _writer;
+
+ HtmlGeneratorInstance(
+ this._options, this._templates, this._packageGraph, this._writer);
+
+ Future generate() async {
+ if (_packageGraph != null) {
+ _generateDocs();
+ _generateSearchIndex();
+ _generateCategoryJson();
+ }
+
+ await _copyResources();
+ if (_options.faviconPath != null) {
+ var bytes = File(_options.faviconPath).readAsBytesSync();
+ // Allow overwrite of favicon.
+ _writer(path.join('static-assets', 'favicon.png'), bytes,
+ allowOverwrite: true);
+ }
+ }
+
+ void _generateCategoryJson() {
+ var encoder = JsonEncoder.withIndent(' ');
+ final List<Map> indexItems = _categorizationItems.map((Categorization e) {
+ Map data = {
+ 'name': e.name,
+ 'qualifiedName': e.fullyQualifiedName,
+ 'href': e.href,
+ 'type': e.kind,
+ };
+
+ if (e.hasCategoryNames) data['categories'] = e.categoryNames;
+ if (e.hasSubCategoryNames) data['subcategories'] = e.subCategoryNames;
+ if (e.hasImage) data['image'] = e.image;
+ if (e.hasSamples) data['samples'] = e.samples;
+ return data;
+ }).toList();
+
+ indexItems.sort((a, b) {
+ var value = compareNatural(a['qualifiedName'], b['qualifiedName']);
+ if (value == 0) {
+ value = compareNatural(a['type'], b['type']);
+ }
+ return value;
+ });
+
+ String json = encoder.convert(indexItems);
+ if (!_options.useBaseHref) {
+ json = json.replaceAll(HTMLBASE_PLACEHOLDER, '');
+ }
+ _writer(path.join('categories.json'), '${json}\n');
+ }
+
+ List<Categorization> _categorizationItems;
+
+ void _generateSearchIndex() {
+ var encoder =
+ _options.prettyIndexJson ? JsonEncoder.withIndent(' ') : JsonEncoder();
+ _categorizationItems = [];
+
+ final List<Map> indexItems = _indexedElements.map((Indexable e) {
+ if (e is Categorization && e.hasCategorization) {
+ _categorizationItems.add(e);
+ }
+ Map data = {
+ 'name': e.name,
+ 'qualifiedName': e.fullyQualifiedName,
+ 'href': e.href,
+ 'type': e.kind,
+ 'overriddenDepth': e.overriddenDepth,
+ };
+ if (e is EnclosedElement) {
+ EnclosedElement ee = e as EnclosedElement;
+ data['enclosedBy'] = {
+ 'name': ee.enclosingElement.name,
+ 'type': ee.enclosingElement.kind
+ };
+
+ data['qualifiedName'] = e.fullyQualifiedName;
+ }
+ return data;
+ }).toList();
+
+ indexItems.sort((a, b) {
+ var value = compareNatural(a['qualifiedName'], b['qualifiedName']);
+ if (value == 0) {
+ value = compareNatural(a['type'], b['type']);
+ }
+ return value;
+ });
+
+ String json = encoder.convert(indexItems);
+ if (!_options.useBaseHref) {
+ json = json.replaceAll(HTMLBASE_PLACEHOLDER, '');
+ }
+ _writer(path.join('index.json'), '${json}\n');
+ }
+
+ void _generateDocs() {
+ if (_packageGraph == null) return;
+
+ generatePackage(_packageGraph, _packageGraph.defaultPackage);
+
+ for (var package in _packageGraph.localPackages) {
+ for (var category in filterNonDocumented(package.categories)) {
+ generateCategory(_packageGraph, category);
+ }
+
+ for (var lib in filterNonDocumented(package.libraries)) {
+ generateLibrary(_packageGraph, lib);
+
+ for (var clazz in filterNonDocumented(lib.allClasses)) {
+ generateClass(_packageGraph, lib, clazz);
+
+ for (var constructor in filterNonDocumented(clazz.constructors)) {
+ if (!constructor.isCanonical) continue;
+ generateConstructor(_packageGraph, lib, clazz, constructor);
+ }
+
+ for (var constant in filterNonDocumented(clazz.constants)) {
+ if (!constant.isCanonical) continue;
+ generateConstant(_packageGraph, lib, clazz, constant);
+ }
+
+ for (var property in filterNonDocumented(clazz.staticProperties)) {
+ if (!property.isCanonical) continue;
+ generateProperty(_packageGraph, lib, clazz, property);
+ }
+
+ for (var property in filterNonDocumented(clazz.allInstanceFields)) {
+ if (!property.isCanonical) continue;
+ generateProperty(_packageGraph, lib, clazz, property);
+ }
+
+ for (var method in filterNonDocumented(clazz.allInstanceMethods)) {
+ if (!method.isCanonical) continue;
+ generateMethod(_packageGraph, lib, clazz, method);
+ }
+
+ for (var operator in filterNonDocumented(clazz.allOperators)) {
+ if (!operator.isCanonical) continue;
+ generateMethod(_packageGraph, lib, clazz, operator);
+ }
+
+ for (var method in filterNonDocumented(clazz.staticMethods)) {
+ if (!method.isCanonical) continue;
+ generateMethod(_packageGraph, lib, clazz, method);
+ }
+ }
+
+ for (var extension in filterNonDocumented(lib.extensions)) {
+ generateExtension(_packageGraph, lib, extension);
+
+ for (var constant in filterNonDocumented(extension.constants)) {
+ generateConstant(_packageGraph, lib, extension, constant);
+ }
+
+ for (var property
+ in filterNonDocumented(extension.staticProperties)) {
+ generateProperty(_packageGraph, lib, extension, property);
+ }
+
+ for (var method
+ in filterNonDocumented(extension.allPublicInstanceMethods)) {
+ generateMethod(_packageGraph, lib, extension, method);
+ }
+
+ for (var method in filterNonDocumented(extension.staticMethods)) {
+ generateMethod(_packageGraph, lib, extension, method);
+ }
+
+ for (var operator in filterNonDocumented(extension.allOperators)) {
+ generateMethod(_packageGraph, lib, extension, operator);
+ }
+
+ for (var property
+ in filterNonDocumented(extension.allInstanceFields)) {
+ generateProperty(_packageGraph, lib, extension, property);
+ }
+ }
+
+ for (var mixin in filterNonDocumented(lib.mixins)) {
+ generateMixins(_packageGraph, lib, mixin);
+ for (var constructor in filterNonDocumented(mixin.constructors)) {
+ if (!constructor.isCanonical) continue;
+ generateConstructor(_packageGraph, lib, mixin, constructor);
+ }
+
+ for (var constant in filterNonDocumented(mixin.constants)) {
+ if (!constant.isCanonical) continue;
+ generateConstant(_packageGraph, lib, mixin, constant);
+ }
+
+ for (var property in filterNonDocumented(mixin.staticProperties)) {
+ if (!property.isCanonical) continue;
+ generateProperty(_packageGraph, lib, mixin, property);
+ }
+
+ for (var property in filterNonDocumented(mixin.allInstanceFields)) {
+ if (!property.isCanonical) continue;
+ generateProperty(_packageGraph, lib, mixin, property);
+ }
+
+ for (var method in filterNonDocumented(mixin.allInstanceMethods)) {
+ if (!method.isCanonical) continue;
+ generateMethod(_packageGraph, lib, mixin, method);
+ }
+
+ for (var operator in filterNonDocumented(mixin.allOperators)) {
+ if (!operator.isCanonical) continue;
+ generateMethod(_packageGraph, lib, mixin, operator);
+ }
+
+ for (var method in filterNonDocumented(mixin.staticMethods)) {
+ if (!method.isCanonical) continue;
+ generateMethod(_packageGraph, lib, mixin, method);
+ }
+ }
+
+ for (var eNum in filterNonDocumented(lib.enums)) {
+ generateEnum(_packageGraph, lib, eNum);
+ for (var property in filterNonDocumented(eNum.allInstanceFields)) {
+ generateProperty(_packageGraph, lib, eNum, property);
+ }
+ for (var operator in filterNonDocumented(eNum.allOperators)) {
+ generateMethod(_packageGraph, lib, eNum, operator);
+ }
+ for (var method in filterNonDocumented(eNum.allInstanceMethods)) {
+ generateMethod(_packageGraph, lib, eNum, method);
+ }
+ }
+
+ for (var constant in filterNonDocumented(lib.constants)) {
+ generateTopLevelConstant(_packageGraph, lib, constant);
+ }
+
+ for (var property in filterNonDocumented(lib.properties)) {
+ generateTopLevelProperty(_packageGraph, lib, property);
+ }
+
+ for (var function in filterNonDocumented(lib.functions)) {
+ generateFunction(_packageGraph, lib, function);
+ }
+
+ for (var typeDef in filterNonDocumented(lib.typedefs)) {
+ generateTypeDef(_packageGraph, lib, typeDef);
+ }
+ }
+ }
+ }
+
+ void generatePackage(PackageGraph packageGraph, Package package) {
+ TemplateData data = PackageTemplateData(_options, packageGraph, package);
+ logInfo('documenting ${package.name}');
+
+ _build(package.filePath, _templates.indexTemplate, data);
+ _build('__404error.html', _templates.errorTemplate, data);
+ }
+
+ void generateCategory(PackageGraph packageGraph, Category category) {
+ logInfo(
+ 'Generating docs for category ${category.name} from ${category.package.fullyQualifiedName}...');
+ TemplateData data = CategoryTemplateData(_options, packageGraph, category);
+
+ _build(category.filePath, _templates.categoryTemplate, data);
+ }
+
+ void generateLibrary(PackageGraph packageGraph, Library lib) {
+ logInfo(
+ 'Generating docs for library ${lib.name} from ${lib.element.source.uri}...');
+ if (!lib.isAnonymous && !lib.hasDocumentation) {
+ packageGraph.warnOnElement(lib, PackageWarning.noLibraryLevelDocs);
+ }
+ TemplateData data = LibraryTemplateData(_options, packageGraph, lib);
+
+ _build(lib.filePath, _templates.libraryTemplate, data);
+ }
+
+ void generateClass(PackageGraph packageGraph, Library lib, Class clazz) {
+ TemplateData data = ClassTemplateData(_options, packageGraph, lib, clazz);
+ _build(clazz.filePath, _templates.classTemplate, data);
+ }
+
+ void generateExtension(
+ PackageGraph packageGraph, Library lib, Extension extension) {
+ TemplateData data =
+ ExtensionTemplateData(_options, packageGraph, lib, extension);
+ _build(extension.filePath, _templates.extensionTemplate, data);
+ }
+
+ void generateMixins(PackageGraph packageGraph, Library lib, Mixin mixin) {
+ TemplateData data = MixinTemplateData(_options, packageGraph, lib, mixin);
+ _build(mixin.filePath, _templates.mixinTemplate, data);
+ }
+
+ void generateConstructor(PackageGraph packageGraph, Library lib, Class clazz,
+ Constructor constructor) {
+ TemplateData data = ConstructorTemplateData(
+ _options, packageGraph, lib, clazz, constructor);
+
+ _build(constructor.filePath, _templates.constructorTemplate, data);
+ }
+
+ void generateEnum(PackageGraph packageGraph, Library lib, Enum eNum) {
+ TemplateData data = EnumTemplateData(_options, packageGraph, lib, eNum);
+
+ _build(eNum.filePath, _templates.enumTemplate, data);
+ }
+
+ void generateFunction(
+ PackageGraph packageGraph, Library lib, ModelFunction function) {
+ TemplateData data =
+ FunctionTemplateData(_options, packageGraph, lib, function);
+
+ _build(function.filePath, _templates.functionTemplate, data);
+ }
+
+ void generateMethod(
+ PackageGraph packageGraph, Library lib, Container clazz, Method method) {
+ TemplateData data =
+ MethodTemplateData(_options, packageGraph, lib, clazz, method);
+
+ _build(method.filePath, _templates.methodTemplate, data);
+ }
+
+ void generateConstant(PackageGraph packageGraph, Library lib, Container clazz,
+ Field property) =>
+ generateProperty(packageGraph, lib, clazz, property);
+
+ void generateProperty(
+ PackageGraph packageGraph, Library lib, Container clazz, Field property) {
+ TemplateData data =
+ PropertyTemplateData(_options, packageGraph, lib, clazz, property);
+
+ _build(property.filePath, _templates.propertyTemplate, data);
+ }
+
+ void generateTopLevelProperty(
+ PackageGraph packageGraph, Library lib, TopLevelVariable property) {
+ TemplateData data =
+ TopLevelPropertyTemplateData(_options, packageGraph, lib, property);
+
+ _build(property.filePath, _templates.topLevelPropertyTemplate, data);
+ }
+
+ void generateTopLevelConstant(
+ PackageGraph packageGraph, Library lib, TopLevelVariable property) =>
+ generateTopLevelProperty(packageGraph, lib, property);
+
+ void generateTypeDef(
+ PackageGraph packageGraph, Library lib, Typedef typeDef) {
+ TemplateData data =
+ TypedefTemplateData(_options, packageGraph, lib, typeDef);
+
+ _build(typeDef.filePath, _templates.typeDefTemplate, data);
+ }
+
+ // TODO: change this to use resource_loader
+ Future _copyResources() async {
+ final prefix = 'package:dartdoc/resources/';
+ for (String resourcePath in resources.resource_names) {
+ if (!resourcePath.startsWith(prefix)) {
+ throw StateError('Resource paths must start with $prefix, '
+ 'encountered $resourcePath');
+ }
+ String destFileName = resourcePath.substring(prefix.length);
+ _writer(path.join('static-assets', destFileName),
+ await loader.loadAsBytes(resourcePath));
+ }
+ }
+
+ void _build(String filename, Template template, TemplateData data) {
+ // Replaces '/' separators with proper separators for the platform.
+ String outFile = path.joinAll(filename.split('/'));
+ String content = template.renderString(data);
+
+ if (!_options.useBaseHref) {
+ content = content.replaceAll(HTMLBASE_PLACEHOLDER, data.htmlBase);
+ }
+ _writer(outFile, content,
+ element: data.self is Warnable ? data.self : null);
+ if (data.self is Indexable) _indexedElements.add(data.self as Indexable);
+ }
+}
diff --git a/lib/src/html/templates.dart b/lib/src/html/templates.dart
index 2b929c3..a47efde 100644
--- a/lib/src/html/templates.dart
+++ b/lib/src/html/templates.dart
@@ -158,21 +158,6 @@
final Template topLevelPropertyTemplate;
final Template typeDefTemplate;
- static Future<Templates> fromContext(GeneratorContext context) {
- String templatesDir = context.templatesDir;
- if (templatesDir != null) {
- return fromDirectory(Directory(templatesDir),
- headerPaths: context.header,
- footerPaths: context.footer,
- footerTextPaths: context.footerTextPaths);
- } else {
- return createDefault(
- headerPaths: context.header,
- footerPaths: context.footer,
- footerTextPaths: context.footerTextPaths);
- }
- }
-
static Future<Templates> createDefault(
{List<String> headerPaths,
List<String> footerPaths,
diff --git a/test/html_generator_test.dart b/test/html_generator_test.dart
index db62526..460c5dc 100644
--- a/test/html_generator_test.dart
+++ b/test/html_generator_test.dart
@@ -6,9 +6,7 @@
import 'dart:io' show File, Directory;
-import 'package:dartdoc/dartdoc.dart';
-import 'package:dartdoc/src/generator_frontend.dart';
-import 'package:dartdoc/src/html/html_generator_backend.dart';
+import 'package:dartdoc/src/html/html_generator.dart';
import 'package:dartdoc/src/html/templates.dart';
import 'package:dartdoc/src/html/resources.g.dart';
import 'package:dartdoc/src/model/package_graph.dart';
@@ -18,12 +16,6 @@
import 'src/utils.dart' as utils;
-// Init a generator without a GeneratorContext and with the default file writer.
-Future<Generator> _initGeneratorForTest() async {
- var backend = HtmlGeneratorBackend(null, await Templates.createDefault());
- return GeneratorFrontEnd(backend);
-}
-
void main() {
group('Templates', () {
Templates templates;
@@ -76,15 +68,13 @@
group('HtmlGenerator', () {
// TODO: Run the HtmlGenerator and validate important constraints.
group('for a null package', () {
- Generator generator;
+ HtmlGenerator generator;
Directory tempOutput;
- FileWriter writer;
setUp(() async {
- generator = await _initGeneratorForTest();
+ generator = await HtmlGenerator.create();
tempOutput = Directory.systemTemp.createTempSync('doc_test_temp');
- writer = DartdocFileWriter(tempOutput.path);
- return generator.generate(null, writer);
+ return generator.generate(null, tempOutput.path);
});
tearDown(() {
@@ -106,17 +96,15 @@
});
group('for a package that causes duplicate files', () {
- Generator generator;
+ HtmlGenerator generator;
PackageGraph packageGraph;
Directory tempOutput;
- FileWriter writer;
setUp(() async {
- generator = await _initGeneratorForTest();
+ generator = await HtmlGenerator.create();
packageGraph = await utils
.bootBasicPackage(utils.testPackageDuplicateDir.path, []);
tempOutput = await Directory.systemTemp.createTemp('doc_test_temp');
- writer = DartdocFileWriter(tempOutput.path);
});
tearDown(() {
@@ -126,7 +114,7 @@
});
test('run generator and verify duplicate file error', () async {
- await generator.generate(packageGraph, writer);
+ await generator.generate(packageGraph, tempOutput.path);
expect(generator, isNotNull);
expect(tempOutput, isNotNull);
String expectedPath =