@@ -35,10 +35,9 @@ The two popular Mustache packages for Dart ([mustache][] and [mustache4dart][])
3535each use [ mirrors] [ ] , which is slow, and whose future is unknown. The
3636[ mustache_template] [ ] package avoids mirrors by restricting context objects to
3737just 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.
4241
4342[ Mustache templating ] : https://mustache.github.io/
4443[ mustache ] : https://pub.dev/packages/mustache
@@ -49,12 +48,8 @@ using mirrors.
4948## Motivation
5049
5150The 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.
5853
5954There are several secondary benefits:
6055
@@ -647,18 +642,22 @@ keys at the time of code generation. For example, given the following template:
647642
648643``` html
649644<h1 >{{ name }}</h1 >
650- {{ #isFeatured }}<strong >Featured</strong >{{ /isFeatured }}
651645<div class =" posts" >
652646{{ #featuredPost }}<h2 >{{ title }}</h2 >{{ /featuredPost }}
653- {{ #posts }}<h2 >{{ title }}</h2 >{{ /posts }}
647+ {{ #posts }}
648+ {{ #isPublished }}
649+ <h2 >{{ title }}</h2 >
650+ {{ /isPublished }}
651+ {{ /posts }}
654652</div >
655653```
656654
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:
662661
663662``` dart
664663String renderUser(User context0) {
@@ -668,7 +667,7 @@ String renderUser(User context0) {
668667}
669668```
670669
671- The ` renderFoo ` function takes a ` Foo ` object, the context object, as
670+ The ` renderUser ` function takes a ` User ` object, the context object, as
672671` context0 ` . Since the context objects exist in a stack and can each be accessed,
673672we must enumerate them. We write various text to the buffer, according to the
674673template, and then return the rendered output.
@@ -693,7 +692,7 @@ that provide each getter.
693692 buffer.write(htmlEscape.convert(context0.name.toString()));
694693```
695694
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
697696` {{ name }} ` uses two brackets, the output must be HTML-escaped. If it were
698697written ` {{{ name }}} ` , then the HTML-escaping call would not be made.
699698
@@ -708,14 +707,14 @@ the renderer.
708707` {{ #isFeatured }}<strong>Featured</strong>{{ /isFeatured }} ` compiles to:
709708
710709``` dart
711- if (context0.b1 == true ) {
710+ if (context0.isFeatured ) {
712711 buffer.write('''<strong>Featured</strong>''');
713712 }
714713```
715714
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 ` .
719718
720719#### Rendering a repeated section
721720
0 commit comments