D3 Plugin to Create & Render Templates Use D3's Data Binding Mechanism

d3-template

(This version is meant for V4/V5 and has not been tested on V3 or earlier versions of D3)

This is version 2 of d3-template. If you have used version 1 before, please check out the explanation.

d3-template is a D3 plugin to support templates using D3's data binding mechanism. This means you can use D3's familiar functionality directly on or with your templates. Apply transitions or add event handlers to template elements with access to the bound data. Render new data on a template thereby updating attributes, styles, properties and text. Also new elements are added and superfluous elements are removed from repeating groups (D3's enter/exit update pattern). This works for both HTML as well as SVG elements both in the browser as well as within Node. Templates will normally be acting on the live DOM, but can be used on virtual DOM's (like jsdom) as well.

If you are looking for existing templating support like Handlebars, Mustache or Nunjucks have a look at d3-templating.

Usage

Templates look like they do in most other templating tools: rendering data by placing expressions within curly braces into HTML or SVG elements. These expressions are data functions (as described in the introduction of the D3 homepage) but lack an explicit return statement. The standard arguments d, i, nodes are present and this is set to the (Template) node being rendered.

The general usage is probably best described by an example (see example on bl.ocks.org):

<div id="person" data-class-alive="{{!d.deathdate}}">
    <div>
        <span>{{`Name: ${d.name}`}}</span>
    </div>
    <div>
        <span>{{`Birthdate: ${d3.timeFormat("%-d %B %Y")(d.date)}`}}</span>
    </div>
    <div>
        <span>Honours given name to:</span>
        <ul data-repeat="{{d.honours.given}}">
            <li>{{d}}</li>
        </ul>
    </div>
</div>
<script>

    // Add event handler to list element before template creation
    d3.select("#person ul li")
        .on("click", function(d, i, nodes) {
            // d will be entry from the honours.given array
            // i will be current index
            // nodes will be the li nodes
            // this will refer to the current node (ie nodes[i])
            console.log(d, i, nodes.length);
        })
    ;

    // Turn selection into a template
    // This will remove some elements from the DOM as well as add some attributes to elements
    // for identification.
    d3.select("#person")
        .template()
    ;

    // ...

    // Render data onto earlier created template
    // Information retrieved from https://en.wikipedia.org/wiki/Alan_Turing#Awards,_honours,_recognition,_and_tributes
    d3.select("#person")
        .render({
            name: "Alan Turing",
            birthdate: new Date(1912, 5, 23),
            deathdate: new Date(1954, 5, 7),
            honours: {
                received: [
                    "Order of the British Empire",
                    "Fellow of the Royal Society"
                ],
                given: [
                    "Good–Turing frequency estimation",
                    "Turing completeness",
                    "Turing degree",
                    "Turing fixed-point combinator",
                    "Turing Institute",
                    "Turing Lecture",
                    "Turing machine examples",
                    "Turing patterns",
                    "Turing reduction",
                    "Turing switch"
                ]
            }
        })
    ;

    // The click event handler is now present on all li elements
</script>

When rendering data onto a template which is already rendered, the new data will be bound and elements will be updated accordingly. A transition can be created to animate the new data rendering.

Installing

To install via npm use npm install d3-template-plugin. Or use/install directly using unpkg.

<script src="https://unpkg.com/d3-template-plugin/build/d3-template.min.js"></script>

Features

The following features are present:

Data rendering onto attributes and text directly.

Data can also be rendered on attributes, styles, properties or class fields indirectly (through data-attr-<name>, data-style-<name>, data-prop-<name> and data-class-<name>). Properties can be useful for setting the value or checked property of HTML input elements for example. The indirect rendering is especially useful for SVG because most browsers do not like 'invalid' attribute values:

<!-- Most browsers do not like this (invalid according to SVG spec) -->
<circle cx="{{scalex(d.x)}}" cy="{{scaley(d.y)}}" r="{{d.radius}}"></circle>

<!-- Browsers are okay with this (valid according to SVG spec) -->
<circle data-attr-cx="{{scalex(d.x)}}" data-attr-cy="{{scaley(d.y)}}" data-attr-r="{{d.radius}}"></circle>

Repeating and conditional groups (through data-repeat and data-if attribute).

A scope group similar to the Javascript with statement (through data-with attribute).

Other templates can be imported. Which template gets imported can be decided on render time based on the bound data.

Possibility to overwrite the template attribute names with custom names. If for example repeat, if and with is preferred over de long names, this can be specified when creating a template. Although such custom attributes are not compliant with HTML5 or SVG specifications, most browsers will accept it without complaining.

Render only part of a template by rendering on a group element (repeat, if, with or import).

Event handlers added onto elements before the template was created (using selection.on()), will be (re)applied when rendering the template.

Rendering can be done within a transition allowing animations.

Tweens (for attribute, style, property or text) can also be used in combination with a transition by providing a tween data function.

Limitations

The following known limitations are present:

Data references (the curly braces) can only be applied to the full element or the full attribute value. (For completeness: Surrounding whitespace is allowed for elements or attributes, but will be removed.)

<!-- This will not be recognised by d3-template -->
<div data-style-width="{{d.width}}px">...</div>

<!-- Use one of the following instead -->
<div data-style-width="{{d.width + 'px'}}">...</div>
<div data-style-width="{{`${d.width}px`}}">...</div>

<!-- This will not be recognised by d3-template -->
<span>{{jobCount}} jobs</span>

<!-- Use one of the following instead -->
<span>{{d.jobCount + " jobs"}}</span>
<span>{{`${d.jobCount} jobs`}}</span>
<span><span>{{d.jobCount}}</span> jobs</span>
<span>{{d.jobCount}}</span> <span>jobs</span>

There should be none or one child element for a grouping element (repeat, if or with). If more child elements are needed, these have to be wrapped in a container element. If only text is present, this has to be wrapped in a container element as well. An exception will be thrown during parsing of the template if such invalid structures are present. (Having no child element on a group is actually useless, but allowed for convenience during development.)

<!-- This is not allowed by d3-template -->
<ul data-repeat="{{d}}">
    <li>{{d}}</li>
    <li class="separator"></li>
</ul>

<!-- Use the following (or something similar) instead -->
<ul data-repeat="{{d}}">
    <li>
        <div>{{d}}</div>
        <div class="separator"></div>
    </li>
</ul>

<!-- This is not allowed by d3-template -->
<div data-repeat="{{d.stats}}">
    Requests: <span>{{d.requests}}</span> (per month)
</div>

<!-- Use the following (or something similar) instead -->
<div data-repeat="{{d.stats}}">
    <div>{{`Requests: ${d.requests} (per month)`}}</div>
</span>

Grouping elements (repeat, if or with) can not be combined on the same element. One has to be wrapped inside the other.

An import element can only be combined with the with grouping to scope (or map) the data for the imported template. Combining import with repeat or if is not allowed.

There is no else for if groupings. A second if needs to be added (with a negated expression) or an import could be used (see if groups explanation).

Setting properties of (for example) HTML input elements can not be done using transitions, since D3 transitions do not support it. It is however possible to create a tween data function.

API Reference

# d3.template(selection[, options]) <>

Creates a template from the specified selection. The selection might be changed as a result of this. Attributes or text consisting of template references will be removed. Child elements of an element containing a valid grouping attribute (data-repeat, data-if or data-with) will be removed. Different elements will have an attribute (data-d3t7s or data-d3t7b) applied for identification purposes.

If options is specified it should be an object containing properties describing the template attribute names being used. Only the properties that need custom values should be present. The following are the default options values:

{
    repeatAttribute: "data-repeat",
    ifAttribute: "data-if",
    withAttribute: "data-with",
    importAttribute: "data-import",
    indirectAttributePrefix: "data-attr-",
    indirectStylePrefix: "data-style-",
    indirectPropertyPrefix: "data-prop-",
    indirectClassPrefix: "data-class-"
}

# d3.render(selection, data) <>

Renders data onto the specified selection. If no template has been created from selection an exception is thrown. If selection is part of a (larger) template and selection is a grouping element like repeat, if, with or import then rendering will be performed. Do remember that rendering on the template (root) will overwrite all template elements. So updating parts of a template should be done with caution.

# selection.template([options]) <>

Creates a template from this selection. The following are all equivalent:

d3.template(selection, options);
selection.template(options);
selection.call(d3.template, options)

# selection.render(data) <>

Renders data onto this selection. The following are all equivalent:

d3.render(selection, data);
selection.render(data);
selection.call(d3.render, data);

# transition.render(data) <>

Renders data onto this transition. The following are all equivalent:

d3.render(transition, data);
transition.render(data);
transition.call(d3.render, data);

Details

Repeating groups

Repeating groups can only be used with arrays as data. The array will be bound to the element when rendered. The group's child element will be appended conform the regular D3 enter/exit pattern. A copy of the child element will be rendered for every array element provided. It will have the corresponding array element bound as data.

<ul id="my-list" data-repeat="{{d}}">
   <li>{{d}}</li>
</ul>
<script>
    var list = d3.select("#my-list").template();

    // Render a list containing the numbers 1 to 4
    list.render([ 1, 2, 3, 4 ]);

    // Render a list of words (replacing the numbers)
    list.render([ "Hello", "I", "Just", "Called", "To", "Say", "I", "Love", "You" ]);

    // Render an empty list (all existing li elements will be removed)
    list.render([]);
</script>

To use the index or length of the repeat group, use the D3 typical i or nodes parameters:

<!-- Insert index and position of element within repeat group -->
<ul data-repeat="{{d}}">
    <li data-index="{{i}}"><span>{{`${i + 1} of ${nodes.length}`}}</span> - <span>{{d}}</span></li>
</ul>

If groups

If groups are conditional elements within a template. It allows a child element to be rendered (or not) based on a condition. If the condition expression evaluaties to a thruthy value, the child is rendered. Otherwise no child will be rendered (an existing child will be removed).

<div data-if="{{d.employees.length > 0}}">
    <div data-repeat="{{d.employees}}">
        <div>...</div>
    </div>
</div>
<div data-if="{{d.employees.length === 0}}">
    <div>There are no employees in this company.</div>
</div>

As can be seen in the example above, there is no else clause for if groups. Another approach would be to have a dynamic import which chooses the correct template.

<div id="templates" style="display: none;">
    <div id="employees">
        <div data-repeat="{{d.employees}}">
            <div>...</div>
        </div>
    </div>
    <div id="no-employees">
        <div>There are no employees in this company.</div>
    </div>
</div>
<div id="my-template" data-import="{{d.employees.length > 0 ? '#employees' : '#no-employees'}}">
        <!-- No child elements allowed here! -->
</div>

Import templates

It is possible to import another template. The data bound to the element during rendering can be used to dynamically decide which template gets imported. The data function for the data-import attribute should answer either a (CSS) selector or a D3 selection. The element can not have children in the template (the imported template will render the child/children).

<div id="templates" style="display: none;">
    <div id="incoming-message">
        <div>
            <div>{{`From: ${d.sender}`}}</div>
                <div>{{d.content}}</div>
            </div>
        </div>
        <div id="outgoing-message">
            <div>{{d.content}}</div>
        </div>
</div>
<div id="messenger">
    <div class="list" data-repeat="{{d}}">
        <div data-import="{{d.sender === 'Me' ? '#outgoing-message' : '#incoming-message'}}">
            <!-- No child elements allowed here! -->
        </div>
    </div>
</div>
<script>
    var messages = [
        { sender: "Someone", content: "Hello world" },
        { sender: "Me", content: "Hello to you too" },
        { sender: "Someone", content: "What are you doing?" },
        { sender: "Me", content: "Writing a D3 template" },
        { sender: "Someone", content: "That sounds interesting" },
        { sender: "Someone", content: "I am already familiar with D3" },
        { sender: "Someone", content: "Where can I learn about that?" },
        { sender: "Me", content: "Check out the other examples" }
    ];

    // Create templates from the two message types incoming and outgoing
    d3.select("#incoming-message").template();
    d3.select("#outgoing-message").template();
    
    // Create template from the messages group and render data
    d3.select("#messenger")
        .template()
        .render(messages)
    ;
</script>

In the example above templates have an id attribute. According to the HTML/SVG specifications these should be unqiue. These id attributes will therefore not be present when the template is rendered on an import. Only the original template will have it.

An import can be combined with with grouping to scope (or map) the data onto the imported template.

<div id="#templates" style="display: none;">
    <div id="person">
        <dl>
            <dt>Name</dt><dd>{{d.fullname}}</dd>
            <dt>Email</dt><dd>{{d.email}}</dd>
        </dl>
    </div>
</div>
<div id="company">
    <h1>{{d.name}}</h1>
    <div class="list" data-repeat="{{d.employees}}">
        <div data-import="{{'#person'}}" data-with="{{employeeToPerson(d)}}">
            <!-- No child elements allowed here! -->
        </div>
    </div>
</div>
<script>
    // Function to convert employee to person
    function employeeToPerson(employee) {
        return {
            fullname: employee.firstname + " " + employee.lastname,
            email: employee.workEmail
        };
    }

    // Create template (for importing)
    d3.select("#person").template();

    // Create template and render data
    d3.select("#company")
        .template()
        .render({
            name: "Some company",
            employees: [
                { firstname: "Alan", lastname: "Turing", workEmail: "alan@some.com", privateEmail: "alan.turing@gmail.com" },
                { firstname: "Alan", lastname: "Kay", workEmail: "alan2@some.com", privateEmail: "alan.kay@yahoo.com" },
                { firstname: "Bret", lastname: "Victor", workEmail: "bret@some.com", privateEmail: "bret.victor@hotmail.com" }
            ] 
        })
    ;

Transitions

Transitions can be combined with rendering data onto a template. This allows for example SVG diagrams to transition from one shape to another. The general approach for rendering on a transition is the following:

selection
    .transition()
        .delay(100)
        .duration(700)
        .render(data)
;

Tween data function

Some data like text or strings do not animate well. Also sometimes a bit more control is needed on the way data is transitioned. In D3 there are tween functions for that. In d3-template these can be created by tagging a data function with the tag 'tween'.

<div id="tweenBlock" data-style-background-color="{{tween:d3.interpolateRgb('white', d.color)}}">
    <span>{{tween:textTween(d.text)}}</span>
</div>
<script>
    function textTween(d) {
        return function(t) {
            return d.substr(0, Math.floor(t * d.length));
        };
    }

    d3.selection("#tweenBlock")
        .template()
        .transition()
            .delay(100)
            .duration(700)
            .render({ fill: "red", text: "Hello world" })
    ;
</script>

The tween data function should return a function accepting a single parameter t in accordance with the regular tween functions attrTween, styleTween and/or tween.

A tween data function is to be used in combination with rendering on a transition. If a tween data function is specified within a template, but the render is performed without an active transition then the final tween result is rendered directly (ie the value returned by calling the tween data function with value 1.0).

Event handlers

If event handlers are applied to a selection before a template is being created from it, these event handlers will be applied to the rendered result as well. When an event handler is called it will receive the normal D3 style arguments d, i, nodes and this will be set to the node receiving the event.

<ul id="lang-list" data-repeat="{{d}}">
    <li>{{d.english}}</li>
</ul>
<script>
    var list = d3.select("#lang-list");

    // Add event handler
    list.select("li").on("click", function(d) {
        window.alert("'" + d.english + "' translates into '" + d.dutch + "' for the Dutch language");
    });
        
    // Create template now that the event handlers are applied
    list.template();

    // Render words
    var words = [
        { english: "one", dutch: "een" },
        { english: "two", dutch: "twee" },
        { english: "three", dutch: "drie" },
        { english: "four", dutch: "vier" }
    ];
    list.render(words);

    // Clicking on a list element will show the alert specified 
</script>

History

d3-template was created to remove some of the burden of having to create each and every DOM element when applying the enter/exit update pattern. For version 1 additional tooling was foreseen which would create data structures based on the templates. Work the other way around so to say. This did not work out as expected and adding the much needed filters did not help. So with version 2 the approach where the template expressions are fields (and filters) is dropped. Beginning with version 2 template expressions are Javascript expressions with parameters d, i, nodes defined as in D3 data functions. Version 1 code will break on the version 2 plugin. Sorry for any inconvenience. The new expressions should have a higher expressiveness and should be eassier to debug. As with version 1: hopefully this is a useful plugin for you.

Author: ErikOnBike
Source Code: https://github.com/ErikOnBike/d3-template 
License: BSD-3-Clause license

#javascript #3d #d3 #templates 

D3 Plugin to Create & Render Templates Use D3's Data Binding Mechanism
Reid  Rohan

Reid Rohan

1654069200

Jest-nunjucks: Jest Processor That Compiles Nunjucks Templates

jest-nunjucks

Jest processor that compiles nunjucks templates

Install

npm i jest-nunjucks --save-dev

Config example

Add to your config

"jest": {
    "transform": {
        "^.+\\.njk$": "jest-nunjucks",
    }
}

To use nunjucks configuration options just add them into global jest config

"jest": {
  "globals": {
    "nunjucks": {
      "config": {
        "throwOnUndefined": true,
        "trimBlocks": true
      }
    }
  },
  "transform": {
    "^.+\\.njk$": "jest-nunjucks"
  }
}

To use set path to look for templates use root option, it will be resolved with jest <rootDir>. If it's unset path for templates will be - <rootDir>;

"jest": {
  "globals": {
    "nunjucks": {
      "root": "./path-to-root-dir",
      "config": {
        "throwOnUndefined": true,
        "trimBlocks": true
      }
    }
  },
  "transform": {
    "^.+\\.njk$": "jest-nunjucks"
  }
}

Author: Denar90
Source Code: https://github.com/denar90/jest-nunjucks 
License: MIT license

#javascript #jest #templates 

Jest-nunjucks: Jest Processor That Compiles Nunjucks Templates

Python Docx Template: Use A Docx As A Jinja2 Template

python-docx-template

Use a docx as a jinja2 template

Introduction

This package uses 2 major packages :

  • python-docx for reading, writing and creating sub documents
  • jinja2 for managing tags inserted into the template docx

python-docx-template has been created because python-docx is powerful for creating documents but not for modifying them.

The idea is to begin to create an example of the document you want to generate with microsoft word, it can be as complex as you want : pictures, index tables, footer, header, variables, anything you can do with word. Then, as you are still editing the document with microsoft word, you insert jinja2-like tags directly in the document. You save the document as a .docx file (xml format) : it will be your .docx template file.

Now you can use python-docx-template to generate as many word documents you want from this .docx template and context variables you will associate.

Share

If you like this project, please rate and share it here : http://rate.re/github/elapouya/python-docx-template

Documentation

Please, read the doc

Other projects

Have a look at some of my other projects :

Author: elapouya
Source Code: https://github.com/elapouya/python-docx-template
License: LGPL-2.1 License

#python #templates 

Python Docx Template: Use A Docx As A Jinja2 Template

Best Free VueJS Admin Templates 2022

Are you looking for some of the best free VueJS admin dashboard templates? Look! In this blog, we give you a list of the best of them from 2022.

Admin dashboards are advantageous to any company. A Vue dashboard allows you to easily see how your company is performing. You may, for example, easily monitor and measure performance and metrics in real-time. Furthermore, it will help you build your business without spending time.

These new free templates include the ability to configure your dashboard. With all of the necessities. So, instead of wasting time creating your own, check out these free ones and get started on your next out-of-the-box admin dashboard.

Read more… 👇😊

 

#vue #vuejs #admin #templates #dashboard #opensource #GitHub 

Best Free VueJS Admin Templates 2022

A Full Featured NextJS eCommerce CMS + 15 NextJS Templates in 2022 — UI-Lib’s Blog

If you want to avoid making a project from scratch yet want to have a blazingly fast spectacular outcome, NextJS templates are here for you. In this blog, we have carefully selected all high-quality NextJS templates for your web app project. 

Read more 👇🤩

#nextjs #templates #ecommerce #cms 

A Full Featured NextJS eCommerce CMS + 15 NextJS Templates in 2022 — UI-Lib’s Blog
Nigel  Uys

Nigel Uys

1652107740

Sprig: Useful Template Functions for Go Templates

Sprig: Template functions for Go templates  

The Go language comes with a built-in template language, but not very many template functions. Sprig is a library that provides more than 100 commonly used template functions.

It is inspired by the template functions found in Twig and in various JavaScript libraries, such as underscore.js.

IMPORTANT NOTES

Sprig leverages mergo to handle merges. In its v0.3.9 release, there was a behavior change that impacts merging template functions in sprig. It is currently recommended to use v0.3.10 or later of that package. Using v0.3.9 will cause sprig tests to fail.

Package Versions

There are two active major versions of the sprig package.

  • v3 is currently stable release series on the master branch. The Go API should remain compatible with v2, the current stable version. Behavior change behind some functions is the reason for the new major version.
  • v2 is the previous stable release series. It has been more than three years since the initial release of v2. You can read the documentation and see the code on the release-2 branch. Bug fixes to this major version will continue for some time.

Usage

Template developers: Please use Sprig's function documentation for detailed instructions and code snippets for the >100 template functions available.

Go developers: If you'd like to include Sprig as a library in your program, our API documentation is available at GoDoc.org.

For standard usage, read on.

Load the Sprig library

To load the Sprig FuncMap:


import (
  "github.com/Masterminds/sprig/v3"
  "html/template"
)

// This example illustrates that the FuncMap *must* be set before the
// templates themselves are loaded.
tpl := template.Must(
  template.New("base").Funcs(sprig.FuncMap()).ParseGlob("*.html")
)

Calling the functions inside of templates

By convention, all functions are lowercase. This seems to follow the Go idiom for template functions (as opposed to template methods, which are TitleCase). For example, this:

{{ "hello!" | upper | repeat 5 }}

produces this:

HELLO!HELLO!HELLO!HELLO!HELLO!

Principles Driving Our Function Selection

We followed these principles to decide which functions to add and how to implement them:

  • Use template functions to build layout. The following types of operations are within the domain of template functions:
    • Formatting
    • Layout
    • Simple type conversions
    • Utilities that assist in handling common formatting and layout needs (e.g. arithmetic)
  • Template functions should not return errors unless there is no way to print a sensible value. For example, converting a string to an integer should not produce an error if conversion fails. Instead, it should display a default value.
  • Simple math is necessary for grid layouts, pagers, and so on. Complex math (anything other than arithmetic) should be done outside of templates.
  • Template functions only deal with the data passed into them. They never retrieve data from a source.
  • Finally, do not override core Go template functions.

Author: Masterminds
Source Code: https://github.com/Masterminds/sprig 
License: MIT license

#go #golang #templates 

Sprig: Useful Template Functions for Go Templates
Nigel  Uys

Nigel Uys

1652076480

Got: A Template Engine That Turns Templates into Go Code

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.

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 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

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

Author: Goradd
Source Code: https://github.com/goradd/got 
License: MIT license

#go #golang #templates 

Got: A Template Engine That Turns Templates into Go Code
Nigel  Uys

Nigel Uys

1652070300

Pongo2: Django-syntax Like Template-engine for Go

pongo2  

pongo2 is a Django-syntax like templating-language (official website).

Install/update using go get (no dependencies required by pongo2):

go get -u github.com/flosch/pongo2/v5

Please use the issue tracker if you're encountering any problems with pongo2 or if you need help with implementing tags or filters (create a ticket!).

First impression of a template

<html>
  <head>
    <title>Our admins and users</title>
  </head>
  {# This is a short example to give you a quick overview of pongo2's syntax. #}
  {% macro user_details(user, is_admin=false) %}
  <div class="user_item">
    <!-- Let's indicate a user's good karma -->
    <h2 {% if (user.karma>= 40) || (user.karma > calc_avg_karma(userlist)+5) %} class="karma-good"{% endif %}>

      <!-- This will call user.String() automatically if available: -->
      {{ user }}
    </h2>

    <!-- Will print a human-readable time duration like "3 weeks ago" -->
    <p>This user registered {{ user.register_date|naturaltime }}.</p>

    <!-- Let's allow the users to write down their biography using markdown;
             we will only show the first 15 words as a preview -->
    <p>The user's biography:</p>
    <p>
      {{ user.biography|markdown|truncatewords_html:15 }}
      <a href="/user/{{ user.id }}/">read more</a>
    </p>

    {% if is_admin %}
    <p>This user is an admin!</p>
    {% endif %}
  </div>
  {% endmacro %}

  <body>
    <!-- Make use of the macro defined above to avoid repetitive HTML code
         since we want to use the same code for admins AND members -->

    <h1>Our admins</h1>
    {% for admin in adminlist %} {{ user_details(admin, true) }} {% endfor %}

    <h1>Our members</h1>
    {% for user in userlist %} {{ user_details(user) }} {% endfor %}
  </body>
</html>

Features

Caveats

Filters

  • date / time: The date and time filter are taking the Golang specific time- and date-format (not Django's one) currently. Take a look on the format here.
  • stringformat: stringformat does not take Python's string format syntax as a parameter, instead it takes Go's. Essentially {{ 3.14|stringformat:"pi is %.2f" }} is fmt.Sprintf("pi is %.2f", 3.14).
  • escape / force_escape: Unlike Django's behaviour, the escape-filter is applied immediately. Therefore there is no need for a force_escape-filter yet.

Tags

  • for: All the forloop fields (like forloop.counter) are written with a capital letter at the beginning. For example, the counter can be accessed by forloop.Counter and the parentloop by forloop.Parentloop.
  • now: takes Go's time format (see date and time-filter).

Misc

  • not in-operator: You can check whether a map/struct/string contains a key/field/substring by using the in-operator (or the negation of it): {% if key in map %}Key is in map{% else %}Key not in map{% endif %} or {% if !(key in map) %}Key is NOT in map{% else %}Key is in map{% endif %}.

Add-ons, libraries and helpers

Official

  • pongo2-addons - Official additional filters/tags for pongo2 (for example a markdown-filter). They are in their own repository because they're relying on 3rd-party-libraries.

3rd-party

Please add your project to this list and send me a pull request when you've developed something nice for pongo2.

Who's using pongo2

I'm compiling a list of pongo2 users. Add your project or company!

API-usage examples

Please see the documentation for a full list of provided API methods.

A tiny example (template string)

// Compile the template first (i. e. creating the AST)
tpl, err := pongo2.FromString("Hello {{ name|capfirst }}!")
if err != nil {
    panic(err)
}
// Now you can render the template with the given
// pongo2.Context how often you want to.
out, err := tpl.Execute(pongo2.Context{"name": "florian"})
if err != nil {
    panic(err)
}
fmt.Println(out) // Output: Hello Florian!

Example server-usage (template file)

package main

import (
    "github.com/flosch/pongo2/v5"
    "net/http"
)

// Pre-compiling the templates at application startup using the
// little Must()-helper function (Must() will panic if FromFile()
// or FromString() will return with an error - that's it).
// It's faster to pre-compile it anywhere at startup and only
// execute the template later.
var tplExample = pongo2.Must(pongo2.FromFile("example.html"))

func examplePage(w http.ResponseWriter, r *http.Request) {
    // Execute the template per HTTP request
    err := tplExample.ExecuteWriter(pongo2.Context{"query": r.FormValue("query")}, w)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
    }
}

func main() {
    http.HandleFunc("/", examplePage)
    http.ListenAndServe(":8080", nil)
}

Author: flosch
Source Code: https://github.com/flosch/pongo2 
License: MIT license

#go #golang #templates #django 

Pongo2: Django-syntax Like Template-engine for Go
Nigel  Uys

Nigel Uys

1650037680

Project-layout: Standard Go Project Layout

Standard Go Project Layout

Overview

This is a basic layout for Go application projects. It's not an official standard defined by the core Go dev team; however, it is a set of common historical and emerging project layout patterns in the Go ecosystem. Some of these patterns are more popular than others. It also has a number of small enhancements along with several supporting directories common to any large enough real world application.

If you are trying to learn Go or if you are building a PoC or a simple project for yourself this project layout is an overkill. Start with something really simple instead (a single main.gofile andgo.mod is more than enough). As your project grows keep in mind that it'll be important to make sure your code is well structured otherwise you'll end up with a messy code with lots of hidden dependencies and global state. When you have more people working on the project you'll need even more structure. That's when it's important to introduce a common way to manage packages/libraries. When you have an open source project or when you know other projects import the code from your project repository that's when it's important to have private (aka internal) packages and code. Clone the repository, keep what you need and delete everything else! Just because it's there it doesn't mean you have to use it all. None of these patterns are used in every single project. Even the vendor pattern is not universal.

With Go 1.14 Go Modules are finally ready for production. Use Go Modules unless you have a specific reason not to use them and if you do then you don’t need to worry about $GOPATH and where you put your project. The basic go.mod file in the repo assumes your project is hosted on GitHub, but it's not a requirement. The module path can be anything though the first module path component should have a dot in its name (the current version of Go doesn't enforce it anymore, but if you are using slightly older versions don't be surprised if your builds fail without it). See Issues 37554 and 32819 if you want to know more about it.

This project layout is intentionally generic and it doesn't try to impose a specific Go package structure.

This is a community effort. Open an issue if you see a new pattern or if you think one of the existing patterns needs to be updated.

Go Directories

/cmd

Main applications for this project.

The directory name for each application should match the name of the executable you want to have (e.g., /cmd/myapp).

Don't put a lot of code in the application directory. If you think the code can be imported and used in other projects, then it should live in the /pkg directory. If the code is not reusable or if you don't want others to reuse it, put that code in the /internal directory. You'll be surprised what others will do, so be explicit about your intentions!

It's common to have a small main function that imports and invokes the code from the /internal and /pkg directories and nothing else.

See the /cmd directory for examples.

/internal

Private application and library code. This is the code you don't want others importing in their applications or libraries. Note that this layout pattern is enforced by the Go compiler itself. See the Go 1.4 release notes for more details. Note that you are not limited to the top level internal directory. You can have more than one internal directory at any level of your project tree.

You can optionally add a bit of extra structure to your internal packages to separate your shared and non-shared internal code. It's not required (especially for smaller projects), but it's nice to have visual clues showing the intended package use. Your actual application code can go in the /internal/app directory (e.g., /internal/app/myapp) and the code shared by those apps in the /internal/pkg directory (e.g., /internal/pkg/myprivlib).

/pkg

Library code that's ok to use by external applications (e.g., /pkg/mypubliclib). Other projects will import these libraries expecting them to work, so think twice before you put something here :-) Note that the internal directory is a better way to ensure your private packages are not importable because it's enforced by Go. The /pkg directory is still a good way to explicitly communicate that the code in that directory is safe for use by others. The I'll take pkg over internal blog post by Travis Jeffery provides a good overview of the pkg and internal directories and when it might make sense to use them.

It's also a way to group Go code in one place when your root directory contains lots of non-Go components and directories making it easier to run various Go tools (as mentioned in these talks: Best Practices for Industrial Programming from GopherCon EU 2018, GopherCon 2018: Kat Zien - How Do You Structure Your Go Apps and GoLab 2018 - Massimiliano Pippi - Project layout patterns in Go).

See the /pkg directory if you want to see which popular Go repos use this project layout pattern. This is a common layout pattern, but it's not universally accepted and some in the Go community don't recommend it.

It's ok not to use it if your app project is really small and where an extra level of nesting doesn't add much value (unless you really want to :-)). Think about it when it's getting big enough and your root directory gets pretty busy (especially if you have a lot of non-Go app components).

The pkg directory origins: The old Go source code used to use pkg for its packages and then various Go projects in the community started copying the pattern (see this Brad Fitzpatrick's tweet for more context).

/vendor

Application dependencies (managed manually or by your favorite dependency management tool like the new built-in Go Modules feature). The go mod vendor command will create the /vendor directory for you. Note that you might need to add the -mod=vendor flag to your go build command if you are not using Go 1.14 where it's on by default.

Don't commit your application dependencies if you are building a library.

Note that since 1.13 Go also enabled the module proxy feature (using https://proxy.golang.org as their module proxy server by default). Read more about it here to see if it fits all of your requirements and constraints. If it does, then you won't need the vendor directory at all.

Service Application Directories

/api

OpenAPI/Swagger specs, JSON schema files, protocol definition files.

See the /api directory for examples.

Web Application Directories

/web

Web application specific components: static web assets, server side templates and SPAs.

Common Application Directories

/configs

Configuration file templates or default configs.

Put your confd or consul-template template files here.

/init

System init (systemd, upstart, sysv) and process manager/supervisor (runit, supervisord) configs.

/scripts

Scripts to perform various build, install, analysis, etc operations.

These scripts keep the root level Makefile small and simple (e.g., https://github.com/hashicorp/terraform/blob/master/Makefile).

See the /scripts directory for examples.

/build

Packaging and Continuous Integration.

Put your cloud (AMI), container (Docker), OS (deb, rpm, pkg) package configurations and scripts in the /build/package directory.

Put your CI (travis, circle, drone) configurations and scripts in the /build/ci directory. Note that some of the CI tools (e.g., Travis CI) are very picky about the location of their config files. Try putting the config files in the /build/ci directory linking them to the location where the CI tools expect them (when possible).

/deployments

IaaS, PaaS, system and container orchestration deployment configurations and templates (docker-compose, kubernetes/helm, mesos, terraform, bosh). Note that in some repos (especially apps deployed with kubernetes) this directory is called /deploy.

/test

Additional external test apps and test data. Feel free to structure the /test directory anyway you want. For bigger projects it makes sense to have a data subdirectory. For example, you can have /test/data or /test/testdata if you need Go to ignore what's in that directory. Note that Go will also ignore directories or files that begin with "." or "_", so you have more flexibility in terms of how you name your test data directory.

See the /test directory for examples.

Other Directories

/docs

Design and user documents (in addition to your godoc generated documentation).

See the /docs directory for examples.

/tools

Supporting tools for this project. Note that these tools can import code from the /pkg and /internal directories.

See the /tools directory for examples.

/examples

Examples for your applications and/or public libraries.

See the /examples directory for examples.

/third_party

External helper tools, forked code and other 3rd party utilities (e.g., Swagger UI).

/githooks

Git hooks.

/assets

Other assets to go along with your repository (images, logos, etc).

/website

This is the place to put your project's website data if you are not using GitHub pages.

See the /website directory for examples.

Directories You Shouldn't Have

/src

Some Go projects do have a src folder, but it usually happens when the devs came from the Java world where it's a common pattern. If you can help yourself try not to adopt this Java pattern. You really don't want your Go code or Go projects to look like Java :-)

Don't confuse the project level /src directory with the /src directory Go uses for its workspaces as described in How to Write Go Code. The $GOPATH environment variable points to your (current) workspace (by default it points to $HOME/go on non-windows systems). This workspace includes the top level /pkg, /bin and /src directories. Your actual project ends up being a sub-directory under /src, so if you have the /src directory in your project the project path will look like this: /some/path/to/workspace/src/your_project/src/your_code.go. Note that with Go 1.11 it's possible to have your project outside of your GOPATH, but it still doesn't mean it's a good idea to use this layout pattern.

Badges

Go Report Card - It will scan your code with gofmt, go vet, gocyclo, golint, ineffassign, license and misspell. Replace github.com/golang-standards/project-layout with your project reference.

Go Report Card

GoDoc - It will provide online version of your GoDoc generated documentation. Change the link to point to your project.

Go Doc

Pkg.go.dev - Pkg.go.dev is a new destination for Go discovery & docs. You can create a badge using the badge generation tool.

PkgGoDev

Release - It will show the latest release number for your project. Change the github link to point to your project.

Release

Notes

A more opinionated project template with sample/reusable configs, scripts and code is a WIP.

If you need help with naming, formatting and style start by running gofmt and golint. Also make sure to read these Go code style guidelines and recommendations:

See Go Project Layout for additional background information.

More about naming and organizing packages as well as other code structure recommendations:

A Chinese Post about Package-Oriented-Design guidelines and Architecture layer

Translations:

Author: Golang-standards
Source Code: https://github.com/golang-standards/project-layout 
License: View license

#go #golang #templates #layout 

Project-layout: Standard Go Project Layout
Nigel  Uys

Nigel Uys

1650030180

Gobase: This is A Simple Skeleton for Golang Applications

GoBase

This is a simple skeleton for golang application. Inspired by development experience and updated according to github.com/golang-standards/project-layout.

How to use?

  1. Clone the repository (with git client git clone github.com/wajox/gobase [project_name] or use it as template on github for creating a new project)
  2. replace github.com/wajox/gobase with [your_pkg_name] in the all files

Structure

  • /api - OpenAPI specs, documentation generated by swag
  • /cmd - apps
  • /db - database migrations and seeds
  • /docs - documentation
  • /internal - application sources for internal usage
  • /pkg - application sources for external usage(SDK and libraries)
  • /test - some stuff for testing purposes

Commands

# install dev tools(wire, golangci-lint, swag, ginkgo)
make install-tools

# start test environment from docker-compose-test.yml
make start-docker-compose-test

# stop test environment from docker-compose-test.yml
make stop-docker-compose-test

# build application
make build

# run all tests
make test-all

# run go generate
make gen

# generate OpenAPI docs with swag
make swagger

# generate source code from .proto files
make proto

# generate dependencies with wire
make deps

Create new project

With clonegopkg

# install clonegopkg
go install github.com/wajox/clonegopkg@latest

# create your project
clonegopkg clone git@github.com:wajox/gobase.git github.com/wajox/newproject

# push to your git repository
cd ~/go/src/github.com/wajox/newproject
git add .
git commit -m "init project from gobase template"
git remote add origin git@github.com:wajox/newproject.git
git push origin master

Tools and packages

  • gin-gonic
  • ginkgo with gomega
  • spf13/viper
  • spf13/cobra
  • envy
  • zerolog
  • golangci-lint
  • wire
  • swag
  • migrate
  • protoc
  • jsonapi
  • docker with docker-compose

Author: Wajox
Source Code: https://github.com/wajox/gobase 
License: MIT License

#go #golang #templates 

Gobase: This is A Simple Skeleton for Golang Applications

Quickly Create and Configure A New Library Or Node.js Project

Start Now

Run one simple command to install and use the interactive project generator. You'll need Node v10 or later.

npx typescript-starter

The interactive CLI will help you create and configure your project automatically.

Since this repo includes the CLI and it's tests, you'll only need to fork or clone this project if you want to contribute. If you find this project useful, please consider leaving a star so others can find it. Thanks!

demo of the typescript-starter command-line interface

Features

So we can have nice things:

  • Generate API documentation (HTML or JSON) without a mess of JSDoc tags to maintain
  • Collocated, atomic, concurrent unit tests with AVA
  • Source-mapped code coverage reports with nyc
  • Configurable code coverage testing (for continuous integration)
  • Automatic linting and formatting using typescript-eslint and Prettier

But first, a good editor

Before you start, consider using an editor with good typescript support.

VS Code (below) is a popular option. Editors with typescript support can provide helpful autocomplete, inline documentation, and code refactoring features.

Also consider installing editor extensions for ESLint and Prettier. These extensions automatically format your code each time you save, and may quickly become invaluable.

Typescript Editor Support – vscode

Developing with typescript-starter

Development zen

To start working, run the watch:build task using npm or yarn.

npm run watch:build

In another terminal tab/window, run the watch:test task:

npm run watch:test

These watch tasks make development much faster and more interactive. They're particularly helpful for TDD/BDD workflows.

These watch tasks will build and watch the entire project for changes (to both the library source files and test source files). As you develop, you can add tests for new functionality – which will initially fail – before developing the new functionality. Each time you save, any changes will be rebuilt and retested.

 typescript-starter's watch task

Since only changed files are rebuilt and retested, this workflow remains fast even for large projects.

Enable stronger type checking (recommended)

To make getting started easier, the default tsconfig.json is using a very flexible configuration. This will allow you to get started without many warnings from Typescript.

To enable additional Typescript type checking features (a good idea for mission-critical or large projects), review the commented-out lines in your typescript compiler options.

Auto-fix and format project

To automatically fix eslint and prettier formatting issues, run:

npm run fix

View test coverage

To generate and view test coverage, run:

npm run cov

This will create an HTML report of test coverage – source-mapped back to Typescript – and open it in your default browser.

source-mapped typescript test coverage example

Generate your API docs

The src folder is analyzed and documentation is automatically generated using TypeDoc.

npm run doc

This command generates API documentation for your library in HTML format and opens it in a browser.

Since types are tracked by Typescript, there's no need to indicate types in JSDoc format. For more information, see the TypeDoc documentation.

To generate and publish your documentation to GitHub Pages use the following command:

npm run doc:publish

Once published, your documentation should be available at the proper GitHub Pages URL for your repo. See typescript-starter's GitHub Pages for an example.

TypeDoc documentation example

For more advanced documentation generation, you can provide your own TypeDoc theme, or build your own documentation using the JSON TypeDoc export:

npm run doc:json

Bump version, update changelog, commit, & tag release

It's recommended that you install commitizen to make commits to your project.

npm install -g commitizen

# commit your changes:
git cz

This project is tooled for conventional changelog to make managing releases easier. See the standard-version documentation for more information on the workflow, or CHANGELOG.md for an example.

# bump package.json version, update CHANGELOG.md, git tag the release
npm run version

You may find a tool like wip helpful for managing work in progress before you're ready to create a meaningful commit.

One-step publish preparation script

Bringing together many of the steps above, this repo includes a one-step release preparation command.

# Prepare a standard release:
npm run prepare-release

This command runs the following tasks:

  • hard-reset: cleans the repo by removing all untracked files and resetting --hard to the latest commit. (Note: this could be destructive.)
  • test: build and fully test the project
  • docs:html: generate the latest version of the documentation
  • docs:publish: publish the documentation to GitHub Pages
  • version: bump package.json version, update CHANGELOG.md, and git tag the release

When the script finishes, it will log the final command needed to push the release commit to the repo and publish the package on the npm registry:

git push --follow-tags origin master; npm publish

Look over the release if you'd like, then execute the command to publish everything.

You can also prepare a non-standard release:

# Or a non-standard release:

# Reset the repo to the latest commit and build everything
npm run hard-reset && npm run test && npm run cov:check && npm run doc:html

# Then version it with standard-version options. e.g.:
# don't bump package.json version
npm run version -- --first-release

# Other popular options include:

# PGP sign it:
# $ npm run version -- --sign

# alpha release:
# $ npm run version -- --prerelease alpha

# And don't forget to push the docs to GitHub pages:
npm run doc:publish

FAQs

Why are there two builds? (main and module)

The src of typescript-starter is compiled into two separate builds: main and module. The main build is configured to use the CommonJS module system. The module build uses the new es6 module system.

Because Node.js LTS releases do not yet support the es6 module system, some projects which depend on your project will follow the main field in package.json. Tools which support the new system (like Rollup, Webpack, or Parcel) will follow the module field, giving them the ability to statically analyze your project. These tools can tree-shake your module build to import only the code they need.

Why put tests next to the source code?

By convention, sample tests in this project are adjacent to the files they test.

  • Such tests are easy to find.
  • You see at a glance if a part of your project lacks tests.
  • Nearby tests can reveal how a part works in context.
  • When you move the source (inevitable), you remember to move the test.
  • When you rename the source file (inevitable), you remember to rename the test file.

(Bullet points taken from the Angular Testing Guide.)

Can I move the tests?

Yes. For some projects, separating tests from the code they test may be desirable. This project is already configured to test any *.spec.ts files located in the src directory, so reorganize your tests however you'd like. You can put them all in a single folder, add tests that test more than one file, or mix and match strategies (e.g. for other types of tests, like integration or e2e tests).

Can I use ts-node for all the things?

Tests are compiled and performed on the final builds in the standard Node.js runtime (rather than an alternative like ts-node) to ensure that they pass in that environment. If you are build a Node.js application, and you are using ts-node in production, you can modify this project to use ts-node rather than a build step.

However, if you're building any kind of library, you should always compile to javascript.

Library authors sometimes make the mistake of distributing their libraries in typescript. Intuitively, this seems like a reasonable course of action, especially if all of your intended consumers will be using typescript as well.

TypeScript has versions, and different versions of TypeScript may not be compatible. Upgrading to a new major version of TypeScript sometimes requires code changes, and must be done project-by-project. Additionally, if you're using the latest version of TypeScript to build your library, and one of your consumers is using an older version in their application, their compiler will be unable to compile your library.

How do I bundle my library for the browser?

The short answer is: don't pre-bundle your library.

Previous versions of typescript-starter included browser bundling using Rollup. This feature has since been removed, since very few libraries should ever be pre-bundled.

If the consumer of your library is using Node.js, bundling is especially unnecessary, since Node.js can reliably resolve dependencies, and bundling may even make debugging more difficult.

If the consumer of your library is a browser application, the application likely has its own build tooling. Very few serious applications are manually bundling their javascript, especially with easy to use, no configuration tools like Parcel available.

Your library is most useful to downstream consumers as a clean, modular codebase, properly exporting features using es6 exports. Consumers can import the exact es6 exports they need from your library, and tree-shake the rest.

How can my library provide different functionality between Node.js and the browser?

In the past, complex javascript libraries have used solutions like Browserify to bundle a version of their application for the browser. Most of these solutions work by allowing library developers to extensively configure and manually override various dependencies with respective browser versions.

For example, where a Node.js application might use Node.js' built-in crypto module, a browser version would need to fall back to a polyfill-like alternative dependency like crypto-browserify.

With es6, this customization and configuration is no longer necessary. Your library can now export different functionality for different consumers. While browser consumers may import a native JavaScript crypto implementation which your library exports, Node.js users can choose to import a different, faster implementation which your library exports.

See hash.ts for a complete example. Two different functions are exported, sha256, and sha256Native. Browser consumers will not be able to import sha256Native, since their bundler will be unable to resolve the built-in Node.js dependency (their bundler will throw an error). Node.js users, however, will be able to import it normally. Each consumer can import the exact functionality they need.

One perceived downside of this solution is that it complicates the library's API. Browser consumers will sometimes import one feature while Node.js users import another. While this argument has merit, we should weigh it against the benefits.

Providing a public API where consumer code is the same between browsers and Node.js is desirable, but it comes at the cost of significant configuration and complexity. In many cases, it requires that code be aware of its environment at runtime, requiring additional complexity and testing.

A better way to provide this developer experience is to provide similar APIs for each environment, and then encourage the use of es6 import aliasing to standardize between them.

For example, in the documentation for typescript-starter, we encourage Node.js users to import sha256Native as sha256. With this convention, we get a standard API without loaders or dependency substitution hacks.

// browser-application.js
import { sha256 } from 'typescript-starter';

// fully-portable code
console.log(sha256('test'));
// node-application.js
import { sha256Native as sha256 } from 'typescript-starter';

// fully-portable code
console.log(sha256('test'));

What about Git hooks to validate commit messages?

This project uses standard-version to automatically update the changelog based on commit messages since the last release. To do this, each relevant commit must be properly formatted.

To ensure all commits follow the proper conventions, you can use a package like commitlint with Husky. However, keep in mind that commit hooks can be confusing, especially for new contributors. They also interfere with some development tools and workflows.

If your project is private, or will primarily receive contributions from long-running contributors, this may be a good fit. Otherwise, this setup may raise the barrier to one-off contributions slightly.

Note, as a maintainer, if you manage your project on GitHub or a similar website, you can now use the Squash and Merge option to add a properly formatted, descriptive commit messages when merging each pull request. This is likely to be more valuable than trying to force one-time contributors to adhere to commit conventions, since you can also maintain a more consistent language style. Because this is the best choice for the vast majority of projects, typescript-starter does not bundle any commit message validation.


Contributing

Pull Requests welcome! To work on the CLI, clone and build the repo, then use npm link to install it globally.

git clone https://github.com/bitjson/typescript-starter.git
cd typescript-starter
npm install
npm test
npm link

Manual testing

To manually test the CLI, you can use the TYPESCRIPT_STARTER_REPO_URL environment variable to test a clone from your local repo. Run npm run build:main -- -w as you're developing, then in a different testing directory:

mkdir typescript-starter-testing
cd typescript-starter-testing
TYPESCRIPT_STARTER_REPO_URL='/local/path/to/typescript-starter' typescript-starter

You can also set TYPESCRIPT_STARTER_REPO_URL to any valid Git URL, such as your fork of this repo:

TYPESCRIPT_STARTER_REPO_URL='https://github.com/YOUR_USERNAME/typescript-starter.git' typescript-starter

If TYPESCRIPT_STARTER_REPO_BRANCH is not provided, it will default to master.

Debug in VS Code

If you're using VS Code, the Debug CLI launch configuration also allows you to immediately build and step through execution of the CLI.

Integration Test Result Diffs

You can compare the integration test results before and after a change by running check-cli before and after applying your changes:

npm run check-cli

Each time you run check-cli, the test results will be committed to the diff directory, allowing you to easily review the differences with git diff HEAD or an interactive Git client like GitHub for Desktop or SourceTree.

If you already have changes in the working directory, try:

git stash && npm run check-cli && git stash pop && npm run check-cli

Author: Bitjson
Source Code: https://github.com/bitjson/typescript-starter 
License: MIT License

#typescript #node #templates 

Quickly Create and Configure A New Library Or Node.js Project
Waylon  Bruen

Waylon Bruen

1648568820

Hermes: Golang Package That Generates Clean, Responsive HTML E-mails

Hermes

Hermes is the Go port of the great mailgen engine for Node.js. Check their work, it's awesome! It's a package that generates clean, responsive HTML e-mails for sending transactional e-mails (welcome e-mails, reset password e-mails, receipt e-mails and so on), and associated plain text fallback.

Demo

Usage

First install the package:

go get -u github.com/matcornic/hermes/v2

Starting from release v2.0.0, Hermes uses Go modules. The latest version of Hermes requires at least Go 1.11 with gomodules enabled. You can still use an Hermes release compatible with prior Go versions by using v1.2.0 release

Then, start using the package by importing and configuring it:

// Configure hermes by setting a theme and your product info
h := hermes.Hermes{
    // Optional Theme
    // Theme: new(Default) 
    Product: hermes.Product{
        // Appears in header & footer of e-mails
        Name: "Hermes",
        Link: "https://example-hermes.com/",
        // Optional product logo
        Logo: "http://www.duchess-france.org/wp-content/uploads/2016/01/gopher.png",
    },
}

Next, generate an e-mail using the following code:

email := hermes.Email{
    Body: hermes.Body{
        Name: "Jon Snow",
        Intros: []string{
            "Welcome to Hermes! We're very excited to have you on board.",
        },
        Actions: []hermes.Action{
            {
                Instructions: "To get started with Hermes, please click here:",
                Button: hermes.Button{
                    Color: "#22BC66", // Optional action button color
                    Text:  "Confirm your account",
                    Link:  "https://hermes-example.com/confirm?token=d9729feb74992cc3482b350163a1a010",
                },
            },
        },
        Outros: []string{
            "Need help, or have questions? Just reply to this email, we'd love to help.",
        },
    },
}

// Generate an HTML email with the provided contents (for modern clients)
emailBody, err := h.GenerateHTML(email)
if err != nil {
    panic(err) // Tip: Handle error with something else than a panic ;)
}

// Generate the plaintext version of the e-mail (for clients that do not support xHTML)
emailText, err := h.GeneratePlainText(email)
if err != nil {
    panic(err) // Tip: Handle error with something else than a panic ;)
}

// Optionally, preview the generated HTML e-mail by writing it to a local file
err = ioutil.WriteFile("preview.html", []byte(emailBody), 0644)
if err != nil {
    panic(err) // Tip: Handle error with something else than a panic ;)
}

This code would output the following HTML template:

And the following plain text:


------------
Hi Jon Snow,
------------

Welcome to Hermes! We're very excited to have you on board.

To get started with Hermes, please click here: https://hermes-example.com/confirm?token=d9729feb74992cc3482b350163a1a010

Need help, or have questions? Just reply to this email, we'd love to help.

Yours truly,
Hermes - https://example-hermes.com/

Copyright © 2017 Hermes. All rights reserved.

Theme templates will be embedded in your application binary. If you want to use external templates (for configuration), use your own theme by implementing hermes.Theme interface with code searching for your files.

More Examples

To run the examples, go to examples folder, then run go run -a *.go. HTML and Plaintext example should be created in given theme folders.

Optionaly you can set the following variables to send automatically the emails to one your mailbox. Nice for testing template in real email clients.

  • HERMES_SEND_EMAILS=true
  • HERMES_SMTP_SERVER=<smtp_server> : for Gmail it's smtp.gmail.com
  • HERMES_SMTP_PORT=<smtp_port> : for Gmail it's 465
  • HERMES_SENDER_EMAIL=<your_sender_email>
  • HERMES_SENDER_IDENTITY=<the sender name>
  • HERMES_SMTP_USER=<smtp user> : usually the same than HERMES_SENDER_EMAIL
  • HERMES_TO=<recipients emails>: split by commas like myadress@test.com,somethingelse@gmail.com

The program will ask for your SMTP password. If needed, you can set it with HERMES_SMTP_PASSWORD variable (but be careful where you put this information !)

Plaintext E-mails

To generate a plaintext version of the e-mail, simply call GeneratePlainText function:

// Generate plaintext email using hermes
emailText, err := h.GeneratePlainText(email)
if err != nil {
    panic(err) // Tip: Handle error with something else than a panic ;)
}

Supported Themes

The following open-source themes are bundled with this package:

RTL Support

To change the default text direction (left-to-right), simply override it as follows:

// Configure hermes by setting a theme and your product info
h := hermes.Hermes {
    // Custom text direction
    TextDirection: hermes.TDRightToLeft,
}

Language Customizations

To customize the e-mail's greeting ("Hi") or signature ("Yours truly"), supply custom strings within the e-mail's Body:

email := hermes.Email{
    Body: hermes.Body{
        Greeting: "Dear",
        Signature: "Sincerely",
    },
}

To use a custom title string rather than a greeting/name introduction, provide it instead of Name:

email := hermes.Email{
    Body: hermes.Body{
        // Title will override `Name`
        Title: "Welcome to Hermes",
    },
}

To customize the Copyright, override it when initializing Hermes within your Product as follows:

// Configure hermes by setting a theme and your product info
h := hermes.Hermes{
    // Optional Theme
    // Theme: new(Default)
    Product: hermes.Product{
        // Appears in header & footer of e-mails
        Name: "Hermes",
        Link: "https://example-hermes.com/",
        // Custom copyright notice
        Copyright: "Copyright © 2017 Dharma Initiative. All rights reserved."
    },
}

To use a custom fallback text at the end of the email, change the TroubleText field of the hermes.Product struct. The default value is If you’re having trouble with the button '{ACTION}', copy and paste the URL below into your web browser.. The {ACTION} placeholder will be replaced with the corresponding text of the supplied action button:

// Configure hermes by setting a theme and your product info
h := hermes.Hermes{
    // Optional Theme
    // Theme: new(Default)
    Product: hermes.Product{
        // Custom trouble text
        TroubleText: "If the {ACTION}-button is not working for you, just copy and paste the URL below into your web browser."
    },
}

Since v2.1.0, Hermes is automatically inlining all CSS to improve compatibility with email clients, thanks to Premailer. You can disable this feature by setting DisableCSSInlining of Hermes struct to true.

h := hermes.Hermes{
    ...
    DisableCSSInlining: true,
}

Elements

Hermes supports injecting custom elements such as dictionaries, tables and action buttons into e-mails.

Action

To inject an action button in to the e-mail, supply the Actions object as follows:

email := hermes.Email{
    Body: hermes.Body{
        Actions: []hermes.Action{
            {
                Instructions: "To get started with Hermes, please click here:",
                Button: hermes.Button{
                    Color: "#22BC66", // Optional action button color
                    Text:  "Confirm your account",
                    Link:  "https://hermes-example.com/confirm?token=d9729feb74992cc3482b350163a1a010",
                },
            },
        },
    },
}

Alternatively, instead of having a button, an action can be an invite code as follows:

email := hermes.Email{
    Body: hermes.Body{
        Actions: []hermes.Action{
            {
                Instructions: "To get started with Hermes, please use the invite code:",
                InviteCode: "123456",
            },
        },
    },
}

To inject multiple action buttons in to the e-mail, supply another struct in Actions slice Action.

Table

To inject a table into the e-mail, supply the Table object as follows:

email := hermes.Email{
    Body: hermes.Body{
        Table: hermes.Table{
            Data: [][]hermes.Entry{
                // List of rows
                {   
                    // Key is the column name, Value is the cell value
                    // First object defines what columns will be displayed
                    {Key: "Item", Value: "Golang"},
                    {Key: "Description", Value: "Open source programming language that makes it easy to build simple, reliable, and efficient software"},
                    {Key: "Price", Value: "$10.99"},
                },
                {
                    {Key: "Item", Value: "Hermes"},
                    {Key: "Description", Value: "Programmatically create beautiful e-mails using Golang."},
                    {Key: "Price", Value: "$1.99"},
                },
            },
            Columns: hermes.Columns{
                // Custom style for each rows
                CustomWidth: map[string]string{
                    "Item":  "20%",
                    "Price": "15%",
                },
                CustomAlignment: map[string]string{
                    "Price": "right",
                },
            },
        },
    },
}

Dictionary

To inject key-value pairs of data into the e-mail, supply the Dictionary object as follows:

email := hermes.Email{
    Body: hermes.Body{
        Dictionary: []hermes.Entry{
            {Key: "Date", Value: "20 November 1887"},
            {Key: "Address", Value: "221B Baker Street, London"},
        },
    },
}

Free Markdown

If you need more flexibility in the content of your generated e-mail, while keeping the same format than any other e-mail, use Markdown content. Supply the FreeMarkdown object as follows:

email := hermes.Email{
        Body: hermes.Body{
            FreeMarkdown: `
> _Hermes_ service will shutdown the **1st August 2017** for maintenance operations. 

Services will be unavailable based on the following schedule:

| Services | Downtime |
| :------:| :-----------: |
| Service A | 2AM to 3AM |
| Service B | 4AM to 5AM |
| Service C | 5AM to 6AM |

---

Feel free to contact us for any question regarding this matter at [support@hermes-example.com](mailto:support@hermes-example.com) or in our [Gitter](https://gitter.im/)

`,
        },
    }
}

This code would output the following HTML template:

And the following plaintext:

------------
Hi Jon Snow,
------------

> 
> 
> 
> Hermes service will shutdown the *1st August 2017* for maintenance
> operations.
> 
> 

Services will be unavailable based on the following schedule:

+-----------+------------+
| SERVICES  |  DOWNTIME  |
+-----------+------------+
| Service A | 2AM to 3AM |
| Service B | 4AM to 5AM |
| Service C | 5AM to 6AM |
+-----------+------------+

Feel free to contact us for any question regarding this matter at support@hermes-example.com ( support@hermes-example.com ) or in our Gitter ( https://gitter.im/ )

Yours truly,
Hermes - https://example-hermes.com/

Copyright © 2017 Hermes. All rights reserved.

Be aware that this content will replace existing tables, dictionary and actions. Only intros, outros, header and footer will be kept.

This is helpful when your application needs sending e-mails, wrote on-the-fly by adminstrators.

Markdown is rendered with Blackfriday, so every thing Blackfriday can do, Hermes can do it as well.

Troubleshooting

  1. After sending multiple e-mails to the same Gmail / Inbox address, they become grouped and truncated since they contain similar text, breaking the responsive e-mail layout.

Simply sending the X-Entity-Ref-ID header with your e-mails will prevent grouping / truncation.

Contributing

See CONTRIBUTING.md

Author: Matcornic
Source Code: https://github.com/matcornic/hermes 
License: Apache-2.0 License

#go #golang #templates 

Hermes: Golang Package That Generates Clean, Responsive HTML E-mails
Reid  Rohan

Reid Rohan

1647669540

Generate: A New Command Line tool & Developer Framework

Command line tool and developer framework for scaffolding out new GitHub projects. Generate offers the robustness and configurability of Yeoman, the expressiveness and simplicity of Slush, and more powerful flow control and composability than either.

Table of contents

Why use Generate?

Why use Generate?

There are other project scaffolders out there, why should you spend your time learning to use Generate?

Generate offers the robustness, power and configurability of Yeoman, with the expressiveness and simplicity of slush and gulp. See the following links if you're interested in a more detailed comparison:

Features

Features

  • advanced flow control: through the use of generators (not es2015 generators), sub-generators, and tasks
  • render templates: use templates to create new files, or replace existing files
  • any engine: use any template engine to render templates, including handlebars, lodash, swig and pug, and anything supported by consolidate. This is useful for generating templates from templates.
  • data: automatically gather data from the user's environment for rendering templates, to populate "hints" in user prompts or for rendering templates, etc.
  • prompts: It's easy to create custom prompts and use the answers for: context for rendering templates, settings options, determining file names, directory structure, and anything else that requires user feedback.
  • macros: create a completely custom generator from the command line using macros.
  • front-matter: use yaml front matter in templates to define settings or defaults on a file-by-file basis
  • smart plugins: Update is built on base, so any "smart" plugin from the Base ecosystem can be used
  • config store: persist configuration settings, global defaults, project-specific defaults, answers to prompts, and so on.
  • streams: full support for gulp and assemble plugins
  • vinyl: files and templates are vinyl files.
  • much more!

Developer toolkit

Developer toolkit

Generate is part of a suite of developer tools that share a common foundation. Any of these tools may be used standalone or together:

  • generate: (you are here) scaffold out new projects
  • assemble: build system for web projects
  • verb: documentation system for code projects
  • update: automate updates of any kind in code projects

All of these applications are built on top of base and templates, which provides a number of benefits, including:

  • similar API/CLI - Learn one, and you will know them all
  • common plugins - create a plugin for one, it will be usable by the others
  • cross-compatible - they can run one another (for example, assemble can run verb to generate markdown documentation, then use that to create an HTML website)

Command line usage

Generate may be installed locally or globally. However, if you wish to run any globally installed generators, Generate's CLI must be installed globally as well.

Install Generate

Install generate globally using npm:

$ npm install --global generate

This adds the gen command to your system path, allowing it to be run from anywhere.

Install a generator

If you'd like to see how generators work, install generate-example:

$ npm install --global generate-example

Then run the example generator with the following command:

$ gen example

Visit the generate-example project for additional steps and guidance.

Next steps

Command line arguments

The syntax for running generators is:

$ gen generator:task
  • generator one or more space-separated generator names
  • task - (optional) one or more comma-separated task names

Examples

# run generate-project's "default" task
$ gen project
# run generate-project's "license" task
$ gen project:license
# run generate-project's "package" task
$ gen project:package

generator.js

If a generator.js is in the current working directory, Generate's CLI will attempt to load it and execute any tasks you've specified at the command line.

Examples

Generators

generators are plugins that can be run by command line or using Generate's API.

Discovering generators

Discovering plugins

Plugins from any applications built on base should work with Generate (and can be used in your generator):

  • base: find base plugins on npm using the baseplugin keyword
  • assemble: find assemble plugins on npm using the assembleplugin keyword
  • generate: find generate plugins on npm using the generateplugin keyword
  • templates: find templates plugins on npm using the templatesplugin keyword
  • update: find update plugins on npm using the updateplugin keyword
  • verb: find verb plugins on npm using the verbplugin keyword

Authoring generators

Visit the documentation for generators to learn how to use, author and publish generators.

More information

About

Related projects

  • assemble: Get the rocks out of your socks! Assemble makes you fast at creating web projects… more | homepage
  • base: Framework for rapidly creating high quality node.js applications, using plugins like building blocks | homepage
  • update: Be scalable! Update is a new, open source developer framework and CLI for automating updates… more | homepage
  • verb: Documentation generator for GitHub projects. Verb is extremely powerful, easy to use, and is used… more | homepage

Community

Are you using Generate in your project? Have you published a generator and want to share your project with the world?

Here are some suggestions!

  • If you get like Generate and want to tweet about it, please feel free to mention @generatejs or use the #generatejs hashtag
  • Show your love by starring Generate and generate
  • Get implementation help on StackOverflow (please use the generatejs tag in questions)
  • Gitter Discuss Generate with us on Gitter
  • If you publish an generator, thank you! To make your project as discoverable as possible, please add the keyword generategenerator to package.json.

Contributing

Pull requests and stars are always welcome. For bugs and feature requests, please create an issue.

Please read the contributing guide for advice on opening issues, pull requests, and coding standards.

Running tests

Running and reviewing unit tests is a great way to get familiarized with a library and its API. You can install dependencies and run tests with the following command:

$ npm install && npm test

You might also be interested in update.

(TOC generated by verb using markdown-toc)

Author: Generate
Source Code: https://github.com/generate/generate 
License: MIT License

#javascript #gulp #rails #cli #templates 

Generate: A New Command Line tool & Developer Framework
Reid  Rohan

Reid Rohan

1647654540

Handlebars-helpers: More than 130 Handlebars helpers in ~20 categories

handlebars-helpers     

More than 130 Handlebars helpers in ~20 categories. Helpers can be used with Assemble, Generate, Verb, Ghost, gulp-handlebars, grunt-handlebars, consolidate, or any node.js/Handlebars project.

You might also be interested in template-helpers.

Install

Install with npm:

$ npm install --save handlebars-helpers

Install with yarn:

$ yarn add handlebars-helpers

Browser usage

See how to use handlebars-helpers in the browser.

Usage

The main export returns a function that needs to be called to expose the object of helpers.

Get all helpers

var helpers = require('handlebars-helpers')();
//=> returns object with all (130+) helpers

Get a specific helper collection

Helper collections are exposed as getters, so only the helpers you want will be required and loaded.

var helpers = require('handlebars-helpers');
var math = helpers.math();
//=> only the `math` helpers

var helpers = require('handlebars-helpers');
var array = helpers.array();
//=> only the `collections` helpers

Get multiple helpers collections

Helper collections are exposed as getters, so only the helpers you want will be required and loaded.

var helpers = require('handlebars-helpers')(['math', 'string']);
//=> only the `math` and `string` helpers

Optionally pass your own handlebars

var handlebars = require('handlebars');
var helpers = require('handlebars-helpers')({
  handlebars: handlebars
});

// or for a specific collection
var math = helpers.math({
  handlebars: handlebars
});

Helpers

Categories

Currently 189 helpers in 20 categories:

All helpers

array helpers

Visit the: code | unit tests | issues)

code helpers

Visit the: code | unit tests | issues)

collection helpers

Visit the: code | unit tests | issues)

