@@ -35,10 +35,9 @@ The two popular Mustache packages for Dart ([mustache][] and [mustache4dart][])
35
35
each use [ mirrors] [ ] , which is slow, and whose future is unknown. The
36
36
[ mustache_template] [ ] package avoids mirrors by restricting context objects to
37
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.
38
+ for Dartdoc. For a time, dartdoc used the mustache package. At that time, the
39
+ majority of dartdoc's execution time was spent generating the HTML output from
40
+ package metadata. Benchmarking showed that much time was spent using mirrors.
42
41
43
42
[ Mustache templating ] : https://mustache.github.io/
44
43
[ mustache ] : https://pub.dev/packages/mustache
@@ -49,12 +48,8 @@ using mirrors.
49
48
## Motivation
50
49
51
50
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.
51
+ the time to generate package documentation. A system that uses static dispatch
52
+ in lieu of mirror-based dispatch is faster on the Dart VM.
58
53
59
54
There are several secondary benefits:
60
55
@@ -647,18 +642,22 @@ keys at the time of code generation. For example, given the following template:
647
642
648
643
``` html
649
644
<h1 >{{ name }}</h1 >
650
- {{ #isFeatured }}<strong >Featured</strong >{{ /isFeatured }}
651
645
<div class =" posts" >
652
646
{{ #featuredPost }}<h2 >{{ title }}</h2 >{{ /featuredPost }}
653
- {{ #posts }}<h2 >{{ title }}</h2 >{{ /posts }}
647
+ {{ #posts }}
648
+ {{ #isPublished }}
649
+ <h2 >{{ title }}</h2 >
650
+ {{ /isPublished }}
651
+ {{ /posts }}
654
652
</div >
655
653
```
656
654
657
- The code generator resolves ` name ` to a String getter on User, ` posts ` to a
658
- ` List<Post> ` getter on User, ` isPublished ` to a ` bool ` getter on ` Post ` , and
659
- ` title ` to a String getter on ` Post ` . It has all of the information it needs to
660
- write out the logic of the template as a simple state machine. This state
661
- machine is written out as the render function and helper functions for partials:
655
+ The code generator resolves ` name ` to a String getter on User, ` featuredPost ` to
656
+ a Post getter on User, ` posts ` to a ` List<Post> ` getter on User, ` isPublished `
657
+ to a ` bool ` getter on ` Post ` , and ` title ` to a String getter on ` Post ` . It has
658
+ all of the information it needs to write out the logic of the template as a
659
+ simple state machine. This state machine is written out as the render function
660
+ and helper functions for partials:
662
661
663
662
``` dart
664
663
String renderUser(User context0) {
@@ -668,7 +667,7 @@ String renderUser(User context0) {
668
667
}
669
668
```
670
669
671
- The ` renderFoo ` function takes a ` Foo ` object, the context object, as
670
+ The ` renderUser ` function takes a ` User ` object, the context object, as
672
671
` context0 ` . Since the context objects exist in a stack and can each be accessed,
673
672
we must enumerate them. We write various text to the buffer, according to the
674
673
template, and then return the rendered output.
@@ -693,7 +692,7 @@ that provide each getter.
693
692
buffer.write(htmlEscape.convert(context0.name.toString()));
694
693
```
695
694
696
- This code calls the ` name ` getter on ` conext0 ` , and then ` toString() ` . Since
695
+ This code calls the ` name ` getter on ` context0 ` , and then ` toString() ` . Since
697
696
` {{ name }} ` uses two brackets, the output must be HTML-escaped. If it were
698
697
written ` {{{ name }}} ` , then the HTML-escaping call would not be made.
699
698
@@ -708,14 +707,14 @@ the renderer.
708
707
` {{ #isFeatured }}<strong>Featured</strong>{{ /isFeatured }} ` compiles to:
709
708
710
709
``` dart
711
- if (context0.b1 == true ) {
710
+ if (context0.isFeatured ) {
712
711
buffer.write('''<strong>Featured</strong>''');
713
712
}
714
713
```
715
714
716
- The text is written only if ` b1 ` is ` true ` (not ` false ` or ` null ` ) . If the
717
- section were inverted (starting with ` {{ ^isFeatured }} ` ), this would be a
718
- ` != true ` check .
715
+ The text is written only if ` isFeatured ` is ` true ` . If the section were
716
+ inverted (starting with ` {{ ^isFeatured }} ` ), then the condition would be
717
+ ` !context0.isFeatured ` .
719
718
720
719
#### Rendering a repeated section
721
720
0 commit comments