Compile-time Tags // MarkoImprove this page

Marko allows developers to control how a custom tag generates JavaScript code at compile-time. A custom compile-time tag developer can provide a "code generator" function for a custom tag.

Custom tag code generator

Let's take a look at how to create a compile-time tag by providing our own code generator. The first step is to register the custom tag in a marko-taglib.json file. We will use the code-generator property to associate the custom tag with a function that will be used to generate the code for the element node at compile-time:

{
    "<greeting>": {
        "code-generator": "./greeting-tag"
    }
}

A code generator module should export a function as shown below:

module.exports =  function generateCode(elNode, generator) {
    // ... 
}

The elNode argument will be an instance of HtmlElement. The generator argument will be an instance of CodeGenerator.

Continuing with the <greeting> example, let's assume we have the following template:

<greeting name="Frank"/>

Let's implement the greeting tag by generating code that will output Hello Frank using console.log:

module.exports = function generateCode(elNode, generator) {
    var builder = generator.builder;
 
    return builder.functionCall('console.log', [
        builder.literal('Hello'),
        elNode.getAttributeValue('name')
    ]);
};

The above code results in the following compiled code:

console.log("Hello", data.name);

In the example code above, the generateCode(elNode, generator) method returns a new FunctionCall node using the provided Builder instance. The builder provides methods for generating nodes associated with JavaScript primitives. This is the recommended way to produce JavaScript code since it ensures that code is generated correctly and with proper formatting. However, for illustration purposes, the following also works (just don't do it):

module.exports = function generateCode(elNode, generator) {
    var nameValue = elNode.getAttributeValue('name');
    generator.write('console.log(');
    generator.write('"Hello"');
    generator.write('');
    generator.generateCode(nameValue);
    generator.write(')');
};

Writing to standard out using console.log() is probably not what you want to do as a custom tag developer. You typically want to produce HTML output. Continuing with the same example, let's update our custom tag implementation to generate code that produces the following HTML output:

<div class="greeting">Hello Frank</div>

To do that we will utilize the builder API to generate the appropriate tree of nodes:

module.exports = function generateCode(elNode, generator) {
    var builder = generator.builder;
 
    return builder.htmlElement(
        'div',
        {
            'class': builder.literal('greeting')
        },
        [
            builder.text(builder.literal('Hello ')),
            builder.text(elNode.getAttributeValue('name'))
        ]);
};

For the following template:

<greeting name="Frank"/>

The code generated by the custom tag will be the following:

out.w("<div class=\"greeting\">Hello Frank</div>");

For the following template, where the name is a non-literal JavaScript expression:

<greeting name=data.name/>

The code generated by the custom tag will be the following:

out.w("<div class=\"greeting\">Hello " +
  escapeXml(data.name) +
  "</div>");

The Builder API

The Builder API plays a crucial role in generating code for a tag. The builder provides the building blocks for generating potentially complex JavaScript code.