comparison helpers

Visit the: code | unit tests | issues)

date helpers

Visit the: code | unit tests | issues)

fs helpers

Visit the: code | unit tests | issues)

html helpers

Visit the: code | unit tests | issues)

i18n helpers

Visit the: code | unit tests | issues)

inflection helpers

Visit the: code | unit tests | issues)

logging helpers

Visit the: code | unit tests | issues)

markdown helpers

Visit the: code | unit tests | issues)

match helpers

Visit the: code | unit tests | issues)

math helpers

Visit the: code | unit tests | issues)

misc helpers

Visit the: code | unit tests | issues)

number helpers

Visit the: code | unit tests | issues)

object helpers

Visit the: code | unit tests | issues)

path helpers

Visit the: code | unit tests | issues)

regex helpers

Visit the: code | unit tests | issues)

string helpers

Visit the: code | unit tests | issues)

url helpers

Visit the: code | unit tests | issues)


array

{{after}}

Returns all of the items in an array after the specified index. Opposite of before.

Params

  • array {Array}: Collection
  • n {Number}: Starting index (number of items to exclude)
  • returns {Array}: Array exluding n items.

Example

<!-- array: ['a', 'b', 'c'] -->
{{after array 1}}
<!-- results in: '["c"]' -->

{{arrayify}}

