Tagscript
Getting Started

Getting started

Install TagScript, render your first template, and understand what the interpreter will and will not do.

TagScript is a template language for the case where the person writing the template is not the person who wrote the app. A Discord server admin building a custom command. A user customising their profile. A support team editing an auto-reply.

You cannot hand those people a JavaScript template literal, Handlebars or EJS, because all of them assume the template author is trusted. TagScript assumes the opposite.

Install

npm install tagscript

No runtime dependencies. ESM, CJS and an IIFE build with the global TagScript.

Your first template

import { Interpreter, RandomParser } from 'tagscript';

const ts = new Interpreter(new RandomParser());

const response = await ts.run('{random:heads,tails}');
response.body; // 'tails'

Now try a tag you did not register:

(await ts.run('{if(1==1):yes|no}')).body; // '{if(1==1):yes|no}'

The interpreter has no built-in tags. A fresh new Interpreter() renders plain text and leaves every {tag} exactly as written. What a template can do is precisely the list of parsers you pass in, and nothing else. There is no host object to reach, no prototype to walk, no require to find.

An unknown tag is not an error and not a crash. It stays in the output as literal text, so a typo shows up instead of vanishing.

Adding data

Templates read values through transformers, which you seed per render. A transformer answers with the keys it chooses to expose, so seeding one never hands the template the object underneath.

import { Interpreter, StrictVarsParser, StringTransformer } from 'tagscript';

const ts = new Interpreter(new StrictVarsParser());

const response = await ts.run('Hi {user}, your surname is {user(2)}', {
	user: new StringTransformer('Parbez Barbhuiya'),
});

response.body; // 'Hi Parbez Barbhuiya, your surname is Barbhuiya'

Something closer to real

import { DefineParser, IfStatementParser, Interpreter, RangeParser, StrictVarsParser } from 'tagscript';

const ts = new Interpreter(new StrictVarsParser(), new DefineParser(), new RangeParser(), new IfStatementParser());

const response = await ts.run('{=(roll):{range:1-6}}You rolled {roll}. {if({roll}==6):Nice!|Try again.}', {}, 500);

response.body; // 'You rolled 6. Nice!'

The 500 is a character limit. Set one on any template you did not write yourself.

charLimit defaults to null, which means unlimited. A template that expands cheaply into a huge string is the one denial of service a template author can still cause, so pass a limit whenever the author is untrusted. See running templates.

Where to go next

  • Syntax is the page to hand to whoever writes your templates.
  • Running templates covers run options, the Response object and limits.
  • Parsers lists every built-in tag and shows how to write your own.
  • Transformers covers exposing data safely.
  • The Discord plugin adds embeds, cooldowns and permissions.

Special thanks

JonSnowbd wrote TagScript in Python. This project is a TypeScript reimagining of it.

Last updated on

On this page

Edit on Github