Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace mustache usage with mustachio #2564

Merged
merged 5 commits into from
Mar 29, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 45 additions & 34 deletions lib/src/generator/dartdoc_generator_backend.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@ import 'package:dartdoc/src/generator/generator_frontend.dart';
import 'package:dartdoc/src/generator/generator_utils.dart' as generator_util;
import 'package:dartdoc/src/generator/template_data.dart';
import 'package:dartdoc/src/generator/templates.dart';
import 'package:dartdoc/src/generator/templates.renderers.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/mustachio/renderer_base.dart';
import 'package:dartdoc/src/warnings.dart';
import 'package:mustache/mustache.dart';
import 'package:path/path.dart' as path show Context;

/// Configuration options for the Dartdoc's default backend.
Expand Down Expand Up @@ -61,38 +62,41 @@ class DartdocGeneratorBackendOptions implements TemplateOptions {
customInnerFooterText = '';
}

class SidebarGenerator<T extends Documentable> {
class SidebarGenerator<T extends TemplateData> {
final Template template;
final Map<T, String> _renderCache = {};
final RenderFunction<T> renderFunction;
final Map<Documentable, String> _renderCache = {};

SidebarGenerator(this.template);
SidebarGenerator(this.template, this.renderFunction);

// Retrieve the render for a specific key, or generate it using the given
// template data if you need.
String getRenderFor(T key, TemplateData templateData) {
return _renderCache[key] ??= template.renderString(templateData);
String getRenderFor(Documentable key, T templateData) {
return _renderCache[key] ??= renderFunction(templateData, template);
}
}

/// Base GeneratorBackend for Dartdoc's supported formats.
abstract class DartdocGeneratorBackend implements GeneratorBackend {
final DartdocGeneratorBackendOptions options;
final Templates templates;
final SidebarGenerator<Library> sidebarForLibrary;
final SidebarGenerator<Container> sidebarForContainer;
final SidebarGenerator<TemplateDataWithLibrary<Documentable>>
sidebarForLibrary;
final SidebarGenerator<TemplateDataWithContainer<Documentable>>
sidebarForContainer;
final path.Context _pathContext;

DartdocGeneratorBackend(
DartdocGeneratorBackendOptions options, this.templates, this._pathContext)
: options = options ?? DartdocGeneratorBackendOptions._defaults(),
sidebarForContainer =
SidebarGenerator(templates.sidebarContainerTemplate),
sidebarForLibrary = SidebarGenerator(templates.sidebarLibraryTemplate);
sidebarForLibrary = SidebarGenerator(
templates.sidebarLibraryTemplate, renderSidebarForLibrary),
sidebarForContainer = SidebarGenerator(
templates.sidebarContainerTemplate, renderSidebarForContainer);

/// Helper method to bind template data and emit the content to the writer.
void render(FileWriter writer, String filename, Template template,
TemplateData data) {
var content = template.renderString(data);
void write(
FileWriter writer, String filename, TemplateData data, String content) {
if (!options.useBaseHref) {
content = content.replaceAll(htmlBasePlaceholder, data.htmlBase);
}
Expand Down Expand Up @@ -128,30 +132,34 @@ abstract class DartdocGeneratorBackend implements GeneratorBackend {
@override
void generatePackage(FileWriter writer, PackageGraph graph, Package package) {
TemplateData data = PackageTemplateData(options, graph, package);
render(writer, package.filePath, templates.indexTemplate, data);
var content = renderIndex(data, templates.indexTemplate);
write(writer, package.filePath, data, content);
}

@override
void generateCategory(
FileWriter writer, PackageGraph packageGraph, Category category) {
TemplateData data = CategoryTemplateData(options, packageGraph, category);
render(writer, category.filePath, templates.categoryTemplate, data);
var content = renderCategory(data, templates.categoryTemplate);
write(writer, category.filePath, data, content);
}

@override
void generateLibrary(
FileWriter writer, PackageGraph packageGraph, Library lib) {
TemplateData data = LibraryTemplateData(
options, packageGraph, lib, sidebarForLibrary.getRenderFor);
render(writer, lib.filePath, templates.libraryTemplate, data);
var content = renderLibrary(data, templates.libraryTemplate);
write(writer, lib.filePath, data, content);
}

@override
void generateClass(
FileWriter writer, PackageGraph packageGraph, Library lib, Class clazz) {
TemplateData data = ClassTemplateData(options, packageGraph, lib, clazz,
sidebarForLibrary.getRenderFor, sidebarForContainer.getRenderFor);
render(writer, clazz.filePath, templates.classTemplate, data);
var content = renderClass(data, templates.classTemplate);
write(writer, clazz.filePath, data, content);
}

@override
Expand All @@ -164,51 +172,53 @@ abstract class DartdocGeneratorBackend implements GeneratorBackend {
extension,
sidebarForLibrary.getRenderFor,
sidebarForContainer.getRenderFor);
render(writer, extension.filePath, templates.extensionTemplate, data);
var content = renderExtension(data, templates.extensionTemplate);
write(writer, extension.filePath, data, content);
}

@override
void generateMixin(
FileWriter writer, PackageGraph packageGraph, Library lib, Mixin mixin) {
TemplateData data = MixinTemplateData(options, packageGraph, lib, mixin,
sidebarForLibrary.getRenderFor, sidebarForContainer.getRenderFor);
render(writer, mixin.filePath, templates.mixinTemplate, data);
var content = renderMixin(data, templates.mixinTemplate);
write(writer, mixin.filePath, data, content);
}

@override
void generateConstructor(FileWriter writer, PackageGraph packageGraph,
Library lib, Class clazz, Constructor constructor) {
TemplateData data = ConstructorTemplateData(options, packageGraph, lib,
clazz, constructor, sidebarForContainer.getRenderFor);

render(writer, constructor.filePath, templates.constructorTemplate, data);
var content = renderConstructor(data, templates.constructorTemplate);
write(writer, constructor.filePath, data, content);
}

@override
void generateEnum(
FileWriter writer, PackageGraph packageGraph, Library lib, Enum eNum) {
TemplateData data = EnumTemplateData(options, packageGraph, lib, eNum,
sidebarForLibrary.getRenderFor, sidebarForContainer.getRenderFor);

render(writer, eNum.filePath, templates.enumTemplate, data);
var content = renderEnum(data, templates.enumTemplate);
write(writer, eNum.filePath, data, content);
}

@override
void generateFunction(FileWriter writer, PackageGraph packageGraph,
Library lib, ModelFunction function) {
TemplateData data = FunctionTemplateData(
options, packageGraph, lib, function, sidebarForLibrary.getRenderFor);

render(writer, function.filePath, templates.functionTemplate, data);
var content = renderFunction(data, templates.functionTemplate);
write(writer, function.filePath, data, content);
}

@override
void generateMethod(FileWriter writer, PackageGraph packageGraph, Library lib,
Container clazz, Method method) {
TemplateData data = MethodTemplateData(options, packageGraph, lib, clazz,
method, sidebarForContainer.getRenderFor);

render(writer, method.filePath, templates.methodTemplate, data);
var content = renderMethod(data, templates.methodTemplate);
write(writer, method.filePath, data, content);
}

@override
Expand All @@ -221,17 +231,18 @@ abstract class DartdocGeneratorBackend implements GeneratorBackend {
Library lib, Container clazz, Field property) {
TemplateData data = PropertyTemplateData(options, packageGraph, lib, clazz,
property, sidebarForContainer.getRenderFor);

render(writer, property.filePath, templates.propertyTemplate, data);
var content = renderProperty(data, templates.propertyTemplate);
write(writer, property.filePath, data, content);
}

@override
void generateTopLevelProperty(FileWriter writer, PackageGraph packageGraph,
Library lib, TopLevelVariable property) {
TemplateData data = TopLevelPropertyTemplateData(
options, packageGraph, lib, property, sidebarForLibrary.getRenderFor);

render(writer, property.filePath, templates.topLevelPropertyTemplate, data);
var content =
renderTopLevelProperty(data, templates.topLevelPropertyTemplate);
write(writer, property.filePath, data, content);
}

@override
Expand All @@ -244,8 +255,8 @@ abstract class DartdocGeneratorBackend implements GeneratorBackend {
Library lib, Typedef typeDef) {
TemplateData data = TypedefTemplateData(
options, packageGraph, lib, typeDef, sidebarForLibrary.getRenderFor);

render(writer, typeDef.filePath, templates.typeDefTemplate, data);
var content = renderTypedef(data, templates.typeDefTemplate);
write(writer, typeDef.filePath, data, content);
}

@override
Expand Down
4 changes: 3 additions & 1 deletion lib/src/generator/html_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import 'package:dartdoc/src/generator/html_resources.g.dart' as resources;
import 'package:dartdoc/src/generator/resource_loader.dart';
import 'package:dartdoc/src/generator/template_data.dart';
import 'package:dartdoc/src/generator/templates.dart';
import 'package:dartdoc/src/generator/templates.renderers.dart';
import 'package:dartdoc/src/model/package.dart';
import 'package:dartdoc/src/model/package_graph.dart';
import 'package:path/path.dart' as path show Context;
Expand All @@ -36,7 +37,8 @@ class HtmlGeneratorBackend extends DartdocGeneratorBackend {
super.generatePackage(writer, graph, package);
// We have to construct the data again. This only happens once per package.
TemplateData data = PackageTemplateData(options, graph, package);
render(writer, '__404error.html', templates.errorTemplate, data);
var content = renderError(data, templates.errorTemplate);
write(writer, '__404error.html', data, content);
}

@override
Expand Down
4 changes: 3 additions & 1 deletion lib/src/generator/markdown_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import 'package:dartdoc/src/generator/generator.dart';
import 'package:dartdoc/src/generator/generator_frontend.dart';
import 'package:dartdoc/src/generator/template_data.dart';
import 'package:dartdoc/src/generator/templates.dart';
import 'package:dartdoc/src/generator/templates.renderers.dart';
import 'package:dartdoc/src/model/package.dart';
import 'package:dartdoc/src/model/package_graph.dart';
import 'package:path/path.dart' as path show Context;
Expand All @@ -32,6 +33,7 @@ class MarkdownGeneratorBackend extends DartdocGeneratorBackend {
super.generatePackage(writer, graph, package);
// We have to construct the data again. This only happens once per package.
TemplateData data = PackageTemplateData(options, graph, package);
render(writer, '__404error.md', templates.errorTemplate, data);
var content = renderError(data, templates.errorTemplate);
write(writer, '__404error.md', data, content);
}
}
6 changes: 5 additions & 1 deletion lib/src/generator/resource_loader.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,12 @@ extension ResourceLoader on ResourceProvider {
throw ArgumentError('path must begin with package:');
}

return (await getResourceFile(path)).readAsBytesSync();
}

Future<File> getResourceFile(String path) async {
var uri = await resolveResourceUri(Uri.parse(path));
return getFile(uri.toFilePath()).readAsBytesSync();
return getFile(uri.toFilePath());
}

/// Helper function for resolving to a non-relative, non-package URI.
Expand Down
83 changes: 59 additions & 24 deletions lib/src/generator/templates.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,60 @@
// 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.

// TODO(srawlins): Add Renderer annotations for more types as the mustachio
// implementation matures.
@Renderer(#renderIndex, Context<PackageTemplateData>(), visibleTypes: {Package})
@Renderer(#renderCategory, Context<CategoryTemplateData>(),
visibleTypes: _visibleTypes)
@Renderer(#renderClass, Context<ClassTemplateData>())
@Renderer(#renderConstructor, Context<ConstructorTemplateData>())
@Renderer(#renderEnum, Context<EnumTemplateData>())
@Renderer(#renderError, Context<PackageTemplateData>())
@Renderer(#renderExtension, Context<ExtensionTemplateData>())
@Renderer(#renderFunction, Context<FunctionTemplateData>())
@Renderer(#renderIndex, Context<PackageTemplateData>())
@Renderer(#renderLibrary, Context<LibraryTemplateData>())
@Renderer(#renderMethod, Context<MethodTemplateData>())
@Renderer(#renderMixin, Context<MixinTemplateData>())
@Renderer(#renderProperty, Context<PropertyTemplateData>())
@Renderer(#renderSidebarForContainer,
Context<TemplateDataWithContainer<Documentable>>())
@Renderer(
#renderSidebarForLibrary, Context<TemplateDataWithLibrary<Documentable>>())
@Renderer(#renderTopLevelProperty, Context<TopLevelPropertyTemplateData>())
@Renderer(#renderTypedef, Context<TypedefTemplateData>())
library dartdoc.templates;

import 'package:analyzer/file_system/file_system.dart';
import 'package:dartdoc/dartdoc.dart';
import 'package:dartdoc/options.dart';
import 'package:dartdoc/src/generator/resource_loader.dart';
import 'package:dartdoc/src/generator/template_data.dart';
import 'package:dartdoc/src/model/feature_set.dart';
import 'package:dartdoc/src/model/language_feature.dart';
import 'package:dartdoc/src/mustachio/annotations.dart';
import 'package:dartdoc/src/mustachio/renderer_base.dart';
import 'package:meta/meta.dart';
import 'package:mustache/mustache.dart';
import 'package:path/path.dart' as path show Context;

const _visibleTypes = {
CallableElementTypeMixin,
Category,
Class,
Constructor,
DefinedElementType,
Documentable,
Enum,
Extension,
FeatureSet,
LanguageFeature,
Library,
Method,
ModelElement,
Package,
// For getters like `isNotEmpty`; perhaps a smell, but currently in use.
String,
TopLevelVariable,
TypeParameter,
};

// resource_loader and the Resource API doesn't support viewing resources like
// a directory listing, so we have to explicitly list the partials.
const _partials_html = <String>[
Expand Down Expand Up @@ -81,7 +120,7 @@ abstract class _TemplatesLoader {

Future<Map<String, String>> loadPartials();

Future<String> loadTemplate(String name);
Future<Template> loadTemplate(String name);
}

/// Loads default templates included in the Dartdoc program.
Expand Down Expand Up @@ -121,9 +160,14 @@ class _DefaultTemplatesLoader extends _TemplatesLoader {
}

@override
Future<String> loadTemplate(String name) =>
resourceProvider.loadResourceAsString(
'package:dartdoc/templates/$_format/$name.$_format');
Future<Template> loadTemplate(String name) async {
var templateFile = await resourceProvider
.getResourceFile('package:dartdoc/templates/$_format/$name.$_format');
return Template.parse(templateFile,
partialResolver: (String partialName) =>
resourceProvider.getResourceFile(
'package:dartdoc/templates/$_format/_$partialName.$_format'));
}
}

/// Loads templates from a specified Directory.
Expand Down Expand Up @@ -155,12 +199,14 @@ class _DirectoryTemplatesLoader extends _TemplatesLoader {
}

@override
Future<String> loadTemplate(String name) async {
var file = _directory.getChildAssumingFile('$name.$_format');
if (!file.exists) {
Future<Template> loadTemplate(String name) async {
var templateFile = _directory.getChildAssumingFile('$name.$_format');
if (!templateFile.exists) {
throw DartdocFailure('Missing required template file: $name.$_format');
}
return file.readAsStringSync();
return Template.parse(templateFile,
partialResolver: (String partialName) async =>
_directory.getChildAssumingFile('_$partialName.$_format'));
}
}

Expand Down Expand Up @@ -208,19 +254,8 @@ class Templates {
}

static Future<Templates> _create(_TemplatesLoader templatesLoader) async {
var partials = await templatesLoader.loadPartials();

Template _partial(String name) {
var partial = partials[name];
if (partial == null || partial.isEmpty) {
throw StateError('Did not find partial "$name"');
}
return Template(partial);
}

Future<Template> _loadTemplate(String templatePath) async {
var templateContents = await templatesLoader.loadTemplate(templatePath);
return Template(templateContents, partialResolver: _partial);
return templatesLoader.loadTemplate(templatePath);
}

var indexTemplate = await _loadTemplate('index');
Expand Down
Loading