Cast the given value to an array.

Params

  • value {any}
  • returns {Array}

Example

{{arrayify "foo"}}
<!-- results in: [ "foo" ] -->

{{before}}

Return all of the items in the collection before the specified count. Opposite of after.

Params

  • array {Array}
  • n {Number}
  • returns {Array}: Array excluding items after the given number.

Example

<!-- array: ['a', 'b', 'c'] -->
{{before array 2}}
<!-- results in: '["a", "b"]' -->

{{eachIndex}}

Params

  • array {Array}
  • options {Object}
  • returns {String}

Example

<!-- array: ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'] -->
{{#eachIndex array}}
  {{item}} is {{index}}
{{/eachIndex}}

{{filter}}

Block helper that filters the given array and renders the block for values that evaluate to true, otherwise the inverse block is returned.

Params

  • array {Array}
  • value {any}
  • options {Object}
  • returns {String}

Example

<!-- array: ['a', 'b', 'c'] -->
{{#filter array "foo"}}AAA{{else}}BBB{{/filter}}
<!-- results in: 'BBB' -->

{{first}}

Returns the first item, or first n items of an array.

Params

  • array {Array}
  • n {Number}: Number of items to return, starting at 0.
  • returns {Array}

Example

{{first "['a', 'b', 'c', 'd', 'e']" 2}}
<!-- results in: '["a", "b"]' -->

{{forEach}}

Iterates over each item in an array and exposes the current item in the array as context to the inner block. In addition to the current array item, the helper exposes the following variables to the inner block:

  • index
  • total
  • isFirst
  • isLast Also, @index is exposed as a private variable, and additional private variables may be defined as hash arguments.

Params

  • array {Array}
  • returns {String}

Example

<!-- accounts = [
{'name': 'John', 'email': 'john@example.com'},
{'name': 'Malcolm', 'email': 'malcolm@example.com'},
{'name': 'David', 'email': 'david@example.com'}
] -->

{{#forEach accounts}}
  <a href="mailto:{{ email }}" title="Send an email to {{ name }}">
    {{ name }}
  </a>{{#unless isLast}}, {{/unless}}
{{/forEach}}

{{inArray}}

Block helper that renders the block if an array has the given value. Optionally specify an inverse block to render when the array does not have the given value.

Params

  • array {Array}
  • value {any}
  • options {Object}
  • returns {String}

Example

<!-- array: ['a', 'b', 'c'] -->
{{#inArray array "d"}}
  foo
{{else}}
  bar
{{/inArray}}
<!-- results in: 'bar' -->

{{isArray}}

Returns true if value is an es5 array.

Params

  • value {any}: The value to test.
  • returns {Boolean}

Example

{{isArray "abc"}}
<!-- results in: false -->

<!-- array: [1, 2, 3] -->
{{isArray array}}
<!-- results in: true -->

{{itemAt}}

Returns the item from array at index idx.

Params

  • array {Array}
  • idx {Number}
  • returns {any} value

Example

<!-- array: ['a', 'b', 'c'] -->
{{itemAt array 1}}
<!-- results in: 'b' -->

{{join}}

Join all elements of array into a string, optionally using a given separator.

Params

  • array {Array}
  • separator {String}: The separator to use. Defaults to ,.
  • returns {String}

Example

<!-- array: ['a', 'b', 'c'] -->
{{join array}}
<!-- results in: 'a, b, c' -->

{{join array '-'}}
<!-- results in: 'a-b-c' -->

{{equalsLength}}

Returns true if the the length of the given value is equal to the given length. Can be used as a block or inline helper.

Params

  • value {Array|String}
  • length {Number}
  • options {Object}
  • returns {String}

{{last}}

Returns the last item, or last n items of an array or string. Opposite of first.

Params

  • value {Array|String}: Array or string.
  • n {Number}: Number of items to return from the end of the array.
  • returns {Array}

Example

<!-- var value = ['a', 'b', 'c', 'd', 'e'] -->

{{last value}}
<!-- results in: ['e'] -->

{{last value 2}}
<!-- results in: ['d', 'e'] -->

{{last value 3}}
<!-- results in: ['c', 'd', 'e'] -->

{{length}}

Returns the length of the given string or array.

Params

  • value {Array|Object|String}
  • returns {Number}: The length of the value.

Example

{{length '["a", "b", "c"]'}}
<!-- results in: 3 -->

<!-- results in: myArray = ['a', 'b', 'c', 'd', 'e']; -->
{{length myArray}}
<!-- results in: 5 -->

<!-- results in: myObject = {'a': 'a', 'b': 'b'}; -->
{{length myObject}}
<!-- results in: 2 -->

{{lengthEqual}}

Alias for equalsLength

{{map}}

Returns a new array, created by calling function on each element of the given array. For example,

Params

  • array {Array}
  • fn {Function}
  • returns {String}

Example

<!-- array: ['a', 'b', 'c'], and "double" is a
fictitious function that duplicates letters -->
{{map array double}}
<!-- results in: '["aa", "bb", "cc"]' -->

{{pluck}}

Map over the given object or array or objects and create an array of values from the given prop. Dot-notation may be used (as a string) to get nested properties.

Params

  • collection {Array|Object}
  • prop {Function}
  • returns {String}

Example

// {{pluck items "data.title"}}
<!-- results in: '["aa", "bb", "cc"]' -->

{{reverse}}

Reverse the elements in an array, or the characters in a string.

Params

  • value {Array|String}
  • returns {Array|String}: Returns the reversed string or array.

Example

<!-- value: 'abcd' -->
{{reverse value}}
<!-- results in: 'dcba' -->
<!-- value: ['a', 'b', 'c', 'd'] -->
{{reverse value}}
<!-- results in: ['d', 'c', 'b', 'a'] -->

{{some}}

Block helper that returns the block if the callback returns true for some value in the given array.

Params

  • array {Array}
  • iter {Function}: Iteratee
  • {Options}: Handlebars provided options object
  • returns {String}

Example

<!-- array: [1, 'b', 3] -->
{{#some array isString}}
  Render me if the array has a string.
{{else}}
  Render me if it doesn't.
{{/some}}
<!-- results in: 'Render me if the array has a string.' -->

{{sort}}

Sort the given array. If an array of objects is passed, you may optionally pass a key to sort on as the second argument. You may alternatively pass a sorting function as the second argument.

Params

  • array {Array}: the array to sort.
  • key {String|Function}: The object key to sort by, or sorting function.

Example

<!-- array: ['b', 'a', 'c'] -->
{{sort array}}
<!-- results in: '["a", "b", "c"]' -->

{{sortBy}}

Sort an array. If an array of objects is passed, you may optionally pass a key to sort on as the second argument. You may alternatively pass a sorting function as the second argument.

Params

  • array {Array}: the array to sort.
  • props {String|Function}: One or more properties to sort by, or sorting functions to use.

Example

<!-- array: [{a: 'zzz'}, {a: 'aaa'}] -->
{{sortBy array "a"}}
<!-- results in: '[{"a":"aaa"}, {"a":"zzz"}]' -->

{{withAfter}}

Use the items in the array after the specified index as context inside a block. Opposite of withBefore.

Params

  • array {Array}
  • idx {Number}
  • options {Object}
  • returns {Array}

Example

<!-- array: ['a', 'b', 'c', 'd', 'e'] -->
{{#withAfter array 3}}
  {{this}}
{{/withAfter}}
<!-- results in: "de" -->

{{withBefore}}

Use the items in the array before the specified index as context inside a block. Opposite of withAfter.

Params

  • array {Array}
  • idx {Number}
  • options {Object}
  • returns {Array}

Example

<!-- array: ['a', 'b', 'c', 'd', 'e'] -->
{{#withBefore array 3}}
  {{this}}
{{/withBefore}}
<!-- results in: 'ab' -->

{{withFirst}}

Use the first item in a collection inside a handlebars block expression. Opposite of withLast.

Params

  • array {Array}
  • idx {Number}
  • options {Object}
  • returns {String}

Example

<!-- array: ['a', 'b', 'c'] -->
{{#withFirst array}}
  {{this}}
{{/withFirst}}
<!-- results in: 'a' -->

{{withGroup}}

Block helper that groups array elements by given group size.

Params

  • array {Array}: The array to iterate over
  • size {Number}: The desired length of each array "group"
  • options {Object}: Handlebars options
  • returns {String}

Example

<!-- array: ['a','b','c','d','e','f','g','h'] -->
{{#withGroup array 4}}
  {{#each this}}
    {{.}}
  {{each}}
  <br>
{{/withGroup}}
<!-- results in: -->
<!-- 'a','b','c','d'<br> -->
<!-- 'e','f','g','h'<br> -->

{{withLast}}

Use the last item or n items in an array as context inside a block. Opposite of withFirst.

Params

  • array {Array}
  • idx {Number}: The starting index.
  • options {Object}
  • returns {String}

Example

<!-- array: ['a', 'b', 'c'] -->
{{#withLast array}}
  {{this}}
{{/withLast}}
<!-- results in: 'c' -->

{{withSort}}

Block helper that sorts a collection and exposes the sorted collection as context inside the block.

Params

  • array {Array}
  • prop {String}
  • options {Object}: Specify reverse="true" to reverse the array.
  • returns {String}

Example

<!-- array: ['b', 'a', 'c'] -->
{{#withSort array}}{{this}}{{/withSort}}
<!-- results in: 'abc' -->

{{unique}}

Block helper that return an array with all duplicate values removed. Best used along with a each helper.

Params

  • array {Array}
  • options {Object}
  • returns {Array}

Example

<!-- array: ['a', 'a', 'c', 'b', 'e', 'e'] -->
{{#each (unique array)}}{{.}}{{/each}}
<!-- results in: 'acbe' -->

code

{{embed}}

Embed code from an external file as preformatted text.

Params

  • filepath {String}: filepath to the file to embed.
  • language {String}: Optionally specify the language to use for syntax highlighting.
  • returns {String}

Example

{{embed 'path/to/file.js'}}
<!-- optionally specify the language to use -->
{{embed 'path/to/file.hbs' 'html')}}

{{gist}}

Embed a GitHub Gist using only the id of the Gist

Params

  • id {String}
  • returns {String}

Example

{{gist "12345"}}

{{jsfiddle}}

Generate the HTML for a jsFiddle link with the given params

Params

  • params {Object}
  • returns {String}

Example

{{jsfiddle id="0dfk10ks" tabs="true"}}

collection

{{isEmpty}}

Inline, subexpression, or block helper that returns true (or the block) if the given collection is empty, or false (or the inverse block, if supplied) if the colleciton is not empty.

Params

  • collection {Object}
  • options {Object}
  • returns {String}

Example

<!-- array: [] -->
{{#isEmpty array}}AAA{{else}}BBB{{/isEmpty}}
<!-- results in: 'AAA' -->

<!-- array: [] -->
{{isEmpty array}}
<!-- results in: true -->

{{iterate}}

Block helper that iterates over an array or object. If an array is given, .forEach is called, or if an object is given, .forOwn is called, otherwise the inverse block is returned.

Params

  • collection {Object|Array}: The collection to iterate over
  • options {Object}
  • returns {String}

comparison

{{and}}

Helper that renders the block if both of the given values are truthy. If an inverse block is specified it will be rendered when falsy. Works as a block helper, inline helper or subexpression.

Params

  • a {any}
  • b {any}
  • options {Object}: Handlebars provided options object
  • returns {String}

Example

<!-- {great: true, magnificent: true} -->
{{#and great magnificent}}A{{else}}B{{/and}}
<!-- results in: 'A' -->

{{compare}}

Render a block when a comparison of the first and third arguments returns true. The second argument is the arithemetic operator to use. You may also optionally specify an inverse block to render when falsy.

Params

  • a {}
  • operator {}: The operator to use. Operators must be enclosed in quotes: ">", "=", "<=", and so on.
  • b {}
  • options {Object}: Handlebars provided options object
  • returns {String}: Block, or if specified the inverse block is rendered if falsey.

{{contains}}

Block helper that renders the block if collection has the given value, using strict equality (===) for comparison, otherwise the inverse block is rendered (if specified). If a startIndex is specified and is negative, it is used as the offset from the end of the collection.

Params

  • collection {Array|Object|String}: The collection to iterate over.
  • value {any}: The value to check for.
  • [startIndex=0] {Number}: Optionally define the starting index.
  • options {Object}: Handlebars provided options object.

Example

<!-- array = ['a', 'b', 'c'] -->
{{#contains array "d"}}
  This will not be rendered.
{{else}}
  This will be rendered.
{{/contains}}

{{default}}

Returns the first value that is not undefined, otherwise the "default" value is returned.

Params

  • value {any}
  • defaultValue {any}
  • returns {String}

{{eq}}

Block helper that renders a block if a is equal to b. If an inverse block is specified it will be rendered when falsy. You may optionally use the compare="" hash argument for the second value.

Params

  • a {String}
  • b {String}
  • options {Object}: Handlebars provided options object
  • returns {String}: Block, or inverse block if specified and falsey.

{{gt}}

Block helper that renders a block if a is greater than b.

If an inverse block is specified it will be rendered when falsy. You may optionally use the compare="" hash argument for the second value.

Params

  • a {String}
  • b {String}
  • options {Object}: Handlebars provided options object
  • returns {String}: Block, or inverse block if specified and falsey.

{{gte}}

Block helper that renders a block if a is greater than or equal to b.

If an inverse block is specified it will be rendered when falsy. You may optionally use the compare="" hash argument for the second value.

Params

  • a {String}
  • b {String}
  • options {Object}: Handlebars provided options object
  • returns {String}: Block, or inverse block if specified and falsey.

{{has}}

Block helper that renders a block if value has pattern. If an inverse block is specified it will be rendered when falsy.

Params

  • val {any}: The value to check.
  • pattern {any}: The pattern to check for.
  • options {Object}: Handlebars provided options object
  • returns {String}

{{isFalsey}}

Returns true if the given value is falsey. Uses the falsey library for comparisons. Please see that library for more information or to report bugs with this helper.

Params

  • val {any}
  • options {Options}
  • returns {Boolean}

{{isTruthy}}

Returns true if the given value is truthy. Uses the falsey library for comparisons. Please see that library for more information or to report bugs with this helper.

Params

  • val {any}
  • options {Options}
  • returns {Boolean}

{{ifEven}}

Return true if the given value is an even number.

Params

  • number {Number}
  • options {Object}: Handlebars provided options object
  • returns {String}: Block, or inverse block if specified and falsey.

Example

{{#ifEven value}}
  render A
{{else}}
  render B
{{/ifEven}}

{{ifNth}}

Conditionally renders a block if the remainder is zero when a operand is divided by b. If an inverse block is specified it will be rendered when the remainder is not zero.

Params

  • {}: {Number}
  • {}: {Number}
  • options {Object}: Handlebars provided options object
  • returns {String}: Block, or inverse block if specified and falsey.

{{ifOdd}}

Block helper that renders a block if value is an odd number. If an inverse block is specified it will be rendered when falsy.

Params

  • value {Object}
  • options {Object}: Handlebars provided options object
  • returns {String}: Block, or inverse block if specified and falsey.

Example

{{#ifOdd value}}
  render A
{{else}}
  render B
{{/ifOdd}}

{{is}}

Block helper that renders a block if a is equal to b. If an inverse block is specified it will be rendered when falsy. Similar to eq but does not do strict equality.

Params

  • a {any}
  • b {any}
  • options {Object}: Handlebars provided options object
  • returns {String}

{{isnt}}

Block helper that renders a block if a is not equal to b. If an inverse block is specified it will be rendered when falsy. Similar to unlessEq but does not use strict equality for comparisons.

Params

  • a {String}
  • b {String}
  • options {Object}: Handlebars provided options object
  • returns {String}

{{lt}}

Block helper that renders a block if a is less than b.

If an inverse block is specified it will be rendered when falsy. You may optionally use the compare="" hash argument for the second value.

Params

  • context {Object}
  • options {Object}: Handlebars provided options object
  • returns {String}: Block, or inverse block if specified and falsey.

{{lte}}

Block helper that renders a block if a is less than or equal to b.

If an inverse block is specified it will be rendered when falsy. You may optionally use the compare="" hash argument for the second value.

Params

  • a {Sring}
  • b {Sring}
  • options {Object}: Handlebars provided options object
  • returns {String}: Block, or inverse block if specified and falsey.

{{neither}}

Block helper that renders a block if neither of the given values are truthy. If an inverse block is specified it will be rendered when falsy.

Params

  • a {any}
  • b {any}
  • options {}: Handlebars options object
  • returns {String}: Block, or inverse block if specified and falsey.

{{not}}

Returns true if val is falsey. Works as a block or inline helper.

Params

  • val {String}
  • options {Object}: Handlebars provided options object
  • returns {String}

{{or}}

Block helper that renders a block if any of the given values is truthy. If an inverse block is specified it will be rendered when falsy.

Params

  • arguments {...any}: Variable number of arguments
  • options {Object}: Handlebars options object
  • returns {String}: Block, or inverse block if specified and falsey.

Example

{{#or a b c}}
  If any value is true this will be rendered.
{{/or}}

{{unlessEq}}

Block helper that always renders the inverse block unless a is is equal to b.

Params

  • a {String}
  • b {String}
  • options {Object}: Handlebars provided options object
  • returns {String}: Inverse block by default, or block if falsey.

{{unlessGt}}

Block helper that always renders the inverse block unless a is is greater than b.

Params

  • a {Object}: The default value
  • b {Object}: The value to compare
  • options {Object}: Handlebars provided options object
  • returns {String}: Inverse block by default, or block if falsey.

{{unlessLt}}

Block helper that always renders the inverse block unless a is is less than b.

Params

  • a {Object}: The default value
  • b {Object}: The value to compare
  • options {Object}: Handlebars provided options object
  • returns {String}: Block, or inverse block if specified and falsey.

{{unlessGteq}}

Block helper that always renders the inverse block unless a is is greater than or equal to b.

Params

  • a {any}
  • b {any}
  • options {Object}: Handlebars provided options object
  • returns {String}: Block, or inverse block if specified and falsey.

{{unlessLteq}}

Block helper that always renders the inverse block unless a is is less than or equal to b.

Params

  • a {any}
  • b {any}
  • options {Object}: Handlebars provided options object
  • returns {String}: Block, or inverse block if specified and falsey.

date

{{year}}

Get the current year.

Example

{{year}}
<!-- 2017 -->

{{moment}}

Use moment as a helper. See helper-date for more details.

fs

{{read}}

Read a file from the file system. This is useful in composing "include"-style helpers using sub-expressions.

Params

  • filepath {String}
  • returns {String}

Example

{{read "a/b/c.js"}}
{{someHelper (read "a/b/c.md")}}

{{readdir}}

Return an array of files from the given directory.

Params

  • directory {String}
  • returns {Array}

html

{{attr}}

Stringify attributes on the options hash.

Params

  • options {Object}
  • returns {String}

Example

<!-- value = 'bar' -->
<div{{attr foo=value}}></div>
<!-- results in: <div foo="bar"></div>

{{css}}

Add an array of <link> tags. Automatically resolves relative paths to options.assets if passed on the context.

Params

  • list {String|Array}: One or more stylesheet urls.
  • returns {String}

Example

<!-- {stylesheets: ['foo.css', 'bar.css']} -->
{{css stylesheets}}

<!-- results in: -->
<!-- <link type="text/css" rel="stylesheet" href="foo.css"> -->
<!-- <link type="text/css" rel="stylesheet" href="bar.css"> -->

{{js}}

Generate one or more <script></script> tags with paths/urls to javascript or coffeescript files.

Params

  • context {Object}
  • returns {String}

Example

{{js scripts}}

{{sanitize}}

Strip HTML tags from a string, so that only the text nodes are preserved.

Params

  • str {String}: The string of HTML to sanitize.
  • returns {String}

Example

{{sanitize "<span>foo</span>"}}
<!-- results in: 'foo' -->

{{ul}}

Block helper for creating unordered lists (<ul></ul>)

Params

  • context {Object}
  • options {Object}
  • returns {String}

{{ol}}

Block helper for creating ordered lists (<ol></ol>)

Params

  • context {Object}
  • options {Object}
  • returns {String}

{{thumbnailImage}}

Returns a <figure> with a thumbnail linked to a full picture

Params

  • context {Object}: Object with values/attributes to add to the generated elements:
  • context.alt {String}
  • context.src {String}
  • context.width {Number}
  • context.height {Number}
  • returns {String}: HTML <figure> element with image and optional caption/link.

i18n

{{i18n}}

i18n helper. See button-i18n for a working example.

Params

  • key {String}
  • options {Object}
  • returns {String}

inflection

{{inflect}}

Returns either the singular or plural inflection of a word based on the given count.

Params

  • count {Number}
  • singular {String}: The singular form
  • plural {String}: The plural form
  • includeCount {String}
  • returns {String}

Example

{{inflect 0 "string" "strings"}}
<!-- "strings" -->
{{inflect 1 "string" "strings"}}
<!-- "string" -->
{{inflect 1 "string" "strings" true}}
<!-- "1 string" -->
{{inflect 2 "string" "strings"}}
<!-- "strings" -->
{{inflect 2 "string" "strings" true}}
<!-- "2 strings" -->

{{ordinalize}}

Returns an ordinalized number as a string.

Params

  • val {String}: The value to ordinalize.
  • returns {String}: The ordinalized number

Example

{{ordinalize 1}}
<!-- '1st' -->
{{ordinalize 21}}
<!-- '21st' -->
{{ordinalize 29}}
<!-- '29th' -->
{{ordinalize 22}}
<!-- '22nd' -->

logging

logging-helpers.

markdown

{{markdown}}

Block helper that converts a string of inline markdown to HTML.

Params

  • context {Object}
  • options {Object}
  • returns {String}

Example

{{#markdown}}
# Foo
{{/markdown}}
<!-- results in: <h1>Foo</h1> -->

{{md}}

Read a markdown file from the file system and inject its contents after converting it to HTML.

Params

  • context {Object}
  • options {Object}
  • returns {String}

Example

{{md "foo/bar.md"}}

match

{{match}}

Returns an array of strings that match the given glob pattern(s). Options may be passed on the options hash or locals.

Params

  • files {Array|String}
  • patterns {Array|String}: One or more glob patterns.
  • locals {Object}
  • options {Object}
  • returns {Array}: Array of matches

Example

{{match (readdir "foo") "*.js"}}
{{match (readdir "foo") (toRegex "\\.js$")}}

{{isMatch}}

Returns true if a filepath contains the given pattern. Options may be passed on the options hash or locals.

Params

  • filepath {String}
  • pattern {String}
  • options {Object}
  • returns {Boolean}

Example

{{isMatch "foo.md" "*.md"}}
<!-- results in: true -->

math

{{abs}}

Return the magnitude of a.

Params

  • a {Number}
  • returns {Number}

{{add}}

Return the sum of a plus b.

Params

  • a {Number}
  • b {Number}
  • returns {Number}

{{avg}}

Returns the average of all numbers in the given array.

Params

  • array {Array}: Array of numbers to add up.
  • returns {Number}

Example

{{avg "[1, 2, 3, 4, 5]"}}
<!-- results in: '3' -->

{{ceil}}

Get the Math.ceil() of the given value.

Params

  • value {Number}
  • returns {Number}

{{divide}}

Divide a by b

Params

  • a {Number}: numerator
  • b {Number}: denominator

{{floor}}

Get the Math.floor() of the given value.

Params

  • value {Number}
  • returns {Number}

{{minus}}

Return the difference of a minus b.

Params

  • a {Number}
  • b {Number}

{{modulo}}

Get the remainder of a division operation.

Params

  • a {Number}
  • b {Number}
  • returns {Number}

{{multiply}}

Return the product of a times b.

Params

  • a {Number}: factor
  • b {Number}: multiplier
  • returns {Number}

{{plus}}

Add a by b.

Params

  • a {Number}: factor
  • b {Number}: multiplier

{{random}}

Generate a random number between two values

Params

  • min {Number}
  • max {Number}
  • returns {String}

{{remainder}}

Get the remainder when a is divided by b.

Params

  • a {Number}: a
  • b {Number}: b

{{round}}

Round the given number.

Params

  • number {Number}
  • returns {Number}

{{subtract}}

Return the product of a minus b.

Params

  • a {Number}
  • b {Number}
  • returns {Number}

{{sum}}

Returns the sum of all numbers in the given array.

Params

  • array {Array}: Array of numbers to add up.
  • returns {Number}

Example

{{sum "[1, 2, 3, 4, 5]"}}
<!-- results in: '15' -->

{{times}}

Multiply number a by number b.

Params

  • a {Number}: factor
  • b {Number}: multiplier
  • returns {Number}

misc

{{option}}

Return the given value of prop from this.options.

Params

  • prop {String}
  • returns {any}

Example

<!-- context = {options: {a: {b: {c: 'ddd'}}}} -->
{{option "a.b.c"}}
<!-- results => `ddd` -->

{{noop}}

Block helper that renders the block without taking any arguments.

Params

  • options {Object}
  • returns {String}

{{typeOf}}

Get the native type of the given value

Params

  • value {any}
  • returns {String}: Returns the type of value.

Example

{{typeOf 1}}
//=> 'number'
{{typeOf "1"}}
//=> 'string'
{{typeOf "foo"}}
//=> 'string'

{{withHash}}

Block helper that builds the context for the block from the options hash.

Params

  • options {Object}: Handlebars provided options object.

number

{{bytes}}

Format a number to it's equivalent in bytes. If a string is passed, it's length will be formatted and returned.

Examples:

  • 'foo' => 3 B
  • 13661855 => 13.66 MB
  • 825399 => 825.39 kB
  • 1396 => 1.4 kB

Params

  • number {Number|String}
  • returns {String}

{{addCommas}}

Add commas to numbers

Params

  • num {Number}
  • returns {Number}

{{phoneNumber}}

Convert a string or number to a formatted phone number.

Params

  • num {Number|String}: The phone number to format, e.g. 8005551212
  • returns {Number}: Formatted phone number: (800) 555-1212

{{toAbbr}}

Abbreviate numbers to the given number of precision. This is for general numbers, not size in bytes.

Params

  • number {Number}
  • precision {Number}
  • returns {String}

{{toExponential}}

Returns a string representing the given number in exponential notation.

Params

  • number {Number}
  • fractionDigits {Number}: Optional. An integer specifying the number of digits to use after the decimal point. Defaults to as many digits as necessary to specify the number.
  • returns {Number}

Example

{{toExponential number digits}};

{{toFixed}}

Formats the given number using fixed-point notation.

Params

  • number {Number}
  • digits {Number}: (Optional) The number of digits to appear after the decimal point; this may be a value between 0 and 20. If this argument is omitted, it is treated as 0.
  • returns {String}: A string representing the given number using fixed-point notation.

Example

{{toFixed "1.1234" 2}}
//=> '1.12'

{{toFloat}}

Params

  • number {Number}
  • returns {Number}

{{toInt}}

Params

  • number {Number}
  • returns {Number}

{{toPrecision}}

Returns a string representing the Number object to the specified precision.

Params

  • number {Number}
  • precision {Number}: (Optional) An integer specifying the number of significant digits. If precison is not between 1 and 100 (inclusive), it will be coerced to 0.
  • returns {String}: A string representing a Number object in fixed-point or exponential notation rounded to precision significant digits.

Example

{{toPrecision "1.1234" 2}}
//=> '1.1'

object

{{extend}}

Extend the context with the properties of other objects. A shallow merge is performed to avoid mutating the context.

Params

  • objects {Object}: One or more objects to extend.
  • returns {Object}

{{forIn}}

Block helper that iterates over the properties of an object, exposing each key and value on the context.

Params

  • context {Object}
  • options {Object}
  • returns {String}

{{forOwn}}

Block helper that iterates over the own properties of an object, exposing each key and value on the context.

Params

  • obj {Object}: The object to iterate over.
  • options {Object}
  • returns {String}

{{toPath}}

Take arguments and, if they are string or number, convert them to a dot-delineated object property path.

Params

  • prop {String|Number}: The property segments to assemble (can be multiple).
  • returns {String}

{{get}}

Use property paths (a.b.c) to get a value or nested value from the context. Works as a regular helper or block helper.

Params

  • prop {String}: The property to get, optionally using dot notation for nested properties.
  • context {Object}: The context object
  • options {Object}: The handlebars options object, if used as a block helper.
  • returns {String}

{{getObject}}

Use property paths (a.b.c) to get an object from the context. Differs from the get helper in that this helper will return the actual object, including the given property key. Also, this helper does not work as a block helper.

Params

  • prop {String}: The property to get, optionally using dot notation for nested properties.
  • context {Object}: The context object
  • returns {String}

{{hasOwn}}

Return true if key is an own, enumerable property of the given context object.

Params

  • key {String}
  • context {Object}: The context object.
  • returns {Boolean}

Example

{{hasOwn context key}}

{{isObject}}

Return true if value is an object.

Params

  • value {String}
  • returns {Boolean}

Example

{{isObject "foo"}}
//=> false

{{JSONparse}}

Parses the given string using JSON.parse.

Params

  • string {String}: The string to parse

Example

<!-- string: '{"foo": "bar"}' -->
{{JSONparse string}}
<!-- results in: { foo: 'bar' } -->

{{JSONstringify}}

Stringify an object using JSON.stringify.

Params

  • obj {Object}: Object to stringify
  • returns {String}

Example

<!-- object: { foo: 'bar' } -->
{{JSONstringify object}}
<!-- results in: '{"foo": "bar"}' -->

{{merge}}

Deeply merge the properties of the given objects with the context object.

Params

  • object {Object}: The target object. Pass an empty object to shallow clone.
  • objects {Object}
  • returns {Object}

{{pick}}

Pick properties from the context object.

Params

  • properties {Array|String}: One or more properties to pick.
  • context {Object}
  • options {Object}: Handlebars options object.
  • returns {Object}: Returns an object with the picked values. If used as a block helper, the values are passed as context to the inner block. If no values are found, the context is passed to the inverse block.

path

{{absolute}}

Get the directory path segment from the given filepath.

Params

  • ext {String}
  • returns {String}

Example

{{absolute "docs/toc.md"}}
<!-- results in: 'docs' -->

{{dirname}}

Get the directory path segment from the given filepath.

Params

  • ext {String}
  • returns {String}

Example

{{dirname "docs/toc.md"}}
<!-- results in: 'docs' -->

{{relative}}

Get the relative filepath from a to b.

Params

  • a {String}
  • b {String}
  • returns {String}

Example

{{relative a b}}

{{basename}}

Get the file extension from the given filepath.

Params

  • ext {String}
  • returns {String}

Example

{{basename "docs/toc.md"}}
<!-- results in: 'toc.md' -->

{{stem}}

Get the "stem" from the given filepath.

Params

  • filepath {String}
  • returns {String}

Example

{{stem "docs/toc.md"}}
<!-- results in: 'toc' -->

{{extname}}

Get the file extension from the given filepath.

Params

  • filepath {String}
  • returns {String}

Example

{{extname "docs/toc.md"}}
<!-- results in: '.md' -->

{{resolve}}

Resolve an absolute path from the given filepath.

Params

  • filepath {String}
  • returns {String}

Example

{{resolve "docs/toc.md"}}
<!-- results in: '/User/dev/docs/toc.md' -->

{{segments}}

Get specific (joined) segments of a file path by passing a range of array indices.

Params

  • filepath {String}: The file path to split into segments.
  • returns {String}: Returns a single, joined file path.

Example

{{segments "a/b/c/d" "2" "3"}}
<!-- results in: 'c/d' -->

{{segments "a/b/c/d" "1" "3"}}
<!-- results in: 'b/c/d' -->

{{segments "a/b/c/d" "1" "2"}}
<!-- results in: 'b/c' -->

regex

{{toRegex}}

Convert the given string to a regular expression.

Params

  • str {String}
  • returns {RegExp}

Example

{{toRegex "foo"}}
<!-- results in: /foo/ -->

{{test}}

Returns true if the given str matches the given regex. A regex can be passed on the context, or using the toRegex helper as a subexpression.

Params

  • str {String}
  • returns {RegExp}

Example

{{test "bar" (toRegex "foo")}}
<!-- results in: false -->
{{test "foobar" (toRegex "foo")}}
<!-- results in: true -->
{{test "foobar" (toRegex "^foo$")}}
<!-- results in: false -->

string

{{append}}

Append the specified suffix to the given string.

Params

  • str {String}
  • suffix {String}
  • returns {String}

Example

<!-- given that "item.stem" is "foo" -->
{{append item.stem ".html"}}
<!-- results in:  'foo.html' -->

{{camelcase}}

camelCase the characters in the given string.

Params

  • string {String}: The string to camelcase.
  • returns {String}

Example

{{camelcase "foo bar baz"}};
<!-- results in:  'fooBarBaz' -->

{{capitalize}}

Capitalize the first word in a sentence.

Params

  • str {String}
  • returns {String}

Example

{{capitalize "foo bar baz"}}
<!-- results in:  "Foo bar baz" -->

{{capitalizeAll}}

Capitalize all words in a string.

Params

  • str {String}
  • returns {String}

Example

{{capitalizeAll "foo bar baz"}}
<!-- results in:  "Foo Bar Baz" -->

{{center}}

Center a string using non-breaking spaces

Params

  • str {String}
  • spaces {String}
  • returns {String}

{{chop}}

Like trim, but removes both extraneous whitespace and non-word characters from the beginning and end of a string.

Params

  • string {String}: The string to chop.
  • returns {String}

Example

{{chop "_ABC_"}}
<!-- results in:  'ABC' -->

{{chop "-ABC-"}}
<!-- results in:  'ABC' -->

{{chop " ABC "}}
<!-- results in:  'ABC' -->

{{dashcase}}

dash-case the characters in string. Replaces non-word characters and periods with hyphens.

Params

  • string {String}
  • returns {String}

Example

{{dashcase "a-b-c d_e"}}
<!-- results in:  'a-b-c-d-e' -->

{{dotcase}}

dot.case the characters in string.

Params

  • string {String}
  • returns {String}

Example

{{dotcase "a-b-c d_e"}}
<!-- results in:  'a.b.c.d.e' -->

{{downcase}}

Lowercase all of the characters in the given string. Alias for lowercase.

Params

  • string {String}
  • returns {String}

Example

{{downcase "aBcDeF"}}
<!-- results in:  'abcdef' -->

{{ellipsis}}

Truncates a string to the specified length, and appends it with an elipsis, .

Params

  • str {String}
  • length {Number}: The desired length of the returned string.
  • returns {String}: The truncated string.

Example

{{ellipsis (sanitize "<span>foo bar baz</span>"), 7}}
<!-- results in:  'foo bar…' -->
{{ellipsis "foo bar baz", 7}}
<!-- results in:  'foo bar…' -->

{{hyphenate}}

Replace spaces in a string with hyphens.

Params

  • str {String}
  • returns {String}

Example

{{hyphenate "foo bar baz qux"}}
<!-- results in:  "foo-bar-baz-qux" -->

{{isString}}

Return true if value is a string.

Params

  • value {String}
  • returns {Boolean}

Example

{{isString "foo"}}
<!-- results in:  'true' -->

{{lowercase}}

Lowercase all characters in the given string.

Params

  • str {String}
  • returns {String}

Example

{{lowercase "Foo BAR baZ"}}
<!-- results in:  'foo bar baz' -->

{{occurrences}}

Return the number of occurrences of substring within the given string.

Params

  • str {String}
  • substring {String}
  • returns {Number}: Number of occurrences

Example

{{occurrences "foo bar foo bar baz" "foo"}}
<!-- results in:  2 -->

{{pascalcase}}

PascalCase the characters in string.

Params

  • string {String}
  • returns {String}

Example

{{pascalcase "foo bar baz"}}
<!-- results in:  'FooBarBaz' -->

{{pathcase}}

path/case the characters in string.

Params

  • string {String}
  • returns {String}

Example

{{pathcase "a-b-c d_e"}}
<!-- results in:  'a/b/c/d/e' -->

{{plusify}}

Replace spaces in the given string with pluses.

Params

  • str {String}: The input string
  • returns {String}: Input string with spaces replaced by plus signs

Example

{{plusify "foo bar baz"}}
<!-- results in:  'foo+bar+baz' -->

{{prepend}}

Prepends the given string with the specified prefix.

Params

  • str {String}
  • prefix {String}
  • returns {String}

Example

<!-- given that "val" is "bar" -->
{{prepend val "foo-"}}
<!-- results in:  'foo-bar' -->

{{raw}}

Render a block without processing mustache templates inside the block.

Params

  • options {Object}
  • returns {String}

Example

{{{{#raw}}}}
{{foo}}
{{{{/raw}}}}
<!-- results in:  '{{foo}}' -->

{{remove}}

Remove all occurrences of substring from the given str.

Params

  • str {String}
  • substring {String}
  • returns {String}

Example

{{remove "a b a b a b" "a "}}
<!-- results in:  'b b b' -->

{{removeFirst}}

Remove the first occurrence of substring from the given str.

Params

  • str {String}
  • substring {String}
  • returns {String}

Example

{{remove "a b a b a b" "a"}}
<!-- results in:  ' b a b a b' -->

{{replace}}

Replace all occurrences of substring a with substring b.

Params

  • str {String}
  • a {String}
  • b {String}
  • returns {String}

Example

{{replace "a b a b a b" "a" "z"}}
<!-- results in:  'z b z b z b' -->

{{replaceFirst}}

Replace the first occurrence of substring a with substring b.

Params

  • str {String}
  • a {String}
  • b {String}
  • returns {String}

Example

{{replace "a b a b a b" "a" "z"}}
<!-- results in:  'z b a b a b' -->

{{reverse}}

Reverse a string.

Params

  • str {String}
  • returns {String}

Example

{{reverse "abcde"}}
<!-- results in:  'edcba' -->

{{sentence}}

Sentence case the given string

Params

  • str {String}
  • returns {String}

Example

{{sentence "hello world. goodbye world."}}
<!-- results in:  'Hello world. Goodbye world.' -->

{{snakecase}}

snake_case the characters in the given string.

Params

  • string {String}
  • returns {String}

Example

{{snakecase "a-b-c d_e"}}
<!-- results in:  'a_b_c_d_e' -->

{{split}}

Split string by the given character.

Params

  • string {String}: The string to split.
  • returns {String} character: Default is an empty string.

Example

{{split "a,b,c" ","}}
<!-- results in:  ['a', 'b', 'c'] -->

{{startsWith}}

Tests whether a string begins with the given prefix.

Params

  • prefix {String}
  • testString {String}
  • options {String}
  • returns {String}

Example

{{#startsWith "Goodbye" "Hello, world!"}}
  Whoops
{{else}}
  Bro, do you even hello world?
{{/startsWith}}

{{titleize}}

Title case the given string.

Params

  • str {String}
  • returns {String}

Example

{{titleize "this is title case"}}
<!-- results in:  'This Is Title Case' -->

{{trim}}

Removes extraneous whitespace from the beginning and end of a string.

Params

  • string {String}: The string to trim.
  • returns {String}

Example

{{trim " ABC "}}
<!-- results in:  'ABC' -->

{{trimLeft}}

Removes extraneous whitespace from the beginning of a string.

Params

  • string {String}: The string to trim.
  • returns {String}

Example

{{trim " ABC "}}
<!-- results in:  'ABC ' -->

{{trimRight}}

Removes extraneous whitespace from the end of a string.

Params

  • string {String}: The string to trim.
  • returns {String}

Example

{{trimRight " ABC "}}
<!-- results in:  ' ABC' -->

{{truncate}}

Truncate a string to the specified length. Also see ellipsis.

Params

  • str {String}
  • limit {Number}: The desired length of the returned string.
  • suffix {String}: Optionally supply a string to use as a suffix to denote when the string has been truncated. Otherwise an ellipsis () will be used.
  • returns {String}: The truncated string.

Example

truncate("foo bar baz", 7);
<!-- results in:  'foo bar' -->
truncate(sanitize("<span>foo bar baz</span>", 7));
<!-- results in:  'foo bar' -->

{{truncateWords}}

Truncate a string to have the specified number of words. Also see truncate.

Params

  • str {String}
  • limit {Number}: The desired length of the returned string.
  • suffix {String}: Optionally supply a string to use as a suffix to denote when the string has been truncated.
  • returns {String}: The truncated string.

Example

truncateWords("foo bar baz", 1);
<!-- results in:  'foo…' -->
truncateWords("foo bar baz", 2);
<!-- results in:  'foo bar…' -->
truncateWords("foo bar baz", 3);
<!-- results in:  'foo bar baz' -->

{{upcase}}

Uppercase all of the characters in the given string. Alias for uppercase.

Params

  • string {String}
  • returns {String}

Example

{{upcase "aBcDeF"}}
<!-- results in:  'ABCDEF' -->

{{uppercase}}

Uppercase all of the characters in the given string. If used as a block helper it will uppercase the entire block. This helper does not support inverse blocks.

Params

  • str {String}: The string to uppercase
  • options {Object}: Handlebars options object
  • returns {String}

Example

{{uppercase "aBcDeF"}}
<!-- results in:  'ABCDEF' -->

url

{{encodeURI}}

Encodes a Uniform Resource Identifier (URI) component by replacing each instance of certain characters by one, two, three, or four escape sequences representing the UTF-8 encoding of the character.

Params

  • str {String}: The un-encoded string
  • returns {String}: The endcoded string

{{escape}}

Escape the given string by replacing characters with escape sequences. Useful for allowing the string to be used in a URL, etc.

Params

  • str {String}
  • returns {String}: Escaped string.

{{decodeURI}}

Decode a Uniform Resource Identifier (URI) component.

Params

  • str {String}
  • returns {String}

{{url_encode}}

Alias for encodeURI.

{{url_decode}}

Alias for decodeURI.

{{urlResolve}}

Take a base URL, and a href URL, and resolve them as a browser would for an anchor tag.

Params

  • base {String}
  • href {String}
  • returns {String}

{{urlParse}}

Parses a url string into an object.

Params

  • str {String}: URL string
  • returns {String}: Returns stringified JSON

{{stripQuerystring}}

Strip the query string from the given url.

Params

  • url {String}
  • returns {String}: the url without the queryString

{{stripProtocol}}

Strip protocol from a url. Useful for displaying media that may have an 'http' protocol on secure connections.

Params

  • str {String}
  • returns {String}: the url with http protocol stripped

Example

<!-- url = 'http://foo.bar' -->
{{stripProtocol url}}
<!-- results in: '//foo.bar' -->

Utils

The following utils are exposed on .utils.

{{changecase}}

Change casing on the given string, optionally passing a delimiter to use between words in the returned string.

Params

  • string {String}: The string to change.
  • returns {String}

Example

utils.changecase('fooBarBaz');
//=> 'foo bar baz'

utils.changecase('fooBarBaz' '-');
//=> 'foo-bar-baz'

{{random}}

Generate a random number

Params

  • min {Number}
  • max {Number}
  • returns {Number}

History

v0.10.0 - 2017-11-17

changes

  • adds unique to array helpers
  • updates css helper to ensure that path.join() is not called on an absolute URL.

v0.9.0 - 2017-07-03

changes

  • all unit tests now use assert instead of should
  • remove fileSize helper in favor of new bytes helper, which does the same thing, but returns B instead of byte or bytes.
  • JSONParse helper is no longer a block helper. It now returns an object, which can be used as a subexpression to achieve the same behavior as before.
  • adds better error handling for path helpers, since node.js errors are terrible. We have a better way to handle errors that will be implemented in a near future release.
  • adds inline helper support to isEmpty, so it can now be used as an inline or block helper
  • adds raw helper
  • adds regex helpers
  • adds inline helper support to most of the comparison helpers, so they can now be used as inline or block helpers
  • adds pluck helper to array helpers
  • adds prepend and append helpers to string helpers
  • adds isTruthy and isFalsey comparison helpers
  • adds escape and url_encode and url_decode URL helpers
  • adds attr helper to html helpers
  • adds year helper to date helpers
  • adds typeOf and frame helpers to misc helpers
  • adds abs, minus, modulo, plus, times to math helpers
  • moves ellipsis helper from html helpers to string helpers
  • moves truncate helper from html helpers to string helpers
  • moves reverse helper from string helpers to array helpers
  • differentiate eq and is helpers so that eq is strict equality and is is not
  • removes mm helper, use match instead

v0.8.4 - 2017-07-03

changes

  • removes strlen helper in favor of fixing the length helper

v0.8.3 - 2017-07-03

changes

  • adds strlen helper
  • adds itemAt helper
  • clean up code comments for array helpers

v0.8.2 - 2017-03-30

changes

  • documentation updates
  • fixes md helper to use sync by default

v0.8.1 - 2017-03-30

changes

v0.8.0 - 2017-01-25

changes

  • handle string arguments in list helpers
  • adds JSONParse helper as an alias for parseJSON

v0.7.6 - 2017-01-08

changes

v0.7.0 - 2016-07-16

changes

  • The or helper can now take a variable number of arguments

v0.6.0 - 2016-05-13

changes

  • the main export is now a function that takes a name or array of names of helper types to load. Example helpers(['string', 'array']) will load only the string and array helpers
  • helper types can alternatively be accessed as methods. example - helpers.path() will return all of the path helpers.
  • handlebars may be provided by the user. if not provided it will fall back to the handlebars-helpers handlebars
  • helpers are now as generic as possible, with little to no code related to assemble, grunt, etc.
  • helpers are lazy-loaded using getters for improved performance
  • Once tests are added for the md and markdown helpers, we'll have 100% unit test coverage on helpers

v0.3.3 - 2013-09-03

changes

  • Adds fileSize helper.
  • Adds startsWith helper.

v0.3.2 - 2013-08-20

changes

  • Adds glob helper.

v0.3.0 - 2013-07-30

changes

  • The project has been refactored, cleaned up, and full documentation has bee put up at http://assemble.io

v0.2.4 - 2013-05-11

changes

  • Adding object globbing utility functions to be used in helpers later.

v0.2.3 - 2013-05-11

changes

  • File globbing added to some helpers. Including md and some file helpers.

v0.2.0 - 2013-05-07

changes

  • A bunch of new tests for markdown and special helpers.
  • Refactored most of the rest of the helpers to separate functions from Handlebars registration.

v0.1.32 - 2013-05-02

changes

  • Updates utils and a number of helpers, including value, property, and stringify.

v0.1.31 - 2013-04-21

changes

  • Fixes relative helper

v0.1.30 - 2013-04-20

changes

  • Refactoring helpers-collection module to separate the functions from the Handlebars helper registration process.

v0.1.25 - 2013-04-16

changes

  • Adding defineSection and renderSection helpers to try to get sections populated in a layout from the page.

v0.1.21 - 2013-04-07

changes

  • Add markdown helpers back, add more tests.

v0.1.20 - 2013-04-06

changes

  • Generalized helpers structure, externalized utilities.

v0.1.11 - 2013-04-05

changes

  • New authors and gist helpers, general cleanup and new tests.

v0.1.10 - 2013-04-04

changes

  • Externalized utility javascript from helpers.js

v0.1.8 - 2013-03-28

changes

  • Gruntfile updated with mocha tests for 71 helpers, bug fixes.

v0.1.7 - 2013-03-18

changes

  • New path helper 'relative', for resolving relative path from one absolute path to another.

v0.1.3 - 2013-03-16

changes

  • New helpers, 'formatPhoneNumber' and 'eachProperty'

v0.1.2 - 2013-03-15

changes

  • Update README.md with documentation, examples.

[v0.1.0] - 2013-03-06

changes

  • First commit.

About

Related projects

  • assemble: Get the rocks out of your socks! Assemble makes you fast at creating web projects… more | homepage
  • template-helpers: Generic JavaScript helpers that can be used with any template engine. Handlebars, Lo-Dash, Underscore, or… more | homepage
  • utils: Fast, generic JavaScript/node.js utility functions. | homepage

Contributing

Pull requests and stars are always welcome. For bugs and feature requests, please create an issue.

Contributors

Release history

Building docs

(This project's readme.md is generated by verb, please don't edit the readme directly. Any changes to the readme must be made in the .verb.md readme template.)

To generate the readme, run the following command:

$ npm install -g verbose/verb#dev verb-generate-readme && verb

Running tests

Running and reviewing unit tests is a great way to get familiarized with a library and its API. You can install dependencies and run tests with the following command:

$ npm install && npm test

Author: Helpers
Source Code: https://github.com/helpers/handlebars-helpers 
License: MIT License

#node #javascript #templates 

Handlebars-helpers: More than 130 Handlebars helpers in ~20 categories
Desmond  Gerber

Desmond Gerber

1646620200

Email-templates: Create, Preview, and Send Custom Email Templates

Email Templates

❤️ Love this project? Support @niftylettuce's FOSS on Patreon or PayPal 🦄

Create, preview, and send custom email templates for Node.js. Highly configurable and supports automatic inline CSS, stylesheets, embedded images and fonts, and much more! Made for sending beautiful emails with Lad.

Table of Contents

Install

By default we recommend pug for your template engine, but you can use any template engine.

npm:

npm install email-templates pug

yarn:

yarn add email-templates pug

Preview

We've added preview-email by default to this package!

This means that (by default) in the development environment (e.g. NODE_ENV=development) your emails will be rendered to the tmp directory for you and automatically opened in the browser.

If you have trouble previewing emails in your browser, you can configure a preview option which gets passed along to open's options (e.g. preview: { open: { app: 'firefox' } }).

See the example below for Open Email Previews in Firefox.

Usage

Debugging

Environment Flag

If you run into any issues with configuration, files, templates, locals, etc, then you can use the DEBUG environment flag:

DEBUG=email-templates node app.js

This will output to the console all debug statements in our codebase for this package.

Inspect Message

As of v3.6.1 you can now inspect the message passed to nodemailer.sendMail internally.

In the response object from email.send, you have access to res.originalMessage:

email
  .send({
    template: 'mars',
    message: {
      to: 'elon@spacex.com'
    },
    locals: {
      name: 'Elon'
    }
  })
  .then(res => {
    console.log('res.originalMessage', res.originalMessage)
  })
  .catch(console.error);

Basic

You can swap the transport option with a Nodemailer transport configuration object or transport instance. We highly recommend using Postmark for your transport (it's the default in Lad).

If you want to send emails in development or test environments, set options.send to true.

const Email = require('email-templates');

const email = new Email({
  message: {
    from: 'niftylettuce@gmail.com'
  },
  // uncomment below to send emails in development/test env:
  // send: true
  transport: {
    jsonTransport: true
  }
});

email
  .send({
    template: 'mars',
    message: {
      to: 'elon@spacex.com'
    },
    locals: {
      name: 'Elon'
    }
  })
  .then(console.log)
  .catch(console.error);

The example above assumes you have the following directory structure:

.
├── app.js
└── emails
    └── mars
        ├── html.pug
        └── subject.pug

And the contents of the pug files are:

html.pug:

p Hi #{name}, p Welcome to Mars, the red planet.

subject.pug:

= `Hi ${name}, welcome to Mars`

Attachments

Please reference Nodemailer's attachment documentation for further reference.

If you want to set default attachments sent with every email:

const Email = require('email-templates');

const email = new Email({
  message: {
    from: 'niftylettuce@gmail.com',
    attachments: [
      {
        filename: 'text1.txt',
        content: 'hello world!'
      }
    ]
  }
});

email
  .send({
    template: 'mars',
    message: {
      to: 'elon@spacex.com'
    },
    locals: {
      name: 'Elon'
    }
  })
  .then(console.log)
  .catch(console.error);

If you want to set attachments sent individually:

const Email = require('email-templates');

const email = new Email({
  message: {
    from: 'niftylettuce@gmail.com'
  },
  transport: {
    jsonTransport: true
  }
});

email
  .send({
    template: 'mars',
    message: {
      to: 'elon@spacex.com',
      attachments: [
        {
          filename: 'text1.txt',
          content: 'hello world!'
        }
      ]
    },
    locals: {
      name: 'Elon'
    }
  })
  .then(console.log)
  .catch(console.error);

Automatic Inline CSS via Stylesheets

Simply include the path or URL to the stylesheet in your template's <head>:

link(rel="stylesheet", href="/css/app.css", data-inline)

This will look for the file /css/app.css in the build/ folder.

If this asset is in another folder, then you will need to modify the default options when creating an Email instance:

const email = new Email({
  // <https://github.com/Automattic/juice>
  juice: true,
  // Override juice global settings <https://github.com/Automattic/juice#juicecodeblocks>
  juiceSettings: {
    tableElements: ['TABLE']
  },
  juiceResources: {
    preserveImportant: true,
    webResources: {
      //
      // this is the relative directory to your CSS/image assets
      // and its default path is `build/`:
      //
      // e.g. if you have the following in the `<head`> of your template:
      // `<link rel="stylesheet" href="style.css" data-inline="data-inline">`
      // then this assumes that the file `build/style.css` exists
      //
      relativeTo: path.resolve('build')
      //
      // but you might want to change it to something like:
      // relativeTo: path.join(__dirname, '..', 'assets')
      // (so that you can re-use CSS/images that are used in your web-app)
      //
    }
  }
});

Render HTML and/or Text

If you don't need this module to send your email, you can still use it to render HTML and/or text templates.

Simply use the email.render(view, locals) method we expose (it's the same method that email.send uses internally).

If you need to render a specific email template file (e.g. the HTML version):

const Email = require('email-templates');

const email = new Email();

email
  .render('mars/html', {
    name: 'Elon'
  })
  .then(console.log)
  .catch(console.error);

The example above assumes you have the following directory structure (note that this example would only render the html.pug file):

.
├── app.js
└── emails
    └── mars
        ├── html.pug
        ├── text.pug
        └── subject.pug

The Promise for email.render resolves with a String (the HTML or text rendered).

If you need pass juiceResources in render function, with this option you don't need create Email instance every time

const Email = require('email-templates');

const email = new Email();

email
  .render({
    path: 'mars/html',
    juiceResources: {
      preserveImportant: true,
      webResources: {
        // view folder path, it will get css from `mars/style.css`
        relativeTo: path.resolve('mars')
      }
    }
  }, {
    name: 'Elon'
  })
  .then(console.log)
  .catch(console.error);

The example above will be useful when you have a structure like this, this will be useful when you have a separate CSS file for every template

.
├── app.js
└── emails
    └── mars
        ├── html.pug
        ├── text.pug
        ├── subject.pug
        └── style.css

The Promise for email.render resolves with a String (the HTML or text rendered).

If you need to render all available template files for a given email template (e.g. html.pug, text.pug, and subject.pug – you can use email.renderAll (this is the method that email.send uses).

const Email = require('email-templates');

const email = new Email();

email
  .renderAll('mars', {
    name: 'Elon'
  })
  .then(console.log)
  .catch(console.error);

If you need to render multiple, specific templates at once (but not all email templates available), then you can use Promise.all in combination with email.render:

const Email = require('email-templates');

const email = new Email();
const locals = { name: 'Elon' };

Promise
  .all([
    email.render('mars/html', locals),
    email.render('mars/text', locals)
  ])
  .then(([ html, text ]) => {
    console.log('html', html);
    console.log('text', text);
  })
  .catch(console.error);

Cache Pug Templates

Out of the box, templates are cached as they are compiled (e.g. as emails are sent, the template they're using is cached). However these templates are not cached in-advance, so the first emails sent of each template will be slower to send.

We strongly suggest to pre-cache your templates with cache-pug-templates (if you're using the default Pug template engine).

If you do not do this, then your Pug templates will re-compile and re-cache every time you deploy new code and restart your app.

Note that you will need to specify the views option to your new CachePugTemplates({ views: '...' }); instance, with views being a file path (Array or String) to your email template directory. See cache-pug-templates documentation for more information.

Localization

All you need to do is simply pass an i18n configuration object as config.i18n (or an empty one as this example shows to use defaults).

Don't want to handle localization and translation yourself? Just use Lad – it's built in and uses mandarin (with automatic Google Translate support) under the hood!

const Email = require('email-templates');

const email = new Email({
  message: {
    from: 'niftylettuce@gmail.com'
  },
  transport: {
    jsonTransport: true
  },
  i18n: {} // <------ HERE
});

email
  .send({
    template: 'mars',
    message: {
      to: 'elon@spacex.com'
    },
    locals: {
      locale: 'en', // <------ CUSTOMIZE LOCALE HERE (defaults to `i18n.defaultLocale` - `en`)
      // is your user french?
      // locale: 'fr',
      name: 'Elon'
    }
  })
  .then(console.log)
  .catch(console.error);

Then slightly modify your templates to use localization functions.

html.pug:

p= `${t('Hi')} ${name},`
p= t('Welcome to Mars, the red planet.')

subject.pug:

p= `${t('Hi')} ${name}, ${t('welcome to Mars')}`

Note that if you use Lad, you have a built-in filter called translate:

p: :translate(locale) Welcome to Mars, the red planet.

Localization using Handlebars template engine

If you are using handlebars and you are using localization files with named values, you will quickly see that there is no way to properly call the t function in your template and specify named values.

If, for example you have this in your translation file:

{
  "greetings": "Hi {{ firstname }}",
  "welcome_message": "Welcome to Mars, the red planet."
}

And you would like to use it in your template like this:

html.hbs:

<p>{{ t "greetings" firstname="Marcus" }}</p>
<p>{{ t "welcome_message" }}</p>

This would not work because the second argument sent by handlebars to the function would be a handlebar helper options object instead of just the named values.

A possible workaround you can use is to introduce your own translation helper in your template locals:

email
  .send({
    template: 'mars',
    message: {
      to: 'elon@spacex.com'
    },
    locals: {
      locale: 'en', // <------ CUSTOMIZE LOCALE HERE (defaults to `i18n.defaultLocale` - `en`)
      // is your user french?
      // locale: 'fr',
      name: 'Elon',
      $t(key, options) {
        // <------ THIS IS OUR OWN TRANSLATION HELPER
        return options.data.root.t(
          { phrase: key, locale: options.data.root.locale },
          options.hash
        );
      }
    }
  })
  .then(console.log)
  .catch(console.error);

Then slightly modify your templates to use your own translation helper functions.

html.hbs:

<p>{{ $t "greetings" firstname="Marcus" }}</p>
<p>{{ $t "welcome_message" }}</p>

Text-Only Email (no HTML)

If you wish to have only a text-based version of your email you can simply pass the option textOnly: true.

Regardless if you use the htmlToText option or not (see next example), it will still render only a text-based version.

const Email = require('email-templates');

const email = new Email({
  message: {
    from: 'niftylettuce@gmail.com'
  },
  transport: {
    jsonTransport: true
  },
  textOnly: true // <----- HERE
});

email
  .send({
    template: 'mars',
    message: {
      to: 'elon@spacex.com'
    },
    locals: {
      name: 'Elon'
    }
  })
  .then(console.log)
  .catch(console.error);

Prefix Subject Lines

You can pass an option to prefix subject lines with a string, which is super useful for deciphering development / staging / production environment emails.

For example, you could make it so on non-production environments the email is prefixed with a [DEVELOPMENT] Some Subject Line Here.

You could do this manually by passing a message.subject property, however if you are storing your subject lines in templates (e.g. subject.ejs or subject.pug) then it's not as easy.

Simply use the subjectPrefix option and set it to whatever you wish (note you will need to append a trailing space if you wish to have a space after the prefix; see example below):

const Email = require('email-templates');

const env = process.env.NODE_ENV || 'development';

const email = new Email({
  message: {
    from: 'niftylettuce@gmail.com'
  },
  transport: {
    jsonTransport: true
  },
  subjectPrefix: env === 'production' ? false : `[${env.toUpperCase()}] `; // <--- HERE
});

Custom Text Template

By default we use html-to-text to generate a plaintext version and attach it as message.text.

If you'd like to customize the text body, you can pass message.text or create a text template file just like you normally would for html and subject.

You may also set config.htmlToText: false to force the usage of the text template file.

const Email = require('email-templates');

const email = new Email({
  message: {
    from: 'niftylettuce@gmail.com'
  },
  transport: {
    jsonTransport: true
  },
  htmlToText: false // <----- HERE
});

email
  .send({
    template: 'mars',
    message: {
      to: 'elon@spacex.com'
    },
    locals: {
      name: 'Elon'
    }
  })
  .then(console.log)
  .catch(console.error);

text.pug:

| Hi #{name},
| Welcome to Mars, the red planet.

Custom Template Engine (e.g. EJS)

Install your desired template engine (e.g. EJS)

npm:

npm install ejs

yarn:

yarn add ejs

Set the extension in options and send an email

const Email = require('email-templates');

const email = new Email({
  message: {
    from: 'niftylettuce@gmail.com'
  },
  transport: {
    jsonTransport: true
  },
  views: {
    options: {
      extension: 'ejs' // <---- HERE
    }
  }
});

Custom Default Message Options

You can configure your Email instance to have default message options, such as a default "From", an unsubscribe header, etc.

For a list of all available message options and fields see the Nodemailer message reference.

Here's an example showing how to set a default custom header and a list unsubscribe header:

const Email = require('email-templates');

const email = new Email({
  message: {
    from: 'niftylettuce@gmail.com',
    headers: {
      'X-Some-Custom-Thing': 'Some-Value'
    },
    list: {
      unsubscribe: 'https://niftylettuce.com/unsubscribe'
    }
  },
  transport: {
    jsonTransport: true
  }
});

Custom Rendering (e.g. from a MongoDB database)

You can pass a custom config.render function which accepts two arguments view and locals and must return a Promise.

Note that if you specify a custom config.render, you should have it use email.juiceResources before returning the final HTML. The example below shows how to do this.

If you wanted to read a stored EJS template from MongoDB, you could do something like:

const ejs = require('ejs');

const email = new Email({
  // ...
  render: (view, locals) => {
    return new Promise((resolve, reject) => {
      // this example assumes that `template` returned
      // is an ejs-based template string
      // view = `${template}/html` or `${template}/subject` or `${template}/text`
      db.templates.findOne({ name: view }, (err, template) => {
        if (err) return reject(err);
        if (!template) return reject(new Error('Template not found'));
        let html = ejs.render(template, locals);
        html = await email.juiceResources(html);
        resolve(html);
      });
    });
  }
});

Absolute Path to Templates

As of v5.0.1+ we now support passing absolute paths to templates for rendering (per discussion in #320.

For both email.send and email.render, the template option passed can be a relative path or absolute:

Relative example:

email
  .send({
    template: 'mars',
    message: {
      to: 'elon@spacex.com'
    },
    locals: {
      name: 'Elon'
    }
  })
  .then(console.log)
  .catch(console.error);

Absolute example:

const path = require('path');

// ...

email
  .send({
    template: path.join(__dirname, 'some', 'folder', 'mars')
    message: {
      to: 'elon@spacex.com'
    },
    locals: {
      name: 'Elon'
    }
  })
  .then(console.log)
  .catch(console.error);

Open Email Previews in Firefox

The preview option can be a custom Object of options to pass along to open's options.

Firefox example:

const email = new Email({
  // ...
  preview: {
    open: {
      app: 'firefox',
      wait: false
    }
  }
});

Options

For a list of all available options and defaults view the configuration object, or reference the list below:

  • views (Object)
    • root (String) - defaults to the current working directory's "emails" folder via path.resolve('emails')
    • options (Object)
      • extension (String) - defaults to 'pug', and is the default file extension for templates
      • map (Object) - a template file extension mapping, defaults to { hbs: 'handlebars', njk: 'nunjucks' } (this is useful if you use different file extension naming conventions)
      • engineSource (Object) - the default template engine source, defaults to consolidate
    • locals (Object) - locals to pass to templates for rendering
      • cache (Boolean) - defaults to false for development and test environments, and true for all others (via process.env.NODE_ENV), whether or not to cache templates
      • pretty (Boolean) - defaults to true, but is automatically set to false for subject templates and text-based emails
  • message (Object) - default Nodemailer message object for messages to inherit (defaults to an empty object {})
  • send (Boolean) - whether or not to send emails, defaults to false for development and test environments, and true for all others (via process.env.NODE_ENV) (NOTE: IF YOU ARE NOT USING NODE_ENV YOU WILL NEED TO MANUALLY SET THIS TO true)
  • preview (Boolean or Object) - whether or not to preview emails using preview-email, defaults to false unless the environment is development (via process.env.NODE_ENV)
  • i18n (Boolean or Object) - translation support for email templates, this accepts an I18N configuration object (defaults to false, which means it is disabled) which is passed along to @ladjs/i18n – see Localization example for more insight
  • render (Function) - defaults to a stable function that accepts two argument, view (String) and locals (Object) - you should not need to set this unless you have a need for custom rendering (see Custom Rendering (e.g. from a MongoDB database))
  • customRender (Boolean) - defaults to false, unless you pass your own render function, and in that case it will be automatically set to true
  • textOnly (Boolean) - whether or not to force text-only rendering of a template and disregard the template folder (defaults to false)
  • htmlToText (Object) - configuration object for html-to-text
    • ignoreImage (Boolean) - defaults to true
  • subjectPrefix (Boolean or String) - defaults to false, but if set to a string it will use that string as a prefix for your emails' subjects
  • juice (Boolean) - whether or not to use juice when rendering templates (defaults to true) (note that if you have a custom rendering function you will need to implement juice in it yourself)
  • juiceResources (Object) - options to pass to juice.juiceResources method (only used if juice option is set to true, see juice's API for more information
    • preserveImportant (Boolean) - defaults to true
    • webResources (Object) - an options object that will be passed to web-resource-inliner
      • relativeTo (String) - defaults to the current working directory's "build" folder via path.resolve('build') (NOTE: YOU SHOULD MODIFY THIS PATH TO WHERE YOUR BUILD/ASSETS FOLDER IS)
      • images (Boolean or Number) - defaults to false, and is whether or not to inline images unless they have an exclusion attribute (see web-resource-inliner for more insight), if it is set to a Number then that is used as the KB threshold
  • transport (Object) - a transport configuration object or a Nodemailer transport instance created via nodemailer.createTransport, defaults to an empty object {}, see Nodemailer transports documentation for more insight
  • getPath (Function) - a function that returns the path to a template file, defaults to function (type, template) { return path.join(template, type); }, and accepts three arguments type, template, and locals

Plugins

You can use any nodemailer plugin. Simply pass an existing transport instance as config.transport.

You should add the nodemailer-base64-to-s3 plugin to convert base64 inline images to actual images stored on Amazon S3 and Cloudfront.

When doing so (as of v4.0.2+), you will need to adjust your email-templates configuration to pass images: true as such:

const email = new Email({
  // ...
  juiceResources: {
    preserveImportant: true,
    webResources: {
      relativeTo: path.resolve('build'),
      images: true // <--- set this as `true`
    }
  }
});

We also highly recommend to add to your default config.locals the following:

Breaking Changes

See the Releases page for an up to date changelog.

v8.0.0

We upgraded html-to-text to v6. As a result, automatically generated text versions of your emails will look slightly different, as per the example below:

+Hi, + +email-templates rocks! + +Cheers, +The team -Hi,email-templates rocks! -Cheers,The team

v7.0.0

We upgraded preview-email to v2.0.0, which supports stream attachments, and additionally the view rendering is slightly different (we simply iterate over header lines and format them in a <pre><code> block). A major version bump was done due to the significant visual change in the preview rendering of emails.

v6.0.0

Performance should be significantly improved as the rendering of subject, html, and text parts now occurs asynchronously in parallel (previously it was in series and had blocking lookup calls).

We removed bluebird and replaced it with a lightweight alternative pify (since all we were using was the Promise.promisify method from bluebird as well).

This package now only supports Node v8.x+ (due to preview-email's open dependency requiring it).

Configuration for the preview option has slightly changed, which now allows you to specify a custom template and stylesheets for preview rendering.

If you were using a custom preview option before, you will need to change it slightly:

const email = new Email({
  // ...
  preview: {
+    open: {
+      app: 'firefox',
+      wait: false
+    }
-    app: 'firefox',
-    wait: false
  }
});

v5.0.0

In version 4.x+, we changed the order of defaults being set. See #313 for more information. This allows you to override message options such as from (even if you have a global default from set).

v4.0.0

See v5.0.0 above

v3.0.0

If you are upgrading from v2 or prior to v3, please note that the following breaking API changes occurred:

You need to have Node v6.4.0+, we recommend using nvm to manage your Node versions.

Instead of calling const newsletter = new EmailTemplate(...args), you now call const email = new Email(options).

  • The arguments you pass to the constructor have changed as well.
  • Previously you'd pass new EmailTemplate(templateDir, options). Now you will need to pass simply one object with a configuration as an argument to the constructor.
  • If your templateDir path is the "emails" folder in the root of your project (basically ./emails folder) then you do not need to pass it at all since it is the default per the configuration object.
  • The previous value for templateDir can be used as such:
  • Note that if you are inlining CSS, you should also make sure that the option for juiceResources.webResources.relativeTo is accurate.

Instead of calling newsletter.render(locals, callback) you now call email.render(template, locals). The return value of email.render when invoked is a Promise and does not accept a callback function.

NOTE: email-templates v3 now has an email.send method (see basic usage example) which uses nodemailer; you should now use email.send instead of email.render!

-newsletter.render({}, (err, result) => {
-  if (err) return console.error(err);
-  console.log(result);
-});
+email.render(template, {}).then(console.log).catch(console.error);

Localized template directories are no longer supported. We now support i18n translations out of the box. See Localization for more info.

A new method email.send has been added. This allows you to create a Nodemailer transport and send an email template all at once (it calls email.render internally). See the Basic usage documentation above for an example.

There are new options options.send and options.preview. Both are Boolean values and configured automatically based off the environment. Take a look at the configuration object. Note that you can optionally pass an Object to preview option, which gets passed along to open's options.

If you wish to send emails in development or test environment (disabled by default), set options.send to true.

Tip

Instead of having to configure this for yourself, you could just use Lad instead.

Related

Contributors

NameWebsite
Nick Baughhttp://niftylettuce.com

Author: Forwardemail
Source Code: https://github.com/forwardemail/email-templates 
License: MIT License

#node #css #templates #email 

Email-templates: Create, Preview, and Send Custom Email Templates