npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@stefanobalocco/jtdal

v8.0.0

Published

Small template engine based on Zope TAL, using data attributes

Readme

<div>
  Hello there, <span data-tdal-replace="name">world</span>
</div>
{
  "name": "General Kenobi"
}

jTDAL

Template Attribute Language for JavaScript

npm License GZipped size

Small template engine based on Zope TAL, using data attributes.

Runs on Node.js and in browsers. Written in TypeScript.

  • 0 dependencies
  • Less than 4 KB gzipped
  • Designer-friendly — templates work in any WYSIWYG editor or browser preview
  • No custom syntax to break HTML validation
  • Compile templates to optimized JavaScript functions

Why another template engine?

I wanted a fast, attribute-based JavaScript template engine and couldn't find one. Mustache is great, but its syntax always felt foreign to me. With data attributes, templates stay valid HTML — you can design them in any WYSIWYG editor or preview them in a browser without rendering data.

Changelog available at the end of the README.

Installation and Usage

Browser

<script type="module">
    import jTDAL from 'https://unpkg.com/@stefanobalocco/jtdal/dist/jTDAL.min.js';

    const templateEngine = new jTDAL();
    const template = '<span data-tdal-content="message">placeholder</span>';
    const render = templateEngine.CompileToFunction(template);
    const result = render({ message: "Hello World" });

    document.getElementById('result').innerHTML = result;
</script>

Node.js

Install via npm:

npm install @stefanobalocco/jtdal

Use it in your project:

import jTDAL from '@stefanobalocco/jtdal';

const templateEngine = new jTDAL();
const template = '<span data-tdal-content="message">placeholder</span>';
const render = templateEngine.CompileToFunction(template);
const result = render({ message: "Hello World" });

console.log(result);
// Output: <span>Hello World</span>

Constructor

constructor(trim = true, strip = true)
  • trim — Trim leading and trailing whitespace from the rendered output
  • strip — Remove HTML comments from the template before compilation

API

CompileToFunction(template)

Compiles a template string into a callable function that accepts a data object. Propagates template parse errors and any errors raised while constructing the generated function.

const render = templateEngine.CompileToFunction(template);
const html = render({ name: "Alice", items: [1, 2, 3] });

CompileToString(template)

Compiles a template string into a string containing the generated function code. Useful for precompiling templates and saving them as JavaScript files. Propagates template parse errors.

const code = templateEngine.CompileToString(template);
// Returns a string like: "function(d){...}"

Template Language

Path Expressions

A path expression identifies a value in the data object using forward-slash notation.

Given this data:

const data = {
  logged: true,
  user: { name: "Alice", permissions: { canEdit: false } }
};

Paths resolve as follows:

  • logged resolves to true
  • user/name resolves to "Alice"
  • user/permissions/canEdit resolves to false

Fallback Paths

Use the pipe | character to provide fallback values. The first truthy resolved value is used:

missing/path | fallback/path | last/resort

A present value that is falsy falls through to the next fallback. Values such as 0, false, "", null, undefined, and NaN therefore do not stop the chain.

MACRO: expressions do not accept | fallbacks.

Falsy Values

Whenever jTDAL evaluates an expression in a boolean context, it follows JavaScript truthiness and additionally treats empty arrays and empty objects as false. Boolean contexts include data-tdal-condition, data-tdal-omittag, boolean attributes, and ! negation.

All other values follow JavaScript truthiness. Empty arrays and objects are falsy only in boolean contexts; content and replace fallback evaluation uses JavaScript truthiness, so they do not select a fallback.

Path Modifiers

Boolean Negation: Prefix with ! to negate a boolean:

!isDisabled

Uppercase Keywords:

  • TRUE — Always returns true (stops fallback evaluation)
  • FALSE — Always returns false (stops fallback evaluation)
  • GLOBAL — Search in global context, bypassing local scopes (e.g. GLOBAL/variableName)
  • REPEAT — Search in the repeat context, bypassing local scopes (e.g. REPEAT/item/number)

Attributes

The template processor applies attributes in this order:

  1. data-tdal-condition — Conditional rendering
  2. data-tdal-repeat — Loop iteration
  3. data-tdal-content — Replace tag content
  4. data-tdal-replace — Replace entire tag
  5. data-tdal-attributes — Dynamic attributes
  6. data-tdal-omittag — Remove tag wrapper

data-tdal-condition

Renders the tag only if the expression evaluates to true; removes it otherwise. The expression can be a path with optional ! negation or a chain of fallbacks separated by |.

Examples:

<!-- Show if isLoggedIn is truthy -->
<div data-tdal-condition="isLoggedIn">Welcome back!</div>

<!-- Show if isGuest is falsy -->
<div data-tdal-condition="!isGuest">Member content</div>

<!-- Check nested path with fallback -->
<div data-tdal-condition="user/permissions/canView | config/defaultVisible">
  Content
</div>

<!-- Always show -->
<div data-tdal-condition="TRUE">Always visible</div>

data-tdal-repeat

Repeats the element for each item in an array or each key in an object. Expects two arguments separated by a space: a name for the loop variable and a path to the collection. A REPEAT object is available to child expressions during iteration.

Syntax:

<li data-tdal-repeat="variable collection/path">...</li>

The REPEAT object provides:

  • index — Current index or object key
  • number — Iteration count, starting from 1
  • even — True on even iterations (2, 4, 6...)
  • odd — True on odd iterations (1, 3, 5...)
  • first — True for the first element
  • last — True for the last element
  • length — Total number of items

Examples:

<!-- Simple array iteration -->
<ul>
  <li data-tdal-repeat="item items" data-tdal-content="item">Item</li>
</ul>

