• Stars
    star
    7
  • Rank 2,294,772 (Top 46 %)
  • Language
    Go
  • License
    MIT License
  • Created almost 6 years ago
  • Updated over 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

GoT is a template engine that turns templates into Go code to compile into your app.

Go Reference Build Status codecov Go Report Card Mentioned in Awesome Go

GoT

GoT (short for Go Templates) is a flexible template engine that generates Go code.

This approach creates extremely fast templates. It also gives you much more freedom than Go's template engine, since at any time you can just switch to Go code to do what you want.

GoT has:

  • Mustache-like syntax similar to Go's built-in template engine
  • The ability to define new tags, so you can create your own template syntax
  • Include files, so you can create a library of tags and templates

The GoT syntax is easy to learn. Get started in minutes.

Features

  • High performance. Since the resulting template is go code, your template will be compiled to fast machine code.
  • Easy to use. The templates themselves are embedded into your go code. The template language is pretty simple and you can do a lot with only a few tags. You can switch into and out of go code at will. Tags are Mustache-like, so similar to Go's template engine.
  • Flexible. The template language makes very few assumptions about the go environment it is in. Most other template engines require you to call the template with a specific function signature. GoT gives you the freedom to call your templates how you want.
  • Translation Support. You can specify that you want to send your strings to a translator before output.
  • Error Support. You can call into go code that returns errors, and have the template stop at that point and return an error to your wrapper function. The template will output its text up to that point, allowing you to easily see where in the template the error occurred.
  • Include Files. Templates can include other templates. You can specify a list of search directories for the include files, allowing you to put include files in a variety of locations, and have include files in one directory that override another directory.
  • Custom Tags. You can define named fragments that you can include at will, and you can define these fragments in include files. To use these fragments, you just use the name of the fragment as the tag. This essentially gives you the ability to create your own template language. When you use a custom tag, you can also include parameters that will replace placeholders in your fragment, giving you even more power to your custom tags.
  • Error Reporting. Errors in your template files are identified by line and character number. No need to guess where the error is.

Using other go libraries, you can have your templates compile when they are changed, use buffer pools to increase performance, and more. Since the templates become go code, you can do what you want.

Installation

go install github.com/goradd/got/got@latest

We also recommend installing goimports and passing GoT the -i flag on the command line. That will format your Go code and fix up the import lines of any generated go files.

go install golang.org/x/tools/cmd/goimports@latest

Command Line Usage

got [options] [files]

options:
	- o: The output directory. If not specified, files will be output at the same 
	     location as the corresponding template.
	- t  fileType: If set, will process all files in the current directory with this suffix. 
	     If not set, you must specify the files at the end of the command line.
	- i: Run `goimports` on the output files, rather than `go fmt`
	- I  directories and/or files:  A list of directories and/or files. 
	     If a directory, it is used as the search path for include files. 
	     If a file, it is automatically added to the front of every file that is processed.  
	     Directories are searched in the order specified and first matching file will be used. 
	     It will always look in the current directory last unless the current directory 
	     is specified in the list in another location. Relative paths must start with a dot (.) 
	     or double-dot (..).  Directories can start with a module name, and based on the 
	     current directory, the correct go.mod file will be searched to know where to look 
	     for include files.
	- d  directory: When using the -t option, will specify a directory to search.
	- v  verbose: Prints information about files being processed
	- r  recursive: Recursively processes directories. Used with the -t option and possibley -d.
	- f  force: Output files are normally not over-written if they are newer than the input file.
	     This otpion will force all input files to over-write the output files.

If a path described above starts with a module path, the actual disk location will be substituted.

examples:

	got -t got -i -o ../templates
	got -I .;../tmpl;example.com/projectTemplates file1.tmpl file2.tmpl

Basic Syntax

Template tags start with {{ and end with }}.

A template starts in go mode. To send simple text or html to output, surround the text with {{ and }} tags with a space or newline separating the tags from the surrounding text. Inside the brackets you will be in text mode.

In the resulting Go code, text will get written to output by calling:

_, err = io.WriteString(_w, <text>)

GoT assumes that the _w variable is available and satisfies the io.Writer interface and optionally the io.StringWriter interface. Usually you would do this by declaring a function at the top of your template that looks like this:

func f(_w io.Writer) (err error)

After compiling the template output together with your program, you call this function to get the template output.

At a minimum, you will need to import the "io" package into the file with your template function. Depending on what tags you use, you might need to add additional items to your import list. Those are mentioned below with each tag.

Example

