Skip to content

Commit 55034c5

Browse files
authored
Introduce a README for mustachio. (#2623)
1 parent 4b616c1 commit 55034c5

File tree

4 files changed

+181
-0
lines changed

4 files changed

+181
-0
lines changed

lib/src/mustachio/annotations.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
// See the Mustachio README at tool/mustachio/README.md for high-level
6+
// documentation.
7+
18
/// Specifies information for generating a Mustache renderer for a [context]
29
/// object, using a Mustache template at [templateUri]
310
class Renderer {

lib/src/mustachio/parser.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5+
// See the Mustachio README at tool/mustachio/README.md for high-level
6+
// documentation.
7+
58
import 'package:charcode/charcode.dart';
69
import 'package:meta/meta.dart';
710
import 'package:source_span/source_span.dart';

lib/src/mustachio/renderer_base.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5+
// See the Mustachio README at tool/mustachio/README.md for high-level
6+
// documentation.
7+
58
import 'dart:collection';
69

710
import 'package:analyzer/file_system/file_system.dart';

tool/mustachio/README.md

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
# Mustachio
2+
3+
_Mustachio_ is a code generation-based Mustache render system designed with
4+
Dartdoc's needs in mind.
5+
6+
## Mustache background
7+
8+
[Mustache templating][] takes a _context object_ and renders it into a
9+
_template_. For example, an instance of the Library class can be rendered into
10+
the `library.html` template file. Mustache is primarily a templating syntax,
11+
where tags specify how _keys_ on the context object are rendered into the
12+
template. For example, consider the following template:
13+
14+
```mustache
15+
<h1>{{ name }}</h1>
16+
{{ #hasDetails }}
17+
<ul>
18+
{{ #details }}
19+
<li>{{ text }}</li>
20+
{{ /details }}
21+
</ul>
22+
{{ /hasDetails }}
23+
```
24+
25+
Mustache specifies that `{{ name }}` represents a _variable_ tag, where the
26+
value of the context object's `name` property is interpolated into the
27+
template. `{{ #hasDetails }}` and `{{ #details }}` each specify a _section_ tag,
28+
where the template content between `{{ #hasDetails }}` and `{{ /hasDetails }}`
29+
is rendered zero, one, or multiple times, possibly with a new context object,
30+
depending on the value of the context object's `hasDetails` property. The catch
31+
is in how a Dart program can access a property of an object via a
32+
runtime-derived String name.
33+
34+
The two popular Mustache packages for Dart ([mustache][] and [mustache4dart][])
35+
each use [mirrors][], which is slow, and whose future is unknown. The
36+
[mustache_template][] package avoids mirrors by restricting context objects to
37+
just Maps and Lists of values. Neither of these existing solutions were optimal
38+
for Dartdoc. For a time, dartdoc used the mustache package. When dartdoc creates
39+
documentation for a package, the majority of the time is spent generating the
40+
HTML output from package metadata. Benchmarking shows that much time is spent
41+
using mirrors.
42+
43+
[Mustache templating]: https://mustache.github.io/
44+
[mustache]: https://pub.dev/packages/mustache
45+
[mustache4dart]: https://pub.dev/packages/mustache4dart
46+
[mirrors]: https://api.dart.dev/stable/dart-mirrors/dart-mirrors-library.html
47+
[mustache_template]: https://pub.dev/packages/mustache_template
48+
49+
## Motivation
50+
51+
The primary motivation to design a new template rendering solution is to reduce
52+
the time to generate package documentation. In mid-2020, on a current MacBook
53+
Pro, it took 12 minutes to generate the Flutter documentation. A solution which
54+
uses static dispatch instead of mirrors's runtime reflection will be much
55+
faster. A prototype demonstrated that a new system which parses templates
56+
ahead-of-time against known context types could render the Flutter documentation
57+
in 3 minutes.
58+
59+
There are several secondary benefits:
60+
61+
* **Correct static typing** - a solution which uses property access on
62+
statically typed objects ensures that properties (getters, specifically)
63+
exist, illuminating typos.
64+
* **Property usage** - a solution which uses normal property access (calling
65+
getters) on statically typed objects allows analyzers and IDEs to understand
66+
when a property is referenced within a template. This is required for
67+
automated refactoring, finding references, finding definition, and “unused”
68+
static analysis.
69+
* **The possibility to restrict API usage** - currently, any custom template
70+
which a package author writes can walk the entire UML diagram of dartdoc's
71+
internals, and any types which can be accessed via public properties from the
72+
primary ModelElement types. This reaches out to include hundreds of types, and
73+
tens of thousands of properties. A code-generation solution allows dartdoc to
74+
declare only a supported, restricted subset.
75+
76+
## Mustache's dynamically typed background
77+
78+
Mustache was originally authored as a templating system to be used in
79+
JavaScript. JavaScript's dynamic typing and use of objects and object properties
80+
lends itself to simple ideas in parsing Mustache. A renderer can request from
81+
any object a property with a String name parsed from a Mustache template
82+
string. JavaScript also has notions of “falsey” and “truthy” which are used in
83+
rendering sections and inverted sections.
84+
85+
This design is a perfect fit for JavaScript. A Mustache renderer for Dart which
86+
accesses properties in "the normal way" (not using mirrors, and not using
87+
dynamic dispatch) requires a non-trivial design. In fact, the two code-generated
88+
renderer designs provided here each require ahead-of-time knowledge of the
89+
complete set of types which may be rendered with Mustache templates. No design
90+
is given for a Mustache renderer which can render arbitrary objects.
91+
92+
## Design overview
93+
94+
### Two rendering methods
95+
96+
When dartdoc generates documentation for a package, it renders context objects
97+
using Mustache templates. For example, an EnumTemplateData instance (for a
98+
specific enum) is rendered using a file, `enum.html`. When generating
99+
documentation for api.dart.dev, pub.dev, and api.flutter.dev, standard, static
100+
templates are used. When generating documentation for Fuchsia, custom templates
101+
are used, which are only known and resolved at runtime.
102+
103+
Two code generation-based rendering methods are designed below. The first
104+
design is for a tool which can generate the code to render objects of specific
105+
types using runtime-interpreted Mustache template blocks. The second design is
106+
for a tool which can generate the code to render objects of specific types using
107+
pre-compiled Mustache template blocks.
108+
109+
**The first tool generates code specific to one set of known types, one renderer
110+
per type.** Each generated renderer accepts an instance of the appropriate type,
111+
and a Mustache template block.
112+
113+
The renderers access properties of objects via normal Dart property access
114+
(without reflection, and without dynamic dispatch), but require complete
115+
mappings from property names to property accessors for each type.
116+
117+
**The second tool generates code specific to one set of known types, and one
118+
set of known templates and partials, one renderer per type-template pair.** Each
119+
generated renderer accepts an instance of the appropriate type. Each template is
120+
pre-encoded into the appropriate renderer, including the parse tree and all key
121+
resolution.
122+
123+
The renderers access properties of objects via normal Dart property access
124+
(without reflection, and without dynamic dispatch).
125+
126+
When using the standard templates to generate documentation, Dartdoc can make
127+
use of the pre-compiled renderers. When using custom templates to generate
128+
documentation, Dartdoc must make use of the renderers which interpret template
129+
blocks at runtime.
130+
131+
## Limitations
132+
133+
Dartdoc's standard templates do not use all features of Mustache. Ergo,
134+
Mustachio does not support all features of Mustache. Namely:
135+
136+
* no support for Lambda tags
137+
* no support for Set Delimiter tags
138+
* no support for resolving Map keys or List indexes
139+
140+
## Parser
141+
142+
The Mustache parser is shared between the two code-generation methods. Parsing
143+
a Mustache template block of text (from a template or a partial) into a syntax
144+
tree, without resolving keys, is a solved problem; Mustachio's [Parser] is not
145+
novel.
146+
147+
The output of this parser is a syntax tree consisting of the following node
148+
types:
149+
150+
* [Text][] node - plain text as it appears in the template
151+
* [Variable][] node - a node containing the variable tag key
152+
* [Section][] node - a node containing the section tag key, whether the section
153+
tag is inverted, and the syntax tree of the section block
154+
* [Partial][] node - a node containing the partial tag key
155+
156+
[Parser]: https://github.com/dart-lang/dartdoc/blob/master/lib/src/mustachio/parser.dart
157+
[Text]: https://github.com/dart-lang/dartdoc/blob/master/lib/src/mustachio/parser.dart#L422
158+
[Variable]: https://github.com/dart-lang/dartdoc/blob/master/lib/src/mustachio/parser.dart#L436
159+
[Section]: https://github.com/dart-lang/dartdoc/blob/master/lib/src/mustachio/parser.dart#L456
160+
[Partial]: https://github.com/dart-lang/dartdoc/blob/master/lib/src/mustachio/parser.dart#L477
161+
162+
## Generated renderer for a specific type which interprets templates at runtime
163+
164+
TODO(srawlins): Write.
165+
166+
## Generated renderer for a specific type and a static template which pre-compiles the templates
167+
168+
TODO(srawlins): Write.

0 commit comments

Comments
 (0)