celer / fire-ts

Fire TS is a templating library for NodeJS which specializes in generating non HTML templates (c, c++, Java, JavaScript, Ruby, Perl, etc)
MIT License
7 stars 1 forks source link

Fire TS

Build Status: Build Status

Introduction

Fire TS is a template engine for generating code.

If you want to generate HTML templates you should consider other template engines

The basics

The syntax for Fire TS looks a lot like JSP, except the base language is JavaScript

<%
    var colors=[ "red","blue","green"];
    var numbers=[1,2,3,5];
%>

#include<stdio.h>

/*%{header}

    The text in this 'header' block can be overridden by what is found in the file that wil be overwritten when this file is generated. Allowing the user to edit this bit of comment or code safely without having to worry about loosing his/her changes.

}%*/

//%{prefix}  

//This code will be preserved, even when the template is re-generated!!!
const char *prefix=">";

//}%

char *colors[]={
    <% for(var i in colors){ 
            var color = colors[i];
    %>
        <%# color %>,
    <% } %>
    NULL 
};

int *numbers[]={ 
    <% numbers.map(function(number){ %>
        <%= number %>,
    <% }); %>
    NULL 
};

void main(){
    int i,j;
    for(i=0;colors[i];i++){
        for(j=0;numbers[j];j++){
            printf("%s %s %d\n",prefix,colors[i],numbers[j]);
        }
    }
}

And will generate this:

#include<stdio.h>

/*%{header}
    This text was pulled from the file that was gonna be over written.  
}%*/

//%{prefix}
const char *prefix="<";
//}%

char *colors[]={
    "red",
    "blue",
    "green",
    NULL 
};

int *numbers[]={ 
    1,
    2,
    3,
    5,
    NULL 
};

void main(){
    int i,j;
    for(i=0;colors[i];i++){
        for(j=0;numbers[j];j++){
            printf("%s %s %d\n",prefix,colors[i],numbers[j]);
        }
    }
}

Writing templates!

Well formated templates!

One of the neat things that Fire TS does is it tries to allow you to both have well formated templates and well formatted output. This means that Fire-TS will count the number of opening and closing braces and then remove the appropriates spacing. By default Fire-TS will look for tabs, although you can set the 'indent' property in the options to change it to spaces, etc.

Here are some examples:

<% for(var i in items){ %> //tabDepth++
    // This will automatically remove 1 tab from the generated output since we're one set of of braces deep
    int <%= items[i].name %>;
<% } %> //tabDepth--;
// Would generate
int foo;
<% for(var i in items){ %>  
        // This will automatically remove 1 tab from the generated output since we're one set of of braces deep,
        int <%= items[i].name %>;
<% } %>
//Would generate
    int foo;
    //Tell Fire-TS to remove two spaces for each level of indention found
    Fire.parseSync("input.fts",{ indent:"  " });
``

## Evaluating code

Anything in: 

