Template System
The DocFX template system provides a flexible way of defining and using templates to control how the final output files are rendered. These files provide the content used to publish a DocFx-generated web site. Note that this is different than the HTML templates discussed in Walkthrough Advanced: Customize Your Website, which are used to control the styling applied to the web site.
As the following DocFX workflow shows,
DocFX loads the set of files and transforms them into different data models using different types of Document Processors. Afterwards, the template system loads these data models, and transforms them into output files based on the document type of the data model.
Each file belongs to a document type. For example, the document type for Markdown files is conceptual
, and the document type for toc.md
files is Toc
.
For a specific Template, each document type can have several Renderers. For a specific file, the template system picks the corresponding Renderers to render the input data model into output files.
Renderer
Renderers are files written in Mustache. It is used to transform the input data model into output files.
Naming rule for a Renderer file
The naming rule for a Renderer file is:
<document_type>.<output_extension>[.primary].tmpl
.
<document_type>
is the document type current Renderer responsible to.<output_extension>
defines the extension of the output files going through current Renderer. For example,conceptual.html.tmpl
transformsfile1.md
into output filefile1.html
, andtoc.json.tmpl
transformstoc.md
into output filetoc.json
.[.primary]
is optional. It is used when there are multiple Renderers with different extension for one particular document type. The output file transformed by the.primary
Renderer is used as the file to be linked. The below example describes the behavior in detail.
Here is an example.
The following template contains two Mustache Renderer files for conceptual
document type:
/- some_template/
|- conceptual.html.primary.tmpl
\- conceptual.mta.json.tmpl
There are two Markdown files A.md
and B.md
, the content for A.md
is:
[Link To B](B.md)
The template system produces two output files for A.md
: A.html
and A.mta.json
, and also two output files for B.md
: B.html
and B.mta.json
. According to conceptual.html.primary.tmpl
, .html
is the primary output file, the link from A.md
to B.md
is resolved to B.html
instead of B.mta.json
, which is to say, the content of A.md
is transformed to:
<a href="B.html">Link To B</a>
Note
If no primary
Renderer is defined, DocFX randomly picks one Renderer as the primary one, and the result is unpredictable.
Renderer in Mustache syntax
Introduction to Mustache
Mustache is a logic-less template syntax containing only tags. It works by expanding tags in a template using values provided in a hash or object. Tags are indicated by the double mustaches. {{name}}
is a tag, it tries to find the name key in current context, and replace it with the value of name. mustache.5 lists the syntax of Mustache in detail.
Naming rule
Renderers in Mustache syntax MUST end with .tmpl
extension.
Mustache Partials
Mustache Partials is also supported in the template system. Partials are common sections of Renderer that can be shared by multiple Renderer files. Partials MUST end with .tmpl.partial
.
For example, inside a Template, there is a Partial file part.tmpl.partial
with content:
Inside Partial
{{ name }}
To reuse this Partial file, Renderer file uses the following syntax:
Inside Renderer
{{ >part }}
It has the same effect with the following Renderer file:
Inside Renderer
Inside Partial
{{ name }}
Extended syntax for Dependencies
When rendering the input data model into output files, for example, html files, the html file may rely on other files to display correctly. For example, the html file dependents on stylesheet file main.css
. We call such file main.css
a Dependency to the Renderer.
DocFX introduces the following syntax to define the dependency for the Renderer:
{{!include('<file_name>')}}
docfx
copies these dependencies to output folder preserving its relative path to the Renderer file.
Tip
Mustache is logic-less, and for a specific {{name}}
tag, Mustache searches its context and its parent context recursively.
So most of the time Preprocessor File is used to re-define the data model used by the Mustache Renderer.
Extended syntax for Master page
In most cases templates with different document types share the same layout and style. For example, most of the pages can share navbar, header, or footer.
DocFX introduces the following syntax to use a master page:
{{!master('<master_page_name>')}}
Inside the master page, the following syntax is used for pages to place their content body:
{{!body}}
For example, with the following master page _master.html
:
<html>
<head></head>
<body>
{{!body}}
<body>
</html>
A template conceptual.html.tmpl
as follows:
{{!master('_master.html')}}
Hello World
renders as the same as:
<html>
<head></head>
<body>
Hello World
<body>
</html>
Preprocessor
Renderers take the input data model produced by the document processor and render them into output files. Sometimes the input data model is not exactly what the Renderers want. The template system introduces the concept of Preprocessor to transform the input data model into exactly what the Renderers want. We call the data model returned by the Preprocessor the View Model. The View Model is the data model applied to the Renderers.
Naming rule for Preprocessor
The naming of the Preprocessor follows the naming of the Renderer, with file extension changes to .js
: <renderer_file_name_without_extension>.js
.
If a Preprocessor has no corresponding Renderer, it still needs to be executed. For example, to run exports.getOptions
function, it should be named as <document_type>.tmpl.js
.
Syntax for Preprocessor
Preprocessors are JavaScript files following ECMAScript 5.1 standard. The template system uses Jint as JavaScript Engine, and provides several additional functions for easy debugging and integration.
Module
Preprocessor leverages the concept of Module as similar to the Module in Node.js. The syntax of Module in Preprocessor is a subset of the one in Node.js. The advantage of the Module concept is that the Preprocessor script file can also be run in Node.js. The Module syntax in Preprocessor is simple,
To export function property from one Module file
common.js
:exports.util = function () {}
To use the exported function property inside
common.js
:var common = require('./common.js'); // call util common.util();
Note
Only relative path starting with ./
is supported.
Log
You can call the following functions to log messages with different error levels: console.log
, console.warn
or console.warning
and console.err
.
Function Signature
A Preprocessor file is also considered as a Module. It MUST export the function property with the signature required by DocFx's prescriptive interop pattern.
There are two functions defined.
Function 1: exports.getOptions
Function property getOptions
takes the data model produced by document processor as the input argument, and the return value must be an object with the following properties:
Property Name | Type | Description |
---|---|---|
isShared | bool | Defines whether the input data model can be accessed by other data models when transform . By default the value is false . If it is set to true , the data model will be stored into Globally Shared Properties. |
A sample exports.getOptions
defined in toc.tmpl.js
is:
exports.getOptions = function (model) {
return {
isShared: true;
};
}
Function 2: exports.transform
Function property transform
takes the data model produced by document processor (described in further detail in The Input Data Model) as the input argument, and returns the View Model. View Model is the exact model to apply the corresponding Renderer.
A sample exports.transform
for conceptual.txt.js
is:
exports.transform = function (model) {
model._title = "Hello World"
return model;
}
If conceptual.txt.tmpl
is:
{{{_title}}}
Then Markdown file A.md
is transformed to A.txt
with content:
Hello World
Tip
For each file, the input data model can be exported to a JSON file by calling docfx build --exportRawModel
.
And the returned View Model can be exported to a JSON file by calling docfx build --exportViewModel
.
The output files are stored in the DocFx destination subdirectory, which defaults to <project-name>\_site\
.
The Input Data Model
The input data model used by transform
not only contains properties extracted from the content of the file, but also system generated properties and globally shared properties.
System Generated Properties
System generated property names start with underscore _
, as listed in the following table:
Name | Description |
---|---|
_rel | The relative path of the root output folder from current output file. For example, if the output file is a/b/c.html from root output folder, then the value is ../../ . |
_path | The path of current output file starting from root output folder. |
_navPath | The relative path of the root TOC file from root output folder, if exists. The root TOC file stands for the TOC file in root output folder. For example, if the output file is html file, the value is toc.html . |
_navRel | The relative path from current output file to the root TOC file, if exists. For example, if the root TOC file is toc.html from root output folder, the value is empty. |
_navKey | The original file path of the root TOC file starting with ~/ . ~/ stands for the folder where docfx.json is in, for example, ~/toc.md . |
_tocPath | The relative path of the TOC file that current output file belongs to from root output folder, if current output file is in that TOC file. If current output file is not defined in any TOC file, the nearest TOC file is picked. |
_tocRel | The relative path from current output file to its TOC file. For example, if the TOC file is a/toc.html from root output folder, the value is ../ . |
_tocKey | The original file path of the TOC file starting with ~/ . ~/ stands for the folder where docfx.json is in, for example, ~/a/toc.yml . |
Note
Users can also override system generated properties by using YAML Header, fileMetadata
or globalMetadata
.
Globally Shared Properties
Globally shared properties are stored in __global
key for every data model. Its initial value is read from global.json
inside the Template if the file exists.
If a data model has isShared
equal to true
with the above getOptions
function property, it is stored in __global._shared
with the original path starting with ~/
as the key.