Skip to content

GreenFlag31/node-tao

Repository files navigation

Node Tao

node-tao template engine representation

TAO is a simple, lightweight and very fast embedded JS templating. It emphasizes great performance, security, and developer experience.

🌟 Features

  • πŸš€ Super Fast
  • πŸ”§ Configurable
  • πŸ”₯ Caching
  • ⚑️ Support for partials
  • πŸ“ Easy template syntax (no prefix needed)
  • πŸ’» Developer experience
  • 🧩 Support for local and global helpers
  • πŸ›‘οΈ Security by design
  • 🟦 Official VS Code Extension

Get Started

Define a template simple.html inside a view directory templates

<!-- templates/simple.html -->
<h1>Hi <%= name %>!</h1>
import { Tao } from 'tao';

const tao = new Tao({ views: path.join(__dirname, 'templates') });

const res = tao.render('simple', { name: 'Tao' });
console.log(res); // <h1>Hi Tao!</h1>

Helpers

Helpers are functions that can be used inside a template. Helpers can be local (only available in a particular render) or global (available everywhere on the instance).

import { Tao } from 'tao';

const tao = new Tao({ views: path.join(__dirname, 'templates') });

// Global helper
function nPlusTwo(n: number) {
  return n + 2;
}
// Global helper need to be registered on the instance
tao.defineHelpers({ nPlusTwo });

// Render a template
app.get('/', (req, res) => {
  // Local helper
  function nPlusOne(n: number) {
    return n + 1;
  }

  const res = tao.render('simple', { name: 'Ben' }, { nPlusOne });
  console.log(res); // <h1>Hi Ben!</h1>
});

Usage:

// simple.html
<%= nPlusOne(1) %>

NB: Always escape (=) the output of a helper function when it includes user-controlled data.

It is also possible to register helpers on globalThis without providing them to the template engine, but it can lead to name collision.

Include

In your template, you might want to include other templates:

<h1>Hi <%= name %>!</h1>
<!-- include "article" template and provide data -->
<%~ include('article', { phone: 'Tao T9' }) %>

Child components will inherit from data and helpers provided in the parent component.

Template prefix options

There are three differents template prefixes:

<!-- Evaluation (''): no escape (ideal for js execution) -->
<% const age = 33; %>
<!-- Interpolation (=): escaping (ideal for data interpolation) -->
<p><%= `Age: ${age}` %></p>
<!-- Raw (~): no escape (ideal for HTML inclusion) -->
<%~ include('product') %>

NB: Those prefix are configurable in the options.

Template paths resolution

TAO will recursively add all templates matching the containing views path definition

import { Tao } from 'tao';

const tao = new Tao({ views: path.join(__dirname, 'templates') });

...such that following structure is ok :

| /templates
|   - simple.html         βœ”οΈ
|   /products
|     - article.html      βœ”οΈ
|     /...
|       - nested.html     βœ”οΈ

By default, fileResolution is set to flexible, which means that you can just provide the unique end of the path:

const res = tao.render('nested'); // accepted βœ”οΈ
const res = tao.render('products/.../nested'); // not necessary ❌

TAO will successfully identify the nested templates without providing the subfolder(s).

Programmatically defined templates

You might want to define programmatically templates:

const headerPartial = `
  <header>
    <h1><%= title %></h1>
  </header>
`;

tao.loadTemplate('@header', headerPartial);
const rendered = tao.render('@header', { title: 'Computer shop' });

Cache Storage

TAO uses cache stores to manage caching. You might want to interact with those stores to retrieve or delete an entry:

tao.helpersStore.remove('myHelperFn');

Security by design

By default, TAO assume you are running your app in production, so no error will be thrown, such that error stack traces are not visible in your browser. Errors will be displayed in your editor console, and visual error representation (see developer experience) is available in your browser by setting debug: true at option initialisation.

🟦 Official VS Code Extension

VS Code Marketplace

Developer experience

All methods, properties are correctly typed and documented, so you should get help from your editor.

In case of an error, a visual representation is available in your browser, giving you all the details and the precise line of the error (if available).

Error representation

NB: set debug: true to activate this option. Do not activate this option in production.

Metrics are also available, so you get usefull informations about the template rendering time, cache hit, mapped templates, etc. in your browser console.

Metrics

If you want to inspect the data provided to the template, it is available directly in your browser console under the object data.

Data

NB: set metrics: true to activate this option. Do not activate this option in production.

FAQs

Some words about this library

It started as a fork of eta, but became a dedicated library because the changes made were too significant. Some parts are still based on eta, especially the template parsing, and if you know eta, the API will be familiar.

If you want to compare tao with eta
  • Tao set security by design: Stack traces are not visible in the browser. Increased security in files mapping.
  • Increased developer experience: Visual error representation, metrics, configuration options are checked.
  • Immutability: Data provided in the template is immutable, ie. template data modification does not affect original data.
  • Clearer API: Scope is well defined and restricted, which also improves security. Clean code practices are enforced.
  • Clearer template syntax: No prefix are needed.
  • Helpers: Global and local helpers, which are clearer and more suitable for little template logic.
  • Flexible template path resolution: With fileResolution mode set to flexible, only end unique paths can be provided, which increases file path readability (aka. namespaces).
  • Performance: Various performance optimization.
Choices
  • No async support: Supporting async rendering (e.g., await include) within templates encourages placing too much logic in the view layer and can be considered as an anti-pattern. Templates should be responsible for displaying data, while controllers should handle logic. Async behavior in templates would also require error handling (e.g., try/catch), adding complexity and possible errors. Async logic in template make it impossible de optimize through a Promise.all or any parallelism, hard to test and debug. In short: if you have async data, fetch it beforehand and render it synchronously.

  • No layouts: Layouts are essentially includes and add unnecessary complexity to the rendering process.

  • No rmWhitespace: Stripping whitespace at the template level yields negligible HTML size savings. Using compression (e.g., via Nginx or other proxies) is far more effective and scalable.

If you think those features are absolutely necessary, please open a new discussion on github and provide an example.


Change logs

V0.0.2: Fix node modules exclusion in files matching for the error template.

V0.0.3: Data exchange for the official extension, and various improvements.

Credits

  • Syntax and some parts of compilation are based on eta.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published