```jsp
<% CODE %> 

will be evaluated as JavaScript, and it can even be multi-line

Let's do some looping
<%
    for(var i=0;i<3;i++){
        if(i==2){
%>
            i is 2
<%  
        }
    }
%>

Which would even track the number of opening and closing braces to properly add and remove tab characters from the output - So your templates and your generated code will look good!

Let's do some looping
i is 2

If you want to actually indent "i is 2" simply add more tabs to it.

Expressions

There are multiple types of expressions

Raw

This will just output the value of the variable or expression with no formatting

    <!-- Output the raw variable or expression -->
    <%=variable%>

JSON Encoded

This will output the JSON encoded variable or expression

    <!-- Output a JSON encoded value - which works well for 'c' escaped strings  -->
    <%#variable%>

URL Encoded

This will output a URL escaped string

    <!-- Output a URL encoded value  -->
    <%%variable%>

Capturing and modifying the template itself


//Will replace Their with There
<%(%>Hello Their <%=name%><%).replace(/Their/,"There")%> 

//Will convert this to lower case 
<%(%>MiXeDcAsE<%).toLowerCase()%>

Using '<%(%>' and '<%)%>' allows you to capture and modify result of that part of the template as a String and modify it. Here are a few examples:

Including sub-templates

If you want to nest templates you an do this:

    <%@ header.fts %>

All the inputs and options pasted to the top template will be passed to the nested templates. If you need to capture variables from one template to the nested templates:

    <%@ header.fts (variableA,variableB) %>

will capture local variableA and variableB and pass them into the nested template. Fire-TS will call opts.render to resolve the template, so you can back it with something that returns a file or a named snippet

Blocks

One of the more advanced features of Fire-TS is that it can preserve the contents of the file it is going to over-write. Let's say for example you want to have a SQL file:

<%
    var colors=[ "red","blue","green"];
    var numbers=[1,2,3,5];
%>

-- %{schema} 

create table colors (
    id bigint auto_increment,
    color varchar(20) not null,
    number bigint not null,
);

-- }%

<% colors.map(function(color){ %>
    <% numbers.map(function(number){ %>
        insert into colors (name) values(<%#color%>,<%=number%>);
    <% }); %>
<% }); %>

Fire-TS will see if the file it is about to over-write exists, and look for blocks ( starting with '%{[A-Za-z0-9]+}' and ending with '}%' ) and read them from the old file, and then insert them into the newly written output. Allowing you to preserve certain parts of older files. In the example above it would let you safely modify the schema and have the inserts regenerated each time!

Embedding

Take a look at bin/fire-ts to get an idea how to use the templating engine, you can install it globally using

    npm install fire-ts -g

You can see embedding examples here: https://github.com/celer/fire-ts/tree/master/examples/embed

Command line

./fire-ts
Options:
  --output    File to write (will reload blocks)            
  --template  The template to use                           
  --compile   The template to compile                       
  --input     The input to use to run the template (as JSON)
  --blocks    The blocks to use (as a JSON hash)            
  --compare   Compare the output file to the specified file 
  --uglify    Uglify the compiled template                  
  --debug     Debug   

Let's run a template

./bin/fire-ts --template test/1.fts

Let's compile a template

./bin/fire-ts --compile test/1.fts

Results in

function template(_$_i,_$_o,_$_oc){_$_o=_$_o||{},_$_o.b=_$_o.blocks||{};var _$_s="",_$_e=_$_o.e||function(_){_$_s+=_};with(_$_o.j||JSON.stringify,_$_o.render,_$_i)_$_e("Hello\n  1\n  2 \n  3\n  4\n  5\n6\n   6\n 7 8\n   A\n     B\n");return _$_s}

License

MIT

API

/**
    Fire Template System

    @module FireTS
    @class Fire
*/

/**

    Read blocks from a given string

    Blocks are primarly used as a way to keep modifications from various files. So typically FireTS will read the blocks from the file that is
    about to be overwritten and make sure they aren't modified when the template is rewritten.

    @param {String} input template or generated file containing one or more named blocks
    @returns {Object} hash of blocks

    Example of a block

    @example
        //%{header}
            This is a block
        //}%

    Reading the above example would return:

    @example
        { header:"\n  This is a block\n//"}

    @method Fire.readBlocks

*/

/**

    Read blocks from a file asynchronously

    @param {String} file The file to read blocks from
    @param {Function} onComplete
        @param {String} onComplete.err The error string
        @param {Object} onComplete.blocks The hash of blocks from file

    @method Fire.readFileBlocks
    @see Fire.readBlocks

*/

/**

    Read blocks from a file synchronously

    @param {String} file The file to read blocks from
    @return {Object} The hash of blocks from file

    @method Fire.readFileBlocksSync
    @see Fire.readBlocks

*/

/**

    Compile a string into a template

    @param {String} input The template to compile
    @param {Object} opts
        @param {Boolean} opts.source Return the source for the template, not the compiled function
        @param {Boolean} opts.uglify Uglify the source (true by default)
        @param {Boolean} opts.async Generate a template which can load files/snippets asynchronously
    @returns {Function or String} String or compiled template 

    @method Fire.compile

*/

/**

    Synchronous template function

    @param {Object} input Input for the template
    @param {Object} opts Options for the template
        @param {Object} opts.blocks The blocks to insert into the file
        @param {Function} opts.render The function to call to render the template (When run synchronously)
            @param {String} opts.render.template The name of the template or snippet to render
            @param {String} opts.render.input The inputs to use for the template
            @param {String} opts.render.opts The options for the template

    @returns {String} The result of running the template

    @example
        var result = template({ name:"George" },{ 
            render: function(template,input,opts){
                return Fire.compile("<%=xi%>")(input)   
            }   
        });

    @method template (synchronous)

*/

/**

    Asynchronous template function

    @param {Object} input Input for the template
    @param {Object} opts Options for the template
        @param {Object} opts.blocks The blocks to insert into the file
        @param {Function} opts.fetch The function to call to fetch a snippet or template asynchrounsly
            @param {String} opts.fetch.template The name of the template or snippet to render
            @param {Function} opts.fetch.onComplete The lambda to call when the snippet/template has been loaded
            @param {String} opts.fetch.onComplete.err The returned as a result of loading the template
            @param {Function} opts.fetch.onComplete.template The template function
    @param {Function} onComplete The callback for when the template has been rendered
            @param {String} opts.onComplete.err The returned as a result of loading the template
            @param {String} opts.onComplete.result The result of running the template

    @example
        template({name:"hello"},{ 
            fetch: function(template,onComplete){
                Fire.parse(template,{async:true},onComplete);
            }
        },function(err,template){
            console.log("Result",template);
        });

    @method template (asynchronous)

*/

/**

    Simulate the combustion template interface

    The returned functions has the following parameters

    @param {Object} input The input template
    @param {Object} opts The options for the template 

    @returns {Function} Function to use for compiling templates

    @example
        var template = Fire.combustion().compile("<%=x%>");
        template({x:5});

    @method Fire.combustion 

*/

/**

    Generate a file from a template

    This function will:

    * Read the blocks from outFile if it exists
    * Generate a new output file, reusing the blocks from the prior outFile

    @param {String} template the template file
    @param {String} outFile the output file
    @param {Object} input the inputs to the template
    @param {Object} options options for the template (see template)

    @returns {String} The result of running the template

    @method Fire.generateSync

*/

/**

    Parse a template file synchronously and return a compiled template

    @param {String} file the template file
    @param {Object} opts the options for the compiler

    @return {Function} The compiled template

    @method Fire.parseSync

*/

/**

    Parse a template file and return a compiled template

    @param {String} file the template file
    @param {Object} opts the options for the compiler
    @param {Function} onComplete The lambda to be called upon completion
        @param {String} onComplete.err The error    
        @param {Function} onComplete.template  compiled template

    @method Fire.parse

*/