forge-templates
v1.3.1
Published
A package for rendering dynamic HTML templates with embedded server-side JavaScript without using eval.
Maintainers
Readme
Forge Templates
Forge Templates is package for rendering dynamic HTML templates with embedded server-side JavaScript.
The templates fully support JavaScript by being embedded into a script at build time. You build the templates into a
script with the forge-templates CLI or using ForgeTemplates.build(....) in your own build script.
Features
- Full JavaScript support in the templates, without using
eval,new Functionor similar functions - Template inheritance, a template can extend a base template and replace its defined content blocks (see Template Inheritance)
- Helper functions make it easy to render an array of data, or execute arbitrary JavaScript in a template (see Helper Functions)
- Building can be done with the CLI, or programmatically with the JavaScript API (see Building the Templates)
- After building, it requires no Node.js or Web APIs, so it's compatible with all execution environments
Sections
Example
Template file: src/templates/names.tmpl.html
<html lang="en">
<head>
<title>${data.title}</title>
</head>
<body>
<h1>${data.title}</h1>
<p>${data.description}</p>
<ul>
${forEach(data.names, (name, index) => `
<li>Name ${index}: ${name}</li>
`)}
</ul>
</body>
</html>Build the templates: CLI
forge-templates src/templates build/forge-build.js --minify-htmlRendering: index.js
import { renderTemplate } from "build/forge-build.js";
const renderedHTML = renderTemplate("names", {
title: "The Names",
description: "This page contains a dynamically generated list of names!",
names: ["Mary", "John", "Kath", "George"]
});
console.log(renderedHTML);
/* Output:
<html lang="en">
<head>
<title>The Names</title>
</head>
<body>
<h1>The Names</h1>
<p>This page contains a dynamically generated list of names!</p>
<ul>
<li>Name 0: Mary</li>
<li>Name 1: John</li>
<li>Name 2: Kath</li>
<li>Name 3: George</li>
</ul>
</body>
</html>
(Whitespace has been manually added for readability)
*/Template Format
A template is normal HTML, but with embedded JavaScript that lives in ${}, in the same way that
template literals work in
JavaScript. A template is required to have .tmpl.html as file extension. Normal .html files will not be recognised
as templates during the build stage.
Passing Data
In a template there is a global variable data exposed. This is exactly what you pass in the
renderTemplate("<name>", data) function. In general, a simple object with strings or other basic data types is
recommended, however, this is not strictly enforced.
Template Inheritance
It is not uncommon to have shared elements between pages, for example a navbar or footer. This is easily done by
extending a base template. A key element to understand inheritance are content blocks. A content block starts with a
name and ends at the end tag. The start of a block has the format ${"{{ <NAME> }}"} where <NAME> is the name of
the content block. The end tag of a content block is ${"{{ end }}"}. Whitespace between the tag identifiers is
optional. An example of a base template with content blocks is below.
base-template.tmpl.html
<html lang="en">
<head>
${"{{ html_head }}"}${"{{ end }}"}
</head>
<body>
${"{{ body }}"}
<p>This will be removed if a template extends this one and overrides the "body" content block</p>
${"{{ end }}"}
</body>
</html>To extend this template you add ${"{{ extend <NAME> }}"}. The template above may be extended like so.
contact.tmpl.html
${"{{ extend base-template }}"}
${"{{ html_head }}"}
<link rel="stylesheet" href="/assets/contact.css">
${"{{ end }}"}
${"{{ body }}"}
<p>Contact us at [email protected]</p>
${"{{ end }}"}Rendering the contact template would result into the following output.
<html lang="en">
<head>
<link rel="stylesheet" href="/assets/contact.css">
</head>
<body>
<p>Contact us at [email protected]</p>
</body>
</html>Helper Functions
There are a few helpful utility functions available in templates. They are quickly explained below. The implementation
of these functions can be found in src/output/html-template/helper-functions.js.
forEach(array: any[], callback: Function): string
This function loops through the given array and executes the given callback for every item. The callback is called with three parameters, the item, index of the item, and the array itself.
<ul>
${forEach(data.someArray, (itemOfArray, index, theArray) => `
<li>The item is: ${itemOfArray}</li>
`)}
</ul>renderIf(condition: boolean, body1: string, body2?: string): string
This is a simple shortcut to have content rendered or not based on a condition. It is an if-statement. It renders body1 if the condition is true, it renders body2 if the condition is false. The body2 is optional, you don't have to provide it.
<div>
${renderIf(data.success, `
<p>It was all successful</p>
`, `
<p>There was an error</p>
`)}
</div>execute(func: Function): string
execute is a function designed to allow for arbitrary JavaScript execution in the
template. The given callback is executed during rendering and the given echo function in the callback can be used
to have HTML output.
<ul>
${execute(echo => {
for(let i = 0; i < 5; i++) {
echo(`<li>Random number ${i+1}: ${Math.random()}</li>`);
}
// If the callback returns a string, it will also be rendered in the HTML (optional)
return `<li>This is the end of the list!</li>`
})}
</ul>Building the Templates
To be able to access the renderTemplate function, you have to build the templates into a single script. There are two
ways you can build the templates. You can either use the CLI or the JavaScript API.
Command Line Interface (CLI)
Building the templates through the CLI is easy. After installing this package, the forge-templates should automatically
be available in your terminal. Use the CLI as follows.
Usage: forge-templates <folder> <file> [options...]
Parameters:
folder The folder with your template files
file Location to save the built script to, a JavaScript file
Options:
--minify-html Enables a basic HTML minifier to make the output script smaller
--only-errors CLI only; Print errors but don't print success messagesJavaScript API
To assist with more automated ways of building the templates, you can also build by calling a function in JavaScript. An example of this is below.
import ForgeTemplates from "forge-templates";
// Options are optional
const options = {
minify_html: true
}
ForgeTemplates.build("<folder>", "<file>", options);