Here is how you might create a very basic template. For purposes of this example, we will call the file example.got and put it in the template package, but you can name the file and package whatever you want.

package template

import "io"

func OutTemplate(_w io.Writer) (err error) {
	var world string = "World"
{{
<p>
    Hello {{world}}!
</p>
}}
  return // make sure the error gets returned
}

To compile this template, call GoT:

got example.got

This will create an example.go file, which you include in your program. You then can call the function you declared:

package main

import (
	"io"
	"os"
	"mypath/template"
)

func main() {
	_ = template.OutTemplate(os.Stdout)
}

This simple example shows a mix of go code and template syntax in the same file. Using GoT's include files, you can separate your go code from template code.

Template Syntax

The following describes how the various open tags work. Most tags end with a }}, unless otherwise indicated. Many tags have a short and a long form. Using the long form does not impact performance, its just there to help your templates have some human readable context to them.

Static Text

{{<space or newline>   Begin to output text as written.
{{! or {{esc           Html escape the text. Html reserved characters, like < or > are 
                       turned into html entities first. This happens when the template is 
                       compiled, so that when the template runs, the string will already 
                       be escaped. 
{{h or {{html          Html escape and html format double-newlines into <p> tags.
{{t or {{translate     Send the text to a translator

The {{! tag requires you to import the standard html package. {{h requires both the html and strings packages.

{{t will wrap the static text with a call to t.Translate(). Its up to you to define this object and make it available to the template. The translation will happen during runtime of your program. We hope that a future implementation of GoT could have an option to send these strings to an i18n file to make it easy to send these to a translation service.

Example

In this example file, note that we start in Go mode, copying the text verbatim to the template file.

package test

import (
	"io"
	"fmt"
)

type Translater interface {
	Translate(string) string
}

func staticTest(_w io.Writer) {
{{
<p>
{{! Escaped html < }}
</p>
}}

{{h
	This is text that is both escaped.
	 
	And has html paragraphs inserted.
}}

}

func translateTest(t Translater, buf *bytes.Buffer) {

{{t Translate me to some language }}

{{!t Translate & escape me > }}

}

Switching Between Go Mode and Template Mode

From within any static text context described above you can switch into go context by using:

{{g or {{go     Change to straight go code.

Go code is copied verbatim to the final template. Use it to set up loops, call special processing functions, etc. End go mode using the }} closing tag. You can also include any other GoT tag inside of Go mode, meaning you can nest Go mode and all the other template tags.

Example

// We start a template in Go mode. The next tag switches to text mode, and then nests
// switching back to go mode.
{{ Here 
{{go 
io.WriteString(_w, "is") 
}} some code wrapping text escaping to go. }}

Dynamic Text

The following tags are designed to surround go code that returns a go value. The value will be converted to a string and sent to the buf. The go code could be a static value, or a function that returns a value.

Tag                       Description                     Example

{{=, {{s, or  {{string    Send a go string to output      {{= fmt.Sprintf("I am %s", sVar) }}
{{i or {{int              Send an int to output           {{ The value is: {{i iVar }} }}
{{u or {{uint             Send an unsigned Integer        {{ The value is: {{u uVar }} }}
{{f or {{float            Send a floating point number    {{ The value is: {{f fVar }} }}
{{b or {{bool             A boolean ("true" or "false")   {{ The value is: {{b bVar }} }}
{{w or {{bytes            A byte slice                    {{ The value is: {{w byteSliceVar }} }}
{{v or {{stringer or      Send any value that implements  {{ The value is: {{objVar}} }}
   {{goIdentifier}}       the Stringer interface.

This last tag can be slower than the other tags since it uses fmt.Sprint() internally, so if this is a heavily used template, avoid it. Usually you will not notice a speed difference though, and the third option can be very convenient. This third option is simply any go variable surrounded by mustaches with no spaces.

The i, u, and f tags use the strconv package, so be sure to include that in your template.

Escaping Dynamic Text

Some value types potentially could produce html reserved characters. The following tags will html escape the output.

{{!=, {{!s or {{!string    HTML escape a go string
{{!w or {{!bytes           HTML escape a byte slice
{{!v or {{!stringer        HTML escape a Stringer
{{!h                       Escape a go string and html format breaks and newlines

These tags require you to import the "html" package. The {{!h tag also requires the "strings" package.

Capturing Errors

These tags will receive two results, the first a value to send to output, and the second an error type. If the error is not nil, processing will stop and the error will be returned by the template function. Therefore, these tags expect to be included in a function that returns an error. Any template text processed so far will still be sent to the output buffer.

{{=e, {{se, {{string,err      Output a go string, capturing an error
{{!=e, {{!se, {{!string,err   HTML escape a go string and capture an error
{{ie or {{int,err             Output a go int and capture an error
{{ue or {{uint,err            Output a go uint and capture an error
{{fe or {{float,err           Output a go float64 and capture an error
{{be or {{bool,err            Output a bool ("true" or "false") and capture an error
{{we, {{bytes,err             Output a byte slice and capture an error
{{!we or {{!bytes,err         HTML escape a byte slice and capture an error
{{ve, {{stringer,err          Output a Stringer and capture an error
{{!ve or {{!stringer,err      HTML escape a Stringer and capture an error
{{e, or {{err                 Execute go code that returns an error, and stop if the error is not nil
Example
func Tester(s string) (out string, err error) {
	if s == "bad" {
		err = errors.New("This is bad.")
	}
	return s
}

func OutTemplate(toPrint string, buf bytes.Buffer) error {
{{=e Tester(toPrint) }}
}

Include Files

Include a GoT source file

{{: "fileName" }} or 
{{include "fileName" }}   Inserts the given file name into the template.

The included file will start in whatever mode the receiving template is in, as if the text was inserted at that spot, so if the include tags are put inside of go code, the included file will start in go mode. The file will then be processed like any other GoT file. Include files can refer to other include files, and so are recursive.

Include files are searched for in the current directory, and in the list of include directories provided on the command line by the -I option.

Example: {{: "myTemplate.inc" }}

Include a text file

{{:! "fileName" }} or             Inserts the given file name into the template
{{includeEscaped "fileName" }}    and html escapes it.
{{:h "fileName" }} or             Inserts the given file name into the template,
{{includeAsHtml "fileName" }}     html escapes it, and converts newlines into html breaks.

Use {{:! to include a file that you surround with a <pre> tag to include a text file and have it appear in an html document looking the same. Use {{:h to include a file without the <pre> tags, but if the file uses extra spaces for indent, those spaces will not indent in the html. These kinds of include files will not be searched for GoT commands.

Defined Fragments

Defined fragments start a block of text that can be included later in a template. The included text will be sent as is, and then processed in whatever mode the template processor is in, as if that text was simply inserted into the template at that spot. You can include the {{ or {{g tags inside of the fragment to force the processor into the text or go modes if needed. The fragment can be defined any time before it is included, including being defined in other include files.

You can add optional parameters to a fragment that will be substituted for placeholders when the fragment is used. You can have up to 9 placeholders ($1 - $9). Parameters should be separated by commas, and can be surrounded by quotes if needed to have a parameter that has a quote or comma in it.

{{< fragName }} or {{define fragName }}   Start a block called "fragName".
{{< fragName <count>}} or                 Start a block called "fragName" that will 
   {{define fragName <count>}}            have <count> parameters.
{{> fragName param1,param2,...}} or       Substitute this tag for the given defined fragment.
  {{put fragName param1,param2,...}} or   
  {{fragName param1,param2,...}}
{{>? fragName param1,param2,...}} or      Substitute this tag for the given defined fragment, 
  {{put? fragName param1,param2,...}}     but if the fragment is not defined, leave blank.

If you attempt to use a fragment that was not previously defined, GoT will panic and stop compiling, unless you use {{>? or {{put? to include the fragment.

param1, param2, ... are optional parameters that will be substituted for $1, $2, ... in the defined fragment. If a parameter is not included when using a fragment, an empty value will be substituted for the parameter in the fragment.

The fragment name is NOT surrounded by quotes, and cannot contain any whitespace in the name. Blocks are ended with a {{end fragName}} tag. The end tag must be just like that, with no spaces after the fragName.

The following fragments are predefined:

  • {{templatePath}} is the full path of the template file being processed
  • {{templateName}} is the base name of the template file being processed, including any extensions
  • {{templateRoot}} is the base name of the template file being processed without any extensions
  • {{templateParent}} is the directory name of the template file being processed, without the preceeding path
  • {{outPath}} is the full path of the output file being written
  • {{outName}} is the base name of the output file being written, including any extensions
  • {{outRoot}} is the base name of the output file being written without any extensions
  • {{outParent}} is the directory name of the output file being written, without the preceeding path

Note that if you are using these in an included file, these will refer to the parent file. Multiple levels of includes will return the information for the top level file being processed.

Example


{{< hFrag }}
<p>
This is my html body.
</p>
{{end hFrag}}

{{< writeMe 2}}
{{// The g tag here forces us to process the text as go code, 
     no matter where the fragment is included }}
{{g 
if "$2" != "" {
	io.WriteString(_w, "$1")
}
}}
{{end writeMe}}


func OutTemplate(_w io.Writer) (err error) {
{{
	<html>
		<body>
			{{> hFrag }}
		</body>
	</html>
}}

{{writeMe "Help Me!", a}}
{{writeMe "Help Me!", }}
 return
}

Comment Tags

{{# or {{//       Comment the template. This is removed from the compiled template.

These tags and anything enclosed in them is removed from the compiled template.

Go Block Tags

{{if <go condition>}}<text block>{{if}}                      
                       A convenience tag for surrounding text with a go "if" statement.
{{if <go condition>}}<text block>{{else}}<text block>{{if}}  
                       Go "if" and "else" statement.
{{if <go condition>}}<text block>{{elseif <go condition>}}<text block>{{if}}    
                       Go "if" and "else if" statement.
{{for <go condition and optional range statement>}}<text block>{{for}}                                   
                       A convenience tag for surrounding text with a go "for" statement.

These tags are substitutes for switching into GO mode and using a for or if statements. <text block> will be in text mode to begin with, so that whatever you put there will be output, but you can switch to go mode if needed.

####Example

{{
{{for num,item := range items }}
<p>Item {{num}} is {{item}}</p>
{{for}}
}}

Join Tags

{{join <slice>, <string>}}<text block>{{join}}    Joins the items of a slice with a string.

Join will output the <text block> for each item of <slice>. Within <text block> the variable _i will be an integer representing the index of the slice item, and _j will be the item itself. <text block> starts in text mode, but you can put GoT commands in it. <string> will be output between the output of each item, creating an effect similar to joining a slice of strings.

####Example

{{g
  items := []string{"a", "b", "c"}
}}
{{join items,", "}}
{{ {{_i}} = {{_j}} }}
{{join}}

Strict Text Block Tag

From within most of the GoT tags, you can insert another GoT tag. GoT will be looking for these as it processes text. If you would like to turn off GoT's processing to output text that is not processed by GoT, you can use:

{{begin *endTag*}}   Starts a strict text block and turns off the GoT parser. 

One thing this is useful for is to use GoT to generate GoT code. End the block with a {{end *endTag*}} tag, where *endTag* is whatever you specified in the begin tag. There can be no space between the endTag and the final brackets The following example will output the entire second line of code with no changes, including all brackets:

{{begin mystrict}}
{{! This is verbatim code }}
{{< all included}}
{{end mystrict}}

Bigger Example

In this example, we will combine multiple files. One, a traditional html template with a place to fill in some body text. Another, a go function declaration that we will use when we want to draw the template. The function will use a traditional web server io.Writer pattern, including the use of a context parameter. Finally, there is an example main.go illustrating how our template function would be called.

index.html

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
    </head>

    <body>
{{# The tag below declares that we want to substitute a named 
    fragment that is declared elsewhere }}
{{put body }}
    </body>
</html>

template.got

package main

import {
	"context"
	"bytes"
}


func writeTemplate(ctx context.Context, buf *bytes.Buffer) {

{{# Define the body that will be inserted into the template }}
{{< body }}
<p>
The caller is: {{=s ctx.Value("caller") }}
</p>
{{end body}}

{{# include the html template. Since the template is html, 
    we need to enter static text mode first }}
{{ 
{{include "index.html"}}
}}

}

main.go

type myHandler struct {}

func (h myHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)  {
	ctx :=  context.WithValue(r.Context(), "caller", r.Referer())
	writeTemplate(ctx, w)	// call the GoT template
}


func main() {
	var r myHandler
	var err error

	*local = "0.0.0.0:8000"

	err = http.ListenAndServe(*local, r)

	if err != nil {
		fmt.Println(err)
	}
}

To compile the template:

got template.got

Build your application and go to http://localhost:8000 in your browser, to see your results

License

GoT is licensed under the MIT License.

Acknowldgements

GoT was influenced by:

Syntax Changes

###v0.10.0 This was a major rewrite with the following changes:

  • defined fragments end with {{end fragName}} tags, rather than {{end}} tags
  • {{else if ...}} is now {{elseif ...}}
  • {{join }} tag will join items with a string
  • The backup tag {{- has been removed
  • Reorganized the lexer and parser to be easier to debug
  • Added many more unit tests. Code coverage > 90%.
  • The output is sent to an io.Writer called _w. This allows more flexible use of the templates, and the ability to wrap them with middleware