<!-- Object iteration -->
<div data-tdal-repeat="user users">
  <h3 data-tdal-content="user/name">Name</h3>
  <p data-tdal-content="user/email">Email</p>
</div>

<!-- Using REPEAT variables -->
<tr data-tdal-repeat="row rows" data-tdal-attributes="data-index REPEAT/row/index">
  <td data-tdal-content="REPEAT/row/number">Index</td>
  <td data-tdal-content="row/value">Value</td>
</tr>

<!-- Nested repeats -->
<div data-tdal-repeat="category categories">
  <h2 data-tdal-content="category/name">Category</h2>
  <ul>
    <li data-tdal-repeat="product category/products" data-tdal-content="product/name">Product</li>
  </ul>
</div>

data-tdal-content

Replaces the tag's inner content with the result of the expression. The expression can be a path with optional ! negation or a MACRO: reference (see Fallback Paths). Prefix with structure to insert raw HTML without escaping. If the expression evaluates to false, the content is removed.

Examples:

<!-- Simple content replacement -->
<span data-tdal-content="username">Default username</span>

<!-- With fallback -->
<div data-tdal-content="errorMessage | defaultMessage">Error</div>

<!-- Using a macro -->
<div data-tdal-content="MACRO:userCard">User info</div>

<!-- HTML content (not escaped) -->
<div data-tdal-content="structure richTextContent">Default</div>

data-tdal-replace

Replaces the entire tag and its contents with the result of the expression. Accepts the same expressions as data-tdal-content. If the expression evaluates to false, the tag and its contents are removed.

Examples:

<!-- Replace tag with value -->
<span data-tdal-replace="status">Status</span>

<!-- Using a macro -->
<div data-tdal-replace="MACRO:navigationMenu">Nav</div>

<!-- HTML content (not escaped) -->
<div data-tdal-replace="structure htmlContent">Placeholder</div>

data-tdal-attributes

Sets or modifies tag attributes. Each pair follows the format attribute expression, separated by ;;. Expressions can be a path with optional ! negation and | fallbacks. Append ? to the attribute name for HTML5 boolean attributes: present when truthy, absent when falsy.

Examples:

<!-- Dynamic single attribute -->
<a href="#" data-tdal-attributes="href user/profileUrl">Profile</a>

<!-- Multiple attributes -->
<a data-tdal-attributes="href link/url;;class link/class">Link</a>

<!-- Boolean flag attributes -->
<input type="checkbox" data-tdal-attributes="checked? user/isActive" />
<button data-tdal-attributes="disabled? !canSubmit">Submit</button>
<option data-tdal-attributes="selected? isDefault">Default</option>

<!-- Mixed attributes -->
<input data-tdal-attributes="type inputType;;checked? isEnabled;;disabled? isLocked" />

<!-- Fallback to preserve existing value -->
<img src="default.jpg" data-tdal-attributes="src dynamicUrl | TRUE" />

data-tdal-omittag

Removes the tag wrapper while keeping its content when the expression evaluates to true. The expression can be a path with optional ! negation or a chain of fallbacks separated by |.

Examples:

<!-- Always remove the tag -->
<span data-tdal-omittag="TRUE">Content</span>
<!-- Result: Content -->

<!-- Never remove the tag -->
<span data-tdal-omittag="FALSE">Content</span>
<!-- Result: <span>Content</span> -->

<!-- Remove tag if variable is truthy -->
<span data-tdal-omittag="hideWrapper">Content</span>

<!-- Remove tag if variable is falsy -->
<span data-tdal-omittag="!showWrapper">Content</span>

Macros

Macros are reusable template fragments (equivalent to TAL's METAL or Mustache partials). Register them with MacroAdd, then reference them using MACRO:name in data-tdal-content or data-tdal-replace.

Register a Macro

const templateEngine = new jTDAL();
const macro = `Hello, <span data-tdal-replace="name"></span>!`;
templateEngine.MacroAdd("helloworld", macro);

MacroAdd returns no value. An invalid macro name throws MacroAdd: Invalid macro name; a template parse failure is propagated. A macro whose registration fails is not registered and does not replace an existing macro of the same name.

Use in Content

Replaces the tag's inner content with the rendered macro:

<span data-tdal-content="MACRO:helloworld">Placeholder</span>
<span data-tdal-content="structure MACRO:helloworld">Placeholder</span>

Use in Replace

Replaces the entire tag with the rendered macro:

<div data-tdal-replace="MACRO:helloworld">Placeholder</div>
<div data-tdal-replace="structure MACRO:helloworld">Placeholder</div>

Macro Context

Macros inherit the data context of the calling template:

<div data-tdal-repeat="name names"><div data-tdal-replace="MACRO:helloworld">Hello, Macro!</div></div>

In this example, the helloworld macro reads name within each loop iteration. With names: ["Alice", "Bob"], this renders:

<div>Hello, Alice!</div><div>Hello, Bob!</div>

Changelog

v8.x

  • Removed STRING: expression support.
  • Switched generated templates from string concatenation to template literals.
  • Reworked repeat code generation.

v7.x

  • Replaced recursive parsing with a streaming stack-based parser.
  • Added stricter validation and template, script, and comment handling.

v6.x

  • Reorganized the project into src, dist, and tests.
  • Replaced the custom build script with tsBuild.

v5.x

  • Moved trim and strip configuration to the constructor.
  • Removed trim and strip parameters from compile and macro methods.

v4.x

  • Replaced the static namespace API with jTDAL instances.
  • Added MACRO: expressions and MacroAdd.

v3.x

  • Moved the package to ESM.

v2.x

  • Removed the jQuery dependency.
  • Added compiled-template APIs.

v1.x

  • Initial jQuery-based release.